diff --git a/README.md b/README.md index bdd10bb..ae3214a 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ it is converted to an attribute set equivalent to `{ root = theArg; }`. | `version` | The version of the derivation. | | `src` | Used by `naersk` as source input to the derivation. When `root` is not set, `src` is also used to discover the `Cargo.toml` and `Cargo.lock`. | | `root` | Used by `naersk` to read the `Cargo.toml` and `Cargo.lock` files. May be different from `src`. When `src` is not set, `root` is (indirectly) used as `src`. | +| `allRefs` | Whether to fetch all refs while fetching Git dependencies. Useful if the wanted revision isn't in the default branch. | | `cargoBuild` | The command to use for the build. The argument must be a function modifying the default value.
Default: `''cargo $cargo_options build $cargo_build_options >> $cargo_build_output_json''` | | `cargoBuildOptions` | Options passed to cargo build, i.e. `cargo build `. These options can be accessed during the build through the environment variable `cargo_build_options`.
Note: naersk relies on the `--out-dir out` option and the `--message-format` option. The `$cargo_message_format` variable is set based on the cargo version.
Note: these values are not (shell) escaped, meaning that you can use environment variables but must be careful when introducing e.g. spaces.
The argument must be a function modifying the default value.
Default: `[ "$cargo_release" ''-j "$NIX_BUILD_CORES"'' "--out-dir" "out" "--message-format=$cargo_message_format" ]` | | `remapPathPrefix` | When `true`, rustc remaps the (`/nix/store`) source paths to `/sources` to reduce the number of dependencies in the closure. Default: `true` | diff --git a/build.nix b/build.nix index 74a6b01..f8c6511 100644 --- a/build.nix +++ b/build.nix @@ -65,10 +65,6 @@ let builtins // import ./builtins { inherit lib writeText remarshal runCommand; }; - # All the git dependencies, as a list - gitDependenciesList = - lib.concatLists (lib.mapAttrsToList (_: ds: ds) gitDependencies); - # This unpacks all git dependencies: # $out/rand # $out/rand/Cargo.toml @@ -127,7 +123,7 @@ let fi done <<< "$tomls" done < <(cat ${ - builtins.toFile "git-deps-json" (builtins.toJSON gitDependenciesList) + builtins.toFile "git-deps-json" (builtins.toJSON gitDependencies) } | jq -cMr '.[]') ''; @@ -173,7 +169,7 @@ let }; } ) - gitDependenciesList + gitDependencies ); }; diff --git a/config.nix b/config.nix index c412f87..1f2d7d8 100644 --- a/config.nix +++ b/config.nix @@ -25,6 +25,10 @@ let # used as `src`. root = attrs0.root or null; + # Whether to fetch all refs while fetching Git dependencies. Useful if + # the wanted revision isn't in the default branch. + allRefs = attrs0.allRefs or false; + # The command to use for the build. cargoBuild = allowFun attrs0 "cargoBuild" @@ -234,7 +238,7 @@ let # Whether we skip pre-building the deps isSingleStep = attrs.singleStep; - inherit (attrs) overrideMain; + inherit (attrs) overrideMain allRefs; # The members we want to build # (list of directory names) diff --git a/default.nix b/default.nix index 2aa3cee..3065785 100644 --- a/default.nix +++ b/default.nix @@ -26,7 +26,7 @@ let let config = mkConfig arg; gitDependencies = - libb.findGitDependencies { inherit (config) cargotomls cargolock; }; + libb.findGitDependencies { inherit (config) cargolock allRefs; }; cargoconfig = if builtinz.pathExists (toString config.root + "/.cargo/config") then builtins.readFile (config.root + "/.cargo/config") diff --git a/lib.nix b/lib.nix index c42fcb6..e317f4c 100644 --- a/lib.nix +++ b/lib.nix @@ -64,99 +64,56 @@ rec mkMetadataKey = name: version: "checksum ${name} ${version} (registry+https://github.com/rust-lang/crates.io-index)"; - # a record: - # { "." = # '.' is the directory of the cargotoml - # [ - # { - # name = "rand"; - # url = "https://github.com/..."; - # checkout = "/nix/store/checkout" - # } - # ] + # Gets all git dependencies in Cargo.lock as a list. + # [ + # { + # name = "rand"; + # url = "https://github.com/..."; + # checkout = "/nix/store/checkout" # } + # ] findGitDependencies = - { cargotomls - , cargolock - }: - let - # This returns all the git dependencies of a particular Cargo.toml. - # tomlDependencies : Cargo.toml -> - # [ - # { - # checkout = { rev = "abcd"; shortRev = "abcd"; outPath = ...; ... }; - # key = "abcd"; - # name = "rand"; - # url = "https://github.com/..."; - # } - # ... - # ] - # - # NOTE: this is terribly inefficient _and_ confusing: - # * the lockfile is read once for every cargo toml entry (n^2) - # * 'fromLockfile' is odd, instead of returning the first matched - # revision it'll return a pseudo lockfile entry - tomlDependencies = cargotoml: - lib.filter (x: ! isNull x) ( - lib.mapAttrsToList - (entryName: v: # The dependecy name + the entry from the cargo toml - if ! (lib.isAttrs v && builtins.hasAttr "git" v) - then null - else - let - # Use the 'package' attribute if it exists, which means this is a renamed dependency - # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml - depName = v.package or entryName; - # predicate that holds if the given lockfile entry is the - # Cargo.toml dependency being looked at (depName) - pred = entry: entry.name == depName && (lib.substring 0 (4 + lib.stringLength v.git) entry.source) == "git+${v.git}"; - - # extract the revision from a Cargo.lock "package.source" - # entry - # git+https://gi.../rand?rev=703452...#703452 - # ^^^^^^ - extractRevision = url: lib.last (lib.splitString "#" url); - - # parse a lockfile entry: - # { name = "rand"; - # version = ...; - # source = "git+https://...rev=703452...#703452"; - # } -> - # { name = "rand"; - # source = "git+https://...rev=703452...#703452"; - # revision = "703452"; - # } - parseLockfileEntry = entry: rec { inherit (entry) name source; revision = extractRevision source; }; - - # List of all entries: [ { name, source, revision } ] - packageLocks = builtins.map parseLockfileEntry (lib.filter pred cargolock.package); + { cargolock, allRefs }: + let + query = p: (lib.substring 0 4 (p.source or "")) == "git+"; - # Find the first revision from the lockfile that: - # * has the same name as the cargo toml entry _if_ the cargo toml does not specify a revision, or - # * has the same name and revision as the cargo toml entry - fromLockfile = lib.findFirst (p: p.name == depName && ((! v?rev) || v.rev == p.revision)) null packageLocks; + extractRevision = source: lib.last (lib.splitString "#" source); + extractPart = part: source: if lib.hasInfix part source then lib.last (lib.splitString part (lib.head (lib.splitString "#" source))) else null; + extractRepoUrl = source: let splitted = lib.head (lib.splitString "?" source); in lib.substring 4 (lib.stringLength splitted) splitted; - # Cargo.lock revision is prioritized, because in Cargo.toml short revisions are allowed - val = v // { rev = fromLockfile.revision or v.rev or null; }; - in - lib.filterAttrs (n: _: n == "rev" || n == "tag" || n == "branch") val // - { - name = depName; - url = val.git; - key = val.rev or val.tag or val.branch or - (throw "No 'rev', 'tag' or 'branch' available to specify key, nor a git revision was found in Cargo.lock"); - checkout = builtins.fetchGit ({ - url = val.git; - } // lib.optionalAttrs (val ? rev) { - rev = val.rev; - } // lib.optionalAttrs (val ? branch) { - ref = val.branch; - } // lib.optionalAttrs (val ? tag) { - ref = val.tag; - }); - } - ) cargotoml.dependencies or { }); + parseLock = lock: + let + source = lock.source; + rev = extractPart "?rev=" source; + tag = extractPart "?tag=" source; + branch = extractPart "?branch=" source; in - lib.mapAttrs (_: x: tomlDependencies x) cargotomls; + { + inherit (lock) name; + revision = extractRevision source; + url = extractRepoUrl source; + } // (lib.optionalAttrs (! isNull branch) { inherit branch; }) + // (lib.optionalAttrs (! isNull tag) { inherit tag; }) + // (lib.optionalAttrs (! isNull rev) { inherit rev; }); + packageLocks = builtins.map parseLock (lib.filter query cargolock.package); + + mkFetch = lock: { + key = lock.revision or lock.rev or lock.tag or lock.branch + or (throw "No 'rev', 'tag' or 'branch' available to specify key, nor a git revision was found in Cargo.lock"); + checkout = builtins.fetchGit ({ + url = lock.url; + rev = lock.revision; + } // lib.optionalAttrs (lock ? rev) { + rev = lock.rev; + } // lib.optionalAttrs (lock ? branch) { + ref = lock.branch; + } // lib.optionalAttrs (lock ? tag) { + ref = lock.tag; + } // lib.optionalAttrs allRefs { + allRefs = true; + }); + } // lock; + in lib.foldl' (acc: e: if lib.any (oe: (oe.url == e.url) && (oe.key == e.key)) acc then acc else acc ++ [e]) [] (builtins.map mkFetch packageLocks); # A very minimal 'src' which makes cargo happy nonetheless dummySrc =