-
-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
12 additions
and
8,245 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -608,227 +608,6 @@ 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:https:///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 = [ "[email protected] <[email protected]>" ]; | ||
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 = [ "[email protected] <[email protected]>" ]; | ||
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 <nixpkgs> {}; | ||
((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 <nixpkgs> {}; | ||
((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 <nixpkgs> {}; | ||
((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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.