├── .envrc ├── .gitignore ├── examples ├── 3-cross-compiling │ ├── templates │ │ └── test.rs.html │ ├── build.rs │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ ├── flake.nix │ ├── flake.lock │ ├── Cargo.lock │ ├── README.md │ └── Cargo.nix ├── 1-hello-world │ ├── src │ │ └── main.rs │ ├── Cargo.toml │ ├── Cargo.lock │ ├── flake.nix │ ├── Cargo.nix │ ├── flake.lock │ └── README.md ├── 2-bigger-project │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ ├── flake.nix │ ├── flake.lock │ └── README.md ├── README.md └── 4-independent-packaging │ ├── flake.nix │ ├── README.md │ └── flake.lock ├── .github ├── ISSUE_TEMPLATE │ └── config.yaml ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitattributes ├── default.nix ├── shell.nix ├── overlay ├── lib │ ├── features.nix │ ├── default.nix │ ├── profiles.nix │ ├── fetch.nix │ ├── rust-triple.nix │ ├── overrides.nix │ └── splice.nix ├── run-tests.nix ├── mkcrate-nobuild.nix ├── default.nix ├── make-package-set │ ├── internal.nix │ └── user-facing.nix ├── workspace-shell.nix ├── overrides.nix ├── mkcrate-utils.sh └── mkcrate.nix ├── Cargo.toml ├── src ├── manifest.rs ├── platform.rs ├── expr.rs └── template.rs ├── CODE_OF_CONDUCT.md ├── LICENSE ├── flake.lock ├── templates └── Cargo.nix.tera ├── CONTRIBUTING.md ├── flake.nix └── README.md /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | **/result* 4 | 5 | .direnv/ 6 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/templates/test.rs.html: -------------------------------------------------------------------------------- 1 | @(test: &str) 2 | 3 | Hello @test 4 | -------------------------------------------------------------------------------- /examples/1-hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /examples/1-hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | authors = ["Eyal Kalderon "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/build.rs: -------------------------------------------------------------------------------- 1 | use ructe::Ructe; 2 | 3 | fn main() { 4 | Ructe::from_env() 5 | .expect("ructe") 6 | .compile_templates("templates") 7 | .unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Positron Solutions 4 | url: https://github.com/positron-solutions 5 | about: Commercial support available 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/github/administering-a-repository/customizing-how-changed-files-appear-on-github 2 | 3 | **/Cargo.nix linguist-generated=true 4 | **/flake.lock linguist-generated=true 5 | -------------------------------------------------------------------------------- /examples/1-hello-world/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cross-compiling" 3 | version = "0.1.0" 4 | authors = ["Valentin Brandl "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | 10 | [build-dependencies] 11 | ructe = "0.9.2" 12 | -------------------------------------------------------------------------------- /examples/2-bigger-project/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bigger-project" 3 | version = "0.1.0" 4 | authors = ["Eyal Kalderon "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | reqwest = "0.10" 9 | tokio = { version = "0.2", features = ["macros"] } 10 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A program which generates some text using an embedded template. 2 | 3 | include!(concat!(env!("OUT_DIR"), "/templates.rs")); 4 | 5 | fn main() { 6 | let mut buf = Vec::new(); 7 | templates::test(&mut buf, "world").unwrap(); 8 | println!("{}", String::from_utf8_lossy(&buf)); 9 | } 10 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import 2 | ( 3 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in 4 | fetchTarball { 5 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; 7 | } 8 | ) 9 | { src = ./.; } 10 | ).defaultNix 11 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | 2 | (import 3 | ( 4 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in 5 | fetchTarball { 6 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 7 | sha256 = lock.nodes.flake-compat.locked.narHash; 8 | } 9 | ) 10 | { src = ./.; } 11 | ).shellNix 12 | -------------------------------------------------------------------------------- /examples/2-bigger-project/src/main.rs: -------------------------------------------------------------------------------- 1 | //! A simple HTTP client using `reqwest`. 2 | 3 | #[tokio::main] 4 | async fn main() -> Result<(), reqwest::Error> { 5 | let res = reqwest::get("https://hyper.rs").await?; 6 | 7 | println!("Status: {}", res.status()); 8 | 9 | let body = res.text().await?; 10 | 11 | println!("Body:\n\n{}", body); 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Activity on the repository is the best way to provide input so that others can 2 | # comment and see issues. Presume the mediocre engineer goes for about $150k 3 | # per year in SF and expect time boxes to be allocated accordingly. Contact 4 | # directly via contact@positron.solutions with "cargo2nix Professional Services" 5 | # in the subject line. 6 | github: positron-solutions 7 | -------------------------------------------------------------------------------- /overlay/lib/features.nix: -------------------------------------------------------------------------------- 1 | { lib }: 2 | let 3 | expandFeatures = 4 | let 5 | inherit (builtins) attrNames concatMap listToAttrs; 6 | inherit (lib) splitString; 7 | in 8 | features: listToAttrs 9 | (map (feature: { name = feature; value = { }; }) 10 | (concatMap (feature: [ feature (builtins.head (splitString "/" feature)) ]) 11 | features)); 12 | in 13 | { inherit expandFeatures; } 14 | -------------------------------------------------------------------------------- /overlay/lib/default.nix: -------------------------------------------------------------------------------- 1 | { callPackage, pkgs }: 2 | { 3 | inherit (callPackage ./features.nix { }) expandFeatures; 4 | inherit (callPackage ./splice.nix { }) splicePackages; 5 | inherit (callPackage ./fetch.nix { }) fetchCrateLocal fetchCrateGit fetchCratesIo fetchCrateAlternativeRegistryExpensive; 6 | inherit (import ./profiles.nix) decideProfile genDrvsByProfile; 7 | inherit (import ./overrides.nix) makeOverride combineOverrides runOverride nullOverride; 8 | 9 | rustTriple = import ./rust-triple.nix; 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo2nix" 3 | version = "0.11.0" 4 | edition = "2021" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | cargo = "0.73.1" 9 | cargo-platform = "0.1.4" 10 | clap = { version = "4.4.6", features = ["derive"] } 11 | clap_complete = { version = "4.4.3" } 12 | colorify = "0.2.3" 13 | anyhow = { version = "1.0.75", features = ["backtrace"] } 14 | pathdiff = "0.2.1" 15 | semver = "1.0.20" 16 | serde = { version = "1.0.189", features = ["derive"] } 17 | sha2 = "0.10.8" 18 | tempfile = "3.8.0" 19 | tera = { version = "1.19.1", default-features = false } 20 | toml = "0.8.2" 21 | -------------------------------------------------------------------------------- /overlay/run-tests.nix: -------------------------------------------------------------------------------- 1 | # Run all tests for a crate. 2 | { stdenvNoCC }: 3 | crate: 4 | env@{ 5 | testCommand ? bin: "${bin}", 6 | ... 7 | }: 8 | let 9 | testBins = crate { compileMode = "test"; }; 10 | in 11 | stdenvNoCC.mkDerivation ((removeAttrs env [ "testCommand" ]) // { 12 | name = "test-${testBins.name}"; 13 | inherit (testBins) src; 14 | CARGO_MANIFEST_DIR = testBins.src; 15 | phases = [ "unpackPhase" "buildPhase" ]; 16 | buildPhase = '' 17 | for f in ${testBins}/bin/*; do 18 | # HACK: cargo produces the crate's main binary in the bin directory if the crate contains example tests. 19 | # The `grep` filters out the main binary, which doesn't contain the help string found in test binaries. 20 | if [[ -x "$f" ]] && grep "By default, all tests are run in parallel" "$f"; then 21 | ${testCommand "$f"} 22 | fi 23 | done 24 | touch $out 25 | ''; 26 | }) 27 | -------------------------------------------------------------------------------- /src/manifest.rs: -------------------------------------------------------------------------------- 1 | use toml::value::Table; 2 | 3 | use serde::Deserialize; 4 | use std::collections::BTreeMap; 5 | 6 | pub type TomlProfile = BTreeMap; 7 | 8 | pub fn extract_profiles(manifest_contents: &str) -> TomlProfile { 9 | #[derive(Debug, Deserialize)] 10 | struct Manifest { 11 | pub profile: Option, 12 | } 13 | 14 | toml::from_str::(manifest_contents) 15 | .ok() 16 | .and_then(|m| m.profile) 17 | .map(|mut profiles_by_name| { 18 | remove_panic(&mut profiles_by_name); 19 | profiles_by_name 20 | }) 21 | .unwrap_or_default() 22 | } 23 | 24 | // Remove the `panic` key from `test` and `bench` profiles, which is ignored by `cargo`. 25 | fn remove_panic(profiles_by_name: &mut TomlProfile) { 26 | for (name, profile) in profiles_by_name.iter_mut() { 27 | if name == "test" || name == "bench" { 28 | profile.remove("panic"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Example projects 2 | 3 | This directory contains some example Cargo workspaces which demonstrate how to 4 | build and test your projects with `cargo2nix`. These also serve as a suite of 5 | integration tests to catch regressions in `cargo2nix` itself. 6 | 7 | ## [Hello World](./1-hello-world) 8 | 9 | No dependencies, just hello world 10 | 11 | ## [Bigger Project](./2-bigger-project) 12 | 13 | Just a program with a few dependencies. The flake expressions don't change that 14 | much. The generated nix expressions are more interesting if you want to 15 | understand the contents of a Cargo.nix. 16 | 17 | ## [Cross Compiling](./3-cross-compiling) 18 | 19 | This project can build on Linux for a variety of targets, including ARM and 20 | wasm. You can use qemu to run the results. 21 | 22 | ## [Independent Packaging](./4-independent-packaging) (Rust Analyzer) 23 | 24 | This shows how to consume someone else's Rust crate and ship a binary without 25 | the need to interact with their repository. 26 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Pull Requests 2 | 3 | Many files are generated. When you draft your changes, please separate out 4 | commits that affect: 5 | 6 | - Cargo.lock 7 | - Cargo.nix 8 | - flake.lock 9 | 10 | Keeping these changes isolated in specific commits makes it much easier to pull 11 | in your changes in parallel with other features. Maintainers may harvest your 12 | changes. We only guarantee to preserve authorship in the git log (when we 13 | remember to do so). 14 | 15 | ### Creating pull requests 16 | 17 | 1. Fork this repository into the personal GitHub account 18 | 2. Select the appropriate branch, release- for stable changes, unstable 19 | for breaking changes 20 | 3. Make changes on the personal fork 21 | 4. Make a Pull Request against this repository 22 | 5. **Allow maintainers to make changes to your pull request** (there's a checkbox) 23 | 6. Once the pull request has been approved, you will be thanked and observe your 24 | changes applied with authorship preserved (if we remember) 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Scope 4 | 5 | This Code of Conduct applies exclusively to this repository, contributors, commentors and maintainers of this 6 | repository, as well as communication between them in and outside the code hosting platform. 7 | 8 | ## Responsibility of Maintainers 9 | 10 | Maintainers of this repository are responsible for explaining the standards of acceptable behaviour 11 | in communication related to `cargo2nix`, and must execute corrective action appropriately and fairly 12 | in response to violation to the standards. 13 | They reserve the ultimate rights to editing, removal and rejection of materials in this repository, 14 | including but not restricted to code, pull requests, issues, wiki entries, documents and comments. 15 | They reserve the ultimate rights to ban, temporarily or permanently, any contributor for behaviours 16 | deemed inappropriate, threatening, offensive or harmful to the users and maintainers of this repository 17 | or violating the ethos of this Code of Conduct. 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 The cargo2nix developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /overlay/mkcrate-nobuild.nix: -------------------------------------------------------------------------------- 1 | # Creates a derivation for a crate for inputs propagation only. 2 | { lib, stdenv }: 3 | { 4 | release, # Compiling in release mode? 5 | name, 6 | version, 7 | registry, 8 | src, 9 | features ? [ ], 10 | dependencies ? { }, 11 | devDependencies ? { }, 12 | buildDependencies ? { }, 13 | compileMode ? "build", 14 | profile, 15 | meta ? { }, 16 | cargoUnstableFlags ? [ ], 17 | rustcLinkFlags ? [ ], 18 | rustcBuildFlags ? [ ], 19 | hostPlatformCpu ? null, 20 | hostPlatformFeatures ? [], 21 | target ? null, 22 | codegenOpts, 23 | profileOpts, 24 | }: 25 | with lib; with builtins; 26 | let 27 | inherit 28 | (({ right, wrong }: { runtimeDependencies = right; buildtimeDependencies = wrong; }) 29 | (partition (drv: drv.stdenv.hostPlatform == stdenv.hostPlatform) 30 | (concatLists [ 31 | (attrValues dependencies) 32 | (optionals (compileMode == "test" || compileMode == "bench") (attrValues devDependencies)) 33 | (attrValues buildDependencies) 34 | ]))) 35 | runtimeDependencies buildtimeDependencies; 36 | in stdenv.mkDerivation { 37 | name = "crate-${name}-${version}"; 38 | propagatedBuildInputs = concatMap (drv: drv.propagatedBuildInputs) runtimeDependencies; 39 | phases = "installPhase fixupPhase"; 40 | installPhase = "mkdir -p $out"; 41 | preferLocalBuild = true; 42 | allowSubstitutes = false; 43 | } 44 | -------------------------------------------------------------------------------- /overlay/lib/profiles.nix: -------------------------------------------------------------------------------- 1 | let 2 | profileNames = [ 3 | "release" 4 | "dev" 5 | "test" 6 | "bench" 7 | "__noProfile" # A placeholder profile for build scripts, which don't respect any profiles. 8 | ]; 9 | in 10 | { 11 | # Decides which profile to use based on compile mode and whether release is enabled. 12 | # Ported from https://github.com/rust-lang/cargo/blob/rust-1.38.0/src/cargo/core/profiles.rs#L86. 13 | decideProfile = compileMode: release: 14 | if compileMode == "test" || compileMode == "bench" || compileMode == "doctest" 15 | then if release then "bench" else "test" 16 | else if compileMode == "build" || compileMode == null 17 | then if release then "release" else "dev" 18 | else 19 | throw "unknown compile mode"; 20 | 21 | # Generates a set whose keys are all available profile names (see above). 22 | # Type: Map ProfileName Profile -> (ProfileName -> Profile -> a) -> Map ProfileName a 23 | genDrvsByProfile = profilesByName: f: 24 | let 25 | nullProfileDrv = f { profileName = "__noProfile"; profile = null; }; 26 | in 27 | builtins.listToAttrs 28 | (builtins.map 29 | (profileName: { 30 | name = profileName; 31 | value = if profilesByName ? "${profileName}" 32 | then f { inherit profileName; profile = profilesByName.${profileName}; } 33 | else nullProfileDrv; 34 | }) 35 | profileNames); 36 | } 37 | -------------------------------------------------------------------------------- /overlay/default.nix: -------------------------------------------------------------------------------- 1 | rust-overlay: 2 | 3 | let 4 | # Overlay is what provides all of Cargo2nix's modifications to nixpkgs 5 | cargo2nixOverlay = final: prev: 6 | let 7 | inherit (final) lib newScope; 8 | pkgs = final; 9 | scope = final: 10 | let 11 | inherit (final) callPackage; 12 | in 13 | { 14 | rustLib = callPackage ./lib { }; 15 | 16 | makePackageSetInternal = callPackage ./make-package-set/internal.nix { }; 17 | 18 | makePackageSet = pkgs.callPackage ./make-package-set/user-facing.nix { }; 19 | 20 | mkRustCrate = import ./mkcrate.nix; 21 | 22 | mkRustCrateNoBuild = callPackage ./mkcrate-nobuild.nix; 23 | 24 | overrides = callPackage ./overrides.nix { }; 25 | 26 | runTests = callPackage ./run-tests.nix { }; 27 | 28 | workspaceShell = import ./workspace-shell.nix; 29 | }; 30 | in 31 | { 32 | # The single top-level attribute that user-facing API's are exposed through 33 | rustBuilder = lib.makeScope newScope scope; 34 | }; 35 | in rec { 36 | # These three overlays are exposed in the cargo2nix flake as cargo2nix.overlays 37 | # The combined overlay is the most conveient to use. 38 | inherit rust-overlay; 39 | cargo2nix = cargo2nixOverlay; 40 | default = combined; 41 | combined = final: prev: 42 | let 43 | composeOverlays = overlays: final: prev: 44 | prev.lib.foldl' (prev.lib.flip prev.lib.extends) (prev.lib.const prev) overlays final; 45 | in 46 | composeOverlays [ rust-overlay cargo2nixOverlay ] final prev; 47 | } 48 | -------------------------------------------------------------------------------- /overlay/lib/fetch.nix: -------------------------------------------------------------------------------- 1 | { buildPackages, lib }: 2 | rec { 3 | fetchCratesIo = { name, version, sha256 }: buildPackages.fetchurl { 4 | name = "${name}-${version}.tar.gz"; 5 | url = "https://crates.io/api/v1/crates/${name}/${version}/download"; 6 | inherit sha256; 7 | }; 8 | 9 | fetchCrateGit = { url, name, version, rev, ref ? "HEAD" }: builtins.fetchGit { 10 | inherit url rev ref; 11 | submodules = true; 12 | }; 13 | 14 | # This implementation of `fetchCrateAlternativeRegistry` assumes that the download URL is updated frequently 15 | # on the registry index as a workaround for the lack of authentication for crate downloads. Each time a crate needs 16 | # to be downloaded, the whole index needs to be recloned to get the download URL, which is potentially expensive. 17 | fetchCrateAlternativeRegistryExpensive = { index, name, version, sha256 }: with buildPackages; stdenvNoCC.mkDerivation { 18 | name = "${name}-${version}.tar.gz"; 19 | src = builtins.fetchGit { url = index; ref = "master"; }; 20 | 21 | CRATE_NAME = name; 22 | CRATE_VERSION = version; 23 | 24 | outputHashAlgo = "sha256"; 25 | outputHashMode = "flat"; 26 | outputHash = sha256; 27 | 28 | nativeBuildInputs = [ curl cacert jq ]; 29 | 30 | builder = builtins.toFile "builder.sh" '' 31 | source "$stdenv/setup" 32 | dl="$(jq -r ".dl" "$src/config.json")" 33 | if [[ "$dl" =~ "{crate}" ]]; then 34 | url="$(sed -e "s/{crate}/$CRATE_NAME/" -e "s/{version}/$CRATE_VERSION/" <<< "$dl")" 35 | else 36 | url="$dl/$CRATE_NAME/$CRATE_VERSION/download" 37 | fi 38 | curl -L "$url" -o "$out" 39 | ''; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /examples/1-hello-world/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | cargo2nix.url = "path:../../"; 4 | # Use a github flake URL for real packages 5 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 6 | flake-utils.follows = "cargo2nix/flake-utils"; 7 | nixpkgs.follows = "cargo2nix/nixpkgs"; 8 | }; 9 | 10 | outputs = inputs: with inputs; # pass through all inputs and bring them into scope 11 | 12 | # Build the output set for each default system and map system sets into 13 | # attributes, resulting in paths such as: 14 | # nix build .#packages.x86_64-linux. 15 | flake-utils.lib.eachDefaultSystem (system: 16 | 17 | # let-in expressions, very similar to Rust's let bindings. These names 18 | # are used to express the output but not themselves paths in the output. 19 | let 20 | 21 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 22 | pkgs = import nixpkgs { 23 | inherit system; 24 | overlays = [ cargo2nix.overlays.default ]; 25 | }; 26 | 27 | # create the workspace & dependencies package set 28 | rustPkgs = pkgs.rustBuilder.makePackageSet { 29 | rustVersion = "1.75.0"; 30 | packageFun = import ./Cargo.nix; 31 | }; 32 | 33 | in rec { 34 | # this is the output (recursive) set (expressed for each system) 35 | 36 | # the packages in `nix build .#packages..` 37 | packages = { 38 | # nix build .#hello-world 39 | # nix build .#packages.x86_64-linux.hello-world 40 | hello-world = (rustPkgs.workspace.hello-world {}); 41 | # nix build 42 | default = packages.hello-world; # rec 43 | }; 44 | } 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /examples/2-bigger-project/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | cargo2nix.url = "path:../../"; 4 | # Use a github flake URL for real packages 5 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 6 | flake-utils.follows = "cargo2nix/flake-utils"; 7 | nixpkgs.follows = "cargo2nix/nixpkgs"; 8 | }; 9 | 10 | outputs = inputs: with inputs; # pass through all inputs and bring them into scope 11 | 12 | # Build the output set for each default system and map system sets into 13 | # attributes, resulting in paths such as: 14 | # nix build .#packages.x86_64-linux. 15 | flake-utils.lib.eachDefaultSystem (system: 16 | 17 | # let-in expressions, very similar to Rust's let bindings. These names 18 | # are used to express the output but not themselves paths in the output. 19 | let 20 | 21 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 22 | pkgs = import nixpkgs { 23 | inherit system; 24 | overlays = [ cargo2nix.overlays.default ]; 25 | }; 26 | 27 | # create the workspace & dependencies package set 28 | rustPkgs = pkgs.rustBuilder.makePackageSet { 29 | rustVersion = "1.75.0"; 30 | packageFun = import ./Cargo.nix; 31 | # packageOverrides = pkgs: pkgs.rustBuilder.overrides.all; # Implied, if not specified 32 | }; 33 | 34 | in rec { 35 | # this is the output (recursive) set (expressed for each system) 36 | 37 | # the packages in `nix build .#packages..` 38 | packages = { 39 | # nix build .#bigger-project 40 | # nix build .#packages.x86_64-linux.bigger-project 41 | bigger-project = (rustPkgs.workspace.bigger-project {}); 42 | # nix build 43 | default = packages.bigger-project; 44 | }; 45 | } 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /examples/4-independent-packaging/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | cargo2nix.url = "path:../../"; 4 | # Use a github flake URL for real packages 5 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 6 | flake-utils.follows = "cargo2nix/flake-utils"; 7 | nixpkgs.follows = "cargo2nix/nixpkgs"; 8 | rust-analyzer-src = { 9 | url = "github:rust-lang/rust-analyzer/?rev=9db515503f11bda7812cdfb9c2839f70f4cb1fdd"; 10 | flake = false; 11 | }; 12 | }; 13 | outputs = inputs: with inputs; 14 | 15 | # Build the output set for each default system, resulting in paths such as: 16 | # nix build .#pkgs.x86_64-linux. 17 | flake-utils.lib.eachDefaultSystem (system: 18 | let 19 | 20 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 21 | pkgs = import nixpkgs { 22 | inherit system; 23 | overlays = [ cargo2nix.overlays.default ]; 24 | }; 25 | 26 | # create the workspace & dependencies package set 27 | rustPkgs = pkgs.rustBuilder.makePackageSet { 28 | # rust toolchain version 29 | rustVersion = "1.75.0"; 30 | # nixified Cargo.lock 31 | packageFun = import ./Cargo.nix; 32 | ignoreLockHash = true; 33 | 34 | # Provide the gperfools lib for linking the final rust-analyzer binary 35 | packageOverrides = pkgs: pkgs.rustBuilder.overrides.all ++ [ 36 | (pkgs.rustBuilder.rustLib.makeOverride { 37 | name = "rust-analyzer"; 38 | overrideAttrs = drv: { 39 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ 40 | pkgs.gperftools 41 | ]; 42 | }; 43 | }) 44 | ]; 45 | 46 | workspaceSrc = rust-analyzer-src; 47 | # You can also use local paths for local development with a checked out copy 48 | # workspaceSrc = ../../../upstream/rust-analyzer; 49 | }; 50 | 51 | in rec { 52 | packages = { 53 | # nix build .#rust-analyzer 54 | rust-analyzer = (rustPkgs.workspace.rust-analyzer {}); 55 | # nix build 56 | default = packages.rust-analyzer; 57 | }; 58 | } 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /examples/1-hello-world/Cargo.nix: -------------------------------------------------------------------------------- 1 | # This file was @generated by cargo2nix 0.11.0. 2 | # It is not intended to be manually edited. 3 | 4 | args@{ 5 | release ? true, 6 | rootFeatures ? [ 7 | "hello-world/default" 8 | ], 9 | rustPackages, 10 | buildRustPackages, 11 | hostPlatform, 12 | hostPlatformCpu ? null, 13 | hostPlatformFeatures ? [], 14 | target ? null, 15 | codegenOpts ? null, 16 | profileOpts ? null, 17 | cargoUnstableFlags ? null, 18 | rustcLinkFlags ? null, 19 | rustcBuildFlags ? null, 20 | mkRustCrate, 21 | rustLib, 22 | lib, 23 | workspaceSrc, 24 | ignoreLockHash, 25 | }: 26 | let 27 | nixifiedLockHash = "86421e8411c52265e52f81b8472270ebd2bf444f999c0a881d1c544b4e8a0d29"; 28 | workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc; 29 | currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock); 30 | lockHashIgnored = if ignoreLockHash 31 | then builtins.trace "Ignoring lock hash" ignoreLockHash 32 | else ignoreLockHash; 33 | in if !lockHashIgnored && (nixifiedLockHash != currentLockHash) then 34 | throw ("Cargo.nix ${nixifiedLockHash} is out of sync with Cargo.lock ${currentLockHash}") 35 | else let 36 | inherit (rustLib) fetchCratesIo fetchCrateLocal fetchCrateGit fetchCrateAlternativeRegistry expandFeatures decideProfile genDrvsByProfile; 37 | profilesByName = { 38 | }; 39 | rootFeatures' = expandFeatures rootFeatures; 40 | overridableMkRustCrate = f: 41 | let 42 | drvs = genDrvsByProfile profilesByName ({ profile, profileName }: mkRustCrate ({ inherit release profile hostPlatformCpu hostPlatformFeatures target profileOpts codegenOpts cargoUnstableFlags rustcLinkFlags rustcBuildFlags; } // (f profileName))); 43 | in { compileMode ? null, profileName ? decideProfile compileMode release }: 44 | let drv = drvs.${profileName}; in if compileMode == null then drv else drv.override { inherit compileMode; }; 45 | in 46 | { 47 | cargo2nixVersion = "0.11.0"; 48 | workspace = { 49 | hello-world = rustPackages.unknown.hello-world."0.1.0"; 50 | }; 51 | "unknown".hello-world."0.1.0" = overridableMkRustCrate (profileName: rec { 52 | name = "hello-world"; 53 | version = "0.1.0"; 54 | registry = "unknown"; 55 | src = fetchCrateLocal workspaceSrc; 56 | }); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /overlay/lib/rust-triple.nix: -------------------------------------------------------------------------------- 1 | # The stdenv.buildPlatform and stdenv.hostPlatform configurations need 2 | # translation to triples that Rust understands. These are consumed when writing 3 | # the synthetic Cargo.toml and when invoking cargo, as CXX_flags, and when 4 | # installing crates. Most of this takes place in mkcrate.nix. 5 | 6 | # Please update this file if you find a missing pair. 7 | # Examples of the kinds of values in Nix platform.system: 8 | # nix repl '' 9 | # nix-repl> lib.systems.doubles.wasi 10 | # [ "wasm64-wasi" "wasm32-wasi" ] 11 | # nix-repl> lib.systems.examples.wasi32 12 | # { config = "wasm32-unknown-wasi"; useLLVM = true; } 13 | 14 | # The list of triples (which are not all triples) that Rust may recognize 15 | # https://rust-lang.github.io/rustup-components-history/ 16 | 17 | platform: 18 | let 19 | cpu = if platform.parsed.cpu.name == "armv6" then "arm" else platform.parsed.cpu.name; 20 | vendor = platform.parsed.vendor.name; 21 | kernel = platform.parsed.kernel.name; 22 | abi = platform.parsed.abi.name; 23 | in { 24 | # Nix platform.system # Supported Rust targets 25 | "i686-linux" = "i686-unknown-linux-${abi}"; 26 | "x86_64-linux" = "x86_64-unknown-linux-${abi}"; 27 | "armv5tel-linux" = "arm-unknown-linux-${abi}"; 28 | "armv6l-linux" = "arm-unknown-linux-${abi}"; 29 | "armv7a-android" = "armv7-linux-androideabi"; 30 | "armv7l-linux" = "armv7-unknown-linux-${abi}"; 31 | "aarch64-darwin" = "aarch64-apple-darwin"; 32 | "aarch64-linux" = "aarch64-unknown-linux-${abi}"; 33 | "mips64el-linux" = "mips64el-unknown-linux-${abi}"; 34 | "x86_64-darwin" = "x86_64-apple-darwin"; 35 | "i686-cygwin" = "i686-pc-windows-${abi}"; 36 | "x86_64-cygwin" = "x86_64-pc-windows-${abi}"; 37 | "x86_64-freebsd" = "x86_64-unknown-freebsd"; 38 | "wasm64-wasi" = "wasm64-wasi"; # unsupported 39 | "wasm32-emscripten" = "wasm32-unknown-emscripten"; 40 | "wasm32-unknown" = "wasm32-unknown-unknown"; 41 | "wasm32-wasi" = "wasm32-wasi"; 42 | "wasm32-unknown-wasi" = "wasm32-wasi"; 43 | "riscv64-linux" = "riscv64gc-unknown-linux-${abi}"; 44 | "riscv32-linux" = "riscv32gc-unknown-linux-${abi}"; 45 | }.${platform.system} or platform.config 46 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | cargo2nix.url = "path:../../"; 4 | # Use a github flake URL for real packages 5 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 6 | flake-utils.follows = "cargo2nix/flake-utils"; 7 | nixpkgs.follows = "cargo2nix/nixpkgs"; 8 | }; 9 | 10 | outputs = inputs: with inputs; 11 | 12 | # Build the output set for each default system and map system sets into 13 | # attributes, resulting in paths such as: 14 | # nix build .#packages.x86_64-linux. 15 | flake-utils.lib.eachDefaultSystem (system: 16 | 17 | # let-in expressions, very similar to Rust's let bindings. These names 18 | # are used to express the output but not themselves paths in the output. 19 | let 20 | 21 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 22 | pkgs = import nixpkgs { 23 | inherit system; 24 | 25 | # crossSystem = { 26 | # config = "aarch64-unknown-linux-gnu"; 27 | # }; 28 | 29 | 30 | # crossSystem = { 31 | # config = "x86_64-unknown-linux-musl"; 32 | # }; 33 | 34 | crossSystem = { 35 | config = "wasm32-unknown-wasi"; 36 | # Nixpkgs currently only supports LLVM lld linker for wasm32-wasi. 37 | useLLVM = true; 38 | }; 39 | 40 | overlays = [ cargo2nix.overlays.default ]; 41 | }; 42 | 43 | # create the workspace & dependencies package set 44 | rustPkgs = pkgs.rustBuilder.makePackageSet { 45 | rustVersion = "1.75.0"; 46 | packageFun = import ./Cargo.nix; 47 | 48 | # If your specific build target requires a difference between Rust and 49 | # nixpkgs, set this target 50 | # target = "aarch64-unknown-linux-gnu"; 51 | # target = "x86_64-unknown-linux-musl"; 52 | # target = "wasm32-wasi"; 53 | }; 54 | 55 | in rec { 56 | # this is the output (recursive) set (expressed for each system) 57 | 58 | # the packages in `nix build .#packages..` 59 | packages = { 60 | # nix build .#cross-compiling 61 | # nix build .#packages.x86_64-linux.cross-compiling 62 | cross-compiling = (rustPkgs.workspace.cross-compiling {}); 63 | # nix build 64 | default = packages.cross-compiling; 65 | }; 66 | } 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /overlay/lib/overrides.nix: -------------------------------------------------------------------------------- 1 | # An overrider has the ability to modify the arguments passed to a function. It takes the original set of arguments 2 | # and returns a new set of arguments, which will then be merged with the original set to become the new arguments 3 | # to the original function. 4 | # type Overrider = Attrs -> Attrs 5 | # 6 | # An override has the ability to modify the arguments passed to a function, and the attribute 7 | # set of the derivation returned by that function. It takes the original set of arguments and 8 | # optionally returns 2 overriders: `overrideArgs`, which overrides the argument set, and `overrideAttr`, 9 | # which overrides the derivation attributes. 10 | # See https://nixos.org/nixpkgs/manual/#sec-pkg-override for more information on `overrideArgs`, and 11 | # https://nixos.org/nixpkgs/manual/#sec-pkg-overrideAttrs for `overrideAttrs`. 12 | # type Override = Attrs -> { overrideArgs: Maybe Overrider, overrideAttrs: Maybe Overrider } 13 | let 14 | combineOverriders = a: b: 15 | if a == null then b 16 | else if b == null then a 17 | else oldAttrs: let attrs = oldAttrs // a oldAttrs; in attrs // b attrs; 18 | 19 | nullOverriders = { overrideArgs = null; overrideAttrs = null; }; 20 | in 21 | { 22 | # Constructs an override for `mkRustCrate`. 23 | # - `registry`, `name`, `version` specify which crates this override applies to. 24 | # - `overrideArgs` overrides the argument set passed to `mkRustCrate`. 25 | # - `overrideAttrs` overrides the attribute set of the derivation returned by `mkRustCrate`. 26 | makeOverride = args@{ registry ? null, name ? null, version ? null, overrideArgs ? null, overrideAttrs ? null }: 27 | assert overrideArgs != null || overrideAttrs != null; 28 | let 29 | matcher = builtins.intersectAttrs { registry = { }; name = { }; version = { }; } args; 30 | overriders = { inherit overrideArgs overrideAttrs; }; 31 | in 32 | args: 33 | if builtins.intersectAttrs matcher args == matcher 34 | then overriders 35 | else nullOverriders; 36 | 37 | combineOverrides = left: right: args: 38 | let 39 | leftOverriders = left args; 40 | rightOverriders = right args; 41 | in 42 | { 43 | overrideArgs = combineOverriders leftOverriders.overrideArgs rightOverriders.overrideArgs; 44 | overrideAttrs = combineOverriders leftOverriders.overrideAttrs rightOverriders.overrideAttrs; 45 | }; 46 | 47 | # Applies an override to a function. 48 | runOverride = override: f: args: 49 | let 50 | overriders = override args; 51 | drv = f (if overriders.overrideArgs == null then args else (args // overriders.overrideArgs args)); 52 | in 53 | if overriders.overrideAttrs == null then drv else drv.overrideAttrs overriders.overrideAttrs; 54 | 55 | nullOverride = _: nullOverriders; 56 | } 57 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-utils": { 20 | "inputs": { 21 | "systems": "systems" 22 | }, 23 | "locked": { 24 | "lastModified": 1694529238, 25 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 26 | "owner": "numtide", 27 | "repo": "flake-utils", 28 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "numtide", 33 | "repo": "flake-utils", 34 | "type": "github" 35 | } 36 | }, 37 | "nixpkgs": { 38 | "locked": { 39 | "lastModified": 1705099185, 40 | "narHash": "sha256-SxJenKtvcrKJd0TyJQMO3p6VA7PEp+vmMnmlKFzWMNs=", 41 | "owner": "nixos", 42 | "repo": "nixpkgs", 43 | "rev": "2bce5ccff0ad7abda23e8bb56434b6877a446694", 44 | "type": "github" 45 | }, 46 | "original": { 47 | "owner": "nixos", 48 | "ref": "release-23.11", 49 | "repo": "nixpkgs", 50 | "type": "github" 51 | } 52 | }, 53 | "root": { 54 | "inputs": { 55 | "flake-compat": "flake-compat", 56 | "flake-utils": "flake-utils", 57 | "nixpkgs": "nixpkgs", 58 | "rust-overlay": "rust-overlay" 59 | } 60 | }, 61 | "rust-overlay": { 62 | "inputs": { 63 | "flake-utils": [ 64 | "flake-utils" 65 | ], 66 | "nixpkgs": [ 67 | "nixpkgs" 68 | ] 69 | }, 70 | "locked": { 71 | "lastModified": 1705112162, 72 | "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", 73 | "owner": "oxalica", 74 | "repo": "rust-overlay", 75 | "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", 76 | "type": "github" 77 | }, 78 | "original": { 79 | "owner": "oxalica", 80 | "repo": "rust-overlay", 81 | "type": "github" 82 | } 83 | }, 84 | "systems": { 85 | "locked": { 86 | "lastModified": 1681028828, 87 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 88 | "owner": "nix-systems", 89 | "repo": "default", 90 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 91 | "type": "github" 92 | }, 93 | "original": { 94 | "owner": "nix-systems", 95 | "repo": "default", 96 | "type": "github" 97 | } 98 | } 99 | }, 100 | "root": "root", 101 | "version": 7 102 | } 103 | -------------------------------------------------------------------------------- /overlay/make-package-set/internal.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | rustBuilder, 5 | rustLib, 6 | stdenv, 7 | mkRustCrate, 8 | mkRustCrateNoBuild, 9 | workspaceShell, 10 | }: 11 | { 12 | packageFun, 13 | workspaceSrc ? null, 14 | rustToolchain, 15 | buildRustPackages ? null, 16 | packageOverrides ? rustBuilder.overrides.all, 17 | fetchCrateAlternativeRegistry ? _: throw "fetchCrateAlternativeRegistry is required, but not specified in makePackageSet", 18 | release ? null, 19 | rootFeatures ? null, 20 | hostPlatformCpu ? null, 21 | hostPlatformFeatures ? [], 22 | target, 23 | codegenOpts ? null, 24 | profileOpts ? null, 25 | cargoUnstableFlags ? [], 26 | rustcLinkFlags ? [], 27 | rustcBuildFlags ? [], 28 | ignoreLockHash, 29 | }: 30 | lib.fix' (self: 31 | let 32 | rustPackages = self; 33 | buildRustPackages' = if buildRustPackages == null then self else buildRustPackages; 34 | mkScope = scope: 35 | let 36 | prevStage = pkgs.__splicedPackages; 37 | scopeSpliced = rustLib.splicePackages (buildRustPackages != null) { 38 | pkgsBuildBuild = scope.buildRustPackages.buildRustPackages; 39 | pkgsBuildHost = scope.buildRustPackages; 40 | pkgsBuildTarget = {}; 41 | pkgsHostHost = {}; 42 | pkgsHostTarget = scope; 43 | pkgsTargetTarget = {}; 44 | } // { 45 | inherit (scope) pkgs buildRustPackages rustToolchain config __splicedPackages; 46 | }; 47 | in 48 | prevStage // prevStage.xorg // prevStage.gnome2 // { inherit stdenv; } // scopeSpliced; 49 | defaultScope = mkScope self; 50 | callPackage = lib.callPackageWith defaultScope; 51 | 52 | mkRustCrate' = lib.makeOverridable (callPackage mkRustCrate { inherit rustLib; }); 53 | combinedOverride = builtins.foldl' rustLib.combineOverrides rustLib.nullOverride packageOverrides; 54 | packageFunWith = { mkRustCrate, buildRustPackages }: lib.fix (rustPackages: packageFun { 55 | inherit rustPackages buildRustPackages lib workspaceSrc target profileOpts codegenOpts cargoUnstableFlags rustcLinkFlags rustcBuildFlags ignoreLockHash; 56 | inherit (stdenv) hostPlatform; 57 | mkRustCrate = rustLib.runOverride combinedOverride mkRustCrate; 58 | rustLib = rustLib // { 59 | inherit fetchCrateAlternativeRegistry; 60 | fetchCrateLocal = path: path; 61 | }; 62 | ${ if release == null then null else "release" } = release; 63 | ${ if rootFeatures == null then null else "rootFeatures" } = rootFeatures; 64 | ${ if hostPlatformCpu == null then null else "hostPlatformCpu" } = hostPlatformCpu; 65 | ${ if hostPlatformFeatures == [] then null else "hostPlatformFeatures" } = hostPlatformFeatures; 66 | }); 67 | 68 | noBuild = packageFunWith { 69 | mkRustCrate = lib.makeOverridable mkRustCrateNoBuild { }; 70 | buildRustPackages = buildRustPackages'.noBuild; 71 | }; 72 | 73 | in packageFunWith { mkRustCrate = mkRustCrate'; buildRustPackages = buildRustPackages'; } // { 74 | inherit rustPackages callPackage pkgs rustToolchain noBuild; 75 | workspaceShell = workspaceShell { inherit pkgs rustPackages rustToolchain; }; 76 | mkRustCrate = mkRustCrate'; 77 | buildRustPackages = buildRustPackages'; 78 | __splicedPackages = defaultScope; 79 | } 80 | ) 81 | -------------------------------------------------------------------------------- /src/platform.rs: -------------------------------------------------------------------------------- 1 | use cargo_platform::{CfgExpr, Platform}; 2 | 3 | use crate::expr::BoolExpr; 4 | 5 | pub fn to_expr(p: &Platform, platform_var: &str) -> BoolExpr { 6 | match p { 7 | Platform::Name(triplet) => { 8 | BoolExpr::Single(format!("{}.config == {:?}", platform_var, triplet)) 9 | } 10 | Platform::Cfg(cfg) => cfg_to_expr(cfg, platform_var), 11 | } 12 | } 13 | 14 | fn platform_single(platform: &str, nix_cpu: &str) -> BoolExpr { 15 | BoolExpr::Single(format!("{}.parsed.cpu.name == {:?}", platform, nix_cpu)) 16 | } 17 | fn target_arch_to_nix(platform: &str, target_arch: &str) -> BoolExpr { 18 | match target_arch { 19 | "arm" => BoolExpr::ors(vec![ 20 | platform_single(platform, "armv6l"), 21 | platform_single(platform, "armv7l"), 22 | ]), 23 | "x86" => platform_single(platform, "i686"), 24 | _ => platform_single(platform, target_arch), 25 | } 26 | } 27 | 28 | fn cfg_to_expr(cfg: &CfgExpr, platform_var: &str) -> BoolExpr { 29 | use self::BoolExpr::{False, Single}; 30 | use cargo_platform::Cfg; 31 | 32 | match cfg { 33 | CfgExpr::Not(c) => cfg_to_expr(c, platform_var).not(), 34 | CfgExpr::All(cfgs) => BoolExpr::ands(cfgs.iter().map(|c| cfg_to_expr(c, platform_var))), 35 | CfgExpr::Any(cfgs) => BoolExpr::ors(cfgs.iter().map(|c| cfg_to_expr(c, platform_var))), 36 | CfgExpr::Value(Cfg::Name(n)) => match n.as_str() { 37 | "windows" => Single(format!("{}.isWindows", platform_var)), 38 | "unix" => Single(format!("{}.isUnix", platform_var)), 39 | _ => False, 40 | }, 41 | CfgExpr::Value(Cfg::KeyPair(k, v)) => match (k.as_str(), v.as_str()) { 42 | ("target_arch", v) => target_arch_to_nix(platform_var, v), 43 | ("target_os", "macos") => Single(format!( 44 | "{}.parsed.kernel.name == {:?}", 45 | platform_var, "darwin" 46 | )), 47 | ("target_os", v) => Single(format!("{}.parsed.kernel.name == {:?}", platform_var, v)), 48 | ("target_family", "unix") => Single(format!("{}.isUnix", platform_var)), 49 | ("target_family", "windows") => Single(format!("{}.isWindows", platform_var)), 50 | ("target_env", "sgx") => { 51 | Single(format!("target == {:?}", "x86_64-fortanix-unknown-sgx")) 52 | } 53 | ("target_env", v) => Single(format!("{}.parsed.abi.name == {:?}", platform_var, v)), 54 | ("target_endian", "little") => Single(format!( 55 | "{}.parsed.cpu.significantByte == {:?}", 56 | platform_var, "littleEndian" 57 | )), 58 | ("target_endian", "big") => Single(format!( 59 | "{}.parsed.cpu.significantByte == {:?}", 60 | platform_var, "bigEndian" 61 | )), 62 | ("target_pointer_width", "32") => { 63 | Single(format!("{}.parsed.cpu.bits == 32", platform_var)) 64 | } 65 | ("target_pointer_width", "64") => { 66 | Single(format!("{}.parsed.cpu.bits == 64", platform_var)) 67 | } 68 | ("target_vendor", v) => { 69 | Single(format!("{}.parsed.vendor.name == {:?}", platform_var, v)) 70 | } 71 | ("target_cpu", v) => Single(format!("{}Cpu == {:?}", platform_var, v)), 72 | ("target_feature", v) => { 73 | Single(format!("builtins.elem {:?} {}Features", v, platform_var,)) 74 | } 75 | _ => False, 76 | }, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /examples/1-hello-world/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "cargo2nix": { 4 | "inputs": { 5 | "flake-compat": "flake-compat", 6 | "flake-utils": "flake-utils", 7 | "nixpkgs": "nixpkgs", 8 | "rust-overlay": "rust-overlay" 9 | }, 10 | "locked": { 11 | "lastModified": 0, 12 | "narHash": "sha256-ptZO38KE7mFYYxTNyPnPk0dE5L4XznugSg+pHlUY1Pk=", 13 | "path": "../../", 14 | "type": "path" 15 | }, 16 | "original": { 17 | "path": "../../", 18 | "type": "path" 19 | } 20 | }, 21 | "flake-compat": { 22 | "flake": false, 23 | "locked": { 24 | "lastModified": 1696426674, 25 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 26 | "owner": "edolstra", 27 | "repo": "flake-compat", 28 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "edolstra", 33 | "repo": "flake-compat", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1694529238, 43 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixpkgs": { 56 | "locked": { 57 | "lastModified": 1705099185, 58 | "narHash": "sha256-SxJenKtvcrKJd0TyJQMO3p6VA7PEp+vmMnmlKFzWMNs=", 59 | "owner": "nixos", 60 | "repo": "nixpkgs", 61 | "rev": "2bce5ccff0ad7abda23e8bb56434b6877a446694", 62 | "type": "github" 63 | }, 64 | "original": { 65 | "owner": "nixos", 66 | "ref": "release-23.11", 67 | "repo": "nixpkgs", 68 | "type": "github" 69 | } 70 | }, 71 | "root": { 72 | "inputs": { 73 | "cargo2nix": "cargo2nix", 74 | "flake-utils": [ 75 | "cargo2nix", 76 | "flake-utils" 77 | ], 78 | "nixpkgs": [ 79 | "cargo2nix", 80 | "nixpkgs" 81 | ] 82 | } 83 | }, 84 | "rust-overlay": { 85 | "inputs": { 86 | "flake-utils": [ 87 | "cargo2nix", 88 | "flake-utils" 89 | ], 90 | "nixpkgs": [ 91 | "cargo2nix", 92 | "nixpkgs" 93 | ] 94 | }, 95 | "locked": { 96 | "lastModified": 1705112162, 97 | "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", 98 | "owner": "oxalica", 99 | "repo": "rust-overlay", 100 | "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", 101 | "type": "github" 102 | }, 103 | "original": { 104 | "owner": "oxalica", 105 | "repo": "rust-overlay", 106 | "type": "github" 107 | } 108 | }, 109 | "systems": { 110 | "locked": { 111 | "lastModified": 1681028828, 112 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 113 | "owner": "nix-systems", 114 | "repo": "default", 115 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 116 | "type": "github" 117 | }, 118 | "original": { 119 | "owner": "nix-systems", 120 | "repo": "default", 121 | "type": "github" 122 | } 123 | } 124 | }, 125 | "root": "root", 126 | "version": 7 127 | } 128 | -------------------------------------------------------------------------------- /examples/2-bigger-project/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "cargo2nix": { 4 | "inputs": { 5 | "flake-compat": "flake-compat", 6 | "flake-utils": "flake-utils", 7 | "nixpkgs": "nixpkgs", 8 | "rust-overlay": "rust-overlay" 9 | }, 10 | "locked": { 11 | "lastModified": 0, 12 | "narHash": "sha256-r2gPm8Iw5UWba/9804tTdTGtP5+yp6X7uyNKjh4+o7Q=", 13 | "path": "../../", 14 | "type": "path" 15 | }, 16 | "original": { 17 | "path": "../../", 18 | "type": "path" 19 | } 20 | }, 21 | "flake-compat": { 22 | "flake": false, 23 | "locked": { 24 | "lastModified": 1696426674, 25 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 26 | "owner": "edolstra", 27 | "repo": "flake-compat", 28 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "edolstra", 33 | "repo": "flake-compat", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1694529238, 43 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixpkgs": { 56 | "locked": { 57 | "lastModified": 1705099185, 58 | "narHash": "sha256-SxJenKtvcrKJd0TyJQMO3p6VA7PEp+vmMnmlKFzWMNs=", 59 | "owner": "nixos", 60 | "repo": "nixpkgs", 61 | "rev": "2bce5ccff0ad7abda23e8bb56434b6877a446694", 62 | "type": "github" 63 | }, 64 | "original": { 65 | "owner": "nixos", 66 | "ref": "release-23.11", 67 | "repo": "nixpkgs", 68 | "type": "github" 69 | } 70 | }, 71 | "root": { 72 | "inputs": { 73 | "cargo2nix": "cargo2nix", 74 | "flake-utils": [ 75 | "cargo2nix", 76 | "flake-utils" 77 | ], 78 | "nixpkgs": [ 79 | "cargo2nix", 80 | "nixpkgs" 81 | ] 82 | } 83 | }, 84 | "rust-overlay": { 85 | "inputs": { 86 | "flake-utils": [ 87 | "cargo2nix", 88 | "flake-utils" 89 | ], 90 | "nixpkgs": [ 91 | "cargo2nix", 92 | "nixpkgs" 93 | ] 94 | }, 95 | "locked": { 96 | "lastModified": 1705112162, 97 | "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", 98 | "owner": "oxalica", 99 | "repo": "rust-overlay", 100 | "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", 101 | "type": "github" 102 | }, 103 | "original": { 104 | "owner": "oxalica", 105 | "repo": "rust-overlay", 106 | "type": "github" 107 | } 108 | }, 109 | "systems": { 110 | "locked": { 111 | "lastModified": 1681028828, 112 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 113 | "owner": "nix-systems", 114 | "repo": "default", 115 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 116 | "type": "github" 117 | }, 118 | "original": { 119 | "owner": "nix-systems", 120 | "repo": "default", 121 | "type": "github" 122 | } 123 | } 124 | }, 125 | "root": "root", 126 | "version": 7 127 | } 128 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "cargo2nix": { 4 | "inputs": { 5 | "flake-compat": "flake-compat", 6 | "flake-utils": "flake-utils", 7 | "nixpkgs": "nixpkgs", 8 | "rust-overlay": "rust-overlay" 9 | }, 10 | "locked": { 11 | "lastModified": 0, 12 | "narHash": "sha256-QKUTF+OZa5YePllH3UaB5msxkHgt96Q3DfEODEnf2nY=", 13 | "path": "../../", 14 | "type": "path" 15 | }, 16 | "original": { 17 | "path": "../../", 18 | "type": "path" 19 | } 20 | }, 21 | "flake-compat": { 22 | "flake": false, 23 | "locked": { 24 | "lastModified": 1696426674, 25 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 26 | "owner": "edolstra", 27 | "repo": "flake-compat", 28 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "edolstra", 33 | "repo": "flake-compat", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1694529238, 43 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixpkgs": { 56 | "locked": { 57 | "lastModified": 1705099185, 58 | "narHash": "sha256-SxJenKtvcrKJd0TyJQMO3p6VA7PEp+vmMnmlKFzWMNs=", 59 | "owner": "nixos", 60 | "repo": "nixpkgs", 61 | "rev": "2bce5ccff0ad7abda23e8bb56434b6877a446694", 62 | "type": "github" 63 | }, 64 | "original": { 65 | "owner": "nixos", 66 | "ref": "release-23.11", 67 | "repo": "nixpkgs", 68 | "type": "github" 69 | } 70 | }, 71 | "root": { 72 | "inputs": { 73 | "cargo2nix": "cargo2nix", 74 | "flake-utils": [ 75 | "cargo2nix", 76 | "flake-utils" 77 | ], 78 | "nixpkgs": [ 79 | "cargo2nix", 80 | "nixpkgs" 81 | ] 82 | } 83 | }, 84 | "rust-overlay": { 85 | "inputs": { 86 | "flake-utils": [ 87 | "cargo2nix", 88 | "flake-utils" 89 | ], 90 | "nixpkgs": [ 91 | "cargo2nix", 92 | "nixpkgs" 93 | ] 94 | }, 95 | "locked": { 96 | "lastModified": 1705112162, 97 | "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", 98 | "owner": "oxalica", 99 | "repo": "rust-overlay", 100 | "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", 101 | "type": "github" 102 | }, 103 | "original": { 104 | "owner": "oxalica", 105 | "repo": "rust-overlay", 106 | "type": "github" 107 | } 108 | }, 109 | "systems": { 110 | "locked": { 111 | "lastModified": 1681028828, 112 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 113 | "owner": "nix-systems", 114 | "repo": "default", 115 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 116 | "type": "github" 117 | }, 118 | "original": { 119 | "owner": "nix-systems", 120 | "repo": "default", 121 | "type": "github" 122 | } 123 | } 124 | }, 125 | "root": "root", 126 | "version": 7 127 | } 128 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ 'release-*' ] 10 | pull_request: 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "build" 18 | build: 19 | strategy: 20 | matrix: 21 | os: [ubuntu-latest, macos-latest] 22 | # The type of runner that the job will run on 23 | runs-on: ${{ matrix.os }} 24 | 25 | # Steps represent a sequence of tasks that will be executed as part of the job 26 | steps: 27 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 28 | - uses: actions/checkout@v3.2.0 29 | - uses: cachix/install-nix-action@v23 30 | with: 31 | extra_nix_config: | 32 | log-lines = 200 33 | - uses: cachix/cachix-action@v12 34 | with: 35 | name: cargo2nix-gh 36 | authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} 37 | # Some paths may confuse cachix. Add them with a pipe to the pushFilter 38 | # pushFilter: "(cargo-1.60.0-x86_64-unknown-linux-gnu$|.drv.chroot$)" 39 | 40 | # Runs a single command using the runners shell 41 | - name: build cargo2nix 42 | run: | 43 | if [[ $RUNNER_OS == Linux ]]; then 44 | nix build 45 | else 46 | nix build \ 47 | --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 48 | fi 49 | 50 | - name: build tests 51 | run: | 52 | if [[ $RUNNER_OS == Linux ]]; then 53 | nix build .#ci 54 | else 55 | nix build .#ci \ 56 | --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 57 | fi 58 | 59 | - name: build examples 60 | run: | 61 | if [[ $RUNNER_OS == Linux ]]; then 62 | nix build ./examples/1-hello-world# \ 63 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA 64 | nix build ./examples/2-bigger-project# \ 65 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA 66 | nix build ./examples/3-cross-compiling# \ 67 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA 68 | nix build ./examples/4-independent-packaging# \ 69 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA 70 | else 71 | nix build ./examples/1-hello-world# \ 72 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA \ 73 | --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 74 | nix build ./examples/2-bigger-project# \ 75 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA \ 76 | --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 77 | # nix build ./examples/3-cross-compiling# 78 | # --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA \ 79 | # --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 80 | nix build ./examples/4-independent-packaging# \ 81 | --override-input cargo2nix github:cargo2nix/cargo2nix?rev=$GITHUB_SHA \ 82 | --override-input nixpkgs github:nixos/nixpkgs/nixpkgs-23.05-darwin 83 | fi 84 | 85 | - name: use dev shell 86 | # enter the shell with no environment paths and verify that toolchain is available 87 | run: nix develop --ignore-environment --command "rustc" "--version" 88 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.5.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 10 | 11 | [[package]] 12 | name = "base64" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 22 | 23 | [[package]] 24 | name = "bytecount" 25 | version = "0.6.4" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" 28 | 29 | [[package]] 30 | name = "cfg-if" 31 | version = "1.0.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 34 | 35 | [[package]] 36 | name = "cross-compiling" 37 | version = "0.1.0" 38 | dependencies = [ 39 | "ructe", 40 | ] 41 | 42 | [[package]] 43 | name = "either" 44 | version = "1.9.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 47 | 48 | [[package]] 49 | name = "itertools" 50 | version = "0.8.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 53 | dependencies = [ 54 | "either", 55 | ] 56 | 57 | [[package]] 58 | name = "lexical-core" 59 | version = "0.7.6" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 62 | dependencies = [ 63 | "arrayvec", 64 | "bitflags", 65 | "cfg-if", 66 | "ryu", 67 | "static_assertions", 68 | ] 69 | 70 | [[package]] 71 | name = "md5" 72 | version = "0.7.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 75 | 76 | [[package]] 77 | name = "memchr" 78 | version = "2.6.4" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 81 | 82 | [[package]] 83 | name = "nom" 84 | version = "5.1.3" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" 87 | dependencies = [ 88 | "lexical-core", 89 | "memchr", 90 | "version_check", 91 | ] 92 | 93 | [[package]] 94 | name = "ructe" 95 | version = "0.9.2" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "c85620b8046f88a870d93d90fa56904dec76cc79139bfcc22e71e87f0cd2169f" 98 | dependencies = [ 99 | "base64", 100 | "bytecount", 101 | "itertools", 102 | "md5", 103 | "nom", 104 | ] 105 | 106 | [[package]] 107 | name = "ryu" 108 | version = "1.0.15" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 111 | 112 | [[package]] 113 | name = "static_assertions" 114 | version = "1.1.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 117 | 118 | [[package]] 119 | name = "version_check" 120 | version = "0.9.4" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 123 | -------------------------------------------------------------------------------- /examples/4-independent-packaging/README.md: -------------------------------------------------------------------------------- 1 | # Independent Packaging 2 | 3 | This is how to use cargo2nix to package Rust software indpenendently of its 4 | hosting repository. This example uses flakes. 5 | 6 | ## Introduction 7 | 8 | It's not always desireable or best to nixify projects by embedding nix files 9 | into their repositories. This is in fact avoided in nixpkgs. Instead, you can 10 | ship a pre-made Cargo.nix and a default.nix that locates and provides the 11 | external source to produce a build. Rust Analyzer is packaged here as a 12 | demonstration. 13 | 14 | First clone the target repository at the commit you wish to generate a build 15 | for: 16 | 17 | ```shell 18 | git clone --depth 1 --branch 2021-11-08 git@github.com:rust-analyzer/rust-analyzer.git 19 | ``` 20 | 21 | Inside the clone, generate / update the lock file and then create a `Cargo.nix` 22 | 23 | ```shell 24 | cargo generate-lockfile 25 | cargo2nix 26 | ``` 27 | 28 | Copy just the generated `Cargo.nix` to the repository you wish to host the new 29 | package. Now we need to make a `flake.nix` to consume the `Cargo.nix` (also 30 | called `packageFun`). 31 | 32 | In flakes, we cannot just use a `fetchGit` etc to insert dependencies ad hoc. 33 | The proper way to get our source is to create a flake input and pass it into the 34 | outputs function. (remember to add this to the argument list) 35 | 36 | ```nix 37 | # in the inputs 38 | rust-analyzer-src = { 39 | url = "github:rust-analyzer/rust-analyzer?rev=2c0f433fd2e838ae181f87019b6f1fefe33c6f54"; 40 | flake = false; 41 | }; 42 | 43 | ``` 44 | 45 | We will also pass a `workspaceSrc` argument to `makePackageSet`. 46 | 47 | ```nix 48 | 49 | rustPkgs = pkgs.rustBuilder.makePackageSet { 50 | rustVersion = "1.75.0"; 51 | packageFun = import ./Cargo.nix; 52 | 53 | workspaceSrc = rust-analyzer-src 54 | # You can also use local paths for local development with a checked out copy 55 | # workspaceSrc = ../../../upstream/rust-analyzer; 56 | }; 57 | 58 | ``` 59 | 60 | The latest version of Rust Analyzer requires a library that we can provide via 61 | an inline override 62 | 63 | ```nix 64 | 65 | # Provide the gperfools lib for linking the final rust-analyzer binary 66 | packageOverrides = pkgs: pkgs.rustBuilder.overrides.all ++ [ 67 | (pkgs.rustBuilder.rustLib.makeOverride { 68 | name = "rust-analyzer"; 69 | overrideAttrs = drv: { 70 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ 71 | pkgs.gperftools 72 | ]; 73 | }; 74 | }) 75 | ]; 76 | 77 | ``` 78 | 79 | Call the workspace function (with no arguments here) to evaluate the output 80 | derivation for our binary: 81 | 82 | ```nix 83 | #... previously expressed rustPkgs 84 | 85 | in rec { 86 | packages = { 87 | rust-analyzer = (rustPkgs.workspace.rust-analyzer {}); 88 | default = packages.rust-analyzer; 89 | }; 90 | } 91 | ``` 92 | 93 | #### Multiple Outputs 94 | 95 | Each derivation created by `mkRustCrate` contains multiple outputs. The `bin` 96 | attribute is the default and contains just the executible compiler artifacts 97 | (using cargo metadata output). By default only the `bin` output is installed, 98 | but you can also use `out` to see the intermediate linking information and other 99 | libraries created during the build. The `out` attribute contains extra files 100 | (which cargo2nix utilizes to coordinate between dependencies) that might be 101 | necessary when being used as a `buildDependency` but will collide when 102 | attempting to install finished software into a profile. This is why the `bin` 103 | attribute is the default output while `out` is used in Cargo.nix. 104 | 105 | You can see the outputs of the derivation using `nix show-derivation` and `jq`: 106 | ``` 107 | nix show-derivation | jq '.[].outputs' 108 | ``` 109 | 110 | ### Building 111 | 112 | You can test that this package builds like so: 113 | 114 | ``` 115 | # with flakes 116 | nix build 117 | ``` 118 | 119 | You will now see a clean binary output at `result-bin/bin/rust-analyzer` 120 | 121 | ### Errata 122 | 123 | This crate is a special case where the flake root has a Cargo.lock that is 124 | **not** the one that generated the Cargo.nix. Currently the overlay can't 125 | handle this, and so `ignoreLockHash` was turned on, set to `true`. 126 | 127 | -------------------------------------------------------------------------------- /examples/4-independent-packaging/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "cargo2nix": { 4 | "inputs": { 5 | "flake-compat": "flake-compat", 6 | "flake-utils": "flake-utils", 7 | "nixpkgs": "nixpkgs", 8 | "rust-overlay": "rust-overlay" 9 | }, 10 | "locked": { 11 | "lastModified": 0, 12 | "narHash": "sha256-uoOYk/O5VCQIOOtD6+7wcZYlA5IZc2Wi9G7ZVmq0NXM=", 13 | "path": "../../", 14 | "type": "path" 15 | }, 16 | "original": { 17 | "path": "../../", 18 | "type": "path" 19 | } 20 | }, 21 | "flake-compat": { 22 | "flake": false, 23 | "locked": { 24 | "lastModified": 1696426674, 25 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 26 | "owner": "edolstra", 27 | "repo": "flake-compat", 28 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "edolstra", 33 | "repo": "flake-compat", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1694529238, 43 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixpkgs": { 56 | "locked": { 57 | "lastModified": 1705099185, 58 | "narHash": "sha256-SxJenKtvcrKJd0TyJQMO3p6VA7PEp+vmMnmlKFzWMNs=", 59 | "owner": "nixos", 60 | "repo": "nixpkgs", 61 | "rev": "2bce5ccff0ad7abda23e8bb56434b6877a446694", 62 | "type": "github" 63 | }, 64 | "original": { 65 | "owner": "nixos", 66 | "ref": "release-23.11", 67 | "repo": "nixpkgs", 68 | "type": "github" 69 | } 70 | }, 71 | "root": { 72 | "inputs": { 73 | "cargo2nix": "cargo2nix", 74 | "flake-utils": [ 75 | "cargo2nix", 76 | "flake-utils" 77 | ], 78 | "nixpkgs": [ 79 | "cargo2nix", 80 | "nixpkgs" 81 | ], 82 | "rust-analyzer-src": "rust-analyzer-src" 83 | } 84 | }, 85 | "rust-analyzer-src": { 86 | "flake": false, 87 | "locked": { 88 | "lastModified": 1704110172, 89 | "narHash": "sha256-k0Ww+VPYWbBklUlRiOk7gqmIoTyRvgNrrsNf80LXpWY=", 90 | "owner": "rust-lang", 91 | "repo": "rust-analyzer", 92 | "rev": "9db515503f11bda7812cdfb9c2839f70f4cb1fdd", 93 | "type": "github" 94 | }, 95 | "original": { 96 | "owner": "rust-lang", 97 | "repo": "rust-analyzer", 98 | "rev": "9db515503f11bda7812cdfb9c2839f70f4cb1fdd", 99 | "type": "github" 100 | } 101 | }, 102 | "rust-overlay": { 103 | "inputs": { 104 | "flake-utils": [ 105 | "cargo2nix", 106 | "flake-utils" 107 | ], 108 | "nixpkgs": [ 109 | "cargo2nix", 110 | "nixpkgs" 111 | ] 112 | }, 113 | "locked": { 114 | "lastModified": 1705112162, 115 | "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", 116 | "owner": "oxalica", 117 | "repo": "rust-overlay", 118 | "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", 119 | "type": "github" 120 | }, 121 | "original": { 122 | "owner": "oxalica", 123 | "repo": "rust-overlay", 124 | "type": "github" 125 | } 126 | }, 127 | "systems": { 128 | "locked": { 129 | "lastModified": 1681028828, 130 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 131 | "owner": "nix-systems", 132 | "repo": "default", 133 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 134 | "type": "github" 135 | }, 136 | "original": { 137 | "owner": "nix-systems", 138 | "repo": "default", 139 | "type": "github" 140 | } 141 | } 142 | }, 143 | "root": "root", 144 | "version": 7 145 | } 146 | -------------------------------------------------------------------------------- /overlay/workspace-shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs, rustPackages, rustToolchain }: 2 | # The resulting function is a decoration of mkShell. Usually mkShell is only 3 | # given an inputsFrom that is a single package or packages without overlap in 4 | # dependencies. However, we seek to provide a shell that is complete for every 5 | # rust crate in the entire workspace. However, if we naively pass all rust 6 | # crates to inputsFrom, many dependencies will be duplicated via propagation or 7 | # multiple inclusion. Therefore, instead create the dependency sets from the 8 | # rust crates are collected and de-duplicated before passing them to the normal 9 | # mkShell. This is also done for the shellHook, just slightly differently. The 10 | # source is really similar to nixpkgs mkShell that it decorates, so study one 11 | # and understand both. 12 | { name ? "nix-shell" 13 | , # a list of packages to add to the shell environment 14 | packages ? [ ] 15 | , # propagate all the inputs from the given derivations 16 | inputsFrom ? [ ] 17 | , buildInputs ? [ ] 18 | , nativeBuildInputs ? [ ] 19 | , propagatedBuildInputs ? [ ] 20 | , propagatedNativeBuildInputs ? [ ] 21 | , ... 22 | }@attrs: 23 | let 24 | lib = pkgs.lib; 25 | 26 | # mkShell in the end will pass through arguments that it doesn't explicitly 27 | # handle onward to mkDerivation. Arguments removed at this point will be 28 | # consumed and propagated by this function. 29 | rest = builtins.removeAttrs attrs [ 30 | "buildInputs" 31 | "nativeBuildInputs" 32 | "propagatedBuildInputs" 33 | "propagatedNativeBuildInputs" 34 | "shellHook" 35 | ]; 36 | 37 | # The crate functions from which we will gather the inputs must be called to 38 | # yield finished crate derivations. It is important to note that they will be 39 | # called with their default arguments. Augmentation of the crates which may 40 | # affect their dependencies in the user's flake will result in inconsistency, 41 | # so if such behavior exists or is added by the user, a mechanism must be 42 | # introduced to propagate these effects on dependencies into the shell. 43 | 44 | # TODO note that if package set is created from nixpkgs for another platform, 45 | # it will be inappropriate for creating a workspace shell. Flakes written for 46 | # cross compilation but still get their workspaceShell from a package set for 47 | # the build platform. 48 | 49 | # TODO This is fragile because any path in the entire set that is not a crate 50 | # function will cause failure. Recent changes to overlay/make-package-set or 51 | # overlay/default, even changes to Nix itself could add a path. Replace this 52 | # with some construct with no possibility of non-crate contamination. 53 | crateFunctions = builtins.removeAttrs rustPackages 54 | ["workspace" 55 | "workspaceShell" 56 | "cargo2nixVersion" 57 | "rustToolchain" 58 | "rustPackages" 59 | "pkgs" 60 | "noBuild" 61 | "mkRustCrate" 62 | "callPackage" 63 | "buildRustPackages" 64 | "__unfix__" 65 | "__splicedPackages"]; 66 | 67 | # Note that out paths must match between the crate and the crates dependencies 68 | # (crates depend on crate.out) or else you will get multiple inclusion and 69 | # crates themselves in the shell depencnedies. Cargo can build rust deps, so 70 | # they are not needed in the development shell, nor will they be used by cargo 71 | # outside of nix builds. 72 | crates = map (pkg: (pkg { }).out) (pkgs.lib.collect builtins.isFunction crateFunctions); 73 | 74 | # This function will extract the attr "name" from all crates, augment this 75 | # list with the "name" from @attrs, remove explicit overlap with inputsFrom, 76 | # and finally de-duplicate with unique. 77 | mergeCrateInputs = name: 78 | (lib.unique 79 | (lib.subtractLists (packages ++ inputsFrom ++ crates) 80 | ((attrs.${name} or [ ]) ++ (lib.flatten (lib.catAttrs name crates))))); 81 | 82 | in pkgs.mkShell (rest // { 83 | 84 | # TODO investigate if cacert is needed or had been omitted in previous implementation 85 | buildInputs = mergeCrateInputs "buildInputs"; 86 | nativeBuildInputs = (mergeCrateInputs "nativeBuildInputs") ++ [ rustToolchain ] ++ (with pkgs; [cacert]); 87 | propagatedBuildInputs = mergeCrateInputs "propagatedBuildInputs"; 88 | propagatedNativeBuildInputs = mergeCrateInputs "propagatedNativeBuildInputs"; 89 | 90 | # Create a composite shellHook from the user passed shellHook and the rust 91 | # crates' shellHooks. This hook will be merged by mkShell with the shellHooks 92 | # in inputsFrom 93 | shellHook = lib.concatStringsSep "\n" (lib.unique (lib.catAttrs "shellHook" 94 | (lib.reverseList crates ++ [ attrs ]))); 95 | 96 | # Configures tools like Rust Analyzer to locate the correct rust-src 97 | RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library"; 98 | }) 99 | -------------------------------------------------------------------------------- /overlay/make-package-set/user-facing.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | buildPackages, 4 | stdenv, 5 | rustBuilder, 6 | }: 7 | args@{ 8 | # required arg 9 | packageFun, 10 | 11 | # optional args 12 | rustChannel ? null, 13 | rustVersion ? null, 14 | rustToolchain ? null, 15 | rustProfile ? "minimal", 16 | extraRustComponents ? [], 17 | packageOverrides ? pkgs: pkgs.rustBuilder.overrides.all, 18 | target ? null, 19 | workspaceSrc ? null, 20 | ignoreLockHash ? false, 21 | ... 22 | }: 23 | let 24 | # These are used in this function, and we clean them out for downstream 25 | extraArgs = builtins.removeAttrs args [ "rustChannel" 26 | "rustVersion" 27 | "rustProfile" 28 | "rustToolchain" 29 | "extraRustComponents" 30 | "packageFun" 31 | "nixifiedLockHash" 32 | "packageOverrides" 33 | "target" ]; 34 | 35 | # Rust targets don't always map perfectly to Nix targets, so they are allowed 36 | # to be independent by specicying an explicit Rust target. 37 | toolchainArgs = { 38 | extensions = [ "rust-src" ] ++ extraRustComponents; 39 | targets = [(rustBuilder.rustLib.rustTriple stdenv.buildPlatform)] ++ 40 | (if target != null 41 | then [ target ] 42 | else [ (rustBuilder.rustLib.rustTriple stdenv.hostPlatform) ]); 43 | }; 44 | 45 | # Normalize the toolchain args and build a toolchain or used the provided 46 | # toolchain. This logic is a bit complicated by legacy use of "rustChannel" 47 | # wich could be a version string in previous eras. Can deprecate at some 48 | # point after pointing everyone to correct usage. Note that rustToolchain 49 | # comes from buildPackages.rust-bin. The overlay has been applied and 50 | # returned via callPackage. 51 | rustToolchain' = 52 | if rustToolchain != null 53 | then 54 | rustToolchain 55 | else 56 | if rustChannel == "nightly" 57 | then 58 | if rustVersion == null || rustVersion == "latest" 59 | then 60 | # don't use explicit "latest" attribute! See oxalica README for more details. 61 | buildPackages.rust-bin.selectLatestNightlyWith 62 | (toolchain: toolchain.${rustProfile}.override toolchainArgs) 63 | else 64 | buildPackages.rust-bin.nightly 65 | .${args.rustVersion} 66 | .${rustProfile}.override toolchainArgs 67 | else 68 | if ((rustChannel != null && 69 | (builtins.match "[0-9]\.[0-9]{1,2}\.[0-9]{1,2}" rustChannel) != null)) 70 | then 71 | if rustVersion != null 72 | then 73 | builtins.throw "You gave rustChannel a version but also specified rustVersion (╯=▃= )╯︵┻━┻" 74 | else 75 | # rustChannel is actually a rustVersion. Treat argument as legacy. 76 | buildPackages.rust-bin.stable 77 | .${args.rustChannel} 78 | .${rustProfile}.override toolchainArgs 79 | else 80 | let 81 | rustChannel' = if rustChannel != null then rustChannel else "stable"; 82 | in 83 | buildPackages.rust-bin 84 | .${rustChannel'} 85 | .${args.rustVersion} 86 | .${rustProfile}.override toolchainArgs; 87 | 88 | 89 | # Cargo2nix requires a rustc in order to run. This runtime dependency is 90 | # grafted on here to avoid noise in the flake and because we know what Rust 91 | # toolchain is in use at this point of expression. 92 | packageOverrides' = pkgs: packageOverrides (pkgs) ++ [(pkgs.rustBuilder.rustLib.makeOverride { 93 | name = "cargo2nix"; 94 | overrideAttrs = drv: { 95 | nativeBuildInputs = drv.nativeBuildInputs or [] ++ [ pkgs.makeWrapper ]; 96 | postFixup = '' 97 | if [[ -x $bin/bin/cargo2nix ]]; then 98 | wrapProgram $bin/bin/cargo2nix --prefix PATH : ${pkgs.lib.makeBinPath [ rustToolchain' ]}; 99 | fi 100 | ''; 101 | }; 102 | })]; 103 | 104 | # This expression finally evaluates the result of makePackageSet. 105 | # makePackageSetInternal is where overrides are applied and the splice is 106 | # performed. Note that buildRustPackages is just buildPackages with a null 107 | # target. 108 | in rustBuilder.makePackageSetInternal (extraArgs // { 109 | inherit packageFun ignoreLockHash workspaceSrc target; 110 | rustToolchain = rustToolchain'; 111 | packageOverrides = packageOverrides' pkgs; 112 | buildRustPackages = buildPackages.rustBuilder.makePackageSetInternal (extraArgs // { 113 | inherit packageFun ignoreLockHash workspaceSrc; 114 | rustToolchain = rustToolchain'; 115 | target = null; 116 | packageOverrides = packageOverrides' buildPackages; 117 | }); 118 | }) 119 | -------------------------------------------------------------------------------- /src/expr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | pub enum BoolExpr { 4 | And(Box, Box), 5 | Or(Box, Box), 6 | Not(Box), 7 | Single(String), 8 | True, 9 | False, 10 | } 11 | 12 | impl BoolExpr { 13 | pub fn ors(iter: impl IntoIterator) -> Self { 14 | use self::BoolExpr::*; 15 | 16 | let mut iter = iter.into_iter(); 17 | 18 | let a = match iter.next() { 19 | None => return False, 20 | Some(a) => a, 21 | }; 22 | 23 | iter.fold(a, |a, b| a.or(b)) 24 | } 25 | 26 | pub fn ands(iter: impl IntoIterator) -> Self { 27 | use self::BoolExpr::*; 28 | 29 | let mut iter = iter.into_iter(); 30 | 31 | let a = match iter.next() { 32 | None => return False, 33 | Some(a) => a, 34 | }; 35 | 36 | iter.fold(a, |a, b| a.and(b)) 37 | } 38 | 39 | pub fn and(self, b: Self) -> Self { 40 | BoolExpr::And(Box::new(self), Box::new(b)) 41 | } 42 | 43 | pub fn or(self, b: Self) -> Self { 44 | BoolExpr::Or(Box::new(self), Box::new(b)) 45 | } 46 | 47 | pub fn not(self) -> Self { 48 | BoolExpr::Not(Box::new(self)) 49 | } 50 | 51 | pub fn as_bool(&self) -> Option { 52 | match self { 53 | BoolExpr::True => Some(true), 54 | BoolExpr::False => Some(false), 55 | _ => None, 56 | } 57 | } 58 | 59 | pub fn simplify(self) -> Self { 60 | use self::BoolExpr::*; 61 | 62 | match self { 63 | And(a, b) => { 64 | let a = a.simplify(); 65 | let b = b.simplify(); 66 | match (a.as_bool(), b.as_bool()) { 67 | (Some(true), _) => b, 68 | (Some(false), _) => False, 69 | (_, Some(true)) => a, 70 | (_, Some(false)) => False, 71 | _ => a.and(b), 72 | } 73 | } 74 | Or(a, b) => { 75 | let a = a.simplify(); 76 | let b = b.simplify(); 77 | match (a.as_bool(), b.as_bool()) { 78 | (Some(true), _) => True, 79 | (Some(false), _) => b, 80 | (_, Some(true)) => True, 81 | (_, Some(false)) => a, 82 | _ => a.or(b), 83 | } 84 | } 85 | Not(a) => { 86 | let a = a.simplify(); 87 | match a { 88 | False => True, 89 | True => False, 90 | a => a.not(), 91 | } 92 | } 93 | a => a, 94 | } 95 | } 96 | } 97 | 98 | #[derive(Eq, PartialEq)] 99 | enum Parent { 100 | PAnd, 101 | POr, 102 | PNot, 103 | PRoot, 104 | } 105 | 106 | impl BoolExpr { 107 | pub fn to_nix(&self) -> impl '_ + fmt::Display { 108 | DisplayFn(move |f: &mut fmt::Formatter| self.write_nix(f, Parent::PRoot)) 109 | } 110 | 111 | fn write_nix(&self, f: &mut fmt::Formatter, parent: Parent) -> fmt::Result { 112 | use self::BoolExpr::*; 113 | use self::Parent::*; 114 | 115 | Ok(match self { 116 | And(a, b) => { 117 | parenthesize_if(parent == PNot, f, |f| { 118 | a.write_nix(f, PAnd)?; 119 | write!(f, " && ")?; 120 | b.write_nix(f, PAnd)?; 121 | Ok(()) 122 | })?; 123 | } 124 | Or(a, b) => { 125 | parenthesize_if(parent == PAnd || parent == PNot, f, |f| { 126 | a.write_nix(f, POr)?; 127 | write!(f, " || ")?; 128 | b.write_nix(f, POr)?; 129 | Ok(()) 130 | })?; 131 | } 132 | Not(a) => { 133 | write!(f, "!")?; 134 | a.write_nix(f, PNot)?; 135 | } 136 | Single(a) => { 137 | let a = a.to_string(); 138 | parenthesize_if(parent == PNot && !is_valid_ident(&a), f, |f| { 139 | write!(f, "{}", a) 140 | })?; 141 | } 142 | True => write!(f, "true")?, 143 | False => write!(f, "false")?, 144 | }) 145 | } 146 | } 147 | 148 | fn parenthesize_if(cond: bool, f: &mut fmt::Formatter, inner: F) -> fmt::Result 149 | where 150 | F: FnOnce(&mut fmt::Formatter) -> fmt::Result, 151 | { 152 | if cond { 153 | write!(f, "(")?; 154 | } 155 | inner(f)?; 156 | if cond { 157 | write!(f, ")")?; 158 | } 159 | Ok(()) 160 | } 161 | 162 | fn is_valid_ident(id: &str) -> bool { 163 | let mut chars = id.chars(); 164 | 165 | match chars.next() { 166 | Some(c) if c.is_ascii_alphabetic() || c == '_' => {} 167 | _ => return false, 168 | } 169 | 170 | chars.all(|c| c.is_ascii_alphanumeric() || "_'-.".contains(c)) 171 | } 172 | 173 | #[derive(Clone)] 174 | struct DisplayFn(pub F); 175 | 176 | impl fmt::Display for DisplayFn 177 | where 178 | F: Fn(&mut fmt::Formatter) -> fmt::Result, 179 | { 180 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 181 | (self.0)(f) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /templates/Cargo.nix.tera: -------------------------------------------------------------------------------- 1 | # This file was @generated by cargo2nix {{ cargo2nix_version }}. 2 | # It is not intended to be manually edited. 3 | 4 | args@{ 5 | release ? true, 6 | rootFeatures ? [ 7 | {%- for feature in root_features %} 8 | "{{ feature }}" 9 | {%- endfor %} 10 | ], 11 | rustPackages, 12 | buildRustPackages, 13 | hostPlatform, 14 | hostPlatformCpu ? null, 15 | hostPlatformFeatures ? [], 16 | target ? null, 17 | codegenOpts ? null, 18 | profileOpts ? null, 19 | cargoUnstableFlags ? null, 20 | rustcLinkFlags ? null, 21 | rustcBuildFlags ? null, 22 | mkRustCrate, 23 | rustLib, 24 | lib, 25 | workspaceSrc, 26 | ignoreLockHash, 27 | }: 28 | let 29 | nixifiedLockHash = "{{ cargo_lock_hash }}"; 30 | workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc; 31 | currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock); 32 | lockHashIgnored = if ignoreLockHash 33 | then builtins.trace "Ignoring lock hash" ignoreLockHash 34 | else ignoreLockHash; 35 | in if !lockHashIgnored && (nixifiedLockHash != currentLockHash) then 36 | throw ("Cargo.nix ${nixifiedLockHash} is out of sync with Cargo.lock ${currentLockHash}") 37 | else let 38 | inherit (rustLib) fetchCratesIo fetchCrateLocal fetchCrateGit fetchCrateAlternativeRegistry expandFeatures decideProfile genDrvsByProfile; 39 | profilesByName = { 40 | {%- for name, profile in profiles %} 41 | {{ name }} = builtins.fromTOML "{{ profile }}"; 42 | {%- endfor %} 43 | }; 44 | rootFeatures' = expandFeatures rootFeatures; 45 | overridableMkRustCrate = f: 46 | let 47 | drvs = genDrvsByProfile profilesByName ({ profile, profileName }: mkRustCrate ({ inherit release profile hostPlatformCpu hostPlatformFeatures target profileOpts codegenOpts cargoUnstableFlags rustcLinkFlags rustcBuildFlags; } // (f profileName))); 48 | in { compileMode ? null, profileName ? decideProfile compileMode release }: 49 | let drv = drvs.${profileName}; in if compileMode == null then drv else drv.override { inherit compileMode; }; 50 | in 51 | { 52 | cargo2nixVersion = "{{ cargo2nix_version }}"; 53 | workspace = { 54 | {%- for crate in workspace_members %} 55 | {{ crate.name }} = rustPackages.unknown.{{ crate.name }}."{{ crate.version }}"; 56 | {%- endfor %} 57 | }; 58 | 59 | {%- for crate in crates %} 60 | "{{ crate.registry }}".{{ crate.name }}."{{ crate.version }}" = overridableMkRustCrate (profileName: rec { 61 | name = "{{ crate.name }}"; 62 | version = "{{ crate.version }}"; 63 | registry = "{{ crate.registry }}"; 64 | 65 | {%- if crate.source.CratesIo.sha256 %} 66 | src = fetchCratesIo { inherit name version; sha256 = "{{ crate.source.CratesIo.sha256 }}"; }; 67 | {%- elif crate.source.Git.url %} 68 | src = fetchCrateGit { 69 | url = {{ crate.source.Git.url }}; 70 | name = "{{ crate.name }}"; 71 | version = "{{ crate.version }}"; 72 | rev = "{{ crate.source.Git.rev }}"; 73 | {%- if crate.source.Git.branch %} 74 | ref = "{{ crate.source.Git.branch }}"; 75 | {%- endif -%} 76 | }; 77 | {%- elif crate.source.Local.path %} 78 | src = fetchCrateLocal workspaceSrc; 79 | {%- elif crate.source.Registry.index %} 80 | src = fetchCrateAlternativeRegistry { 81 | index = {{ crate.source.Registry.index }}; 82 | name = "{{ crate.name }}"; 83 | version = "{{ crate.version }}"; 84 | sha256 = "{{ crate.source.Registry.sha256 }}"; 85 | }; 86 | {%- else %} 87 | # ERROR: Could not resolve source: {{ crate.source | safe | json_encode() }} 88 | {%- endif -%} 89 | 90 | {%- if crate.features | length > 0 %} 91 | features = builtins.concatLists [ 92 | {%- for feature in crate.features %} 93 | {%- if feature.activated_by %} 94 | (lib.optional ({{ feature.activated_by }}) "{{ feature.name }}") 95 | {%- else %} 96 | [ "{{ feature.name }}" ] 97 | {%- endif %} 98 | {%- endfor %} 99 | ]; 100 | {%- endif %} 101 | 102 | {%- if crate.dependencies | length > 0 %} 103 | dependencies = { 104 | {%- for dep in crate.dependencies %} 105 | {%- if dep.is_proc_macro -%} 106 | {%- set package_set = 'buildRustPackages' -%} 107 | {%- set profile = '{ profileName = "__noProfile"; }' -%} 108 | {%- else -%} 109 | {%- set package_set = 'rustPackages' -%} 110 | {%- set profile = '{ inherit profileName; }' -%} 111 | {%- endif -%} 112 | {%- if dep.cfg_condition %} 113 | {%- set attribute = '${ if ' ~ dep.cfg_condition ~ ' then "' ~ dep.extern_name ~ '" else null }' -%} 114 | {%- else %} 115 | {%- set attribute = dep.extern_name -%} 116 | {%- endif %} 117 | {{ attribute }} = ({{ package_set }}."{{ dep.registry }}".{{ dep.name }}."{{ dep.version }}" {{ profile }}).out; 118 | {%- endfor %} 119 | }; 120 | {%- endif %} 121 | 122 | {%- if crate.dev_dependencies | length > 0 %} 123 | devDependencies = { 124 | {%- for dep in crate.dev_dependencies %} 125 | {%- if dep.is_proc_macro -%} 126 | {%- set package_set = 'buildRustPackages' -%} 127 | {%- set profile = '{ profileName = "__noProfile"; }' -%} 128 | {%- else -%} 129 | {%- set package_set = 'rustPackages' -%} 130 | {%- set profile = '{ inherit profileName; }' -%} 131 | {%- endif -%} 132 | {%- if dep.cfg_condition %} 133 | {%- set attribute = '${ if ' ~ dep.cfg_condition ~ ' then "' ~ dep.extern_name ~ '" else null }' -%} 134 | {%- else %} 135 | {%- set attribute = dep.extern_name -%} 136 | {%- endif %} 137 | {{ attribute }} = ({{ package_set }}."{{ dep.registry }}".{{ dep.name }}."{{ dep.version }}" {{ profile }}).out; 138 | {%- endfor %} 139 | }; 140 | {%- endif %} 141 | 142 | {%- if crate.build_dependencies | length > 0 %} 143 | buildDependencies = { 144 | {%- for dep in crate.build_dependencies %} 145 | {%- if dep.cfg_condition %} 146 | {%- set attribute = '${ if ' ~ dep.cfg_condition ~ ' then "' ~ dep.extern_name ~ '" else null }' -%} 147 | {%- else %} 148 | {%- set attribute = dep.extern_name -%} 149 | {%- endif %} 150 | {{ attribute }} = (buildRustPackages."{{ dep.registry }}".{{ dep.name }}."{{ dep.version }}" { profileName = "__noProfile"; }).out; 151 | {%- endfor %} 152 | }; 153 | {%- endif %} 154 | }); 155 | {% endfor %} 156 | } 157 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This is a project that only succeeds through use. Every build brings us closer 4 | to being a general tool for packaging and distributing Rust software with nix. 5 | 6 | ## Issues 7 | 8 | When opening an issue, you can help us and help yourself. 9 | 10 | ### How to Debug on Your Own 11 | 12 | Check the [common issues][common issues] section. Likely fixes: 13 | 14 | - A build override, perhaps modifying a value for `buildPhase` or `unpackPhase` 15 | - An override of dependencies such as in [overrides][overrides] 16 | - A modification of the build steps in [mkcrate.nix][mkcrate] or 17 | [mkcrate-utils.sh][mkcrate-utils] 18 | 19 | You can [replicate the exact build environment][replicate] for a failing derivation and try 20 | to build it yourself. This will allow you to make changes and develop phase 21 | overrides or source patches. 22 | 23 | [common issues]: https://github.com/cargo2nix/cargo2nix#common-issues 24 | [overrides]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix 25 | [mkcrate]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix 26 | [mkcrate-utils]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate-utils.sh 27 | [replicate]: https://github.com/cargo2nix/cargo2nix#declarative-debug--development-shell 28 | 29 | ### Examples Builds We Maintain 30 | 31 | Check inside of [examples][examples]. Can you build these examples? Can you 32 | replicate your issue in a branch on one of our examples? 33 | 34 | [examples]: https://github.com/cargo2nix/cargo2nix/tree/master/examples 35 | 36 | ### Files We Sometimes Use in Debugging 37 | 38 | - Cargo.toml 39 | - flake.nix 40 | - build.rs 41 | - .cargo/config.toml 42 | - Cargo.nix 43 | - flake.lock 44 | - Cargo.lock 45 | 46 | Please attach whichever files are present / relevant. The build output from 47 | `nix log` as it instructs you to look at when a drv fails is also helpful. 48 | 49 | ### Reporting a crate from Crates.io that does not build 50 | 51 | `cargo2nix` should be capable of building any unmodified crate available on 52 | [Crates.io]. If you run into a public crate which fails to build with 53 | `cargo2nix`, please report it to us and it will be treated as a bug. 54 | 55 | [Crates.io]: https://crates.io/ 56 | 57 | There are two different scenarios in which a crate might fail to build, and each 58 | will be handled slightly differently when reported to our issue tracker: 59 | 60 | #### 1. Failure to build due to a missing system dependency 61 | 62 | In this scenario, a crate fails to build due to a non-Rust system dependency 63 | being unavailable during the build process, e.g. the [`openssl-sys`] crate 64 | failing due to OpenSSL not being declared as a build input in Nix, or the 65 | [`ring`] crate failing to build due to an implicit dependency on the [Apple 66 | Security Framework] on macOS. 67 | 68 | [`openssl-sys`]: https://crates.io/crates/openssl-sys 69 | [`ring`]: https://crates.io/crates/ring 70 | [Apple Security Framework]: https://developer.apple.com/documentation/security 71 | 72 | If a build failure like this arises, a new package override for the offending 73 | Rust crate should be added to the [overlay/overrides.nix] file. The fix will 74 | constitute at least a patch version bump to `cargo2nix`, since it is a fix of 75 | existing functionality. 76 | 77 | [overlay/overrides.nix]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix 78 | 79 | #### 2. Failure to build due to a defect in cargo2nix 80 | 81 | In this scenario, there is a bug in either the Nix overlay which builds the 82 | crates, the `cargo2nix` CLI tool which generates the `Cargo.nix` expression, or 83 | both. 84 | 85 | If a build failure like this arises, there may be an issue with the underlying 86 | logic used to resolve crate feature flags or the code generation of the 87 | `Cargo.nix` expression for the workspace. These sorts of bugs will be 88 | investigated thoroughly and may result in either a patch or minor version bump 89 | to `cargo2nix`, depending on whether the fix results in a breaking change to the 90 | `Cargo.nix` file format or the `mkRustCrate` logic. 91 | 92 | ## Usage Help 93 | 94 | Using cargo2nix is about 5% running the cargo2nix tool and 95% figuring out how 95 | to construct the right nix expressions. The cargo feature you need may or may 96 | not be supported yet by our overlay or cargo2nix's ability to comprehend your 97 | `Cargo.toml` 98 | 99 | ### Cargo2nix overlay API 100 | 101 | Some of the features our overlay supports may be difficult to find. There's 102 | some documentation in the [nix flake][nix flake]. The [examples][examples] 103 | contain some more hints. Ultimately, arguments to `rustBuilder.makePackageSet` 104 | are consumed in [make-package-set/user-facing.nix][user-facing], which calls 105 | into `make-package-set/internal.nix` 106 | 107 | [nix flake]: https://github.com/cargo2nix/cargo2nix/blob/master/flake.nix 108 | [examples]: https://github.com/cargo2nix/cargo2nix/blob/master/examples 109 | [user-facing]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/make-package-set/simplified.nix 110 | 111 | Each output in `rustPkgs.workspace.` supports arguments. Many are 112 | demonstrated in the [nix flake] comments, but check [mkcrate.nix] for how they 113 | are used. 114 | 115 | [mkcrate.nix]: https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix 116 | [nix flake]: https://github.com/cargo2nix/cargo2nix/blob/master/flake.nix 117 | 118 | ## Pull Requests 119 | 120 | Many files are generated. When you draft your changes, please separate out 121 | commits that affect: 122 | 123 | - Cargo.lock 124 | - Cargo.nix 125 | - flake.lock 126 | 127 | Keeping these changes isolated in specific commits makes it much easier to pull 128 | in your changes in parallel with other features. Maintainers may harvest your 129 | changes. We only guarantee to preserve authorship in the git log (when we 130 | remember to do so). 131 | 132 | ### Creating pull requests 133 | 134 | 1. Fork this repository into the personal GitHub account 135 | 2. Select the appropriate branch, release- for stable changes, unstable 136 | for breaking changes 137 | 3. Make changes on the personal fork 138 | 4. Make a Pull Request against this repository 139 | 5. **Allow maintainers to make changes to your pull request** (there's a checkbox) 140 | 6. Once the pull request has been approved, you will be thanked and observe your 141 | changes applied with authorship preserved (if we remember) 142 | -------------------------------------------------------------------------------- /examples/2-bigger-project/README.md: -------------------------------------------------------------------------------- 1 | # A bigger project 2 | 3 | This is a larger `bin` project which requires some dependencies from Crates.io. 4 | 5 | ## Introduction 6 | 7 | This example assumes that you have already read through the [Hello World] 8 | project and have your machine set up with Nix and `cargo2nix`. If this is not 9 | the case, it is strongly recommended that you follow that guide first. 10 | 11 | [Hello World]: ../1-hello-world/README.md 12 | 13 | Let's begin by creating another Cargo crate called `bigger-project` which 14 | depends on some crates from Crates.io, which in turn require some non-Rust 15 | system dependencies. 16 | 17 | ## Generating the Cargo project 18 | 19 | As described in the previous example, fire up a Rust bin crate project with 20 | `cargo new bigger-project` 21 | 22 | Edit your `src/main.rs` with the following Rust code: 23 | 24 | ```rust 25 | //! A simple HTTP client using `reqwest`. 26 | 27 | #[tokio::main] 28 | async fn main() -> Result<(), reqwest::Error> { 29 | let res = reqwest::get("https://hyper.rs").await?; 30 | 31 | println!("Status: {}", res.status()); 32 | 33 | let body = res.text().await?; 34 | 35 | println!("Body:\n\n{}", body); 36 | 37 | Ok(()) 38 | } 39 | ``` 40 | 41 | Then, add the following lines to the `[dependencies]` table of the `Cargo.toml`: 42 | 43 | ```toml 44 | reqwest = "0.10" 45 | tokio = { version = "0.2", features = ["macros"] } 46 | ``` 47 | 48 | ## Generating a Cargo.nix 49 | 50 | Like the previous example, we generate a `Cargo.lock` and `Cargo.nix` for our 51 | crate by running the two commands below: 52 | 53 | ```bash 54 | cargo generate-lockfile 55 | cargo2nix 56 | ``` 57 | 58 | ## Writing the Flake.nix 59 | 60 | Let's create a new file called [`flake.nix`] and declare a function with the 61 | following arguments: 62 | 63 | [`flake.nix`]: ./flake.nix 64 | 65 | ```nix 66 | { 67 | inputs = { 68 | cargo2nix.url = "path:../../"; 69 | # Use a github flake URL for real packages 70 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 71 | flake-utils.follows = "cargo2nix/flake-utils"; 72 | nixpkgs.follows = "cargo2nix/nixpkgs"; 73 | }; 74 | 75 | outputs = inputs: with inputs; # pass through all inputs and bring them into scope 76 | 77 | # Build the output set for each default system and map system sets into 78 | # attributes, resulting in paths such as: 79 | # nix build .#packages.x86_64-linux. 80 | flake-utils.lib.eachDefaultSystem (system: 81 | 82 | # let-in expressions, very similar to Rust's let bindings. These names 83 | # are used to express the output but not themselves paths in the output. 84 | let 85 | 86 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 87 | pkgs = import nixpkgs { 88 | inherit system; 89 | overlays = [ cargo2nix.overlays.default ]; 90 | }; 91 | 92 | # create the workspace & dependencies package set 93 | rustPkgs = pkgs.rustBuilder.makePackageSet { 94 | rustVersion = "1.75.0"; 95 | packageFun = import ./Cargo.nix; 96 | # packageOverrides = pkgs: pkgs.rustBuilder.overrides.all; # Implied, if not specified 97 | }; 98 | 99 | in rec { 100 | # this is the output (recursive) set (expressed for each system) 101 | 102 | # the packages in `nix build .#packages..` 103 | packages = { 104 | # nix build .#bigger-project 105 | # nix build .#packages.x86_64-linux.bigger-project 106 | bigger-project = (rustPkgs.workspace.bigger-project {}); 107 | # nix build 108 | default = packages.bigger-project; 109 | }; 110 | } 111 | ); 112 | } 113 | ``` 114 | 115 | :warning: Remember to add the flake to version control (and `Cargo.nix` and 116 | `flake.lock` while you're at it) 117 | 118 | ```bash 119 | git init 120 | git add flake.nix 121 | flake check 122 | # generate the flake.lock 123 | git add flake.lock 124 | git add Cargo.nix 125 | ``` 126 | 127 | ### About Overrides 128 | 129 | Our `bigger-project` crate depends on both [`reqwest`] and [`tokio`], providing 130 | a simple asynchronous HTTP client. By default, `reqwest` relies on the 131 | [`native-tls`] crate for SSL support, which requires certain system dependencies 132 | depending on the host platform. Specifically: 133 | 134 | * On Linux and NixOS, we depend on OpenSSL (via the [`openssl`] crate). 135 | * On macOS, we depend on Secure Transport (via the [`security-framework`] 136 | crate). 137 | 138 | [`reqwest`]: https://github.com/seanmonstar/reqwest 139 | [`tokio`]: https://github.com/tokio-rs/tokio 140 | [`native-tls`]: https://github.com/sfackler/rust-native-tls 141 | [`openssl`]: https://github.com/sfackler/rust-openssl 142 | [`security-framework`]: https://github.com/kornelski/rust-security-framework 143 | 144 | The required system dependencies will be magically injected into the build by 145 | `cargo2nix` using the `packageOverrides` argument for `makePackageSet` without 146 | needing any extra work, but we will get into what that means later on. 147 | 148 | You might have noticed that we did not specify any external dependencies to be 149 | used in our build, such as `openssl` or `darwin.apple_sdk.frameworks.Security`. 150 | This is because the `cargo2nix` overlay provides a collection of _crate 151 | overrides_ for many popular Crates.io dependencies by default, tucked away in 152 | `rustBuilder.overrides.all`. If you're curious, you can view our existing 153 | library of provided crate overrides in [overlay/overrides.nix]. 154 | 155 | [overlay/overrides.nix]: ../../overlay/overrides.nix 156 | 157 | `rustBuilder.overrides.all` is a list, so you can always add your own custom 158 | overrides by appending `++ [ myOverride1 myOverride2 ]` to the end. We won't 159 | delve into how custom overrides work in this example, but you should at least be 160 | aware that the option exists. 161 | 162 | > Side note: if you'd like to submit more crate overrides for default inclusion 163 | > in the next version of `cargo2nix`, [feel free to open a pull request]! 164 | 165 | [feel free to open a pull request]: ./../../CONTRIBUTING.md 166 | 167 | Save the `flake.nix` file and quit. Your `cargo2nix` project is ready for 168 | building! 169 | 170 | ## Building 171 | 172 | To compile the `bigger-project` binary with Nix, simply run: 173 | 174 | ```bash 175 | nix build 176 | ``` 177 | 178 | This will create a `result` symlink in the current directory with the following 179 | structure: 180 | 181 | ```text 182 | ls -al result-bin 183 | result-bin -> /nix/store/kkx76zvrm02hsh09kw694r5ma5f4wrjf-crate-bigger-project-0.1.0-bin 184 | 185 | tree result-bin 186 | result-bin 187 | └── bin 188 | └── bigger-project 189 | 190 | ``` 191 | 192 | Running the `bigger-project` binary will print the following output to the 193 | screen: 194 | 195 | ```text 196 | $ ./result/bin/hello-world 197 | Status: 200 OK 198 | Body: 199 | 200 | 201 | 202 | 203 | # lots more HTML here... 204 | ``` 205 | 206 | Now that we're getting the hang of building crates with `cargo2nix`, let's 207 | create a project which relies on external resources located outside of the `src` 208 | directory. 209 | -------------------------------------------------------------------------------- /overlay/lib/splice.nix: -------------------------------------------------------------------------------- 1 | { lib }: 2 | let 3 | # NOTE(@dingxiangfei2009): override `override`, `overrideDerivation` and `overrideAttrs` 4 | spliceFunctor = 5 | { funBuildBuild, funBuildHost, funBuildTarget 6 | , funHostHost, funHostTarget 7 | , funTargetTarget 8 | }: 9 | arg: 10 | let 11 | tryRedex = f: arg: if f == null then null else f arg; 12 | null2Attrs = v: lib.optionalAttrs (v != null) v; 13 | defaultFun = lib.findFirst (f: f != null) null [ 14 | funHostTarget 15 | funBuildHost 16 | funTargetTarget 17 | funHostHost 18 | funBuildTarget 19 | funBuildBuild 20 | ]; 21 | defaultValue = defaultFun arg; 22 | 23 | valueBuildBuild = tryRedex funBuildBuild arg; 24 | valueBuildHost = tryRedex funBuildHost arg; 25 | valueBuildTarget = tryRedex funBuildTarget arg; 26 | valueHostHost = tryRedex funHostHost arg; 27 | valueHostTarget = tryRedex funHostTarget arg; 28 | valueTargetTarget = tryRedex funTargetTarget arg; 29 | in 30 | if lib.isFunction defaultValue then 31 | spliceFunctor { 32 | funHostTarget = valueHostTarget; 33 | funBuildHost = valueBuildHost; 34 | funTargetTarget = valueTargetTarget; 35 | funHostHost = valueHostHost; 36 | funBuildTarget = valueBuildTarget; 37 | funBuildBuild = valueBuildBuild; 38 | } 39 | else if lib.isDerivation defaultValue then 40 | spliceDerivation { 41 | drvHostTarget = null2Attrs valueHostTarget; 42 | drvBuildHost = null2Attrs valueBuildHost; 43 | drvTargetTarget = null2Attrs valueTargetTarget; 44 | drvHostHost = null2Attrs valueHostHost; 45 | drvBuildTarget = null2Attrs valueBuildTarget; 46 | drvBuildBuild = null2Attrs valueBuildBuild; 47 | } 48 | else if lib.isAttrs defaultValue then 49 | splicePkgs { 50 | pkgsHostTarget = null2Attrs valueHostTarget; 51 | pkgsBuildHost = null2Attrs valueBuildHost; 52 | pkgsTargetTarget = null2Attrs valueTargetTarget; 53 | pkgsHostHost = null2Attrs valueHostHost; 54 | pkgsBuildTarget = null2Attrs valueBuildTarget; 55 | pkgsBuildBuild = null2Attrs valueBuildBuild; 56 | } 57 | else 58 | defaultValue; 59 | 60 | # Taken from 61 | 62 | spliceDerivation = 63 | { drvBuildBuild, drvBuildHost, drvBuildTarget 64 | , drvHostHost, drvHostTarget 65 | , drvTargetTarget 66 | }: 67 | let 68 | defaultValue = lib.findFirst (f: f != null && f != {}) null [ 69 | drvHostTarget 70 | drvBuildHost 71 | drvTargetTarget 72 | drvHostHost 73 | drvBuildTarget 74 | drvBuildBuild 75 | ]; 76 | augmentedValue = 77 | defaultValue 78 | # TODO(@Ericson2314): Stop using old names after transition period 79 | // (lib.optionalAttrs (drvBuildHost != null && drvBuildHost != {}) { nativeDrv = drvBuildHost; }) 80 | // (lib.optionalAttrs (drvHostTarget != null && drvHostTarget != {}) { crossDrv = drvHostTarget; }) 81 | // 82 | { 83 | __spliced = 84 | (lib.optionalAttrs (drvBuildBuild != null) { buildBuild = drvBuildBuild; }) // 85 | (lib.optionalAttrs (drvBuildTarget != null) { buildTarget = drvBuildTarget; }) // 86 | { hostHost = drvHostHost; } // 87 | (lib.optionalAttrs (drvTargetTarget != null) { targetTarget = drvTargetTarget; }); 88 | }; 89 | in 90 | augmentedValue // 91 | splicePkgs { 92 | pkgsBuildBuild = tryGetOutputs drvBuildBuild; 93 | pkgsBuildHost = tryGetOutputs drvBuildHost; 94 | pkgsBuildTarget = tryGetOutputs drvBuildTarget; 95 | pkgsHostHost = tryGetOutputs drvHostHost; 96 | pkgsHostTarget = getOutputs drvHostTarget; 97 | pkgsTargetTarget = tryGetOutputs drvTargetTarget; 98 | } // 99 | lib.optionalAttrs 100 | (defaultValue ? override && defaultValue ? overrideDerivation) 101 | { 102 | override = spliceFunctor { 103 | funBuildBuild = drvBuildBuild.override or null; 104 | funBuildHost = drvBuildHost.override or null; 105 | funBuildTarget = drvBuildTarget.override or null; 106 | funHostHost = drvHostHost.override or null; 107 | funHostTarget = drvHostTarget.override or null; 108 | funTargetTarget = drvTargetTarget.override or null; 109 | }; 110 | overrideDerivation = spliceFunctor { 111 | funBuildBuild = drvBuildBuild.overrideDerivation or null; 112 | funBuildHost = drvBuildHost.overrideDerivation or null; 113 | funBuildTarget = drvBuildTarget.overrideDerivation or null; 114 | funHostHost = drvHostHost.overrideDerivation or null; 115 | funHostTarget = drvHostTarget.overrideDerivation or null; 116 | funTargetTarget = drvTargetTarget.overrideDerivation or null; 117 | }; 118 | }; 119 | 120 | # Get the set of outputs of a derivation. If one derivation fails to 121 | # evaluate we don't want to diverge the entire splice, so we fall back 122 | # on {} 123 | tryGetOutputs = value0: 124 | let 125 | inherit (builtins.tryEval value0) success value; 126 | in 127 | getOutputs (lib.optionalAttrs success value); 128 | 129 | getOutputs = value: 130 | lib.genAttrs 131 | (value.outputs or (lib.optional (value ? out) "out")) 132 | (output: value.${output}); 133 | 134 | splicePkgs = { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget 135 | , pkgsHostHost, pkgsHostTarget 136 | , pkgsTargetTarget 137 | }: let 138 | mash = 139 | # Other pkgs sets 140 | pkgsBuildBuild // 141 | pkgsBuildTarget // 142 | pkgsHostHost // 143 | pkgsTargetTarget // 144 | # The same pkgs sets one probably intends 145 | pkgsBuildHost // 146 | pkgsHostTarget; 147 | merge = name: { 148 | inherit name; 149 | value = 150 | let 151 | defaultValue = mash.${name}; 152 | in 153 | if lib.isFunction defaultValue then 154 | spliceFunctor { 155 | funBuildBuild = pkgsBuildBuild.${name} or null; 156 | funBuildHost = pkgsBuildHost.${name} or null; 157 | funBuildTarget = pkgsBuildTarget.${name} or null; 158 | funHostHost = pkgsHostHost.${name} or null; 159 | funHostTarget = pkgsHostTarget.${name} or null; 160 | funTargetTarget = pkgsTargetTarget.${name} or null; 161 | } 162 | else if lib.isDerivation defaultValue then 163 | # The derivation along with its outputs, which we recur 164 | # on to splice them together. 165 | spliceDerivation { 166 | drvBuildBuild = pkgsBuildBuild.${name} or null; 167 | drvBuildHost = pkgsBuildHost.${name} or null; 168 | drvBuildTarget = pkgsBuildTarget.${name} or null; 169 | drvHostHost = pkgsHostHost.${name} or null; 170 | drvHostTarget = pkgsHostTarget.${name} or null; 171 | drvTargetTarget = pkgsTargetTarget.${name} or null; 172 | } 173 | else if lib.isAttrs defaultValue then 174 | # Just recur on plain attrsets 175 | splicePkgs { 176 | pkgsBuildBuild = pkgsBuildBuild.${name} or {}; 177 | pkgsBuildHost = pkgsBuildHost.${name} or {}; 178 | pkgsBuildTarget = pkgsBuildTarget.${name} or {}; 179 | pkgsHostHost = pkgsHostHost.${name} or {}; 180 | pkgsHostTarget = pkgsHostTarget.${name} or {}; 181 | pkgsTargetTarget = pkgsTargetTarget.${name} or {}; 182 | } 183 | else 184 | defaultValue; 185 | }; 186 | in 187 | lib.listToAttrs (map merge (lib.attrNames mash)); 188 | 189 | splicePackages = 190 | actuallySplice: 191 | { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget 192 | , pkgsHostHost, pkgsHostTarget 193 | , pkgsTargetTarget 194 | } @ args: 195 | if actuallySplice then 196 | splicePkgs args 197 | else 198 | pkgsHostTarget; 199 | in 200 | { inherit splicePackages; } 201 | -------------------------------------------------------------------------------- /src/template.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::path::{Path, PathBuf}; 3 | 4 | use anyhow::{anyhow, Result}; 5 | use cargo::core::{dependency::DepKind, source::GitReference, Package, PackageId, SourceId}; 6 | use serde::Serialize; 7 | 8 | use crate::manifest::TomlProfile; 9 | use crate::{platform, BoolExpr, Feature as FeatureStr, Optionality, ResolvedPackage}; 10 | 11 | #[derive(Debug, Serialize)] 12 | pub struct BuildPlan { 13 | pub cargo2nix_version: String, 14 | pub cargo_lock_hash: String, 15 | pub root_features: Vec, 16 | pub profiles: BTreeMap, 17 | pub workspace_members: Vec, 18 | pub crates: Vec, 19 | } 20 | 21 | impl BuildPlan { 22 | pub fn from_items( 23 | cargo_lock_hash: String, 24 | root_pkgs: Vec<&'_ Package>, 25 | profiles: TomlProfile, 26 | rpkgs_by_id: BTreeMap>, 27 | cwd: &Path, 28 | ) -> Result { 29 | let root_features = root_pkgs 30 | .iter() 31 | .map(|pkg| format!("{}/default", pkg.name())) 32 | .collect(); 33 | 34 | let profiles = profiles 35 | .into_iter() 36 | .map(|(name, profile)| (name, toml::to_string(&profile).unwrap())) 37 | .map(|(name, toml)| (name, toml.replace("${", "\\${").escape_debug().to_string())) 38 | .collect(); 39 | 40 | let workspace_members = root_pkgs 41 | .into_iter() 42 | .map(|pkg| Member { 43 | name: pkg.name().to_string(), 44 | version: pkg.version().to_string(), 45 | }) 46 | .collect(); 47 | 48 | let crates = rpkgs_by_id 49 | .into_iter() 50 | .map(|(pkg_id, resolved_pkg)| { 51 | let (deps, dev_deps, build_deps) = to_dependencies(&resolved_pkg); 52 | Ok(Crate { 53 | name: pkg_id.name().to_string(), 54 | version: pkg_id.version().to_string(), 55 | registry: to_registry_string(pkg_id.source_id()), 56 | source: to_source(&resolved_pkg, cwd)?, 57 | features: to_features(&resolved_pkg.features), 58 | dependencies: deps, 59 | dev_dependencies: dev_deps, 60 | build_dependencies: build_deps, 61 | }) 62 | }) 63 | .collect::>()?; 64 | 65 | Ok(BuildPlan { 66 | cargo2nix_version: env!("CARGO_PKG_VERSION").to_string(), 67 | cargo_lock_hash, 68 | root_features, 69 | profiles, 70 | workspace_members, 71 | crates, 72 | }) 73 | } 74 | } 75 | 76 | #[derive(Debug, Serialize)] 77 | pub struct Member { 78 | pub name: String, 79 | pub version: String, 80 | } 81 | 82 | #[derive(Debug, Serialize)] 83 | pub struct Crate { 84 | pub name: String, 85 | pub version: String, 86 | pub registry: String, 87 | pub source: Source, 88 | pub features: Vec, 89 | pub dependencies: Vec, 90 | pub dev_dependencies: Vec, 91 | pub build_dependencies: Vec, 92 | } 93 | 94 | #[derive(Debug, Serialize)] 95 | pub enum Source { 96 | CratesIo { 97 | sha256: String, 98 | }, 99 | Git { 100 | url: String, 101 | rev: String, 102 | branch: Option, 103 | }, 104 | Local { 105 | path: PathBuf, 106 | }, 107 | Registry { 108 | index: String, 109 | sha256: String, 110 | }, 111 | } 112 | 113 | #[derive(Debug, Serialize)] 114 | pub struct Feature { 115 | pub name: String, 116 | pub activated_by: Option, 117 | } 118 | 119 | #[derive(Debug, Serialize)] 120 | pub struct Dependency { 121 | pub name: String, 122 | pub extern_name: String, 123 | pub version: String, 124 | pub registry: String, 125 | pub cfg_condition: Option, 126 | pub is_proc_macro: bool, 127 | } 128 | 129 | fn to_registry_string(src_id: SourceId) -> String { 130 | if src_id.is_path() { 131 | "unknown".to_string() 132 | } else if src_id.is_git() { 133 | format!("git+{}", src_id.url()) 134 | } else { 135 | src_id.as_url().to_string() 136 | } 137 | } 138 | 139 | fn to_source(pkg: &ResolvedPackage<'_>, cwd: &Path) -> Result { 140 | let id = pkg.pkg.package_id(); 141 | 142 | let source = if id.source_id().is_registry() { 143 | Source::CratesIo { 144 | sha256: pkg 145 | .checksum 146 | .as_ref() 147 | .map(|c| c.to_string()) 148 | .ok_or(anyhow!("checksum is required for crates.io package {}", id))?, 149 | } 150 | } else if id.source_id().is_git() { 151 | let branch = if let Some(GitReference::Branch(branch)) = id.source_id().git_reference() { 152 | Some(branch.to_owned()) 153 | } else { 154 | None 155 | }; 156 | Source::Git { 157 | url: id.source_id().url().to_string(), 158 | rev: id 159 | .source_id() 160 | .precise() 161 | .map(|p| p.to_string()) 162 | .ok_or(anyhow!("precise ref not found for git package {}", id))?, 163 | branch, 164 | } 165 | } else if id.source_id().is_path() { 166 | Source::Local { 167 | path: pathdiff::diff_paths(Path::new(id.source_id().url().path()), cwd) 168 | .map(|p| { 169 | if p.to_string_lossy().len() == 0 { 170 | p.join(".") // map degenerate empty path to "." for tera logic 171 | } else { 172 | p 173 | } 174 | }) 175 | .ok_or(anyhow!("path is not absolute for local package {}", id))?, 176 | } 177 | } else if id.source_id().is_registry() { 178 | Source::Registry { 179 | index: id.source_id().url().to_string(), 180 | sha256: pkg.checksum.as_ref().map(|c| c.to_string()).ok_or(anyhow!( 181 | "checksum is required for alternate registry package {}", 182 | id 183 | ))?, 184 | } 185 | } else { 186 | return Err(anyhow!("unsupported source for {}", id)); 187 | }; 188 | 189 | Ok(source) 190 | } 191 | 192 | fn to_features(features: &BTreeMap, Optionality<'_>>) -> Vec { 193 | features 194 | .iter() 195 | .map( 196 | |(name, optionality)| match optionality.to_expr("rootFeatures'").simplify() { 197 | BoolExpr::True => Feature { 198 | name: name.to_string(), 199 | activated_by: None, 200 | }, 201 | expr => Feature { 202 | name: name.to_string(), 203 | activated_by: Some(expr.to_nix().to_string()), 204 | }, 205 | }, 206 | ) 207 | .collect() 208 | } 209 | 210 | fn to_dependencies( 211 | pkg: &ResolvedPackage<'_>, 212 | ) -> (Vec, Vec, Vec) { 213 | let mut dependencies = Vec::new(); 214 | let mut dev_dependencies = Vec::new(); 215 | let mut build_dependencies = Vec::new(); 216 | 217 | for ((pkg_id, kind), dep) in &pkg.deps { 218 | let platforms = match dep.platforms { 219 | None => BoolExpr::True, 220 | Some(ref platforms) => BoolExpr::ors( 221 | platforms 222 | .iter() 223 | .map(|p| platform::to_expr(p, "hostPlatform")), 224 | ), 225 | }; 226 | 227 | let cfg_condition = match dep 228 | .optionality 229 | .to_expr("rootFeatures'") 230 | .and(platforms) 231 | .simplify() 232 | { 233 | BoolExpr::True => None, 234 | expr => Some(expr.to_nix().to_string()), 235 | }; 236 | 237 | let dep = Dependency { 238 | name: pkg_id.name().to_string(), 239 | extern_name: dep.extern_name.to_string(), 240 | version: pkg_id.version().to_string(), 241 | registry: to_registry_string(pkg_id.source_id()), 242 | cfg_condition, 243 | is_proc_macro: crate::is_proc_macro(&dep.pkg), 244 | }; 245 | 246 | match kind { 247 | DepKind::Normal => dependencies.push(dep), 248 | DepKind::Development => dev_dependencies.push(dep), 249 | DepKind::Build => build_dependencies.push(dep), 250 | } 251 | } 252 | 253 | (dependencies, dev_dependencies, build_dependencies) 254 | } 255 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs?ref=release-23.11"; 4 | rust-overlay = { 5 | url = "github:oxalica/rust-overlay"; 6 | inputs.nixpkgs.follows = "nixpkgs"; 7 | inputs.flake-utils.follows = "flake-utils"; 8 | }; 9 | flake-utils.url = "github:numtide/flake-utils"; 10 | flake-compat = { 11 | url = "github:edolstra/flake-compat"; 12 | flake = false; 13 | }; 14 | }; 15 | 16 | outputs = inputs: with inputs; 17 | let 18 | overlays = import ./overlay (rust-overlay.overlays.default); 19 | combinedOverlay = overlays.combined; 20 | 21 | in flake-utils.lib.eachDefaultSystem (system: 22 | let 23 | # 1. Setup nixpkgs with rust and cargo2nix overlays. 24 | pkgs = import nixpkgs { 25 | inherit system; 26 | overlays = [ 27 | combinedOverlay 28 | ]; 29 | # set `crossSystem` (see examples/3-cross-compiling) for configuring cross 30 | }; 31 | 32 | # 2. Builds the rust package set, which contains all crates in your cargo workspace's dependency graph. 33 | # `makePackageSet` accepts the following arguments: 34 | # - `packageFun` (required): The generated `Cargo.nix` file, which returns the whole dependency graph. 35 | # - `ignoreLockHash` (optional): Set to `true` to turn off the hash check between Cargo.lock and Cargo.nix. 36 | # - `workspaceSrc` (optional): Sources for the workspace can be provided or default to the current directory. 37 | # You must set some combination of `rustChannel` + `rustVersion` or `rustToolchain`. 38 | # - `rustToolchain` (optional): Completely override the toolchain. Must provide rustc, cargo, rust-std, and rust-src components 39 | # - `rustChannel` (optional): "nightly" "stable" "beta". To support legacy use, this can be a version when supplied alone. If unspecified, defaults to "stable". 40 | # - `rustVersion` (optional): "1.75.0" "2023-12-30". If not supplied, "latest" will be assumed. 41 | # - `rustProfile` (optional): "minimal" or "default" usually. "minimal" if not specified (for faster builds) 42 | # - `extraRustComponents` (optional): ["rustfmt" "clippy"]. 43 | # - `packageOverrides` (optional): 44 | # A function taking a package set and returning a list of overrides. 45 | # Overrides are introduced to provide native inputs to build the crates generated in `Cargo.nix`. 46 | # See `overlay/lib/overrides.nix` on how to create overrides and `overlay/overrides.nix` for a list of predefined overrides. 47 | # Most of the time, you can just use `overrides.all`. You can hand-pick overrides later if your build becomes too slow. 48 | # - `rootFeatures` (optional): 49 | # A list of activated features on your workspace's crates. 50 | # Each feature should be of the form `[/]`. 51 | # If `/` is omitted, the crate is activated with no default features. 52 | # The default behavior is to activate all crates with default features. 53 | # - `fetchCrateAlternativeRegistry` (optional): A fetcher for crates on alternative registries. 54 | # - `release` (optional): Whether to enable release mode (equivalent to `cargo build --release`), defaults to `true`. 55 | # - `hostPlatformCpu` (optional): 56 | # Equivalent to rust's target-cpu codegen option. If specified "-Ctarget-cpu=" will be added to the set of rust 57 | # flags used for compilation of the package set. 58 | # - `hostPlatformFeatures` (optional): 59 | # Equivalent to rust's target-feature codegen option. If specified "-Ctarget-feature=" will be added to the set of rust 60 | # flags used for compilation of the package set. The value should be a list of the features to be turned on, without the leading "+", 61 | # e.g. `[ "aes" "sse2" "ssse3" "sse4.1" ]`. They will be prefixed with a "+", and comma delimited before passing through to rust. 62 | # Crates that check for CPU features such as the `aes` crate will be evaluated against this argument. 63 | # cargoUnstableFlags (optional): 64 | # Passes "-Z" flags that affect cargo unstable features. Example [ "build-std=panic_abort" "std" ] 65 | # rustcLinkFlags (optional): 66 | # Pass extra flags directly to rustc during non-build invocations 67 | # rustcBuildFlags (optional): 68 | # Pass extra flags directly to Rustc during build invocations 69 | # - `target` (optional): 70 | # Set an explicit Rust output target. Overrides the translation 71 | # from Nix targets to Rust targets. See overlay/lib/rust-triple.nix 72 | # for more info. 73 | rustPkgs = pkgs.rustBuilder.makePackageSet { 74 | packageFun = import ./Cargo.nix; 75 | rustVersion = "1.75.0"; 76 | packageOverrides = pkgs: pkgs.rustBuilder.overrides.all; 77 | }; 78 | # `rustPkgs` now contains all crates in the dependency graph. 79 | # To build normal binaries, use `rustPkgs... { }`. 80 | # To build test binaries (equivalent to `cargo build --tests`), use 81 | # `rustPkgs...{ compileMode = "test"; }`. 82 | # To build bench binaries (equivalent to `cargo build --benches`), use 83 | # `rustPkgs...{ compileMode = "bench"; }`. 84 | # For convenience, you can also refer to the crates in the workspace using 85 | # `rustPkgs.workspace.`. 86 | # 87 | # When a crate is not associated with any registry, such as when building 88 | # locally, the registry is "unknown" as shown below: 89 | # rustPkgs.unknown.cargo2nix."0.11.0" 90 | # An example of a crates.io path: 91 | # rustPkgs."registry+https://github.com/rust-lang/crates.io-index".openssl."0.10.30" 92 | 93 | cargo2nix = (rustPkgs.workspace.cargo2nix {}); # supports override & overrideAttrs 94 | 95 | # The workspace defines a development shell with all of the dependencies 96 | # and environment settings necessary for a regular `cargo build`. 97 | # Passes through all arguments to pkgs.mkShell for adding supplemental 98 | # dependencies. 99 | workspaceShell = (rustPkgs.workspaceShell { 100 | # packages = [ pkgs.somethingExtra ]; 101 | # shellHook = '' 102 | # export PS1="\033[0;31m☠dev-shell☠ $ \033[0m" 103 | # ''; 104 | }); # supports override & overrideAttrs 105 | 106 | # A shell for users to quickly bootstrap projects. Contains cargo2nix 107 | # and the rustToolchain used to build this cargo2nix. 108 | bootstrapShell = pkgs.mkShell { 109 | packages = [ cargo2nix ]; 110 | # inputsFrom = [ cargo2nix ]; 111 | nativeBuildInputs = cargo2nix.nativeBuildInputs; 112 | }; 113 | 114 | in rec { 115 | 116 | devShells = { 117 | # nix develop 118 | default = workspaceShell; 119 | # nix develop .#bootstrap 120 | bootstrap = bootstrapShell; 121 | }; 122 | 123 | packages = rec { 124 | # nix build .#packages.x86_64-linux.cargo2nix 125 | # nix build .#cargo2nix 126 | inherit cargo2nix; 127 | # nix build 128 | default = cargo2nix; 129 | 130 | # `runTests` runs all tests for a crate inside a Nix derivation. This 131 | # may be problematic as Nix may restrict filesystem, network access, 132 | # socket creation, which the test binary may need. 133 | # If you run to those problems, build test binaries (as shown above in 134 | # workspace derivation arguments) and run them manually outside a Nix 135 | # derivation.s 136 | ci = pkgs.rustBuilder.runTests rustPkgs.workspace.cargo2nix { 137 | /* Add `depsBuildBuild` test-only deps here, if any. */ 138 | }; 139 | 140 | # for legacy users 141 | shell = devShells.default; 142 | }; 143 | 144 | apps = rec { 145 | # nix run .#cargo2nix 146 | # nix run github:cargo2nix/cargo2nix 147 | cargo2nix = { type = "app"; program = "${packages.default}/bin/cargo2nix"; }; 148 | # nix run 149 | # nix run github:cargo2nix/cargo2nix 150 | default = cargo2nix; 151 | }; 152 | } 153 | ) // { 154 | # The above outputs are mapped over system for `nix run` and `nix develop` 155 | # workflows. They are merged with these system-independent attributes, 156 | # which are top level attributes can be used directly in downstream 157 | # flakes. If `cargo2nix` is your flake input, `cargo2nix.overlay` is the 158 | # overlay. 159 | inherit overlays; 160 | # Nix flake check complains. I will keep this attribute alive until next 161 | # version branch-off. 162 | overlay = builtins.trace 163 | "cargo2nix.overlay is deprecated. Use cargo2nix.overlays.default" overlays.default; 164 | }; 165 | } 166 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/README.md: -------------------------------------------------------------------------------- 1 | # Project with cross compiling 2 | 3 | Nix's stdenv tooling can be used to create binaries that target many platforms, 4 | linked in many ways. You can use this to create static binaries or to target 5 | entirely different architectures. 6 | 7 | Cargo2nix has to properly handle build-host offsets in `build.rs` scripts and 8 | when linking binaries. For the sake of demonstration, this project will use the 9 | [`ructe`] template engine to generate a statically-embedded template file. 10 | 11 | [`ructe`]: https://github.com/kaj/ructe 12 | 13 | ## Configuring Nixpkgs for Cross 14 | 15 | When configuring `nixpkgs`, two arguments are very critical: 16 | 17 | - `system` configures what kind of builder will be used. For example, if your 18 | machine is a darwin (mac) machine and you specify `x86_64-linux` as the 19 | system, your nix program will look for a linux builder such as [`nix-docker`] 20 | and delegate the build out to it. 21 | 22 | - `crossSystem` configures the host. If the host platform does not match the 23 | build system `system`, then you are cross compiling and the package offsets, 24 | All the programs that run during the build will be native to `system` still, 25 | but they will produce output for the `crossSystem` host. 26 | 27 | When you only set `system`, you get a native build for that platform, producing 28 | output for the same platform. If you set `crossSystem`, you can configure 29 | linking and target options the differ from the build platform. This is how a 30 | Linux machine can build for aarch64 etc. 31 | 32 | In general, throughout cargo2nix, you can assume that `system` is the 33 | `buildPlatform` and `crossSystem` is the `hostPlatform` 34 | configuration. 35 | 36 | [`nix-docker`]: https://github.com/LnL7/nix-docker 37 | 38 | ### CrossSystem Values 39 | 40 | A variety of configuration options can be viewed in `lib.systems.examples`: 41 | 42 | ```shell 43 | nix repl '' 44 | nix-repl> lib.systems.examples.wasi32 45 | { config = "wasm32-unknown-wasi"; useLLVM = true; } 46 | ``` 47 | 48 | Most crossSystem values are simple like this. Others make more specific 49 | changes. Many only set the `config` attribute to a host triple + abi like 50 | `x86_64-unknown-linux-musl`. 51 | 52 | ### Macros - Build or Host? 53 | 54 | Consider the following: 55 | 56 | - [`include_str!()`] 57 | - [`include_bytes!()`] 58 | 59 | [`include_str!()`]: https://doc.rust-lang.org/std/macro.include_str.html 60 | [`include_bytes!()`]: https://doc.rust-lang.org/std/macro.include_bytes.html 61 | 62 | They are supposed to expand into something that is used in the final binary, but 63 | the expansion will happen while still on the build platform. Rust doesn't 64 | really care what the target is when evaluating `build.rs` scripts. 65 | 66 | ### Rust & Nix Targets 67 | 68 | The Nix and Rust concept of systems don't line up perfectly. In some cases 69 | cargo2nix does translation of the Nix target to a Rust target. See 70 | [`rust-triple.nix`]. If you need to use one target for nixpkgs and a specific 71 | target for Rust when targeting the `hostPlatform`, then 72 | 73 | [`rust-triple.nix`]: ../../overlay/lib/rust-triple.nix 74 | 75 | ## Generating the project 76 | 77 | As in the previous project, set up a bin crate. 78 | 79 | Make a template directory called `templates` and write a template called `test.rs.html`: 80 | 81 | ```text 82 | @(test: &str) 83 | 84 | Hello @test 85 | ``` 86 | 87 | Add the following line to the `[build-dependencies]` table of the `Cargo.toml`: 88 | 89 | ```toml 90 | ructe = "0.9.2" 91 | ``` 92 | 93 | Next, we create a [Cargo build script] named `build.rs` at the crate root: 94 | 95 | [Cargo build script]: https://doc.rust-lang.org/cargo/reference/build-scripts.html 96 | 97 | ```rust 98 | use ructe::Ructe; 99 | 100 | fn main() { 101 | Ructe::from_env() 102 | .expect("ructe") 103 | .compile_templates("templates") 104 | .unwrap(); 105 | } 106 | ``` 107 | 108 | This build script will generate a Rust source file named `$OUT_DIR/templates.rs` 109 | which we can statically embed into our program with `include_str!()`. Let's open 110 | the `src/main.rs` file and write some code that will do just that: 111 | 112 | ```rust 113 | //! A program which generates some text using an embedded template. 114 | 115 | include!(concat!(env!("OUT_DIR"), "/templates.rs")); 116 | 117 | fn main() { 118 | let mut buf = Vec::new(); 119 | templates::test(&mut buf, "world").unwrap(); 120 | println!("{}", String::from_utf8_lossy(&buf)); 121 | } 122 | ``` 123 | 124 | Our `templates/test.rs.html` file has been converted into a callable Rust 125 | function named `templates::tests`. In the above code, we use this function to 126 | generate the UTF-8 byte string `Hello world` and print it to the screen. 127 | 128 | ## Generating a Cargo.nix 129 | 130 | Like the previous example, we generate a `Cargo.lock` and `Cargo.nix` for our 131 | crate by running the two commands below: 132 | 133 | ```bash 134 | cargo generate-lockfile 135 | cargo2nix 136 | ``` 137 | 138 | ## Writing the Flake.nix 139 | 140 | Here's where things get a lot more interesting than the previous examples. 141 | 142 | ## Providing Flakes With pkgsCross 143 | 144 | While we are still using flake utils to streamline how the flake defines outputs 145 | for typical systems, the use of `crossSystem` sets the output. 146 | 147 | Create a new file called [`flake.nix`]: 148 | 149 | [`flake.nix`]: ./flake.nix 150 | 151 | ```nix 152 | { 153 | inputs = { 154 | cargo2nix.url = "path:../../"; 155 | # Use a github flake URL for real packages 156 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 157 | flake-utils.follows = "cargo2nix/flake-utils"; 158 | nixpkgs.follows = "cargo2nix/nixpkgs"; 159 | }; 160 | 161 | outputs = inputs: with inputs; 162 | 163 | # Build the output set for each default system and map system sets into 164 | # attributes, resulting in paths such as: 165 | # nix build .#packages.x86_64-linux. 166 | flake-utils.lib.eachDefaultSystem (system: 167 | 168 | # let-in expressions, very similar to Rust's let bindings. These names 169 | # are used to express the output but not themselves paths in the output. 170 | let 171 | 172 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 173 | pkgs = import nixpkgs { 174 | inherit system; 175 | 176 | # crossSystem = { 177 | # config = "aarch64-unknown-linux-gnu"; 178 | # }; 179 | 180 | 181 | # crossSystem = { 182 | # config = "x86_64-unknown-linux-musl"; 183 | # }; 184 | 185 | crossSystem = { 186 | config = "wasm32-unknown-wasi"; 187 | # Nixpkgs currently only supports LLVM lld linker for wasm32-wasi. 188 | useLLVM = true; 189 | }; 190 | 191 | overlays = [ cargo2nix.overlays.default ]; 192 | }; 193 | 194 | # create the workspace & dependencies package set 195 | rustPkgs = pkgs.rustBuilder.makePackageSet { 196 | rustVersion = "1.75.0"; 197 | packageFun = import ./Cargo.nix; 198 | 199 | # If your specific build target requires a difference between Rust and 200 | # nixpkgs, set this target 201 | # target = "aarch64-unknown-linux-gnu"; 202 | # target = "x86_64-unknown-linux-musl"; 203 | # target = "wasm32-wasi"; 204 | }; 205 | 206 | in rec { 207 | # this is the output (recursive) set (expressed for each system) 208 | 209 | # the packages in `nix build .#packages..` 210 | packages = { 211 | # nix build .#cross-compiling 212 | # nix build .#packages.x86_64-linux.cross-compiling 213 | cross-compiling = (rustPkgs.workspace.cross-compiling {}); 214 | # nix build 215 | default = packages.cross-compiling; 216 | }; 217 | } 218 | ); 219 | } 220 | ``` 221 | 222 | Save the `flake.nix` file and quit. Let's verify this works as expected by 223 | building it! 224 | 225 | ### :warning: Remember to Add Flake to Version Control 226 | 227 | Remember to add the flake to version control (and `Cargo.nix` and `flake.lock` 228 | while you're at it) 229 | 230 | ```bash 231 | git init 232 | git add flake.nix 233 | # generate the flake.lock 234 | flake check 235 | # add generated to version control 236 | git add flake.lock 237 | git add Cargo.nix 238 | ``` 239 | 240 | ## Building 241 | 242 | Build the binary output 243 | 244 | ```bash 245 | nix build 246 | ``` 247 | 248 | This will create a `result` symlink in the current directory with the following 249 | structure: 250 | 251 | ```text 252 | ls -al result-bin 253 | result-bin -> /nix/store/xfhfa9pshhix98qlxmjmhf9200k4r7id-crate-cross-compiling-0.1.0-bin 254 | 255 | tree result-bin 256 | result-bin 257 | └── bin 258 | └── cross-compiling.wasm 259 | ``` 260 | 261 | ### Inspecting and Running Outputs 262 | 263 | Running the `cross-compiling` binary for your `buildPlatform` will fail. 264 | 265 | ```text 266 | ./result-bin/bin/cross-compiling.wasm 267 | # bash: ./result-bin/bin/cross-compiling.wasm: cannot execute binary file: Exec format error 268 | ``` 269 | 270 | You need a runtime or emulator. For the `wasm32-wasi` example, you can use 271 | `wasmtime` available from nixpkgs: 272 | 273 | ```bash 274 | nix shell nixpkgs#wasmtime 275 | wasmtime result-bin/bin/cross-compiling.wasm 276 | # Hello world 277 | ``` 278 | 279 | For the `aarch64-unknown-linux-gnu` example, you will need qemu: 280 | 281 | ```bash 282 | nix shell nixpkgs#qemu 283 | qemu-aarch64 result-bin/bin/static-resources 284 | # Hello world 285 | ``` 286 | 287 | Musl binaries will just run on the native platform, so 288 | `lib.systems.examples.musl64` runs on Linux and so on. 289 | 290 | To inspect the file format and linking, use `file` and `ldd`: 291 | 292 | ```bash 293 | file result-bin/bin/cross-compiling.wasm 294 | # result-bin/bin/cross-compiling.wasm: WebAssembly (wasm) binary module version 0x1 (MVP) 295 | 296 | ldd result-bin/bin/cross-compiling.wasm 297 | # not a dynamic executable 298 | ``` 299 | 300 | ### Using a Remote Builder 301 | 302 | Many cross compiling toolchains only work on Darwin or Linux. Linux is usually 303 | the stronger platform in this regard, except when targeting other Mac platforms. 304 | To enable Macs to build binaries for a Linux runtime (such as when making Docker 305 | containers) etc, you can run a local "remote" builder on Docker itself. 306 | 307 | LnL7 wrote an excellent set of tools for this in [Nix Darwin] and [Nix Docker]. If 308 | you have these tools installed, you can select the builder by just switching up 309 | the nix command just a bit: 310 | 311 | ```bash 312 | nix build .#packages.x86_64-linux.cross-compiling 313 | ``` 314 | 315 | [Nix Docker]: https://github.com/LnL7/nix-docker 316 | [Nix Darwing]: https://github.com/LnL7/nix-darwin 317 | [Docker Desktop]: https://www.docker.com/products/docker-desktop 318 | -------------------------------------------------------------------------------- /examples/1-hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | This guide demonstrates how to nixify a vanilla `bin` crate generated by `cargo 4 | new`. 5 | 6 | ### Nix & Nix Flakes 7 | 8 | We assume that you **already** have the [Nix package manager] **installed** on 9 | your machine before we get started. **You must have [nix flakes] enabled**. 10 | Flakes are still relatively new, but it is highly recommended to use nix with 11 | flake support even if you are new to nix because of the simplifications and 12 | better guard rails for your workflows. 13 | 14 | If you encounter any difficulties with this guide, please run `nix build` in 15 | this directory with the pre-provided `flake.nix`, `flake.lock` and `Cargo.nix` 16 | as these should be completely reproducible and work on most systems. **File an 17 | issue if this step fails.** 18 | 19 | [Nix package manager]: https://nixos.org/nix/ 20 | [Nix flakes]: https://serokell.io/blog/practical-nix-flakes 21 | 22 | ## Overview 23 | 24 | These steps show you how to bootstrap & **maintain** both a `Cargo.nix` and nix 25 | flake for your project. 26 | 27 | - Generating the minimal cargo bin project 28 | - Generating the `Cargo.nix` file 29 | - Writing a `flake.nix` file to consume the `Cargo.nix` and provide the outputs 30 | for `nix build` commands 31 | - Building & using outputs 32 | 33 | ## Generating the Cargo project 34 | 35 | If you don't have cargo installed, you can use nix to enter a shell that has a 36 | Rust toolchain available. Then just create a default binary crate project. 37 | 38 | ```bash 39 | # adds cargo2nix rustc & cargo to PATH 40 | nix develop github:cargo2nix/cargo2nix#bootstrap 41 | 42 | cargo new hello-world 43 | # Created binary (application) `hello-world` package 44 | ``` 45 | 46 | Now you have a mostly empty `Cargo.toml` and `src/main.rs` file. 47 | 48 | ### Generate Cargo.lock 49 | 50 | Cargo2nix uses the `Cargo.toml` and `Cargo.lock` files to generate an equivalent 51 | nix expression for building your project (and its dependencies). Create this 52 | lockfile by either running `cargo build` or `cargo generate-lockfile` to be more 53 | specific. 54 | 55 | ```bash 56 | cargo generate-lockfile 57 | ``` 58 | 59 | 60 | ## Generating a Cargo.nix 61 | 62 | If you haven't installed cargo2nix, you can run it directly from the flake 63 | definition from github like so: 64 | 65 | ```bash 66 | nix run github:cargo2nix/cargo2nix 67 | ``` 68 | 69 | Generate the `Cargo.nix` file 70 | 71 | ```bash 72 | cargo2nix 73 | 74 | # Alternatively you can run cargo2nix directly 75 | nix run github:cargo2nix/cargo2nix 76 | ``` 77 | 78 | 79 | The nix expression inside `Cargo.nix` is a function that returns the dependency 80 | graph of your Cargo crate or [crate workspace]. 81 | 82 | [crate workspace]: https://doc.rust-lang.org/edition-guide/rust-2018/cargo-and-crates-io/cargo-workspaces-for-multi-package-projects.html 83 | 84 | #### :warning: Keeping Cargo.nix Up to Date :warning: 85 | 86 | > Any time that you modify the `Cargo.toml` or `Cargo.lock` file for your 87 | > project, you should always remember to re-run `cargo2nix` to update the 88 | > `Cargo.nix` as well. 89 | 90 | You will want both `Cargo.lock` and `Cargo.nix` under version control for 91 | reproducibility. (although only providing `Cargo.nix` is sufficient for nix 92 | builds) 93 | 94 | ## Writing a flake.nix 95 | 96 | In order to consume the `Cargo.nix` and provide outputs for `nix build` and 97 | other `nix` commands, you need to write a `flake.nix` 98 | 99 | The overally structure of a flake is very simple. It's a nix expression that 100 | describes `inputs` and `outputs`. All of the inputs are usually other flakes. 101 | The output paths tell `nix` commands such as `nix develop` and `nix build` what 102 | to use to do their work. 103 | 104 | [`flake.nix`]: ./flake.nix 105 | 106 | ```nix 107 | { 108 | inputs = { 109 | cargo2nix.url = "path:../../"; 110 | # Use a github flake URL for real packages 111 | # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 112 | flake-utils.follows = "cargo2nix/flake-utils"; 113 | nixpkgs.follows = "cargo2nix/nixpkgs"; 114 | }; 115 | 116 | outputs = inputs: with inputs; # pass through all inputs and bring them into scope 117 | 118 | # Build the output set for each default system and map system sets into 119 | # attributes, resulting in paths such as: 120 | # nix build .#packages.x86_64-linux. 121 | flake-utils.lib.eachDefaultSystem (system: 122 | 123 | # let-in expressions, very similar to Rust's let bindings. These names 124 | # are used to express the output but not themselves paths in the output. 125 | let 126 | 127 | # create nixpkgs that contains rustBuilder from cargo2nix overlay 128 | pkgs = import nixpkgs { 129 | inherit system; 130 | overlays = [ cargo2nix.overlays.default ]; 131 | }; 132 | 133 | # create the workspace & dependencies package set 134 | rustPkgs = pkgs.rustBuilder.makePackageSet { 135 | rustVersion = "1.75.0"; 136 | packageFun = import ./Cargo.nix; 137 | }; 138 | 139 | in rec { 140 | # this is the output (recursive) set (expressed for each system) 141 | 142 | # the packages in `nix build .#packages..` 143 | packages = { 144 | # nix build .#hello-world 145 | # nix build .#packages.x86_64-linux.hello-world 146 | hello-world = (rustPkgs.workspace.hello-world {}); 147 | # nix build 148 | default = packages.hello-world; # rec 149 | }; 150 | } 151 | ); 152 | } 153 | ``` 154 | 155 | ### :warning: Adding Flake to Git :warning: 156 | 157 | Nix flake commands have a strong assumption that your project is using 158 | git for version control and that the `flake.nix` has been added. 159 | 160 | ```bash 161 | git add flake.nix 162 | 163 | nix flake check 164 | # verifies the flake and generates a flake.lock file 165 | 166 | # you want to add this to version control for distributing reproducibility 167 | git add flake.lock 168 | ``` 169 | 170 | ### Understanding the Flake Expressions 171 | 172 | Again, it's basically just a declaration of inputs and then an outputs function 173 | for consuming those inputs. For more information, read up on the [flake 174 | schema]. Each output path usually corresponds to a specific nix command that 175 | will use that output. 176 | 177 | [flake schema]: https://nixos.wiki/wiki/Flakes#Flake_schema 178 | 179 | Occasionally you want to modify how an output is built, and that is why nix 180 | expressions are defined in the nix language and flakes are expressions rather 181 | than just flat declarations like a yaml file. 182 | 183 | #### Constructing Nixpkgs 184 | 185 | The flake input for nixpkgs needs to be instantiated into an actual nixpkgs. 186 | The [overlays] inject some extras, like the `rust-bin` set of Rust toolchains 187 | and functions like `rustBuilder.makePackageSet`. **Note the single quote in 188 | the name!** 189 | 190 | [overlays]: https://nixos.wiki/wiki/Overlays 191 | 192 | ```nix 193 | pkgs = import nixpkgs { 194 | inherit system; 195 | overlays = [(import "${cargo2nix}/overlay") 196 | rust-overlay.overlays.default]; 197 | }; 198 | ``` 199 | 200 | #### Constructing the Workspace & Dependencies Set 201 | 202 | We use `makePackageSet` to import our `Cargo.nix` file and build it using the 203 | `"stable"` release channel of Rust. The output of this function is a tree 204 | structure containing the entire dependency graph from our `Cargo.nix`, which we 205 | call `rustPkgs`. 206 | 207 | The structure of `rustPkgs` is organized as follows: 208 | 209 | ```nix 210 | rustPkgs."unknown".hello-world."0.1.0" { } 211 | # ^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^ ^^^ 212 | # | | | | 213 | # Source -----------------+ | | | 214 | # - Crates.io URL | | | 215 | # - Git repository URL | | | 216 | # - Alterative registry URL | | | 217 | # - `unknown` for path deps | | | 218 | # | | | 219 | # Name ---------------+ | | 220 | # | | 221 | # Version -----------+ | 222 | # | 223 | # Optional build arguments -------+ 224 | # - `compileMode = "test"` 225 | # - `compileMode = "bench"` 226 | # - `buildInputs`, `nativeBuildInputs`, etc. 227 | ``` 228 | 229 | We can refer to any crate present in our `Cargo.lock` file using this notation, 230 | and build it if we want. 231 | 232 | #### Getting Our Derivations Out of the Workspace Set 233 | 234 | Packages from the workspace are also duplicated as a `workspace` set. Each 235 | output is a function that must be called (see the main `Cargo.nix` for 236 | arguments) to obtain a final derivation. The derivations contain multiple 237 | outputs. The default is `bin` attribute that is appropriate for installation 238 | into a [nix profile]. The `out` attribute contains the linking information used 239 | in dependents. 240 | 241 | [nix profile]: https://nixos.org/manual/nix/unstable/package-management/profiles.html 242 | 243 | ```nix 244 | # this expression is the output ("bin", not "out" by default) of our sweet derviation 245 | (rustPkgs.workspace.hello-world {}) 246 | ``` 247 | 248 | #### The System Argument 249 | 250 | `system`, is a string which declares what platform Nix should build this project 251 | for. This is _not_ a [Rust target triple], but rather a [Nix system parameter] 252 | such as `"x86_64-linux"` or `"x86_64-darwin"`. 253 | 254 | [Rust target triple]: https://forge.rust-lang.org/release/platform-support.html 255 | [Nix system parameter]: https://nixos.org/nixos/nix-pills/nixpkgs-parameters.html#idm140737319760528 256 | 257 | #### The Rust Overlay 258 | 259 | The [Rust Overlay] is a flake that provides a Rust toolchain for use as a 260 | dependency during build. You can use other versions of Rust by setting 261 | `rustVersion` to `1.75.0` for example. `rustChannel` can also be set. See the 262 | oxalica Rust Overlay [reference] for a list of all public attributes. 263 | 264 | [reference]: https://github.com/oxalica/rust-overlay/blob/master/docs/reference.md 265 | [Rust Overlay]: https://github.com/oxalica/rust-overlay 266 | [handy Nixpkgs overlay for Rust]: https://github.com/oxalica/rust-overlay 267 | [release channels]: https://doc.rust-lang.org/edition-guide/rust-2018/rustup-for-managing-rust-versions.html 268 | 269 | ## Building 270 | 271 | To compile the `hello-world` binary with Nix, simply run: 272 | 273 | ```bash 274 | nix build 275 | ``` 276 | 277 | This will create a `result-bin` symlink in the current directory with the following 278 | structure: 279 | 280 | ```bash 281 | ls -al result-bin 282 | /nix/store/bv9ylpmdixrhzqzd1hcd67pzhip3qc9a-crate-hello-world-0.1.0-bin 283 | 284 | tree result-bin 285 | result-bin 286 | └── bin 287 | └── hello-world 288 | ``` 289 | 290 | Running the `hello-world` binary will print the following message to the screen: 291 | 292 | ```text 293 | $ ./result-bin/bin/hello-world 294 | Hello, world! 295 | ``` 296 | 297 | Awesome! You've just built your first Rust project with Nix using `cargo2nix`. 298 | :tada: 299 | 300 | In the next example, we'll take a look at building a more complex Cargo project 301 | with some Crates.io dependencies. 302 | -------------------------------------------------------------------------------- /examples/3-cross-compiling/Cargo.nix: -------------------------------------------------------------------------------- 1 | # This file was @generated by cargo2nix 0.11.0. 2 | # It is not intended to be manually edited. 3 | 4 | args@{ 5 | release ? true, 6 | rootFeatures ? [ 7 | "cross-compiling/default" 8 | ], 9 | rustPackages, 10 | buildRustPackages, 11 | hostPlatform, 12 | hostPlatformCpu ? null, 13 | hostPlatformFeatures ? [], 14 | target ? null, 15 | codegenOpts ? null, 16 | profileOpts ? null, 17 | cargoUnstableFlags ? null, 18 | rustcLinkFlags ? null, 19 | rustcBuildFlags ? null, 20 | mkRustCrate, 21 | rustLib, 22 | lib, 23 | workspaceSrc, 24 | ignoreLockHash, 25 | }: 26 | let 27 | nixifiedLockHash = "c787740d469acf71a36abd0a236a69f5b797b67eff93b9b6bd0321ea533a45ef"; 28 | workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc; 29 | currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock); 30 | lockHashIgnored = if ignoreLockHash 31 | then builtins.trace "Ignoring lock hash" ignoreLockHash 32 | else ignoreLockHash; 33 | in if !lockHashIgnored && (nixifiedLockHash != currentLockHash) then 34 | throw ("Cargo.nix ${nixifiedLockHash} is out of sync with Cargo.lock ${currentLockHash}") 35 | else let 36 | inherit (rustLib) fetchCratesIo fetchCrateLocal fetchCrateGit fetchCrateAlternativeRegistry expandFeatures decideProfile genDrvsByProfile; 37 | profilesByName = { 38 | }; 39 | rootFeatures' = expandFeatures rootFeatures; 40 | overridableMkRustCrate = f: 41 | let 42 | drvs = genDrvsByProfile profilesByName ({ profile, profileName }: mkRustCrate ({ inherit release profile hostPlatformCpu hostPlatformFeatures target profileOpts codegenOpts cargoUnstableFlags rustcLinkFlags rustcBuildFlags; } // (f profileName))); 43 | in { compileMode ? null, profileName ? decideProfile compileMode release }: 44 | let drv = drvs.${profileName}; in if compileMode == null then drv else drv.override { inherit compileMode; }; 45 | in 46 | { 47 | cargo2nixVersion = "0.11.0"; 48 | workspace = { 49 | cross-compiling = rustPackages.unknown.cross-compiling."0.1.0"; 50 | }; 51 | "registry+https://github.com/rust-lang/crates.io-index".arrayvec."0.5.2" = overridableMkRustCrate (profileName: rec { 52 | name = "arrayvec"; 53 | version = "0.5.2"; 54 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 55 | src = fetchCratesIo { inherit name version; sha256 = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"; }; 56 | features = builtins.concatLists [ 57 | [ "array-sizes-33-128" ] 58 | ]; 59 | }); 60 | 61 | "registry+https://github.com/rust-lang/crates.io-index".base64."0.11.0" = overridableMkRustCrate (profileName: rec { 62 | name = "base64"; 63 | version = "0.11.0"; 64 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 65 | src = fetchCratesIo { inherit name version; sha256 = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"; }; 66 | features = builtins.concatLists [ 67 | [ "default" ] 68 | [ "std" ] 69 | ]; 70 | }); 71 | 72 | "registry+https://github.com/rust-lang/crates.io-index".bitflags."1.3.2" = overridableMkRustCrate (profileName: rec { 73 | name = "bitflags"; 74 | version = "1.3.2"; 75 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 76 | src = fetchCratesIo { inherit name version; sha256 = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"; }; 77 | features = builtins.concatLists [ 78 | [ "default" ] 79 | ]; 80 | }); 81 | 82 | "registry+https://github.com/rust-lang/crates.io-index".bytecount."0.6.4" = overridableMkRustCrate (profileName: rec { 83 | name = "bytecount"; 84 | version = "0.6.4"; 85 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 86 | src = fetchCratesIo { inherit name version; sha256 = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7"; }; 87 | }); 88 | 89 | "registry+https://github.com/rust-lang/crates.io-index".cfg-if."1.0.0" = overridableMkRustCrate (profileName: rec { 90 | name = "cfg-if"; 91 | version = "1.0.0"; 92 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 93 | src = fetchCratesIo { inherit name version; sha256 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"; }; 94 | }); 95 | 96 | "unknown".cross-compiling."0.1.0" = overridableMkRustCrate (profileName: rec { 97 | name = "cross-compiling"; 98 | version = "0.1.0"; 99 | registry = "unknown"; 100 | src = fetchCrateLocal workspaceSrc; 101 | buildDependencies = { 102 | ructe = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".ructe."0.9.2" { profileName = "__noProfile"; }).out; 103 | }; 104 | }); 105 | 106 | "registry+https://github.com/rust-lang/crates.io-index".either."1.9.0" = overridableMkRustCrate (profileName: rec { 107 | name = "either"; 108 | version = "1.9.0"; 109 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 110 | src = fetchCratesIo { inherit name version; sha256 = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"; }; 111 | }); 112 | 113 | "registry+https://github.com/rust-lang/crates.io-index".itertools."0.8.2" = overridableMkRustCrate (profileName: rec { 114 | name = "itertools"; 115 | version = "0.8.2"; 116 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 117 | src = fetchCratesIo { inherit name version; sha256 = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"; }; 118 | features = builtins.concatLists [ 119 | [ "default" ] 120 | [ "use_std" ] 121 | ]; 122 | dependencies = { 123 | either = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".either."1.9.0" { inherit profileName; }).out; 124 | }; 125 | }); 126 | 127 | "registry+https://github.com/rust-lang/crates.io-index".lexical-core."0.7.6" = overridableMkRustCrate (profileName: rec { 128 | name = "lexical-core"; 129 | version = "0.7.6"; 130 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 131 | src = fetchCratesIo { inherit name version; sha256 = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"; }; 132 | features = builtins.concatLists [ 133 | [ "arrayvec" ] 134 | [ "correct" ] 135 | [ "default" ] 136 | [ "ryu" ] 137 | [ "static_assertions" ] 138 | [ "std" ] 139 | [ "table" ] 140 | ]; 141 | dependencies = { 142 | arrayvec = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".arrayvec."0.5.2" { inherit profileName; }).out; 143 | bitflags = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bitflags."1.3.2" { inherit profileName; }).out; 144 | cfg_if = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".cfg-if."1.0.0" { inherit profileName; }).out; 145 | ryu = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".ryu."1.0.15" { inherit profileName; }).out; 146 | static_assertions = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".static_assertions."1.1.0" { inherit profileName; }).out; 147 | }; 148 | }); 149 | 150 | "registry+https://github.com/rust-lang/crates.io-index".md5."0.7.0" = overridableMkRustCrate (profileName: rec { 151 | name = "md5"; 152 | version = "0.7.0"; 153 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 154 | src = fetchCratesIo { inherit name version; sha256 = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"; }; 155 | features = builtins.concatLists [ 156 | [ "default" ] 157 | [ "std" ] 158 | ]; 159 | }); 160 | 161 | "registry+https://github.com/rust-lang/crates.io-index".memchr."2.6.4" = overridableMkRustCrate (profileName: rec { 162 | name = "memchr"; 163 | version = "2.6.4"; 164 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 165 | src = fetchCratesIo { inherit name version; sha256 = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"; }; 166 | features = builtins.concatLists [ 167 | [ "alloc" ] 168 | [ "std" ] 169 | [ "use_std" ] 170 | ]; 171 | }); 172 | 173 | "registry+https://github.com/rust-lang/crates.io-index".nom."5.1.3" = overridableMkRustCrate (profileName: rec { 174 | name = "nom"; 175 | version = "5.1.3"; 176 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 177 | src = fetchCratesIo { inherit name version; sha256 = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b"; }; 178 | features = builtins.concatLists [ 179 | [ "alloc" ] 180 | [ "default" ] 181 | [ "lexical" ] 182 | [ "lexical-core" ] 183 | [ "std" ] 184 | ]; 185 | dependencies = { 186 | lexical_core = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".lexical-core."0.7.6" { inherit profileName; }).out; 187 | memchr = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".memchr."2.6.4" { inherit profileName; }).out; 188 | }; 189 | buildDependencies = { 190 | version_check = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".version_check."0.9.4" { profileName = "__noProfile"; }).out; 191 | }; 192 | }); 193 | 194 | "registry+https://github.com/rust-lang/crates.io-index".ructe."0.9.2" = overridableMkRustCrate (profileName: rec { 195 | name = "ructe"; 196 | version = "0.9.2"; 197 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 198 | src = fetchCratesIo { inherit name version; sha256 = "c85620b8046f88a870d93d90fa56904dec76cc79139bfcc22e71e87f0cd2169f"; }; 199 | dependencies = { 200 | base64 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.11.0" { inherit profileName; }).out; 201 | bytecount = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytecount."0.6.4" { inherit profileName; }).out; 202 | itertools = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".itertools."0.8.2" { inherit profileName; }).out; 203 | md5 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".md5."0.7.0" { inherit profileName; }).out; 204 | nom = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".nom."5.1.3" { inherit profileName; }).out; 205 | }; 206 | }); 207 | 208 | "registry+https://github.com/rust-lang/crates.io-index".ryu."1.0.15" = overridableMkRustCrate (profileName: rec { 209 | name = "ryu"; 210 | version = "1.0.15"; 211 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 212 | src = fetchCratesIo { inherit name version; sha256 = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"; }; 213 | }); 214 | 215 | "registry+https://github.com/rust-lang/crates.io-index".static_assertions."1.1.0" = overridableMkRustCrate (profileName: rec { 216 | name = "static_assertions"; 217 | version = "1.1.0"; 218 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 219 | src = fetchCratesIo { inherit name version; sha256 = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"; }; 220 | }); 221 | 222 | "registry+https://github.com/rust-lang/crates.io-index".version_check."0.9.4" = overridableMkRustCrate (profileName: rec { 223 | name = "version_check"; 224 | version = "0.9.4"; 225 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 226 | src = fetchCratesIo { inherit name version; sha256 = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"; }; 227 | }); 228 | 229 | } 230 | -------------------------------------------------------------------------------- /overlay/overrides.nix: -------------------------------------------------------------------------------- 1 | { rustLib, lib, pkgs, buildPackages }: 2 | 3 | let 4 | inherit (rustLib) makeOverride nullOverride; 5 | 6 | # The bindings in this let expression are used below in the recursive set to 7 | # create overrides. They are not themselves overrides. See the list of `all` 8 | # overrides and their definitions below. 9 | 10 | # For example, openssl builds to separate directories in nixpkgs while the 11 | # Rust crate expects all of the output to be in one directory. We use a 12 | # symlink join to create an output of the sum of two of openssl's normal 13 | # outputs. This is one example of a nixpkg requiring some slight finesse to 14 | # be used as a buildInput for a Rust crate derivation. 15 | 16 | envize = s: builtins.replaceStrings ["-"] ["_"] (lib.toUpper s); 17 | 18 | patchOpenssl = pkgs: 19 | if pkgs.stdenv.hostPlatform.libc == "musl" 20 | then pkgs.openssl.override { 21 | static = true; 22 | } 23 | else if pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform 24 | then pkgs.openssl 25 | else (pkgs.openssl.override { 26 | # We only need `perl` at build time. It's also used as the interpreter for one 27 | # of the produced binaries (`c_rehash`), but they'll be removed later. 28 | perl = pkgs.buildPackages.buildPackages.perl; 29 | }).overrideAttrs (drv: { 30 | installTargets = "install_sw"; 31 | outputs = [ "dev" "out" "bin" ]; 32 | # Remove binaries, we need only libraries. 33 | postFixup = '' 34 | ${drv.postFixup} 35 | rm -rf $bin/* 36 | ''; 37 | }); 38 | 39 | joinOpenssl = openssl: buildPackages.symlinkJoin { 40 | name = "openssl"; paths = with openssl; [ out dev ]; 41 | }; 42 | 43 | patchPostgresql = pkgs: pkgs.postgresql.override { 44 | openssl = patchOpenssl pkgs; 45 | # Remove `systemd` input as it breaks cross compilation. 46 | enableSystemd = false; 47 | }; 48 | 49 | patchCurl = pkgs: 50 | let 51 | openssl = patchOpenssl pkgs; 52 | in pkgs.curl.override { 53 | inherit openssl; 54 | nghttp2 = pkgs.nghttp2.override { inherit openssl; }; 55 | libssh2 = pkgs.libssh2.override { inherit openssl; }; 56 | libkrb5 = pkgs.libkrb5.override { inherit openssl; }; 57 | }; 58 | 59 | in rec { 60 | patches = { inherit patchOpenssl patchCurl patchPostgresql joinOpenssl;}; 61 | 62 | # Don't forget to add new overrides here. 63 | all = [ 64 | capLints 65 | cc 66 | curl-sys 67 | fsevent-sys 68 | libgit2-sys 69 | libdbus-sys 70 | libssh2-sys 71 | libudev-sys 72 | openssl-sys 73 | pkg-config 74 | pq-sys 75 | prost-build 76 | protoc 77 | rand 78 | rand_os 79 | rdkafka-sys 80 | sqlx-macros 81 | reqwest 82 | ring 83 | zmq-sys 84 | ]; 85 | 86 | capLints = makeOverride { 87 | registry = "registry+https://github.com/rust-lang/crates.io-index"; 88 | overrideArgs = old: { rustcLinkFlags = old.rustcLinkFlags or [ ] ++ [ "--cap-lints" "warn" ]; }; 89 | }; 90 | 91 | # Every crate that depends on the cc crate (usually build scripts) will have the xcbuild 92 | cc = if pkgs.stdenv.hostPlatform.isDarwin 93 | then makeOverride { 94 | name = "cc"; 95 | overrideAttrs = drv: { 96 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ 97 | pkgs.xcbuild 98 | ]; 99 | }; 100 | } 101 | else nullOverride; 102 | 103 | curl-sys = makeOverride { 104 | name = "curl-sys"; 105 | overrideAttrs = drv: { 106 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ 107 | (patchCurl pkgs) 108 | ]; 109 | }; 110 | }; 111 | 112 | fsevent-sys = if pkgs.stdenv.hostPlatform.isDarwin 113 | then makeOverride { 114 | name = "fsevent-sys"; 115 | overrideAttrs = drv: { 116 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ 117 | pkgs.darwin.apple_sdk.frameworks.CoreServices 118 | ]; 119 | }; 120 | } 121 | else nullOverride; 122 | 123 | reqwest = if pkgs.stdenv.hostPlatform.isDarwin 124 | then makeOverride { 125 | name = "reqwest"; 126 | overrideAttrs = drv: { 127 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ 128 | pkgs.darwin.apple_sdk.frameworks.Security 129 | ]; 130 | }; 131 | } 132 | else nullOverride; 133 | 134 | libdbus-sys = pkgs.rustBuilder.rustLib.makeOverride { 135 | name = "libdbus-sys"; 136 | overrideAttrs = drv: { 137 | buildInputs = drv.buildInputs or [ ] ++ [ 138 | pkgs.dbus 139 | ]; 140 | }; 141 | }; 142 | 143 | libudev-sys = pkgs.rustBuilder.rustLib.makeOverride { 144 | name = "libudev-sys"; 145 | overrideAttrs = drv: { 146 | buildInputs = drv.buildInputs or [ ] ++ [ 147 | pkgs.udev 148 | ]; 149 | buildPhase = '' 150 | runHook overrideCargoManifest 151 | runHook setBuildEnv 152 | export PATH=$PATH:$(dirname $RUSTC) 153 | runHook runCargo 154 | ''; 155 | }; 156 | }; 157 | 158 | libgit2-sys = if pkgs.stdenv.hostPlatform.isDarwin 159 | then makeOverride { 160 | name = "libgit2-sys"; 161 | overrideAttrs = drv: { 162 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ 163 | pkgs.darwin.apple_sdk.frameworks.Security 164 | pkgs.darwin.apple_sdk.frameworks.CoreFoundation 165 | ]; 166 | buildInputs = drv.buildInputs or [ ] ++ [ 167 | pkgs.libgit2 168 | ]; 169 | preferLocalBuild = true; 170 | allowSubstitutes = false; 171 | }; 172 | } 173 | else nullOverride; 174 | 175 | libssh2-sys = makeOverride { 176 | name = "libssh2-sys"; 177 | overrideAttrs = drv: { 178 | buildInputs = drv.buildInputs or [ ] ++ [ pkgs.openssl.dev pkgs.zlib.dev ]; 179 | }; 180 | }; 181 | 182 | libsqlite3-sys = pkgs.rustBuilder.rustLib.makeOverride { 183 | name = "libsqlite3-sys"; 184 | overrideAttrs = drv: { 185 | buildInputs = drv.buildInputs or [ ] ++ [ pkgs.sqlite ]; 186 | }; 187 | }; 188 | 189 | openssl-sys = makeOverride { 190 | name = "openssl-sys"; 191 | overrideAttrs = drv: { 192 | # The setup hook will set the variables both for building openssl-sys and 193 | # in dependent derivations. This mechanism will also set the variable 194 | # inside our development shell. Because the setupHook does not add the 195 | # joinOpenssl derivation as a dependnecy, we have to include it in 196 | # nativeBuildInputs as well or the variable will point to a path not 197 | # visible to the derivation at build time. 198 | buildInputs = drv.buildInputs or [ ] ++ [(joinOpenssl (patchOpenssl pkgs.buildPackages))]; 199 | 200 | shellHook = drv.shellHook or "" + '' 201 | export ${envize (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.buildPlatform)}_OPENSSL_DIR=${lib.escapeShellArg (joinOpenssl (patchOpenssl pkgs.buildPackages))} 202 | export ${envize (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform)}_OPENSSL_DIR=${lib.escapeShellArg (joinOpenssl (patchOpenssl pkgs))} 203 | export OPENSSL_NO_VENDOR=1 # fixed 0.9.60 204 | export RUSTFLAGS="''${RUSTFLAGS:-} --cfg ossl111 --cfg ossl110 --cfg ossl101" 205 | ''; 206 | 207 | # setupHook is also a means of injecting the build environment for a dependency 208 | # setupHook = buildPackages.writeText "openssl-sys-setup-env.sh" '' 209 | # openssl-sys-setup-env() { 210 | # export ${envize (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.buildPlatform)}_OPENSSL_DIR=${lib.escapeShellArg (joinOpenssl (patchOpenssl pkgs.buildPackages))} 211 | # export ${envize (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform)}_OPENSSL_DIR=${lib.escapeShellArg (joinOpenssl (patchOpenssl pkgs))} 212 | # export OPENSSL_NO_VENDOR=1 # fixed 0.9.60 213 | # export RUSTFLAGS="''${RUSTFLAGS:-} --cfg ossl111 --cfg ossl110 --cfg ossl101" 214 | # } 215 | # addEnvHooks "$hostOffset" openssl-sys-setup-env 216 | # ''; 217 | }; 218 | }; 219 | 220 | pkg-config = makeOverride { 221 | name = "pkg-config"; 222 | overrideAttrs = drv: { 223 | # Every crate that depends on the pkg-config crate also gets pkg-config and this environment 224 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ 225 | pkgs.pkg-config 226 | ]; 227 | shellHook = drv.shellHook or "" + '' 228 | export PGK_CONFIG_ALLOW_CROSS=1 229 | ''; 230 | }; 231 | }; 232 | 233 | pq-sys = 234 | let 235 | binEcho = s: "${pkgs.buildPackages.writeShellScriptBin "bin-echo" "echo ${s}"}/bin/bin-echo"; 236 | fake_pg_config = binEcho "${(patchPostgresql pkgs.buildPackages).lib}/lib"; 237 | in 238 | makeOverride { 239 | name = "pq-sys"; 240 | overrideAttrs = drv: { 241 | # We can't use the host `pg_config` here, as it might not run on build platform. `pq-sys` only needs 242 | # to know the `lib` directory for `libpq`, so just create a fake binary that gives it exactly that. 243 | nativeBuildInputs = drv.nativeBuildInputs or [ ] ++ [ 244 | fake_pg_config 245 | ]; 246 | shellHook = drv.shellHook + '' 247 | PG_CONFIG_${envize pkgs.stdenv.buildPlatform.config}="${fake_pg_config}" 248 | ''; 249 | }; 250 | }; 251 | 252 | # Note that protobuff is from buildPackages and runs at the crate's 253 | # build-time, so it's a nativeBuildInput. Every crate that depends on 254 | # prost-build might need protoc at runtime, so it's propagated. 255 | prost-build = makeOverride { 256 | name = "prost-build"; 257 | overrideAttrs = drv: { 258 | setupHook = buildPackages.writeText "prost-build-setup-env.sh" '' 259 | prost-build-setup-env () { 260 | PROTOC="${pkgs.buildPackages.buildPackages.protobuf}/bin/protoc" 261 | } 262 | addEnvHooks "$hostOffset" prost-build-setup-env 263 | ''; 264 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ 265 | pkgs.buildPackages.buildPackages.protobuf 266 | ]; 267 | }; 268 | }; 269 | 270 | protoc = makeOverride { 271 | name = "protoc"; 272 | overrideAttrs = drv: { 273 | propagatedNativeBuildInputs = drv.propagatedNativeBuildInputs or [ ] ++ [ pkgs.buildPackages.buildPackages.protobuf ]; 274 | }; 275 | }; 276 | 277 | rand = if pkgs.stdenv.hostPlatform.isDarwin 278 | then makeOverride { 279 | name = "rand"; 280 | overrideAttrs = drv: { 281 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ pkgs.darwin.apple_sdk.frameworks.Security ]; 282 | }; 283 | } 284 | else nullOverride; 285 | 286 | rand_os = if pkgs.stdenv.hostPlatform.isDarwin 287 | then makeOverride { 288 | name = "rand_os"; 289 | overrideAttrs = drv: { 290 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ pkgs.darwin.apple_sdk.frameworks.Security ]; 291 | }; 292 | } 293 | else nullOverride; 294 | 295 | sqlx-macros = if pkgs.stdenv.hostPlatform.isDarwin 296 | then makeOverride { 297 | name = "sqlx-macros"; 298 | overrideAttrs = drv: { 299 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ pkgs.darwin.apple_sdk.frameworks.SystemConfiguration ]; 300 | }; 301 | } 302 | else nullOverride; 303 | 304 | rdkafka-sys = makeOverride { 305 | name = "rdkafka-sys"; 306 | overrideAttrs = drv: { 307 | postConfigure = '' 308 | ${drv.postConfigure or ""} 309 | patchShebangs --build librdkafka/configure 310 | ''; 311 | }; 312 | }; 313 | 314 | ring = if pkgs.stdenv.hostPlatform.isDarwin 315 | then makeOverride { 316 | name = "ring"; 317 | overrideAttrs = drv: { 318 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ pkgs.darwin.apple_sdk.frameworks.Security ]; 319 | }; 320 | } 321 | else nullOverride; 322 | 323 | zmq-sys = makeOverride { 324 | name = "zmq-sys"; 325 | overrideAttrs = drv: { 326 | buildInputs = drv.buildInputs or [ ] ++ [ pkgs.zeromq ]; 327 | }; 328 | }; 329 | } 330 | -------------------------------------------------------------------------------- /overlay/mkcrate-utils.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | extractFileExt() { 4 | local name=$(basename "$1") 5 | echo "${name##*.}" 6 | } 7 | 8 | extractHash() { 9 | local name=$(basename "$1") 10 | echo "${name%%-*}" 11 | } 12 | 13 | makeExternCrateFlags() { 14 | for (( i=1; i<$#; i+=2 )); do 15 | local extern_name="${@:$i:1}" 16 | local crate="${@:((i+1)):1}" 17 | [ -f "$crate/.cargo-info" ] || continue 18 | 19 | local crate_name=$(jq -r '.name' "$crate/.cargo-info") 20 | local proc_macro=$(jq -r '.proc_macro' "$crate/.cargo-info") 21 | 22 | if [ "$proc_macro" ]; then 23 | echo "--extern" "${extern_name}=$crate/lib/$proc_macro" 24 | elif [ -f "$crate/lib/lib${crate_name}.rlib" ]; then 25 | echo "--extern" "${extern_name}=$crate/lib/lib${crate_name}.rlib" 26 | elif [ -f "$crate/lib/lib${crate_name}.so" ]; then 27 | echo "--extern" "${extern_name}=$crate/lib/lib${crate_name}.so" 28 | elif [ -f "$crate/lib/lib${crate_name}.a" ]; then 29 | echo "--extern" "${extern_name}=$crate/lib/lib${crate_name}.a" 30 | elif [ -f "$crate/lib/lib${crate_name}.dylib" ]; then 31 | echo "--extern" "${extern_name}=$crate/lib/lib${crate_name}.dylib" 32 | else 33 | echo "do not know how to find $extern_name ($crate_name)" >&2 34 | exit 1 35 | fi 36 | 37 | echo "-L dependency=$crate/lib/deps" 38 | if [ -f "$crate/lib/.link-flags" ]; then 39 | cat "$crate/lib/.link-flags" 40 | fi 41 | done 42 | } 43 | 44 | loadExternCrateLinkFlags() { 45 | for (( i=1; i<$#; i+=2 )); do 46 | local extern_name="${@:$i:1}" 47 | local crate="${@:((i+1)):1}" 48 | [ -f "$crate/.cargo-info" ] || continue 49 | 50 | local crate_name=$(jq -r '.name' "$crate/.cargo-info") 51 | if [ -f "$crate/lib/.link-flags" ]; then 52 | cat "$crate/lib/.link-flags" | sort -u 53 | fi 54 | done 55 | } 56 | 57 | loadDepKeys() { 58 | for (( i=2; i<=$#; i+=2 )); do 59 | local crate="${@:$i:1}" 60 | [ -f "$crate/.cargo-info" ] && [ -f "$crate/lib/.dep-keys" ] || continue 61 | cat "$crate/lib/.dep-keys" 62 | done 63 | } 64 | 65 | linkExternCrateToDeps() { 66 | local deps_dir=$1; shift 67 | for (( i=1; i<$#; i+=2 )); do 68 | local dep="${@:((i+1)):1}" 69 | [ -f "$dep/.cargo-info" ] || continue 70 | 71 | local crate_name=$(jq -r '.name' "$dep/.cargo-info") 72 | local metadata=$(jq -r '.metadata' "$dep/.cargo-info") 73 | local proc_macro=$(jq -r '.proc_macro' "$dep/.cargo-info") 74 | 75 | if [ "$proc_macro" ]; then 76 | local ext=$(extractFileExt "$proc_macro") 77 | ln -sf "$dep/lib/$proc_macro" "$deps_dir/$(basename "$proc_macro.$ext")-$metadata.$ext" 78 | else 79 | ln -sf "$dep/lib/lib${crate_name}.rlib" "$deps_dir/lib${crate_name}-${metadata}.rlib" 80 | fi 81 | 82 | ( 83 | shopt -s nullglob 84 | for subdep in "$dep/lib/deps"/*; do 85 | local subdep_name=$(basename "$subdep") 86 | ln -sf "$subdep" "$deps_dir/$subdep_name" 87 | done 88 | ) 89 | done 90 | } 91 | 92 | upper() { 93 | echo "${1^^}" 94 | } 95 | single_quote_whitespaced() { 96 | if [[ $1 == *[[:space:]]* ]]; then 97 | echo "'$1'" 98 | else 99 | echo $1 100 | fi 101 | } 102 | 103 | dumpDepInfo() { 104 | local link_flags="$1"; shift 105 | local dep_keys="$1"; shift 106 | local cargo_links="$1"; shift 107 | local dep_files="$1"; shift 108 | local depinfo="$1"; shift 109 | 110 | while read line; do 111 | [[ "x$line" =~ xcargo:([^=]+)=(.*) ]] || continue 112 | local key="${BASH_REMATCH[1]}" 113 | local val="${BASH_REMATCH[2]}" 114 | 115 | case $key in 116 | rustc-link-lib) ;& 117 | rustc-flags) ;& 118 | rustc-cfg) ;& 119 | rustc-env) ;& 120 | rerun-if-changed) ;& 121 | rerun-if-env-changed) ;& 122 | warning) 123 | ;; 124 | rustc-link-search) 125 | echo "-L $(printf '%q' "$val")" >> "$link_flags" 126 | ;; 127 | *) 128 | if [ -e "$val" ]; then 129 | local dep_file_target="$dep_files/DEP_$(upper "$cargo_links")_$(upper "$key")" 130 | cp -r "$val" "$dep_file_target" 131 | val=$dep_file_target 132 | fi 133 | printf 'DEP_%s_%s=%s\n' "$(upper "$cargo_links")" "$(upper "$key")" "$(single_quote_whitespaced "$val")" >> "$dep_keys" 134 | esac 135 | done < "$depinfo" 136 | } 137 | 138 | install_crate2() { 139 | mkdir -p $out 140 | mkdir -p $bin 141 | mkdir -p "$out/lib" 142 | # shellcheck disable=SC2046 143 | cp $(jq -rR 'fromjson? 144 | | select(.reason == "compiler-artifact") 145 | | .filenames 146 | | map(select(test("\\.(a|rlib|so|dylib|dll|lib|wasm)$"))) 147 | | .[]' < .cargo-build-output) "$out/lib" 2> /dev/null || : 148 | 149 | jq -rR 'fromjson? 150 | | select(.reason == "build-script-executed") 151 | | (.linked_libs | map("-l \(.)")) + (.linked_paths | map("-L \(.)")) 152 | | .[]' < .cargo-build-output >> "$out/lib/.link-flags" 153 | 154 | mkdir -p "$bin/bin" 155 | mkdir -p "$out/bin" 156 | # shellcheck disable=SC2046 157 | bin_output=$(jq -rR 'fromjson? 158 | | select(.reason == "compiler-artifact") 159 | | .executable 160 | | select(.)' < .cargo-build-output) 161 | cp $bin_output "$bin/bin" 2> /dev/null || rmdir "$bin/bin" 162 | cp $bin_output "$out/bin" 2> /dev/null || rmdir "$out/bin" 163 | 164 | local out_dir 165 | if out_dir="$(jq -rR 'fromjson? | select(.reason == "build-script-executed") | .out_dir' < .cargo-build-output)"; then 166 | touch "$out/lib/.dep-keys" 167 | 168 | # `links` envs aren't included in build output so we have to manually parse them. 169 | if [[ -f "$out_dir/../output" ]]; then 170 | grep -P '^cargo:(?!rerun-if-changed|rerun-if-env-changed|rustc-link-lib|rustc-link-search|rustc-flags|rustc-cfg|rustc-env|rustc-cdylib-link-arg|warning)' \ 171 | "$out_dir/../output" | while IFS= read -r line; do 172 | [[ "$line" =~ cargo:([^=]+)=(.*) ]] || continue 173 | local key="${BASH_REMATCH[1]}" 174 | local val="${BASH_REMATCH[2]}" 175 | printf 'DEP_%s_%s=%s\n' "$(upper "$cargo_links")" "$(upper "$key")" "$(single_quote_whitespaced "$val")" >> "$out/lib/.dep-keys" 176 | done || : 177 | fi 178 | 179 | if [[ -d "$out_dir" ]] && { grep "$out_dir" "$out/lib/.dep-keys" || grep "$out_dir" "$out/lib/.link-flags"; }; then 180 | local installed_out_dir="$out/build_script_output" 181 | sed -i "s#$out_dir#$installed_out_dir#g" "$out/lib/.dep-keys" 182 | sed -i "s#$out_dir#$installed_out_dir#g" "$out/lib/.link-flags" 183 | cp -r "$out_dir" "$installed_out_dir" 184 | fi 185 | fi 186 | 187 | loadExternCrateLinkFlags $dependencies >> "$out/lib/.link-flags" 188 | 189 | if (shopt -s failglob; : "$out/lib"/*.rlib) 2> /dev/null; then 190 | mkdir -p "$out/lib/deps" 191 | linkExternCrateToDeps "$out/lib/deps" $dependencies 192 | fi 193 | 194 | jq -n '{name:$name, metadata:$metadata, version:$version, proc_macro:$procmacro}' \ 195 | --arg name "$crateName" \ 196 | --arg metadata "$NIX_RUST_METADATA" \ 197 | --arg procmacro "$(basename "$(jq -rR 'fromjson? | select(.target.kind[0] == "proc-macro") | .filenames[0]' < .cargo-build-output)")" \ 198 | --arg version "$version" > "$out/.cargo-info" 199 | 200 | } 201 | 202 | install_crate() { 203 | # This function is only used for rustc older than 1.41.0. It's 204 | # becoming almost completely unused. Look for an opportunity to 205 | # remove as old Rust versions fall out of use. 206 | local host_triple="$1" 207 | local mode="$2" 208 | pushd "target/${host_triple}/${mode}" 209 | 210 | local needs_deps= 211 | local has_output= 212 | mkdir -p $out 213 | mkdir -p $bin 214 | for output in *; do 215 | if [ -d "$output" ]; then 216 | continue 217 | elif [ -x "$output" ]; then 218 | mkdir -p "$bin/bin" 219 | cp "$output" "$bin/bin/" 220 | mkdir -p "$out/bin" 221 | cp "$output" "$out/bin/" 222 | has_output=1 223 | else 224 | case $(extractFileExt "$output") in 225 | rlib) 226 | mkdir -p "$out/lib/.dep-files" 227 | cp "$output" "$out/lib/" 228 | 229 | local link_flags="$out/lib/.link-flags" 230 | local dep_keys="$out/lib/.dep-keys" 231 | touch "$link_flags" "$dep_keys" 232 | 233 | for depinfo in build/*/output; do 234 | dumpDepInfo "$link_flags" "$dep_keys" "$cargo_links" "$out/lib/.dep-files" "$depinfo" 235 | done 236 | 237 | needs_deps=1 238 | has_output=1 239 | ;; 240 | a) ;& 241 | so) ;& 242 | wasm) ;& 243 | lib) ;& 244 | dll) ;& 245 | dylib) 246 | mkdir -p "$out/lib" 247 | cp "$output" "$out/lib/" 248 | has_output=1 249 | ;; 250 | *) 251 | continue 252 | esac 253 | fi 254 | done 255 | popd 256 | 257 | touch "$out/lib/.link-flags" 258 | loadExternCrateLinkFlags $dependencies >> "$out/lib/.link-flags" 259 | 260 | if [ "$isProcMacro" ]; then 261 | pushd "target/${mode}" 262 | for output in *; do 263 | if [ -d "$output" ]; then 264 | continue 265 | fi 266 | case $(extractFileExt "$output") in 267 | so) ;& 268 | dylib) 269 | isProcMacro=$(basename "$output") 270 | mkdir -p "$out/lib" 271 | cp "$output" "$out/lib" 272 | needs_deps=1 273 | has_output=1 274 | ;; 275 | *) 276 | continue 277 | esac 278 | done 279 | popd 280 | fi 281 | 282 | if [ ! "$has_output" ]; then 283 | echo NO OUTPUT IS FOUND 284 | exit 1 285 | fi 286 | 287 | if [ "$needs_deps" ]; then 288 | mkdir -p "$out/lib/deps" 289 | linkExternCrateToDeps "$out/lib/deps" $dependencies 290 | fi 291 | 292 | echo {} | jq \ 293 | '{name:$name, metadata:$metadata, version:$version, proc_macro:$procmacro}' \ 294 | --arg name "$crateName" \ 295 | --arg metadata "$NIX_RUST_METADATA" \ 296 | --arg procmacro "$isProcMacro" \ 297 | --arg version "$version" > "$out/.cargo-info" 298 | } 299 | 300 | cargoVerbosityLevel() { 301 | local level=${1:-0} 302 | local verbose_flag="" 303 | 304 | if (( level >= 1 )); then 305 | verbose_flag="-v" 306 | elif (( level >= 7 )); then 307 | verbose_flag="-vv" 308 | fi 309 | 310 | echo ${verbose_flag} 311 | } 312 | 313 | cargoRelativeManifest() { 314 | local manifest_path=$(cargo metadata --format-version 1 --no-deps | jq -c ".packages[] | select(.name == \"$1\") | .manifest_path" | tr -d '"') 315 | local workspace_path=$(cargo metadata --format-version 1 --no-deps | jq -c '.workspace_root' | tr -d '"') 316 | workspace_path="${workspace_path%/}/" 317 | 318 | echo "${manifest_path#"$workspace_path"}" 319 | } 320 | 321 | sanitizeTomlForRemarshal () { 322 | # Remarshal was failing on table names of the form: 323 | # [key."cfg(foo = \"a\", bar = \"b\"))".path] 324 | # The regex to find or deconstruct these strings must find, in order, 325 | # these components: open bracket, open quote, open escaped quote, and 326 | # their closing pairs. Because each quoted path can contain multiple 327 | # quote escape pairs, a loop is employed to match the first quote escape, 328 | # which the sed will replace with a single quote equivalent until all 329 | # escaped quote pairs are replaced. The grep regex is identical to the 330 | # sed regex but does not destructure the match into groups for 331 | # restructuring in the replacement. 332 | while grep '\[[^"]*"[^\\"]*\\"[^\\"]*\\"[^"]*[^]]*\]' $1; do 333 | sed -i -r 's/\[([^"]*)"([^\\"]*)\\"([^\\"]*)\\"([^"]*)"([^]]*)\]/[\1"\2'"'"'\3'"'"'\4"\5]/g' $1 334 | done; 335 | } 336 | 337 | reducePackageToml () { 338 | # This function needs to remove any package keys that conflict with 339 | # dependency control or target and profile configuration. 340 | # https://doc.rust-lang.org/cargo/reference/manifest.html 341 | local manifestPatch="$3" 342 | local registry="$registry" 343 | remarshal -if toml -of json "$1" \ 344 | | jq 'del(."cargo-features", 345 | .replace, 346 | .patch, 347 | .dependencies, 348 | ."build-dependencies", 349 | .["dev-dependencies"], 350 | .target) 351 | + '"$manifestPatch" \ 352 | | jq 'del(.[][] | nulls)' \ 353 | | remarshal -if json -of toml > "$2" 354 | } 355 | 356 | reduceWorkspaceToml () { 357 | # This function needs to remove any workspace keys that conflict with 358 | # dependency control or target and profile configuration. 359 | # https://doc.rust-lang.org/cargo/reference/workspaces.html 360 | local crate_path="$3" 361 | remarshal -if toml -of json $1 \ 362 | | jq ".workspace.members = [\"$crate_path\"] 363 | | del( .workspace.dependencies?, 364 | .workspace.\"default-members\", 365 | .workspace.exclude?, 366 | .patch, 367 | .replace) 368 | | with_entries(select( .value != null ))" \ 369 | | jq "del(.[][] | nulls)" \ 370 | | remarshal -if json -of toml > $2 371 | } 372 | -------------------------------------------------------------------------------- /overlay/mkcrate.nix: -------------------------------------------------------------------------------- 1 | { 2 | rustToolchain, 3 | lib, 4 | pkgs, 5 | buildPackages, 6 | rustLib, 7 | stdenv, 8 | }: 9 | { 10 | release, # Compiling in release mode? 11 | name, 12 | version, 13 | registry, 14 | src, 15 | features ? [ ], 16 | dependencies ? { }, 17 | devDependencies ? { }, 18 | buildDependencies ? { }, 19 | compileMode ? "build", 20 | profile, 21 | profileOpts ? null, 22 | codegenOpts ? null, 23 | meta ? { }, 24 | cargoUnstableFlags ? [ ], 25 | rustcLinkFlags ? [ ], 26 | rustcBuildFlags ? [ ], 27 | target ? null, 28 | hostPlatformCpu ? null, 29 | hostPlatformFeatures ? [], 30 | NIX_DEBUG ? 0, 31 | }: 32 | with builtins; with lib; 33 | let 34 | inherit (rustLib) rustTriple decideProfile; 35 | wrapper = rustpkg: pkgs.writeScriptBin rustpkg '' 36 | #!${stdenv.shell} 37 | . ${./mkcrate-utils.sh} 38 | isBuildScript= 39 | args=("$@") 40 | for i in "''${!args[@]}"; do 41 | if [ "xmetadata=" = "x''${args[$i]::9}" ]; then 42 | args[$i]=metadata=$NIX_RUST_METADATA 43 | elif [ "x--crate-name" = "x''${args[$i]}" ] && [ "xbuild_script_" = "x''${args[$i+1]::13}" ]; then 44 | isBuildScript=1 45 | fi 46 | done 47 | if [ "$isBuildScript" ]; then 48 | args+=($NIX_RUST_BUILD_FLAGS) 49 | else 50 | args+=($NIX_RUST_LINK_FLAGS) 51 | fi 52 | touch invoke.log 53 | echo "''${args[@]}" >>invoke.log 54 | exec ${rustToolchain}/bin/${rustpkg} "''${args[@]}" 55 | ''; 56 | 57 | ccForBuild="${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}cc"; 58 | cxxForBuild="${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}c++"; 59 | targetPrefix = stdenv.cc.targetPrefix; 60 | cc = stdenv.cc; 61 | ccForHost="${cc}/bin/${targetPrefix}cc"; 62 | cxxForHost="${cc}/bin/${targetPrefix}c++"; 63 | rustBuildTriple = rustTriple stdenv.buildPlatform; 64 | rustHostTriple = if (target != null) then target else rustTriple stdenv.hostPlatform; 65 | depMapToList = deps: 66 | flatten 67 | (sort (a: b: elemAt a 0 < elemAt b 0) 68 | (mapAttrsToList (name: value: [ name "${value}" ]) deps)); 69 | buildCmd = 70 | let 71 | hasDefaultFeature = elem "default" features; 72 | featuresWithoutDefault = if hasDefaultFeature 73 | then filter (feature: feature != "default") features 74 | else features; 75 | buildMode = { 76 | "test" = "--tests"; 77 | "bench" = "--benches"; 78 | }.${compileMode} or ""; 79 | featuresArg = if featuresWithoutDefault == [ ] 80 | then "" 81 | else "--features ${concatStringsSep "," featuresWithoutDefault}"; 82 | in 83 | if compileMode != "doctest" then '' 84 | ${rustToolchain}/bin/cargo build $CARGO_VERBOSE ${optionalString release "--release"} --target ${rustHostTriple} ${buildMode} \ 85 | ${featuresArg} ${optionalString (!hasDefaultFeature) "--no-default-features"} \ 86 | ${optionalString (builtins.length cargoUnstableFlags > 0) "-Z ${lib.strings.concatStringsSep "," cargoUnstableFlags}"} \ 87 | --message-format json-diagnostic-rendered-ansi | tee .cargo-build-output \ 88 | 1> >(jq 'select(.message != null) .message.rendered' -r) 89 | '' 90 | # Note: Doctest doesn't yet support no-run https://github.com/rust-lang/rust/pull/83857 91 | # So instead of persiting the binaries with 92 | # RUSTDOCFLAGS="-Zunstable-options --persist-doctests $(pwd)/target/rustdoctest -o $(pwd)/target/rustdoctest" cargo test --doc | tee .cargo-doctest-output 93 | # we just introduce a new compile mode 94 | # 95 | # We also filter -l linkage flags, as rustdoc doesn't support them 96 | # 97 | # And _also_ detect if there are no lib crates, in which case skip, because thats an error for rustdoc 98 | # 99 | # This does not abort on failure. The output should be inspected for failures 100 | else '' 101 | echo "Performing Doctests" 102 | export NIX_RUST_LINK_FLAGS=$(echo "$NIX_RUST_LINK_FLAGS" | sed 's/ \-l \w*//g') 103 | ${rustToolchain}/bin/cargo read-manifest | jq -e '.targets | .[] | select(.crate_types[] | contains ("lib")) | any' >/dev/null && \ 104 | ( ${rustToolchain}/bin/cargo test --doc --no-fail-fast \ 105 | ${featuresArg} ${optionalString (!hasDefaultFeature) "--no-default-features"} \ 106 | -- -Z unstable-options --format json \ 107 | | tee results.json \ 108 | || true) \ 109 | || echo "No lib crate detected" 110 | ''; 111 | 112 | inherit 113 | (({ right, wrong }: { runtimeDependencies = right; buildtimeDependencies = wrong; }) 114 | (partition (drv: drv.stdenv.hostPlatform == stdenv.hostPlatform) 115 | (concatLists [ 116 | (attrValues dependencies) 117 | (optionals (compileMode != "build") (attrValues devDependencies)) 118 | (attrValues buildDependencies) 119 | ]))) 120 | runtimeDependencies buildtimeDependencies; 121 | 122 | drvAttrs = { 123 | inherit src version meta NIX_DEBUG; 124 | name = "crate-${name}-${version}${optionalString (compileMode != "build") "-${compileMode}"}"; 125 | 126 | # Adding libiconv is a convenience hack. It really isn't needed by every 127 | # derivation and should instead be added / propagated where appropriate, but 128 | # until someone decides to investigate the actual dependencies, it remains 129 | # here instead of in overrides. 130 | buildInputs = runtimeDependencies ++ lib.optionals stdenv.hostPlatform.isDarwin [ pkgs.libiconv ]; 131 | propagatedBuildInputs = lib.unique (concatMap (drv: drv.propagatedBuildInputs) runtimeDependencies); 132 | nativeBuildInputs = [ rustToolchain ] ++ buildtimeDependencies; 133 | 134 | depsBuildBuild = 135 | let inherit (buildPackages.buildPackages) stdenv jq remarshal; 136 | in [ stdenv.cc jq remarshal ]; 137 | 138 | # Running the default `strip -S` command on Darwin corrupts the 139 | # .rlib files in "lib/". 140 | # 141 | # See https://github.com/NixOS/nixpkgs/pull/34227 142 | stripDebugList = if stdenv.isDarwin then [ "bin" ] else null; 143 | 144 | passthru = { 145 | inherit 146 | name 147 | version 148 | registry 149 | dependencies 150 | devDependencies 151 | buildDependencies 152 | features; 153 | shell = pkgs.mkShell (removeAttrs drvAttrs ["src"]); 154 | }; 155 | 156 | dependencies = depMapToList dependencies; 157 | buildDependencies = depMapToList buildDependencies; 158 | devDependencies = depMapToList (optionalAttrs (compileMode != "build") devDependencies); 159 | 160 | extraRustcLinkFlags = 161 | optionals (hostPlatformCpu != null) ([("-Ctarget-cpu=" + hostPlatformCpu)]) ++ 162 | optionals (hostPlatformFeatures != []) [("-Ctarget-feature=" + (concatMapStringsSep "," (feature: "+" + feature) hostPlatformFeatures))] ++ 163 | rustcLinkFlags; 164 | 165 | extraRustcBuildFlags = rustcBuildFlags; 166 | 167 | configureCargo = '' 168 | mkdir -p .cargo 169 | cat > .cargo/config <<'EOF' 170 | [target."${rustBuildTriple}"] 171 | linker = "${ccForBuild}" 172 | '' + optionalString (codegenOpts != null && codegenOpts ? "${rustBuildTriple}") ('' 173 | rustflags = [ 174 | '' + concatStringsSep ", " (concatMap (opt: [''"-C"'' ''"${opt}"'']) codegenOpts."${rustBuildTriple}") + "\n" + '' 175 | ] 176 | 177 | # HACK: 2019-08-01: wasm32-wasi always uses `wasm-ld` 178 | # HACK: 2021-12-29: x86_64-fortanix-unknown-sgx always use `ld` 179 | '') + optionalString (rustBuildTriple != rustHostTriple && rustHostTriple != "wasm32-wasi" && rustHostTriple != "wasm32-unknown-unknown" && rustHostTriple != "x86_64-fortanix-unknown-sgx") ('' 180 | [target."${rustHostTriple}"] 181 | linker = "${ccForHost}" 182 | ''+ optionalString (codegenOpts != null && codegenOpts ? "${rustHostTriple}") ('' 183 | rustflags = [ 184 | '' + concatStringsSep ", " (concatMap (opt: [''"-C"'' ''"${opt}"'']) codegenOpts."${rustHostTriple}") + "\n" + '' 185 | ] 186 | 187 | '')) + optionalString (profileOpts != null && profileOpts."${decideProfile compileMode release}" != null) ('' 188 | [profile.${decideProfile compileMode release}] 189 | '' + concatStringsSep "\n" (mapAttrsToList (n: a: "${n} = " + ( 190 | if isInt a then "${toString a}" 191 | else (if isBool a then (if a then "true" else "false") 192 | else ''"${a}"''))) 193 | (profileOpts."${decideProfile compileMode release}")) 194 | ) + "\n" + '' 195 | EOF 196 | ''; 197 | 198 | configurePhase = '' 199 | runHook preConfigure 200 | runHook configureCargo 201 | runHook postConfigure 202 | ''; 203 | 204 | manifestPatch = toJSON { 205 | features = genAttrs features (_: [ ]); 206 | profile.${ decideProfile compileMode release } = profile; 207 | }; 208 | 209 | overrideCargoManifest = '' 210 | . ${./mkcrate-utils.sh} 211 | 212 | # Synthesize a lock file 213 | echo "[[package]]" > Cargo.lock 214 | echo name = \"${name}\" >> Cargo.lock 215 | echo version = \"${version}\" >> Cargo.lock 216 | registry="${registry}" 217 | if [ "$registry" != "unknown" ]; then 218 | echo source = \"registry+''${registry}\" >> Cargo.lock 219 | fi 220 | 221 | manifest_path=$(cargoRelativeManifest ${name}) 222 | manifest_dir=''${manifest_path%Cargo.toml} 223 | 224 | # Rewrite the crate's toml 225 | if [ -n "$manifest_dir" ]; then pushd $manifest_dir; fi 226 | mv Cargo.toml Cargo.original.toml 227 | sanitizeTomlForRemarshal Cargo.original.toml 228 | reducePackageToml Cargo.original.toml Cargo.toml "$manifestPatch" 229 | if [ -n "$manifest_dir" ]; then popd; fi 230 | 231 | # If the crate is a workspace, reduce it to a crate of just a workspace of a single crate 232 | if [ $manifest_path != "Cargo.toml" ]; then 233 | mv Cargo.toml Cargo.workspace.toml 234 | sanitizeTomlForRemarshal Cargo.workspace.toml 235 | reduceWorkspaceToml Cargo.workspace.toml Cargo.toml "$manifest_dir" 236 | fi 237 | 238 | ''; 239 | 240 | setBuildEnv = '' 241 | CARGO_BUILD_INCREMENTAL=0 #builds inside nix sandbox use nix caching, not cargo target caching 242 | MINOR_RUSTC_VERSION="$(${rustToolchain}/bin/rustc --version | cut -d . -f 2)" 243 | 244 | if (( MINOR_RUSTC_VERSION < 41 )); then 245 | isProcMacro="$( 246 | remarshal -if toml -of json "''${manifest_dir}Cargo.original.toml" \ 247 | | jq -r 'if .lib."proc-macro" or .lib."proc_macro" then "1" else "" end' \ 248 | )" 249 | fi 250 | 251 | crateName="$( 252 | remarshal -if toml -of json "''${manifest_dir}Cargo.original.toml" \ 253 | | jq -r 'if .lib."name" then .lib."name" else "${replaceStrings ["-"] ["_"] name}" end' \ 254 | )" 255 | 256 | . ${./mkcrate-utils.sh} 257 | 258 | export CARGO_VERBOSE=`cargoVerbosityLevel $NIX_DEBUG` 259 | export NIX_RUST_METADATA=`extractHash $out` 260 | export CARGO_HOME=`pwd`/.cargo 261 | 262 | mkdir -p deps build_deps 263 | linkFlags=(`makeExternCrateFlags $dependencies $devDependencies | sort -u`) 264 | buildLinkFlags=(`makeExternCrateFlags $buildDependencies | sort -u`) 265 | linkExternCrateToDeps `realpath deps` $dependencies $devDependencies 266 | linkExternCrateToDeps `realpath build_deps` $buildDependencies 267 | 268 | export NIX_RUST_LINK_FLAGS="''${linkFlags[@]} -L dependency=$(realpath deps) $extraRustcLinkFlags" 269 | export NIX_RUST_BUILD_FLAGS="''${buildLinkFlags[@]} -L dependency=$(realpath build_deps) $extraRustcBuildFlags" 270 | export RUSTC=${wrapper "rustc"}/bin/rustc 271 | export CLIPPY_DRIVER=${wrapper "clippy-driver"}/bin/clippy-driver 272 | export RUSTDOC=${wrapper "rustdoc"}/bin/rustdoc 273 | 274 | readarray -t depKeys <<< `loadDepKeys $dependencies` 275 | 276 | if (( NIX_DEBUG >= 1 )); then 277 | echo $NIX_RUST_LINK_FLAGS 278 | echo $NIX_RUST_BUILD_FLAGS 279 | for key in ''${depKeys[@]}; do 280 | echo $key 281 | done 282 | fi 283 | ''; 284 | 285 | runCargo = '' 286 | ( 287 | set -euo pipefail 288 | if (( NIX_DEBUG >= 1 )); then 289 | set -x 290 | fi 291 | env \ 292 | "CC_${stdenv.buildPlatform.config}"="${ccForBuild}" \ 293 | "CXX_${stdenv.buildPlatform.config}"="${cxxForBuild}" \ 294 | "CC_${rustHostTriple}"="${ccForHost}" \ 295 | "CXX_${rustHostTriple}"="${cxxForHost}" \ 296 | ''${depKeys:+"''${depKeys[@]}"} \ 297 | ${buildCmd} 298 | ) 299 | ''; 300 | 301 | buildPhase = '' 302 | runHook preBuild 303 | runHook overrideCargoManifest 304 | runHook setBuildEnv 305 | runHook runCargo 306 | runHook postBuild 307 | ''; 308 | 309 | outputs = ["bin" "out"]; 310 | 311 | installPhase = '' 312 | runHook preInstall 313 | '' + (if compileMode != "doctest" then '' 314 | mkdir -p $out/lib 315 | cargo_links="$(remarshal -if toml -of json "''${manifest_dir}Cargo.original.toml" | jq -r '.package.links | select(. != null)')" 316 | if (( MINOR_RUSTC_VERSION < 41 )); then 317 | install_crate ${rustHostTriple} ${if release then "release" else "debug"} 318 | else 319 | install_crate2 ${rustHostTriple} 320 | fi 321 | '' else '' 322 | mkdir -p $out/share 323 | mkdir -p $bin 324 | touch results.json 325 | mv results.json $out/share/ 326 | '') + '' 327 | runHook postInstall 328 | ''; 329 | }; 330 | in 331 | stdenv.mkDerivation drvAttrs 332 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cargo2nix 2 | 3 | [![darwin & linux CI](https://github.com/cargo2nix/cargo2nix/actions/workflows/ci.yml/badge.svg)](https://github.com/cargo2nix/cargo2nix/actions/?workflow=CI) 4 | [![flakes supported](https://img.shields.io/badge/flake-supported-green)](https://nixos.wiki/wiki/Flakes) 5 | [![latest release](https://img.shields.io/github/v/tag/cargo2nix/cargo2nix?color=%23009922&label=release)](https://github.com/cargo2nix/cargo2nix/releases) 6 | 7 | Bring [Nix](https://nixos.org/nix) dependency management to your Rust project! 8 | 9 | - **Development Shell** - knowing all the dependencies means easy creation of 10 | complete shells. Run `nix develop` or `direnv allow` in this repo and see! 11 | - **Caching** - CI & CD pipelines move faster when purity guarantees allow 12 | skipping more work! 13 | - **Reproducibility** - Pure builds. Access to all of 14 | [nixpkgs](https://github.com/NixOS/nixpkgs) for repeatable environment setup 15 | across multiple distributions and platforms 16 | 17 | ## Run it now! 18 | 19 | With [nix](https://nixos.org/nix) (with flake support) installed, generate a 20 | `Cargo.nix` for your project: 21 | 22 | ```bash 23 | # Use nix to get cargo2nix & rust toolchain on your path 24 | nix develop github:cargo2nix/cargo2nix#bootstrap 25 | 26 | # In directory with Cargo.toml & Cargo.lock files (cargo generate-lockfile) 27 | cargo2nix 28 | 29 | # Or skip the shell and run it directly 30 | nix run github:cargo2nix/cargo2nix 31 | 32 | # You'll need this in version control 33 | git add Cargo.nix 34 | ``` 35 | 36 | ### Use what you generated! 37 | 38 | To consume your new `Cargo.nix`, write a nix expression like that found in the 39 | [hello world](./examples/1-hello-world/flake.nix) example. 40 | 41 | A bare minimum flake.nix: 42 | 43 | ```nix 44 | { 45 | inputs = { 46 | cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0"; 47 | flake-utils.follows = "cargo2nix/flake-utils"; 48 | nixpkgs.follows = "cargo2nix/nixpkgs"; 49 | }; 50 | 51 | outputs = inputs: with inputs; 52 | flake-utils.lib.eachDefaultSystem (system: 53 | let 54 | pkgs = import nixpkgs { 55 | inherit system; 56 | overlays = [cargo2nix.overlays.default]; 57 | }; 58 | 59 | rustPkgs = pkgs.rustBuilder.makePackageSet { 60 | rustVersion = "1.75.0"; 61 | packageFun = import ./Cargo.nix; 62 | }; 63 | 64 | in rec { 65 | packages = { 66 | # replace hello-world with your package name 67 | hello-world = (rustPkgs.workspace.hello-world {}); 68 | default = packages.hello-world; 69 | }; 70 | } 71 | ); 72 | } 73 | ``` 74 | 75 | For a more complete project with CI & CD mostly ready to go, check out 76 | [Unixsocks][unixsocks] or cargo2nix's own [CI workflow.](./.github/workflows/ci.yml) 77 | 78 | [unixsocks]: https://github.com/positron-solutions/unixsocks 79 | 80 | #### Build with nix 81 | 82 | ```shell 83 | # these must be in version control! 84 | git add flake.nix Cargo.nix 85 | 86 | nix build 87 | ... 88 | ... 89 | ... 90 | ./result-bin/bin/hello 91 | hello world! 92 | ``` 93 | 94 | Check out our series of [example projects](./examples) which showcase how to use 95 | `cargo2nix` in more detail. 96 | 97 | ### Development environment 98 | 99 | In this repo, simply use `nix develop` or `direnv allow`. Even if you are on 100 | a bare NixOS system or fresh OSX environment with no dependencies or toolchains 101 | installed, you will have everything you need to run `cargo build`. See the 102 | `devShell` attribute in `flake.nix` to see how to prepare this kind of shell. 103 | 104 | The `workspaceShell` function, created by [`makePackagSet`](#Arguments) accepts all the same options as the nix [`mkShell`] function. 105 | 106 | [`mkShell`]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell 107 | 108 | ### Maintaining your project 109 | 110 | In your flake, you can choose your cargo2nix version by changing the URL. 111 | 112 | | Flake URL | Result | 113 | |-------------------------------------------|:------------------------------------------------------------:| 114 | | github:cargo2nix/cargo2nix/ | latest release (check repo's default branch, release-0.11.0) | 115 | | github:cargo2nix/cargo2nix/release-0.11.0 | use a specific release | 116 | | github:cargo2nix/cargo2nix/unstable | latest features & fixes | 117 | 118 | Only use unstable for developing with the latest features. PR's against old 119 | releases can be accepted but no active support will be done. **The default 120 | branch for this repo is updated whenever a new release tag is made.** Only 121 | specific release branches are "stable." 122 | 123 | Update your flake lock with the latest or a specific version of cargo2nix: 124 | 125 | ```shell 126 | nix flake lock --update-input cargo2nix 127 | nix flake lock --update-input cargo2nix --override-input cargo2nix github:cargo2nix/cargo2nix/?rev=d45481420482fa7d9b0a62836555e24ec07d93be 128 | ``` 129 | 130 | If you need newer versions of Rust or the flake-utils inputs, just specify them 131 | using url instead of follows. 132 | 133 | ### Arguments to `makePackageSet` 134 | 135 | The `makePackageSet` function from [the 136 | overlay](./overlay/make-package-set/user-facing.nix) accepts arguments that 137 | adjust how the workspace is built. Only the `packageFun` argument is required. 138 | Cargo2nix's own [flake.nix](./flake.nix) has more information. 139 | 140 | - `rustVersion` - is either a version string or YYYY-MM-DD date-string 141 | - `rustChannel` - `"nightly"` `"beta"` `"stable"` 142 | - `rustProfile` - `"default"` or `"minimal"` usually 143 | - `extraRustComponents` - `["clippy" "miri"]` etc 144 | 145 | - `workspaceSrc` - override where the source is supplied relative to the 146 | Cargo.nix 147 | - `rootFeatures` - a list of `foo/feature` strings for workspace crate features 148 | - `packageOverrides` - control over the individual crate overrides used to make 149 | them compatible on some platforms, for example to tweak C lib consumption 150 | 151 | - `target` - setting an explicit target, useful when cross compiling to obtain a 152 | specific Rust target that doesn't align with the nixpkgs target 153 | 154 | #### Contents of Package Set 155 | 156 | `rustPkgs` contains all crates in the dependency graph and some extra 157 | conveniences for development. The workspace crates are also exposed via a 158 | workspace attribute. 159 | 160 | `rustPkgs...` is an example of a crate **function** 161 | path. Calling the function results in a completed derivation, which can be used 162 | as a flake output. They support all the normal behaviors such as `override` and 163 | `overrideAttrs`. See [mkCrate.nix](./overlay/mkcrate.nix) for the full set of 164 | arguments the crate function supports. 165 | 166 | `rustPkgs.workspace.` are usually the packages you will use. The other 167 | paths look like: 168 | 169 | `rustPkgs."registry+https://github.com/rust-lang/crates.io-index".openssl."0.10.30"` 170 | 171 | `rustPkgs.workspaceShell` is a derivation using Nix's standard `mkShell`, 172 | embelished with information we learned from the dependencies and their 173 | overrides, enabling vanilla `cargo build` to work in a `nix develop` shell. 174 | 175 | #### Overrides 176 | 177 | _This is for finished derivations, not for dependencies. Keep reading below for 178 | using `makeOverride` in the dependency tree._ 179 | 180 | `workspaceShell` and crates both support [`override`](o) and 181 | [`overrideAttrs`](oa) like normal Nix derivations. This allows you to customize 182 | the workspace shell or a build step in your workspace crate very easily. See 183 | `nix show-derivation` and `nix show-derivation #devShell` for more information. 184 | 185 | [o]: https://nixos.org/manual/nixpkgs/stable/#sec-pkg-override 186 | [oa]: https://nixos.org/manual/nixpkgs/stable/#sec-pkg-overrideAttrs 187 | 188 | #### More Control 189 | 190 | You can make overrides to packages in the dependency tree. See examples in 191 | [overrides.nix](./overlay/overrides.nix). Overriding the `buildPhase` etc is 192 | possible for a single crate without modifying `mkcrate.nix` in cargo2nix 193 | directly. The output of `nix show-derivation` can be valuable when determining 194 | what the current output result is. 195 | 196 | **The most important function in cargo2nix source is mkcrate.nix** because it's 197 | how we store information in dependents and replay them back when 198 | building dependents. It is vital for building crates in isolation. 199 | 200 | ## How it works 201 | 202 | - The `cargo2nix` utility reads the Rust workspace configuration and 203 | `Cargo.lock` and generates nix expressions that encode some of the feature, 204 | platform, and target logic into a `Cargo.nix` 205 | 206 | - The cargo2nix [Nixpkgs](https://github.com/NixOS/nixpkgs) [overlay](./overlay) 207 | consumes the `Cargo.nix`, feeding it what you pass to `makePackageSet` to 208 | provide workspace outputs you can expose in your nix flake 209 | 210 | - Because we know all of the dependencies, it's easy to create a shell from those 211 | dependencies as environment setup using the `workspaceShell` function and 212 | exposing the result in the `devShell` flake output 213 | 214 | ### Building crates isolated from each other 215 | 216 | Just like regular `cargo` builds, the Nix dependencies form a [DAG][DAG], but purity 217 | means we only expose essential information to dependencies and manually invoke 218 | `cargo`. Communication from dependencies to dependents is handled by writing 219 | some extra outputs and then reading those outputs inside the next dependent 220 | build. 221 | 222 | There's two broad categories of information that need to be transmitted when 223 | hand-building crates in isolation: 224 | 225 | - **Global information** 226 | 227 | - target such as `x86_64-unknown-linux-gnu` 228 | - cargo actions such as `build` or `test` 229 | - features which turn on optional dependencies & downstream features via logic 230 | in the [`Cargo.nix`](./Cargo.nix) expressions 231 | 232 | This information is known before any of the crates are built. It's used at 233 | evaluation time to decide what will be built. See `nix show-derivation` results. 234 | 235 | - **Propagated information** 236 | 237 | Each dependency writes information such as linker flags alongside its rlib and 238 | other outputs. When the dependent is going to consume the dependency, it 239 | reads this information back. 240 | 241 | Derivations are evaluated in Nix with global information available. During the 242 | build, rlibs and dependency information are propagated back up the DAG. Each 243 | derivation's build shell combines the linking, features, target, and other 244 | information. You can see how it's used in 245 | [`mkcrate.nix`](./overlay/mkcrate.nix) 246 | 247 | [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph 248 | 249 | ### Limitations implied by purity 250 | 251 | Evaluation of nix derivations doesn't require building anything. If you want to 252 | build a specific variant of a crate in a workspace with Nix, we would have to 253 | know this when building all of its dependencies. This means certain behavior to 254 | switch features and optional dependencies on or off depends on what _else_ is 255 | being built. By default `cargo2nix` will build crates as if all other crates in 256 | the workspace _might_ be build. This can be somewhat controlled with the 257 | `rootFeatures` argument. (see `rootFeatures` in [Cargo.nix](./Cargo.nix)). 258 | This actually improves caching but may rarely result in a long build for an 259 | unneeded dependency (which your workspace should put behind a non-default 260 | top-level feature). Cargo isn't any better at this aspect of caching vs 261 | rebuilding. 262 | 263 | ## Common issues 264 | 265 | 1. Flakes require `flake.nix` and `Cargo.nix` to be in version control. `git 266 | add flake.nix Cargo.nix` etc. Remember to keep them up to date! Before 267 | building the examples, you will usually need to update their pin of 268 | cargo2nix: `nix flake lock --update-input cargo2nix` or nix may complane 269 | about paths. 270 | 271 | 1. Old versions of the `cargo2nix.overlay` usually cannot consume newer versions 272 | of the `Cargo.nix` that an updated cargo2nix will produce. Update your 273 | inputs with `nix flake lock --update-input cargo2nix` or `nix build 274 | --update-input cargo2nix` 275 | 276 | 1. When building `sys` crates, `build.rs` scripts may themselves attempt to 277 | provide native dependencies that could be missing. See the 278 | `overlay/overrides.nix` for patterns of common solutions for fixing up 279 | specific deps. 280 | 281 | To provide your own override, pass a modified `packageOverrides` to 282 | `pkgs.rustBuilder.makePackageSet`: 283 | 284 | ```nix 285 | rustPkgs = pkgs.rustBuilder.makePackageSet { 286 | # ... required arguments not shown 287 | 288 | # Use the existing all list of overrides and append your override 289 | packageOverrides = pkgs: pkgs.rustBuilder.overrides.all ++ [ 290 | 291 | # parentheses disambiguate each makeOverride call as a single list element 292 | (pkgs.rustBuilder.rustLib.makeOverride { 293 | name = "fantasy-zlib-sys"; 294 | overrideAttrs = drv: { 295 | propagatedBuildInputs = drv.propagatedBuildInputs or [ ] ++ [ 296 | pkgs.zlib.dev 297 | ]; 298 | }; 299 | }) 300 | ]; 301 | }; 302 | ``` 303 | 304 | 1. Each derivation function in `rustBuilder.makePackageSet` has it's outputs 305 | created within [mkcrate.nix](./overlay/mkcrate.nix), using some bash 306 | functions in [mkcrate-utils.sh](./overlay/mkcrate-utils.sh). We try not to 307 | copy more than necessary to the outputs, but this also means sometimes 308 | skipping a necessary file. Each derivation is [multiple output], using `bin` 309 | and `out`. The default output is `bin` and should contain just what's 310 | necessary at runtime, possibly linked to other files in the Nix store. This 311 | output si for installation into a Nix profile or shell. The `out` output 312 | contains all of this and extra information necessary for dependents to 313 | consume the crate, usually linking information, which will collide if you 314 | attempt to install several such derivations. 315 | 316 | [multiple output]: https://nixos.org/manual/nixpkgs/stable/#chap-multiple-output 317 | 318 | 1. Non-deterministic rustc or linker behavior can lead to binary-incompatible 319 | crates. Nix cannot protect from non-determinism, only impurity. Override 320 | your builds with `preferLocalBuild = true;` `allowSubstitutes = false;` for 321 | the affected package. This has been seen more often because of 322 | nondeterministic macros. See #184 for more information. 323 | 324 | 1. Nixpkgs is a rolling release, and that means breakages occur but you have 325 | many potential successful versions to choose from. View the [CI logs] and 326 | check the `flake.lock` for rev information from recent successes. Update to 327 | a specific input version with: 328 | 329 | ```shell 330 | nix flake lock --override-input nixpkgs github:nixpgks/nixpkgs?rev=a284564b7f75ac4db73607db02076e8da9d42c9d 331 | ``` 332 | 333 | [CI logs]: https://github.com/cargo2nix/cargo2nix/actions/?workflow=CI 334 | 335 | 1. Toml parsing / conversion issues `Error: Cannot convert data to TOML (Invalid 336 | type )` 337 | 338 | `jq` and `remarshal` are used to read & modify toml files in some 339 | cases. Lines of the form: ```[key."cfg(foo = \"a\", bar = \"b\"))".path]``` 340 | could produce breakage when `jq` output was fed back to `remarshal`. There 341 | are workarounds in place to catch many cases. See #149 for more information 342 | and report any newly found breakage until a total solution is in place. 343 | 344 | 1. Git dependencies and crates from alternative Cargo registries rely on 345 | `builtins.fetchGit` to support fetching from private Git repositories. This 346 | means that such dependencies cannot be evaluated with `restrict-eval` 347 | applied. 348 | 349 | Also, if your Git dependency is tied to a Git branch, e.g. `master`, and you 350 | would like to force it to update on upstream changes, you should append 351 | `--option tarball-ttl 0` to your `nix-build` command. 352 | 353 | ## Declarative build debugging shell 354 | 355 | You can load a nix shell for any crate derivation in the dependency tree. This 356 | is the same environment the `cargo2nix` overlay will build them in. 357 | 358 | To do this, first find the .drv for your dependency by using, for example, `nix 359 | show-derivation | grep colorify` 360 | 361 | ```bash 362 | nix show-derivation | rg -o "/nix.*crate.*colorify.*drv" 363 | nix/store/whi3jprrpzlnvic9fsn5f69sddazp5sb-colorify-0.2.3.tar.gz 364 | 365 | # ignore environment to remove your shell's impurities 366 | nix develop --ignore-environment nix/store/whi3jprrpzlnvic9fsn5f69sddazp5sb-colorify-0.2.3.tar.gz 367 | 368 | # the environment is now as it is when nix builds the package 369 | echo $src 370 | nix/store/whi3jprrpzlnvic9fsn5f69sddazp5sb-colorify-0.2.3.tar.gz 371 | 372 | # If you are working on a dependency and need the source (or a fresh copy) you 373 | # can unpack the $src variable. Through nix stdenv, tar is available in pure 374 | # shells 375 | mkdir debug 376 | cp $src debug 377 | cd debug 378 | tar -xzfv whi3jprrpzlnvic9fsn5f69sddazp5sb-colorify-0.2.3.tar.gz 379 | cd 380 | ``` 381 | 382 | You will need to override your `Cargo.toml` and `Cargo.lock` in this shell, so 383 | make sure that you have them backed up if your are directly using your clone of 384 | your project instead of unpacking fresh sources like above. 385 | 386 | Now you just need to run the `$configurePhase` and `$buildPhase` steps in order. 387 | You can find additional phases that may exist in overrides by running `env | 388 | grep Phase` 389 | 390 | ```bash 391 | echo $configurePhase 392 | # runHook preConfigure runHook configureCargo runHook postConfigure 393 | 394 | runHook preConfigure # usually does nothing 395 | runHook findCrate 396 | runHook configureCargo 397 | runHook postConfigure # usually does nothing 398 | 399 | echo $buildPhase 400 | # runHook overrideCargoManifest runHook setBuildEnv runHook runCargo 401 | 402 | runHook overrideCargoManifest # This overrides your .cargo folder, e.g. for setting cross-compilers 403 | runHook setBuildEnv # This sets up linker flags for the `rustc` invocations 404 | runHook runCargo 405 | ``` 406 | 407 | If `runCargo` succeeds, you will have a completed output ready for the (usually) 408 | less interesting `$installPhase`. If there's a problem, inspecting the `env` or 409 | reading the generated `Cargo.lock` etc should yield clues. If you've unpacked a 410 | fresh source and are using the `--ignore-environment` switch, everything is 411 | identical to how the overlay builds the crate, cutting out guess work. 412 | 413 | ## Contributing 414 | 415 | See [Contributing](./CONTRIBUTING.md) for potentially more information. 416 | 417 | 1. Fork this repository into the personal GitHub account 418 | 2. Select the appropriate branch, release- for stable changes, unstable 419 | for breaking changes 420 | 3. Make changes on the personal fork 421 | 4. Make a Pull Request against this repository 422 | 5. **Allow maintainers to make changes to your pull request** (there's a checkbox) 423 | 6. Once the pull request has been approved, you will be thanked and observe your 424 | changes applied with authorship preserved (if we remember) 425 | 426 | ## Credits 427 | 428 | The design for the Nix overlay is inspired by the excellent work done by James 429 | Kay, which is described [here][blog-1] and [here][blog-2]. His source is 430 | available [here][mkRustCrate]. This work would have been impossible without 431 | these fantastic write-ups. Special thanks to James Kay! 432 | 433 | [blog-1]: https://www.hadean.com/blog/managing-rust-dependencies-with-nix-part-i 434 | [blog-2]: https://www.hadean.com/blog/managing-rust-dependencies-with-nix-part-ii 435 | [mkRustCrate]: https://github.com/Twey/mkRustCrate 436 | 437 | ## License 438 | 439 | `cargo2nix` is free and open source software distributed under the terms of the 440 | [MIT License](./LICENSE). 441 | --------------------------------------------------------------------------------