nbuild_core/models/
nix.rs

1//! This model is used to create / print a nix derivation.
2
3use std::{cell::RefCell, fs, rc::Rc};
4
5use cargo_metadata::{camino::Utf8PathBuf, semver::Version};
6
7use super::Source;
8
9/// A package for a nix [buildRustCrate] block.
10///
11/// [buildRustCrate]: https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/rust.section.md#buildrustcrate-compiling-rust-crates-using-nix-instead-of-cargo-compiling-rust-crates-using-nix-instead-of-cargo
12#[derive(Debug, PartialEq)]
13pub struct Package {
14    pub(super) name: String,
15    pub(super) version: Version,
16    pub(super) source: Source,
17    pub(super) lib_name: Option<String>,
18    pub(super) lib_path: Option<Utf8PathBuf>,
19    pub(super) build_path: Option<Utf8PathBuf>,
20    pub(super) proc_macro: bool,
21    pub(super) features: Vec<String>,
22    pub(super) dependencies: Vec<Dependency>,
23    pub(super) build_dependencies: Vec<Dependency>,
24    pub(super) edition: String,
25    pub(super) printed: bool,
26}
27
28/// Used to keep track of the dependencies of a package and whether they have any renames.
29#[derive(Debug, PartialEq)]
30pub struct Dependency {
31    pub(super) package: Rc<RefCell<Package>>,
32    pub(super) rename: Option<String>,
33}
34
35impl Package {
36    /// Write the package to a derivation file at `.nbuild.nix`
37    pub fn into_file(self) -> Result<(), std::io::Error> {
38        let expr = self.into_derivative();
39
40        fs::write(".nbuild.nix", expr)
41    }
42
43    /// The name of the package
44    pub fn name(&self) -> &str {
45        &self.name
46    }
47
48    /// Turn the package into a derivation string.
49    pub fn into_derivative(self) -> String {
50        let Self {
51            name,
52            version,
53            source,
54            lib_name: _,
55            lib_path: _,
56            build_path: _,
57            proc_macro: _,
58            features: _,
59            dependencies,
60            build_dependencies,
61            edition,
62            printed: _,
63        } = self;
64
65        // Used to append all the dependency details unto
66        let mut build_details = Default::default();
67
68        let dep_idents: Vec<_> = dependencies
69            .into_iter()
70            .map(|d| {
71                let identifier = d.package.borrow().identifier();
72                Self::to_details(&d, &mut build_details);
73                identifier
74            })
75            .collect();
76
77        let build_deps = if build_dependencies.is_empty() {
78            Default::default()
79        } else {
80            let dep_idents: Vec<_> = build_dependencies
81                .into_iter()
82                .map(|d| {
83                    let identifier = d.package.borrow().identifier();
84                    Self::to_details(&d, &mut build_details);
85                    identifier
86                })
87                .collect();
88            format!("\n    buildDependencies = [{}];", dep_idents.join(" "))
89        };
90
91        format!(
92            r#"{{ pkgs ? import <nixpkgs> {{
93  overlays = [ (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz")) ];
94}} }}:
95
96let
97  sourceFilter = name: type:
98    let
99      baseName = builtins.baseNameOf (builtins.toString name);
100    in
101      ! (
102        # Filter out git
103        baseName == ".gitignore"
104        || (type == "directory" && baseName == ".git")
105
106        # Filter out build results
107        || (
108          type == "directory" && baseName == "target"
109        )
110
111        # Filter out nix-build result symlinks
112        || (
113          type == "symlink" && pkgs.lib.hasPrefix "result" baseName
114        )
115      );
116  rustVersion = pkgs.rust-bin.stable."1.68.0".default;
117  defaultCrateOverrides = pkgs.defaultCrateOverrides // {{
118    opentelemetry-proto = attrs: {{ buildInputs = [ pkgs.protobuf ]; }};
119  }};
120  fetchCrate = {{ crateName, version, sha256 }}: pkgs.fetchurl {{
121    # https://www.pietroalbini.org/blog/downloading-crates-io/
122    # Not rate-limited, CDN URL.
123    name = "${{crateName}}-${{version}}.tar.gz";
124    url = "https://static.crates.io/crates/${{crateName}}/${{crateName}}-${{version}}.crate";
125    inherit sha256;
126  }};
127  buildRustCrate = pkgs.buildRustCrate.override {{
128    rustc = rustVersion;
129    inherit defaultCrateOverrides fetchCrate;
130  }};
131  preBuild = "rustc -vV";
132
133  # Core
134  {} = buildRustCrate rec {{
135    crateName = "{}";
136    version = "{}";
137
138    {}
139
140    dependencies = [
141      {}
142    ];{}
143    edition = "{}";
144    codegenUnits = 16;
145    extraRustcOpts = [ "-C embed-bitcode=no" ];
146    inherit preBuild;
147  }};
148
149  # Dependencies
150{}
151in
152{}
153"#,
154            name,
155            name,
156            version,
157            Self::get_source(&source),
158            dep_idents.join("\n      "),
159            build_deps,
160            edition,
161            build_details.join("\n"),
162            name
163        )
164    }
165
166    /// Recursively add a dependency unto `details`
167    fn to_details(dependency: &Dependency, build_details: &mut Vec<String>) {
168        let mut this = dependency.package.borrow_mut();
169
170        // Only print once
171        if this.printed {
172            return;
173        }
174
175        let features = if this.features.is_empty() {
176            Default::default()
177        } else {
178            format!(
179                "\n    features = [{}];",
180                this.features
181                    .iter()
182                    .map(|f| format!("\"{f}\""))
183                    .collect::<Vec<_>>()
184                    .join(" ")
185            )
186        };
187
188        let lib_name = if let Some(lib_name) = &this.lib_name {
189            format!("\n    libName = \"{lib_name}\";")
190        } else {
191            Default::default()
192        };
193        let lib_path = if let Some(lib_path) = &this.lib_path {
194            format!("\n    libPath = \"{lib_path}\";")
195        } else {
196            Default::default()
197        };
198        let build_path = if let Some(build_path) = &this.build_path {
199            format!("\n    build = \"{build_path}\";")
200        } else {
201            Default::default()
202        };
203        let proc_macro = if this.proc_macro {
204            "\n    procMacro = true;"
205        } else {
206            Default::default()
207        };
208
209        let mut renames = Vec::new();
210
211        let deps = if this.dependencies.is_empty() {
212            Default::default()
213        } else {
214            let dep_idents: Vec<_> = this
215                .dependencies
216                .iter()
217                .map(|d| {
218                    if let Some(rename) = &d.rename {
219                        renames.push((
220                            d.package.borrow().name.clone(),
221                            rename.clone(),
222                            d.package.borrow().version.to_string(),
223                        ));
224                    }
225
226                    d.package.borrow().identifier()
227                })
228                .collect();
229            format!("\n    dependencies = [{}];", dep_idents.join(" "))
230        };
231        let build_deps = if this.build_dependencies.is_empty() {
232            Default::default()
233        } else {
234            let dep_idents: Vec<_> = this
235                .build_dependencies
236                .iter()
237                .map(|d| {
238                    if let Some(rename) = &d.rename {
239                        renames.push((
240                            d.package.borrow().name.clone(),
241                            rename.clone(),
242                            d.package.borrow().version.to_string(),
243                        ));
244                    }
245
246                    d.package.borrow().identifier()
247                })
248                .collect();
249            format!("\n    buildDependencies = [{}];", dep_idents.join(" "))
250        };
251
252        let crate_renames = if renames.is_empty() {
253            Default::default()
254        } else {
255            let renames = renames
256                .into_iter()
257                .map(|(name, rename, version)| {
258                    format!("\"{name}\" = [{{ rename = \"{rename}\"; version = \"{version}\"; }}];")
259                })
260                .collect::<Vec<_>>()
261                .join(" ");
262
263            format!("\n    crateRenames = {{{renames}}};")
264        };
265
266        let details = format!(
267            r#"  {} = buildRustCrate rec {{
268    crateName = "{}";{}
269    version = "{}";
270
271    {}{}{}{}{}{}{}{}
272    edition = "{}";
273    crateBin = [];
274    codegenUnits = 16;
275    extraRustcOpts = [ "-C embed-bitcode=no" ];
276    inherit preBuild;
277  }};"#,
278            this.identifier(),
279            this.name,
280            lib_name,
281            this.version,
282            Self::get_source(&this.source),
283            lib_path,
284            build_path,
285            proc_macro,
286            deps,
287            build_deps,
288            crate_renames,
289            features,
290            this.edition,
291        );
292
293        build_details.push(details);
294
295        for dependency in this
296            .dependencies
297            .iter()
298            .chain(this.build_dependencies.iter())
299        {
300            Self::to_details(dependency, build_details);
301        }
302
303        this.printed = true;
304    }
305
306    /// Helper to get a deterministic identifier for a package
307    fn identifier(&self) -> String {
308        format!(
309            "{}_{}",
310            self.name,
311            self.version.to_string().replace(['.', '+'], "_")
312        )
313    }
314
315    /// Helper to get the source definition
316    fn get_source(source: &Source) -> String {
317        match source {
318            Source::Local(path) => format!(
319                "src = pkgs.lib.cleanSourceWith {{ filter = sourceFilter;  src = {}; }};",
320                path.display()
321            ),
322            Source::CratesIo(sha256) => format!("sha256 = \"{sha256}\";"),
323        }
324    }
325}
326
327#[cfg(test)]
328mod tests {
329    use std::{path::PathBuf, str::FromStr};
330
331    use super::*;
332
333    use pretty_assertions::assert_eq;
334
335    impl From<Package> for Dependency {
336        fn from(package: Package) -> Self {
337            Self {
338                package: Rc::new(RefCell::new(package)),
339                rename: None,
340            }
341        }
342    }
343
344    impl From<PathBuf> for Source {
345        fn from(path: PathBuf) -> Self {
346            Self::Local(path)
347        }
348    }
349
350    impl From<&str> for Source {
351        fn from(sha: &str) -> Self {
352            Self::CratesIo(sha.to_string())
353        }
354    }
355
356    #[test]
357    fn simple_package() {
358        let package = Package {
359            name: "simple".to_string(),
360            version: "0.1.0".parse().unwrap(),
361            source: PathBuf::from_str("/cargo-nbuild/nbuild-core/tests/simple")
362                .unwrap()
363                .into(),
364            lib_name: None,
365            lib_path: None,
366            build_path: None,
367            proc_macro: false,
368            dependencies: vec![Package {
369                name: "itoa".to_string(),
370                version: "1.0.6".parse().unwrap(),
371                source: "itoa_sha".into(),
372                lib_name: None,
373                lib_path: None,
374                build_path: None,
375                proc_macro: false,
376                dependencies: Default::default(),
377                build_dependencies: Default::default(),
378                features: Default::default(),
379                edition: "2018".to_string(),
380                printed: false,
381            }
382            .into()],
383            build_dependencies: vec![Package {
384                name: "arbitrary".to_string(),
385                version: "1.3.0".parse().unwrap(),
386                source: "arbitrary_sha".into(),
387                lib_name: None,
388                lib_path: None,
389                build_path: None,
390                proc_macro: false,
391                dependencies: Default::default(),
392                build_dependencies: Default::default(),
393                features: Default::default(),
394                edition: "2018".to_string(),
395                printed: false,
396            }
397            .into()],
398            features: Default::default(),
399            edition: "2021".to_string(),
400            printed: false,
401        };
402
403        let actual = package.into_derivative();
404
405        assert_eq!(
406            actual,
407            r#"{ pkgs ? import <nixpkgs> {
408  overlays = [ (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz")) ];
409} }:
410
411let
412  sourceFilter = name: type:
413    let
414      baseName = builtins.baseNameOf (builtins.toString name);
415    in
416      ! (
417        # Filter out git
418        baseName == ".gitignore"
419        || (type == "directory" && baseName == ".git")
420
421        # Filter out build results
422        || (
423          type == "directory" && baseName == "target"
424        )
425
426        # Filter out nix-build result symlinks
427        || (
428          type == "symlink" && pkgs.lib.hasPrefix "result" baseName
429        )
430      );
431  rustVersion = pkgs.rust-bin.stable."1.68.0".default;
432  defaultCrateOverrides = pkgs.defaultCrateOverrides // {
433    opentelemetry-proto = attrs: { buildInputs = [ pkgs.protobuf ]; };
434  };
435  fetchCrate = { crateName, version, sha256 }: pkgs.fetchurl {
436    # https://www.pietroalbini.org/blog/downloading-crates-io/
437    # Not rate-limited, CDN URL.
438    name = "${crateName}-${version}.tar.gz";
439    url = "https://static.crates.io/crates/${crateName}/${crateName}-${version}.crate";
440    inherit sha256;
441  };
442  buildRustCrate = pkgs.buildRustCrate.override {
443    rustc = rustVersion;
444    inherit defaultCrateOverrides fetchCrate;
445  };
446  preBuild = "rustc -vV";
447
448  # Core
449  simple = buildRustCrate rec {
450    crateName = "simple";
451    version = "0.1.0";
452
453    src = pkgs.lib.cleanSourceWith { filter = sourceFilter;  src = /cargo-nbuild/nbuild-core/tests/simple; };
454
455    dependencies = [
456      itoa_1_0_6
457    ];
458    buildDependencies = [arbitrary_1_3_0];
459    edition = "2021";
460    codegenUnits = 16;
461    extraRustcOpts = [ "-C embed-bitcode=no" ];
462    inherit preBuild;
463  };
464
465  # Dependencies
466  itoa_1_0_6 = buildRustCrate rec {
467    crateName = "itoa";
468    version = "1.0.6";
469
470    sha256 = "itoa_sha";
471    edition = "2018";
472    crateBin = [];
473    codegenUnits = 16;
474    extraRustcOpts = [ "-C embed-bitcode=no" ];
475    inherit preBuild;
476  };
477  arbitrary_1_3_0 = buildRustCrate rec {
478    crateName = "arbitrary";
479    version = "1.3.0";
480
481    sha256 = "arbitrary_sha";
482    edition = "2018";
483    crateBin = [];
484    codegenUnits = 16;
485    extraRustcOpts = [ "-C embed-bitcode=no" ];
486    inherit preBuild;
487  };
488in
489simple
490"#
491        );
492    }
493
494    #[test]
495    fn workspace() {
496        let base = PathBuf::from_str("/cargo-nbuild/nbuild-core/tests/workspace").unwrap();
497
498        let libc = RefCell::new(Package {
499            name: "libc".to_string(),
500            version: "0.2.144".parse().unwrap(),
501            source: "sha".into(),
502            lib_name: None,
503            lib_path: None,
504            build_path: None,
505            proc_macro: false,
506            dependencies: Default::default(),
507            build_dependencies: Default::default(),
508            features: Default::default(),
509            edition: "2015".to_string(),
510            printed: false,
511        })
512        .into();
513
514        let package = Package {
515            name: "parent".to_string(),
516            version: "0.1.0".parse().unwrap(),
517            source: base.join("parent").into(),
518            lib_name: None,
519            lib_path: None,
520            build_path: None,
521            proc_macro: false,
522            dependencies: vec![
523                Package {
524                    name: "child".to_string(),
525                    version: "0.1.0".parse().unwrap(),
526                    source: base.join("child").into(),
527                    lib_name: None,
528                    lib_path: None,
529                    build_path: None,
530                    proc_macro: false,
531                    dependencies: vec![
532                        Package {
533                            name: "fnv".to_string(),
534                            version: "1.0.7".parse().unwrap(),
535                            source: "sha".into(),
536                            lib_name: None,
537                            lib_path: Some("lib.rs".into()),
538                            build_path: None,
539                            proc_macro: false,
540                            dependencies: Default::default(),
541                            build_dependencies: Default::default(),
542                            features: Default::default(),
543                            edition: "2015".to_string(),
544                            printed: false,
545                        }
546                        .into(),
547                        Package {
548                            name: "itoa".to_string(),
549                            version: "1.0.6".parse().unwrap(),
550                            source: "sha".into(),
551                            lib_name: None,
552                            lib_path: None,
553                            build_path: None,
554                            proc_macro: false,
555                            dependencies: Default::default(),
556                            build_dependencies: Default::default(),
557                            features: Default::default(),
558                            edition: "2018".to_string(),
559                            printed: false,
560                        }
561                        .into(),
562                        Dependency {
563                            package: Rc::clone(&libc),
564                            rename: None,
565                        },
566                        Dependency {
567                            package: RefCell::new(Package {
568                                name: "rename".to_string(),
569                                version: "0.1.0".parse().unwrap(),
570                                source: base.join("rename").into(),
571                                lib_name: Some("lib_rename".to_string()),
572                                lib_path: None,
573                                build_path: None,
574                                proc_macro: false,
575                                dependencies: Default::default(),
576                                build_dependencies: Default::default(),
577                                features: Default::default(),
578                                edition: "2021".to_string(),
579                                printed: false,
580                            })
581                            .into(),
582                            rename: Some("new_name".to_string()),
583                        },
584                        Package {
585                            name: "rustversion".to_string(),
586                            version: "1.0.12".parse().unwrap(),
587                            source: "sha".into(),
588                            lib_name: None,
589                            lib_path: None,
590                            build_path: Some("build/build.rs".into()),
591                            proc_macro: true,
592                            dependencies: Default::default(),
593                            build_dependencies: Default::default(),
594                            features: Default::default(),
595                            edition: "2018".to_string(),
596                            printed: false,
597                        }
598                        .into(),
599                    ],
600                    build_dependencies: vec![Package {
601                        name: "arbitrary".to_string(),
602                        version: "1.3.0".parse().unwrap(),
603                        source: "sha".into(),
604                        lib_name: None,
605                        lib_path: None,
606                        build_path: None,
607                        proc_macro: false,
608                        dependencies: Default::default(),
609                        build_dependencies: Default::default(),
610                        features: Default::default(),
611                        edition: "2018".to_string(),
612                        printed: false,
613                    }
614                    .into()],
615                    features: vec!["one".to_string()],
616                    edition: "2021".to_string(),
617                    printed: false,
618                }
619                .into(),
620                Package {
621                    name: "itoa".to_string(),
622                    version: "0.4.8".parse().unwrap(),
623                    source: "sha".into(),
624                    lib_name: None,
625                    lib_path: None,
626                    build_path: None,
627                    proc_macro: false,
628                    dependencies: Default::default(),
629                    build_dependencies: Default::default(),
630                    features: Default::default(),
631                    edition: "2018".to_string(),
632                    printed: false,
633                }
634                .into(),
635                Dependency {
636                    package: libc,
637                    rename: None,
638                },
639                Package {
640                    name: "targets".to_string(),
641                    version: "0.1.0".parse().unwrap(),
642                    source: base.join("targets").into(),
643                    lib_name: None,
644                    lib_path: None,
645                    build_path: None,
646                    proc_macro: false,
647                    dependencies: Default::default(),
648                    build_dependencies: Default::default(),
649                    features: vec!["unix".to_string()],
650                    edition: "2021".to_string(),
651                    printed: false,
652                }
653                .into(),
654            ],
655            build_dependencies: Default::default(),
656            features: Default::default(),
657            edition: "2021".to_string(),
658            printed: false,
659        };
660
661        let actual = package.into_derivative();
662
663        assert_eq!(
664            actual,
665            r#"{ pkgs ? import <nixpkgs> {
666  overlays = [ (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz")) ];
667} }:
668
669let
670  sourceFilter = name: type:
671    let
672      baseName = builtins.baseNameOf (builtins.toString name);
673    in
674      ! (
675        # Filter out git
676        baseName == ".gitignore"
677        || (type == "directory" && baseName == ".git")
678
679        # Filter out build results
680        || (
681          type == "directory" && baseName == "target"
682        )
683
684        # Filter out nix-build result symlinks
685        || (
686          type == "symlink" && pkgs.lib.hasPrefix "result" baseName
687        )
688      );
689  rustVersion = pkgs.rust-bin.stable."1.68.0".default;
690  defaultCrateOverrides = pkgs.defaultCrateOverrides // {
691    opentelemetry-proto = attrs: { buildInputs = [ pkgs.protobuf ]; };
692  };
693  fetchCrate = { crateName, version, sha256 }: pkgs.fetchurl {
694    # https://www.pietroalbini.org/blog/downloading-crates-io/
695    # Not rate-limited, CDN URL.
696    name = "${crateName}-${version}.tar.gz";
697    url = "https://static.crates.io/crates/${crateName}/${crateName}-${version}.crate";
698    inherit sha256;
699  };
700  buildRustCrate = pkgs.buildRustCrate.override {
701    rustc = rustVersion;
702    inherit defaultCrateOverrides fetchCrate;
703  };
704  preBuild = "rustc -vV";
705
706  # Core
707  parent = buildRustCrate rec {
708    crateName = "parent";
709    version = "0.1.0";
710
711    src = pkgs.lib.cleanSourceWith { filter = sourceFilter;  src = /cargo-nbuild/nbuild-core/tests/workspace/parent; };
712
713    dependencies = [
714      child_0_1_0
715      itoa_0_4_8
716      libc_0_2_144
717      targets_0_1_0
718    ];
719    edition = "2021";
720    codegenUnits = 16;
721    extraRustcOpts = [ "-C embed-bitcode=no" ];
722    inherit preBuild;
723  };
724
725  # Dependencies
726  child_0_1_0 = buildRustCrate rec {
727    crateName = "child";
728    version = "0.1.0";
729
730    src = pkgs.lib.cleanSourceWith { filter = sourceFilter;  src = /cargo-nbuild/nbuild-core/tests/workspace/child; };
731    dependencies = [fnv_1_0_7 itoa_1_0_6 libc_0_2_144 rename_0_1_0 rustversion_1_0_12];
732    buildDependencies = [arbitrary_1_3_0];
733    crateRenames = {"rename" = [{ rename = "new_name"; version = "0.1.0"; }];};
734    features = ["one"];
735    edition = "2021";
736    crateBin = [];
737    codegenUnits = 16;
738    extraRustcOpts = [ "-C embed-bitcode=no" ];
739    inherit preBuild;
740  };
741  fnv_1_0_7 = buildRustCrate rec {
742    crateName = "fnv";
743    version = "1.0.7";
744
745    sha256 = "sha";
746    libPath = "lib.rs";
747    edition = "2015";
748    crateBin = [];
749    codegenUnits = 16;
750    extraRustcOpts = [ "-C embed-bitcode=no" ];
751    inherit preBuild;
752  };
753  itoa_1_0_6 = buildRustCrate rec {
754    crateName = "itoa";
755    version = "1.0.6";
756
757    sha256 = "sha";
758    edition = "2018";
759    crateBin = [];
760    codegenUnits = 16;
761    extraRustcOpts = [ "-C embed-bitcode=no" ];
762    inherit preBuild;
763  };
764  libc_0_2_144 = buildRustCrate rec {
765    crateName = "libc";
766    version = "0.2.144";
767
768    sha256 = "sha";
769    edition = "2015";
770    crateBin = [];
771    codegenUnits = 16;
772    extraRustcOpts = [ "-C embed-bitcode=no" ];
773    inherit preBuild;
774  };
775  rename_0_1_0 = buildRustCrate rec {
776    crateName = "rename";
777    libName = "lib_rename";
778    version = "0.1.0";
779
780    src = pkgs.lib.cleanSourceWith { filter = sourceFilter;  src = /cargo-nbuild/nbuild-core/tests/workspace/rename; };
781    edition = "2021";
782    crateBin = [];
783    codegenUnits = 16;
784    extraRustcOpts = [ "-C embed-bitcode=no" ];
785    inherit preBuild;
786  };
787  rustversion_1_0_12 = buildRustCrate rec {
788    crateName = "rustversion";
789    version = "1.0.12";
790
791    sha256 = "sha";
792    build = "build/build.rs";
793    procMacro = true;
794    edition = "2018";
795    crateBin = [];
796    codegenUnits = 16;
797    extraRustcOpts = [ "-C embed-bitcode=no" ];
798    inherit preBuild;
799  };
800  arbitrary_1_3_0 = buildRustCrate rec {
801    crateName = "arbitrary";
802    version = "1.3.0";
803
804    sha256 = "sha";
805    edition = "2018";
806    crateBin = [];
807    codegenUnits = 16;
808    extraRustcOpts = [ "-C embed-bitcode=no" ];
809    inherit preBuild;
810  };
811  itoa_0_4_8 = buildRustCrate rec {
812    crateName = "itoa";
813    version = "0.4.8";
814
815    sha256 = "sha";
816    edition = "2018";
817    crateBin = [];
818    codegenUnits = 16;
819    extraRustcOpts = [ "-C embed-bitcode=no" ];
820    inherit preBuild;
821  };
822  targets_0_1_0 = buildRustCrate rec {
823    crateName = "targets";
824    version = "0.1.0";
825
826    src = pkgs.lib.cleanSourceWith { filter = sourceFilter;  src = /cargo-nbuild/nbuild-core/tests/workspace/targets; };
827    features = ["unix"];
828    edition = "2021";
829    crateBin = [];
830    codegenUnits = 16;
831    extraRustcOpts = [ "-C embed-bitcode=no" ];
832    inherit preBuild;
833  };
834in
835parent
836"#
837        );
838    }
839}