From efbbdc16e6defc1ac5243697bf9671fbe0720f80 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Wed, 15 Mar 2023 19:41:48 -0700 Subject: [PATCH 1/5] Revert "carnix,cratesIO: remove" in doc/languages-frameworks/rust.section.md This reverts the part of commit 82fe76d1cd0ff6607c4c6383fb9620f6615a84a0 that affected doc/languages-frameworks/rust.section.md --- doc/languages-frameworks/rust.section.md | 221 +++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index b3f4405ee93..4f2ecc2cd4d 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -642,6 +642,227 @@ buildPythonPackage rec { } ``` +## `buildRustCrate`: Compiling Rust crates using Nix instead of Cargo {#compiling-rust-crates-using-nix-instead-of-cargo} + +### Simple operation {#simple-operation} + +When run, `cargo build` produces a file called `Cargo.lock`, +containing pinned versions of all dependencies. Nixpkgs contains a +tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used +to turn a `Cargo.lock` into a Nix expression. + +That Nix expression calls `rustc` directly (hence bypassing Cargo), +and can be used to compile a crate and all its dependencies. Here is +an example for a minimal `hello` crate: + +```ShellSession +$ cargo new hello +$ cd hello +$ cargo build + Compiling hello v0.1.0 (file:///tmp/hello) + Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs +$ carnix -o hello.nix --src ./. Cargo.lock --standalone +$ nix-build hello.nix -A hello_0_1_0 +``` + +Now, the file produced by the call to `carnix`, called `hello.nix`, looks like: + +```nix +# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone +{ stdenv, buildRustCrate, fetchgit }: +let kernel = stdenv.buildPlatform.parsed.kernel.name; + # ... (content skipped) +in +rec { + hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; + hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "hello"; + version = "0.1.0"; + authors = [ "pe@pijul.org " ]; + src = ./.; + inherit dependencies buildDependencies features; + }; + hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {}; + hello_0_1_0_features = f: updateFeatures f (rec { + hello_0_1_0.default = (f.hello_0_1_0.default or true); + }) [ ]; +} +``` + +In particular, note that the argument given as `--src` is copied +verbatim to the source. If we look at a more complicated +dependencies, for instance by adding a single line `libc="*"` to our +`Cargo.toml`, we first need to run `cargo build` to update the +`Cargo.lock`. Then, `carnix` needs to be run again, and produces the +following nix file: + +```nix +# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone +{ stdenv, buildRustCrate, fetchgit }: +let kernel = stdenv.buildPlatform.parsed.kernel.name; + # ... (content skipped) +in +rec { + hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; + hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "hello"; + version = "0.1.0"; + authors = [ "pe@pijul.org " ]; + src = ./.; + inherit dependencies buildDependencies features; + }; + libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "libc"; + version = "0.2.36"; + authors = [ "The Rust Project Developers" ]; + sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l"; + inherit dependencies buildDependencies features; + }; + hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ { + dependencies = mapFeatures features ([ libc_0_2_36 ]); + }; + hello_0_1_0_features = f: updateFeatures f (rec { + hello_0_1_0.default = (f.hello_0_1_0.default or true); + libc_0_2_36.default = true; + }) [ libc_0_2_36_features ]; + libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ { + features = mkFeatures (features.libc_0_2_36 or {}); + }; + libc_0_2_36_features = f: updateFeatures f (rec { + libc_0_2_36.default = (f.libc_0_2_36.default or true); + libc_0_2_36.use_std = + (f.libc_0_2_36.use_std or false) || + (f.libc_0_2_36.default or false) || + (libc_0_2_36.default or false); + }) []; +} +``` + +Here, the `libc` crate has no `src` attribute, so `buildRustCrate` +will fetch it from [crates.io](https://crates.io). A `sha256` +attribute is still needed for Nix purity. + +### Handling external dependencies {#handling-external-dependencies} + +Some crates require external libraries. For crates from +[crates.io](https://crates.io), such libraries can be specified in +`defaultCrateOverrides` package in nixpkgs itself. + +Starting from that file, one can add more overrides, to add features +or build inputs by overriding the hello crate in a separate file. + +```nix +with import {}; +((import ./hello.nix).hello {}).override { + crateOverrides = defaultCrateOverrides // { + hello = attrs: { buildInputs = [ openssl ]; }; + }; +} +``` + +Here, `crateOverrides` is expected to be a attribute set, where the +key is the crate name without version number and the value a function. +The function gets all attributes passed to `buildRustCrate` as first +argument and returns a set that contains all attribute that should be +overwritten. + +For more complicated cases, such as when parts of the crate's +derivation depend on the crate's version, the `attrs` argument of +the override above can be read, as in the following example, which +patches the derivation: + +```nix +with import {}; +((import ./hello.nix).hello {}).override { + crateOverrides = defaultCrateOverrides // { + hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") { + postPatch = '' + substituteInPlace lib/zoneinfo.rs \ + --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" + ''; + }; + }; +} +``` + +Another situation is when we want to override a nested +dependency. This actually works in the exact same way, since the +`crateOverrides` parameter is forwarded to the crate's +dependencies. For instance, to override the build inputs for crate +`libc` in the example above, where `libc` is a dependency of the main +crate, we could do: + +```nix +with import {}; +((import hello.nix).hello {}).override { + crateOverrides = defaultCrateOverrides // { + libc = attrs: { buildInputs = []; }; + }; +} +``` + +### Options and phases configuration {#options-and-phases-configuration} + +Actually, the overrides introduced in the previous section are more +general. A number of other parameters can be overridden: + +- The version of `rustc` used to compile the crate: + + ```nix + (hello {}).override { rust = pkgs.rust; }; + ``` + +- Whether to build in release mode or debug mode (release mode by + default): + + ```nix + (hello {}).override { release = false; }; + ``` + +- Whether to print the commands sent to `rustc` when building + (equivalent to `--verbose` in cargo: + + ```nix + (hello {}).override { verbose = false; }; + ``` + +- Extra arguments to be passed to `rustc`: + + ```nix + (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }; + ``` + +- Phases, just like in any other derivation, can be specified using + the following attributes: `preUnpack`, `postUnpack`, `prePatch`, + `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate, + this is run before calling the "build" script), `postConfigure` + (after the "build" script),`preBuild`, `postBuild`, `preInstall` and + `postInstall`. As an example, here is how to create a new module + before running the build script: + + ```nix + (hello {}).override { + preConfigure = '' + echo "pub const PATH=\"${hi.out}\";" >> src/path.rs" + ''; + }; + ``` + +### Features {#features} + +One can also supply features switches. For example, if we want to +compile `diesel_cli` only with the `postgres` feature, and no default +features, we would write: + +```nix +(callPackage ./diesel.nix {}).diesel { + default = false; + postgres = true; +} +``` + +Where `diesel.nix` is the file generated by Carnix, as explained above. + ## Setting Up `nix-shell` {#setting-up-nix-shell} Oftentimes you want to develop code from within `nix-shell`. Unfortunately From 50f57ac692c736b75ee7958c57b4111bf62fee9c Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Mon, 22 Aug 2022 14:29:50 -0700 Subject: [PATCH 2/5] doc/../rust.section.md: fix incorrect header depths The headings for the Rust section are structured incorrectly in two ways: 1. The section "Compiling non-Rust packages that include Rust code" is totally specific to `buildRustPackage`. It should be a child of the "Compiling Rust applications with Cargo" section. 1. The section "Setting up `nix-shell`" is totally specific to `buildRustCrate`. It should be a child of the "Compiling Rust crates using Nix instead of Cargo" section. - Rust - Compiling Rust applications with Cargo - ... - Compiling non-Rust packages that include Rust code - ... - Compiling Rust crates using Nix instead of Cargo - ... - Setting Up `nix-shell` - ... - Rust - Compiling Rust applications with Cargo - ... - Compiling non-Rust packages that include Rust code - ... - Compiling Rust crates using Nix instead of Cargo - ... - Setting Up `nix-shell` - ... --- doc/languages-frameworks/rust.section.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index 4f2ecc2cd4d..1073a97888e 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -411,13 +411,13 @@ rustPlatform.buildRustPackage rec { } ``` -## Compiling non-Rust packages that include Rust code {#compiling-non-rust-packages-that-include-rust-code} +### Compiling non-Rust packages that include Rust code {#compiling-non-rust-packages-that-include-rust-code} Several non-Rust packages incorporate Rust code for performance- or security-sensitive parts. `rustPlatform` exposes several functions and hooks that can be used to integrate Cargo in non-Rust packages. -### Vendoring of dependencies {#vendoring-of-dependencies} +#### Vendoring of dependencies {#vendoring-of-dependencies} Since network access is not allowed in sandboxed builds, Rust crate dependencies need to be retrieved using a fetcher. `rustPlatform` @@ -477,7 +477,7 @@ added. To find the correct hash, you can first use `lib.fakeSha256` or `lib.fakeHash` as a stub hash. Building `cargoDeps` will then inform you of the correct hash. -### Hooks {#hooks} +#### Hooks {#hooks} `rustPlatform` provides the following hooks to automate Cargo builds: @@ -513,7 +513,7 @@ you of the correct hash. * `bindgenHook`: for crates which use `bindgen` as a build dependency, lets `bindgen` find `libclang` and `libclang` find the libraries in `buildInputs`. -### Examples {#examples} +#### Examples {#examples} #### Python package using `setuptools-rust` {#python-package-using-setuptools-rust} @@ -863,7 +863,7 @@ features, we would write: Where `diesel.nix` is the file generated by Carnix, as explained above. -## Setting Up `nix-shell` {#setting-up-nix-shell} +### Setting Up `nix-shell` {#setting-up-nix-shell} Oftentimes you want to develop code from within `nix-shell`. Unfortunately `buildRustCrate` does not support common `nix-shell` operations directly From 4716768513350cd9a7f4b93daf100285e16a5348 Mon Sep 17 00:00:00 2001 From: Adam Joseph <54836058+amjoseph-nixpkgs@users.noreply.github.com> Date: Fri, 17 Mar 2023 19:09:50 +0000 Subject: [PATCH 3/5] Update doc/languages-frameworks/rust.section.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- doc/languages-frameworks/rust.section.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index 1073a97888e..2b4b01718d7 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -648,7 +648,7 @@ buildPythonPackage rec { When run, `cargo build` produces a file called `Cargo.lock`, containing pinned versions of all dependencies. Nixpkgs contains a -tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used +tool called `crate2Nix` (`nix-shell -p crate2nix`), which can be used to turn a `Cargo.lock` into a Nix expression. That Nix expression calls `rustc` directly (hence bypassing Cargo), From 618d8e6a62455729200843a9f31a5bf1a465a739 Mon Sep 17 00:00:00 2001 From: Adam Joseph <54836058+amjoseph-nixpkgs@users.noreply.github.com> Date: Fri, 17 Mar 2023 19:10:15 +0000 Subject: [PATCH 4/5] Update doc/languages-frameworks/rust.section.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- doc/languages-frameworks/rust.section.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index 2b4b01718d7..c2eba3f1481 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -693,7 +693,7 @@ In particular, note that the argument given as `--src` is copied verbatim to the source. If we look at a more complicated dependencies, for instance by adding a single line `libc="*"` to our `Cargo.toml`, we first need to run `cargo build` to update the -`Cargo.lock`. Then, `carnix` needs to be run again, and produces the +`Cargo.lock`. Then, `crate2nix` needs to be run again, and produces the following nix file: ```nix From e369d78b70bbcca7018689cb1ec04d103ac91b5c Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Fri, 17 Mar 2023 12:13:13 -0700 Subject: [PATCH 5/5] remove references to crate2nix other than a link to its docs --- doc/languages-frameworks/rust.section.md | 113 ++--------------------- 1 file changed, 6 insertions(+), 107 deletions(-) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index c2eba3f1481..7ee2dea2daf 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -648,99 +648,13 @@ buildPythonPackage rec { When run, `cargo build` produces a file called `Cargo.lock`, containing pinned versions of all dependencies. Nixpkgs contains a -tool called `crate2Nix` (`nix-shell -p crate2nix`), which can be used -to turn a `Cargo.lock` into a Nix expression. +tool called `crate2Nix` (`nix-shell -p crate2nix`), which can be +used to turn a `Cargo.lock` into a Nix expression. That Nix +expression calls `rustc` directly (hence bypassing Cargo), and can +be used to compile a crate and all its dependencies. -That Nix expression calls `rustc` directly (hence bypassing Cargo), -and can be used to compile a crate and all its dependencies. Here is -an example for a minimal `hello` crate: - -```ShellSession -$ cargo new hello -$ cd hello -$ cargo build - Compiling hello v0.1.0 (file:///tmp/hello) - Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs -$ carnix -o hello.nix --src ./. Cargo.lock --standalone -$ nix-build hello.nix -A hello_0_1_0 -``` - -Now, the file produced by the call to `carnix`, called `hello.nix`, looks like: - -```nix -# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone -{ stdenv, buildRustCrate, fetchgit }: -let kernel = stdenv.buildPlatform.parsed.kernel.name; - # ... (content skipped) -in -rec { - hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; - hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { - crateName = "hello"; - version = "0.1.0"; - authors = [ "pe@pijul.org " ]; - src = ./.; - inherit dependencies buildDependencies features; - }; - hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {}; - hello_0_1_0_features = f: updateFeatures f (rec { - hello_0_1_0.default = (f.hello_0_1_0.default or true); - }) [ ]; -} -``` - -In particular, note that the argument given as `--src` is copied -verbatim to the source. If we look at a more complicated -dependencies, for instance by adding a single line `libc="*"` to our -`Cargo.toml`, we first need to run `cargo build` to update the -`Cargo.lock`. Then, `crate2nix` needs to be run again, and produces the -following nix file: - -```nix -# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone -{ stdenv, buildRustCrate, fetchgit }: -let kernel = stdenv.buildPlatform.parsed.kernel.name; - # ... (content skipped) -in -rec { - hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; - hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { - crateName = "hello"; - version = "0.1.0"; - authors = [ "pe@pijul.org " ]; - src = ./.; - inherit dependencies buildDependencies features; - }; - libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { - crateName = "libc"; - version = "0.2.36"; - authors = [ "The Rust Project Developers" ]; - sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l"; - inherit dependencies buildDependencies features; - }; - hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ { - dependencies = mapFeatures features ([ libc_0_2_36 ]); - }; - hello_0_1_0_features = f: updateFeatures f (rec { - hello_0_1_0.default = (f.hello_0_1_0.default or true); - libc_0_2_36.default = true; - }) [ libc_0_2_36_features ]; - libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ { - features = mkFeatures (features.libc_0_2_36 or {}); - }; - libc_0_2_36_features = f: updateFeatures f (rec { - libc_0_2_36.default = (f.libc_0_2_36.default or true); - libc_0_2_36.use_std = - (f.libc_0_2_36.use_std or false) || - (f.libc_0_2_36.default or false) || - (libc_0_2_36.default or false); - }) []; -} -``` - -Here, the `libc` crate has no `src` attribute, so `buildRustCrate` -will fetch it from [crates.io](https://crates.io). A `sha256` -attribute is still needed for Nix purity. +See [`crate2nix`'s documentation](https://github.com/kolloch/crate2nix#known-restrictions) +for instructions on how to use it. ### Handling external dependencies {#handling-external-dependencies} @@ -848,21 +762,6 @@ general. A number of other parameters can be overridden: }; ``` -### Features {#features} - -One can also supply features switches. For example, if we want to -compile `diesel_cli` only with the `postgres` feature, and no default -features, we would write: - -```nix -(callPackage ./diesel.nix {}).diesel { - default = false; - postgres = true; -} -``` - -Where `diesel.nix` is the file generated by Carnix, as explained above. - ### Setting Up `nix-shell` {#setting-up-nix-shell} Oftentimes you want to develop code from within `nix-shell`. Unfortunately