PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/doc/languages-frameworks/rust.section.md

https://codeberg.org/matthiasbeyer/nixpkgs
Markdown | 410 lines | 331 code | 79 blank | 0 comment | 0 complexity | 8f3de2e390905ca6693e04d45139ed76 MD5 | raw file
  1. ---
  2. title: Rust
  3. author: Matthias Beyer
  4. date: 2017-03-05
  5. ---
  6. # Rust
  7. To install the rust compiler and cargo put
  8. ```
  9. rustc
  10. cargo
  11. ```
  12. into the `environment.systemPackages` or bring them into
  13. scope with `nix-shell -p rustc cargo`.
  14. For daily builds (beta and nightly) use either rustup from
  15. nixpkgs or use the [Rust nightlies
  16. overlay](#using-the-rust-nightlies-overlay).
  17. ## Compiling Rust applications with Cargo
  18. Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`:
  19. ```
  20. rustPlatform.buildRustPackage rec {
  21. pname = "ripgrep";
  22. version = "11.0.2";
  23. src = fetchFromGitHub {
  24. owner = "BurntSushi";
  25. repo = pname;
  26. rev = version;
  27. sha256 = "1iga3320mgi7m853la55xip514a3chqsdi1a1rwv25lr9b1p7vd3";
  28. };
  29. cargoSha256 = "17ldqr3asrdcsh4l29m3b5r37r5d0b3npq1lrgjmxb6vlx6a36qh";
  30. meta = with stdenv.lib; {
  31. description = "A fast line-oriented regex search tool, similar to ag and ack";
  32. homepage = "https://github.com/BurntSushi/ripgrep";
  33. license = licenses.unlicense;
  34. maintainers = [ maintainers.tailhook ];
  35. platforms = platforms.all;
  36. };
  37. }
  38. ```
  39. `buildRustPackage` requires a `cargoSha256` attribute which is computed over
  40. all crate sources of this package. Currently it is obtained by inserting a
  41. fake checksum into the expression and building the package once. The correct
  42. checksum can be then take from the failed build.
  43. Per the instructions in the [Cargo Book](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html)
  44. best practices guide, Rust applications should always commit the `Cargo.lock`
  45. file in git to ensure a reproducible build. However, a few packages do not, and
  46. Nix depends on this file, so if it missing you can use `cargoPatches` to apply
  47. it in the `patchPhase`. Consider sending a PR upstream with a note to the
  48. maintainer describing why it's important to include in the application.
  49. The fetcher will verify that the `Cargo.lock` file is in sync with the `src`
  50. attribute, and fail the build if not. It will also will compress the vendor
  51. directory into a tar.gz archive.
  52. ### Building a crate for a different target
  53. To build your crate with a different cargo `--target` simply specify the `target` attribute:
  54. ```nix
  55. pkgs.rustPlatform.buildRustPackage {
  56. (...)
  57. target = "x86_64-fortanix-unknown-sgx";
  58. }
  59. ```
  60. ## Compiling Rust crates using Nix instead of Cargo
  61. ### Simple operation
  62. When run, `cargo build` produces a file called `Cargo.lock`,
  63. containing pinned versions of all dependencies. Nixpkgs contains a
  64. tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used
  65. to turn a `Cargo.lock` into a Nix expression.
  66. That Nix expression calls `rustc` directly (hence bypassing Cargo),
  67. and can be used to compile a crate and all its dependencies. Here is
  68. an example for a minimal `hello` crate:
  69. $ cargo new hello
  70. $ cd hello
  71. $ cargo build
  72. Compiling hello v0.1.0 (file:///tmp/hello)
  73. Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs
  74. $ carnix -o hello.nix --src ./. Cargo.lock --standalone
  75. $ nix-build hello.nix -A hello_0_1_0
  76. Now, the file produced by the call to `carnix`, called `hello.nix`, looks like:
  77. ```
  78. # Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone
  79. { lib, stdenv, buildRustCrate, fetchgit }:
  80. let kernel = stdenv.buildPlatform.parsed.kernel.name;
  81. # ... (content skipped)
  82. in
  83. rec {
  84. hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; };
  85. hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate {
  86. crateName = "hello";
  87. version = "0.1.0";
  88. authors = [ "pe@pijul.org <pe@pijul.org>" ];
  89. src = ./.;
  90. inherit dependencies buildDependencies features;
  91. };
  92. hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {};
  93. hello_0_1_0_features = f: updateFeatures f (rec {
  94. hello_0_1_0.default = (f.hello_0_1_0.default or true);
  95. }) [ ];
  96. }
  97. ```
  98. In particular, note that the argument given as `--src` is copied
  99. verbatim to the source. If we look at a more complicated
  100. dependencies, for instance by adding a single line `libc="*"` to our
  101. `Cargo.toml`, we first need to run `cargo build` to update the
  102. `Cargo.lock`. Then, `carnix` needs to be run again, and produces the
  103. following nix file:
  104. ```
  105. # Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone
  106. { lib, stdenv, buildRustCrate, fetchgit }:
  107. let kernel = stdenv.buildPlatform.parsed.kernel.name;
  108. # ... (content skipped)
  109. in
  110. rec {
  111. hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; };
  112. hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate {
  113. crateName = "hello";
  114. version = "0.1.0";
  115. authors = [ "pe@pijul.org <pe@pijul.org>" ];
  116. src = ./.;
  117. inherit dependencies buildDependencies features;
  118. };
  119. libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate {
  120. crateName = "libc";
  121. version = "0.2.36";
  122. authors = [ "The Rust Project Developers" ];
  123. sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l";
  124. inherit dependencies buildDependencies features;
  125. };
  126. hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {
  127. dependencies = mapFeatures features ([ libc_0_2_36 ]);
  128. };
  129. hello_0_1_0_features = f: updateFeatures f (rec {
  130. hello_0_1_0.default = (f.hello_0_1_0.default or true);
  131. libc_0_2_36.default = true;
  132. }) [ libc_0_2_36_features ];
  133. libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ {
  134. features = mkFeatures (features.libc_0_2_36 or {});
  135. };
  136. libc_0_2_36_features = f: updateFeatures f (rec {
  137. libc_0_2_36.default = (f.libc_0_2_36.default or true);
  138. libc_0_2_36.use_std =
  139. (f.libc_0_2_36.use_std or false) ||
  140. (f.libc_0_2_36.default or false) ||
  141. (libc_0_2_36.default or false);
  142. }) [];
  143. }
  144. ```
  145. Here, the `libc` crate has no `src` attribute, so `buildRustCrate`
  146. will fetch it from [crates.io](https://crates.io). A `sha256`
  147. attribute is still needed for Nix purity.
  148. ### Handling external dependencies
  149. Some crates require external libraries. For crates from
  150. [crates.io](https://crates.io), such libraries can be specified in
  151. `defaultCrateOverrides` package in nixpkgs itself.
  152. Starting from that file, one can add more overrides, to add features
  153. or build inputs by overriding the hello crate in a seperate file.
  154. ```
  155. with import <nixpkgs> {};
  156. ((import ./hello.nix).hello {}).override {
  157. crateOverrides = defaultCrateOverrides // {
  158. hello = attrs: { buildInputs = [ openssl ]; };
  159. };
  160. }
  161. ```
  162. Here, `crateOverrides` is expected to be a attribute set, where the
  163. key is the crate name without version number and the value a function.
  164. The function gets all attributes passed to `buildRustCrate` as first
  165. argument and returns a set that contains all attribute that should be
  166. overwritten.
  167. For more complicated cases, such as when parts of the crate's
  168. derivation depend on the crate's version, the `attrs` argument of
  169. the override above can be read, as in the following example, which
  170. patches the derivation:
  171. ```
  172. with import <nixpkgs> {};
  173. ((import ./hello.nix).hello {}).override {
  174. crateOverrides = defaultCrateOverrides // {
  175. hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") {
  176. postPatch = ''
  177. substituteInPlace lib/zoneinfo.rs \
  178. --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo"
  179. '';
  180. };
  181. };
  182. }
  183. ```
  184. Another situation is when we want to override a nested
  185. dependency. This actually works in the exact same way, since the
  186. `crateOverrides` parameter is forwarded to the crate's
  187. dependencies. For instance, to override the build inputs for crate
  188. `libc` in the example above, where `libc` is a dependency of the main
  189. crate, we could do:
  190. ```
  191. with import <nixpkgs> {};
  192. ((import hello.nix).hello {}).override {
  193. crateOverrides = defaultCrateOverrides // {
  194. libc = attrs: { buildInputs = []; };
  195. };
  196. }
  197. ```
  198. ### Options and phases configuration
  199. Actually, the overrides introduced in the previous section are more
  200. general. A number of other parameters can be overridden:
  201. - The version of rustc used to compile the crate:
  202. ```
  203. (hello {}).override { rust = pkgs.rust; };
  204. ```
  205. - Whether to build in release mode or debug mode (release mode by
  206. default):
  207. ```
  208. (hello {}).override { release = false; };
  209. ```
  210. - Whether to print the commands sent to rustc when building
  211. (equivalent to `--verbose` in cargo:
  212. ```
  213. (hello {}).override { verbose = false; };
  214. ```
  215. - Extra arguments to be passed to `rustc`:
  216. ```
  217. (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; };
  218. ```
  219. - Phases, just like in any other derivation, can be specified using
  220. the following attributes: `preUnpack`, `postUnpack`, `prePatch`,
  221. `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate,
  222. this is run before calling the "build" script), `postConfigure`
  223. (after the "build" script),`preBuild`, `postBuild`, `preInstall` and
  224. `postInstall`. As an example, here is how to create a new module
  225. before running the build script:
  226. ```
  227. (hello {}).override {
  228. preConfigure = ''
  229. echo "pub const PATH=\"${hi.out}\";" >> src/path.rs"
  230. '';
  231. };
  232. ```
  233. ### Features
  234. One can also supply features switches. For example, if we want to
  235. compile `diesel_cli` only with the `postgres` feature, and no default
  236. features, we would write:
  237. ```
  238. (callPackage ./diesel.nix {}).diesel {
  239. default = false;
  240. postgres = true;
  241. }
  242. ```
  243. Where `diesel.nix` is the file generated by Carnix, as explained above.
  244. ## Setting Up `nix-shell`
  245. Oftentimes you want to develop code from within `nix-shell`. Unfortunately
  246. `buildRustCrate` does not support common `nix-shell` operations directly
  247. (see [this issue](https://github.com/NixOS/nixpkgs/issues/37945))
  248. so we will use `stdenv.mkDerivation` instead.
  249. Using the example `hello` project above, we want to do the following:
  250. - Have access to `cargo` and `rustc`
  251. - Have the `openssl` library available to a crate through it's _normal_
  252. compilation mechanism (`pkg-config`).
  253. A typical `shell.nix` might look like:
  254. ```
  255. with import <nixpkgs> {};
  256. stdenv.mkDerivation {
  257. name = "rust-env";
  258. nativeBuildInputs = [
  259. rustc cargo
  260. # Example Build-time Additional Dependencies
  261. pkgconfig
  262. ];
  263. buildInputs = [
  264. # Example Run-time Additional Dependencies
  265. openssl
  266. ];
  267. # Set Environment Variables
  268. RUST_BACKTRACE = 1;
  269. }
  270. ```
  271. You should now be able to run the following:
  272. ```
  273. $ nix-shell --pure
  274. $ cargo build
  275. $ cargo test
  276. ```
  277. ### Controlling Rust Version Inside `nix-shell`
  278. To control your rust version (i.e. use nightly) from within `shell.nix` (or
  279. other nix expressions) you can use the following `shell.nix`
  280. ```
  281. # Latest Nightly
  282. with import <nixpkgs> {};
  283. let src = fetchFromGitHub {
  284. owner = "mozilla";
  285. repo = "nixpkgs-mozilla";
  286. # commit from: 2019-05-15
  287. rev = "9f35c4b09fd44a77227e79ff0c1b4b6a69dff533";
  288. sha256 = "18h0nvh55b5an4gmlgfbvwbyqj91bklf1zymis6lbdh75571qaz0";
  289. };
  290. in
  291. with import "${src.out}/rust-overlay.nix" pkgs pkgs;
  292. stdenv.mkDerivation {
  293. name = "rust-env";
  294. buildInputs = [
  295. # Note: to use use stable, just replace `nightly` with `stable`
  296. latest.rustChannels.nightly.rust
  297. # Add some extra dependencies from `pkgs`
  298. pkgconfig openssl
  299. ];
  300. # Set Environment Variables
  301. RUST_BACKTRACE = 1;
  302. }
  303. ```
  304. Now run:
  305. ```
  306. $ rustc --version
  307. rustc 1.26.0-nightly (188e693b3 2018-03-26)
  308. ```
  309. To see that you are using nightly.
  310. ## Using the Rust nightlies overlay
  311. Mozilla provides an overlay for nixpkgs to bring a nightly version of Rust into scope.
  312. This overlay can _also_ be used to install recent unstable or stable versions
  313. of Rust, if desired.
  314. To use this overlay, clone
  315. [nixpkgs-mozilla](https://github.com/mozilla/nixpkgs-mozilla),
  316. and create a symbolic link to the file
  317. [rust-overlay.nix](https://github.com/mozilla/nixpkgs-mozilla/blob/master/rust-overlay.nix)
  318. in the `~/.config/nixpkgs/overlays` directory.
  319. $ git clone https://github.com/mozilla/nixpkgs-mozilla.git
  320. $ mkdir -p ~/.config/nixpkgs/overlays
  321. $ ln -s $(pwd)/nixpkgs-mozilla/rust-overlay.nix ~/.config/nixpkgs/overlays/rust-overlay.nix
  322. The latest version can be installed with the following command:
  323. $ nix-env -Ai nixos.latest.rustChannels.stable.rust
  324. Or using the attribute with nix-shell:
  325. $ nix-shell -p nixos.latest.rustChannels.stable.rust
  326. To install the beta or nightly channel, "stable" should be substituted by
  327. "nightly" or "beta", or
  328. use the function provided by this overlay to pull a version based on a
  329. build date.
  330. The overlay automatically updates itself as it uses the same source as
  331. [rustup](https://www.rustup.rs/).