naersk/config.nix

391 lines
15 KiB
Nix
Raw Normal View History

2019-11-18 13:49:27 +00:00
{ lib, libb, builtinz, arg }:
let
2020-02-06 17:31:34 +00:00
allowFun = attrs0: attrName: default:
if builtins.hasAttr attrName attrs0 then
if lib.isFunction attrs0.${attrName} then
attrs0.${attrName} default
else
let
finalTy = builtins.typeOf default;
actualTy = builtins.typeOf attrs0.${attrName};
in
throw "${attrName} should be a function from ${finalTy} to ${finalTy}, but is a ${actualTy}"
2020-02-06 17:31:34 +00:00
else default;
2020-02-06 17:31:34 +00:00
mkAttrs = attrs0: rec
2020-02-07 11:11:32 +00:00
{
# The name of the derivation.
name = attrs0.name or null;
2020-02-07 11:11:32 +00:00
# The version of the derivation.
version = attrs0.version or null;
2020-02-07 11:11:32 +00:00
# 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`.
src = attrs0.src or null;
2020-02-07 11:11:32 +00:00
# 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`.
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. Requires Nix 2.4+.
gitAllRefs = attrs0.gitAllRefs or false;
# Whether to fetch submodules while fetching Git dependencies. Requires Nix
# 2.4+.
2021-04-18 03:39:01 +00:00
gitSubmodules = attrs0.gitSubmodules or false;
2020-02-07 11:11:32 +00:00
# The command to use for the build.
cargoBuild =
allowFun attrs0 "cargoBuild"
''cargo $cargo_options build $cargo_build_options >> $cargo_build_output_json'';
2020-02-07 11:11:32 +00:00
2020-02-07 14:38:16 +00:00
# Options passed to cargo build, i.e. `cargo build <OPTS>`. These options
# can be accessed during the build through the environment variable
# `cargo_build_options`. <br/>
# 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.<br/>
2020-02-07 11:11:32 +00:00
# Note: these values are not (shell) escaped, meaning that you can use
# environment variables but must be careful when introducing e.g. spaces. <br/>
cargoBuildOptions =
2021-08-19 04:25:39 +00:00
allowFun attrs0 "cargoBuildOptions" [ "$cargo_release" ''-j "$NIX_BUILD_CORES"'' "--message-format=$cargo_message_format" ];
2020-02-07 11:11:32 +00:00
2020-02-18 16:32:22 +00:00
# When `true`, rustc remaps the (`/nix/store`) source paths to `/sources`
# to reduce the number of dependencies in the closure.
remapPathPrefix = attrs0.remapPathPrefix or true;
2020-02-18 13:51:59 +00:00
# The commands to run in the `checkPhase`. Do not forget to set
# [`doCheck`](https://nixos.org/nixpkgs/manual/#ssec-check-phase).
2020-02-07 11:11:32 +00:00
cargoTestCommands =
allowFun attrs0 "cargoTestCommands" [ ''cargo $cargo_options test $cargo_test_options'' ];
2020-02-07 14:38:16 +00:00
# Options passed to cargo test, i.e. `cargo test <OPTS>`. These options
# can be accessed during the build through the environment variable
# `cargo_test_options`. <br/>
2020-02-07 11:11:32 +00:00
# Note: these values are not (shell) escaped, meaning that you can use
# environment variables but must be careful when introducing e.g. spaces. <br/>
cargoTestOptions =
allowFun attrs0 "cargoTestOptions" [ "$cargo_release" ''-j "$NIX_BUILD_CORES"'' ];
# Extra `nativeBuildInputs` to all derivations.
nativeBuildInputs = attrs0.nativeBuildInputs or [];
2020-02-07 11:11:32 +00:00
# Extra `buildInputs` to all derivations.
buildInputs = attrs0.buildInputs or [];
2020-02-07 14:38:16 +00:00
# Options passed to all cargo commands, i.e. `cargo <OPTS> ...`. These
# options can be accessed during the build through the environment variable
# `cargo_options`. <br/>
2020-02-07 11:11:32 +00:00
# Note: these values are not (shell) escaped, meaning that you can use
# environment variables but must be careful when introducing e.g. spaces. <br/>
cargoOptions =
2021-08-19 04:25:39 +00:00
allowFun attrs0 "cargoOptions" [ ];
2020-02-07 11:11:32 +00:00
# When true, `cargo doc` is run and a new output `doc` is generated.
doDoc = attrs0.doDoc or false;
# The commands to run in the `docPhase`. Do not forget to set `doDoc`.
cargoDocCommands =
allowFun attrs0 "cargoDocCommands" [ ''cargo $cargo_options doc $cargo_doc_options'' ];
# Options passed to cargo doc, i.e. `cargo doc <OPTS>`. These options
# can be accessed during the build through the environment variable
# `cargo_doc_options`. <br/>
# Note: these values are not (shell) escaped, meaning that you can use
# environment variables but must be careful when introducing e.g. spaces. <br/>
cargoDocOptions =
allowFun attrs0 "cargoDocOptions" [ "--offline" "$cargo_release" ''-j "$NIX_BUILD_CORES"'' ];
2020-02-07 14:38:16 +00:00
# When true, all cargo builds are run with `--release`. The environment
# variable `cargo_release` is set to `--release` iff this option is set.
2020-02-07 11:11:32 +00:00
release = attrs0.release or true;
2020-02-07 11:11:32 +00:00
# An override for all derivations involved in the build.
override = attrs0.override or (x: x);
# An override for the top-level (last, main) derivation. If both `override`
# and `overrideMain` are specified, _both_ will be applied to the top-level
# derivation.
overrideMain = attrs0.overrideMain or (x: x);
2020-02-07 11:11:32 +00:00
# When true, no intermediary (dependency-only) build is run. Enabling
# `singleStep` greatly reduces the incrementality of the builds.
singleStep = attrs0.singleStep or false;
# When true, the resulting binaries are copied to `$out/bin`. <br/>
# Note: this relies on cargo's `--message-format` argument, set in the
# default `cargoBuildOptions`.
2020-02-07 11:11:32 +00:00
copyBins = attrs0.copyBins or true;
2020-07-20 09:25:45 +00:00
# When true, the resulting binaries are copied to `$out/lib`. <br/> Note:
# this relies on cargo's `--message-format` argument, set in the default
# `cargoBuildOptions`.
copyLibs = attrs0.copyLibs or false;
# A [`jq`](https://stedolan.github.io/jq) filter for selecting which build
# artifacts to release. This is run on cargo's
# [`--message-format`](https://doc.rust-lang.org/cargo/reference/external-tools.html#json-messages)
# JSON output. <br/>
# The value is written to the `cargo_bins_jq_filter` variable.
copyBinsFilter = attrs0.copyBinsFilter or
''select(.reason == "compiler-artifact" and .executable != null and .profile.test == false)'';
2020-07-20 09:25:45 +00:00
# A [`jq`](https://stedolan.github.io/jq) filter for selecting which build
# artifacts to release. This is run on cargo's
# [`--message-format`](https://doc.rust-lang.org/cargo/reference/external-tools.html#json-messages)
# JSON output. <br/> The value is written to the `cargo_libs_jq_filter`
# variable. Default: `''select(.reason == "compiler-artifact" and
# ((.target.kind | contains(["staticlib"])) or (.target.kind |
# contains(["cdylib"]))) and .filenames != null and .profile.test ==
# false)''`
copyLibsFilter = attrs0.copyLibsFilter or
''select(.reason == "compiler-artifact" and ((.target.kind | contains(["staticlib"])) or (.target.kind | contains(["cdylib"]))) and .filenames != null and .profile.test == false)'';
2020-02-07 11:11:32 +00:00
# When true, the documentation is generated in a different output, `doc`.
copyDocsToSeparateOutput = attrs0.copyDocsToSeparateOutput or true;
2020-02-07 11:11:32 +00:00
# When true, the build fails if the documentation step fails; otherwise
# the failure is ignored.
doDocFail = attrs0.doDocFail or false;
2020-02-07 11:11:32 +00:00
# When true, references to the nix store are removed from the generated
# documentation.
removeReferencesToSrcFromDocs = attrs0.removeReferencesToSrcFromDocs or true;
2020-02-07 11:11:32 +00:00
# When true, the build output of intermediary builds is compressed with
# [`Zstandard`](https://facebook.github.io/zstd/). This reduces the size
# of closures.
compressTarget = attrs0.compressTarget or true;
2020-02-07 11:11:32 +00:00
# When true, the `target/` directory is copied to `$out`.
copyTarget = attrs0.copyTarget or false;
# Optional hook to run after the compilation is done; inside this script,
# `$out/bin` contains compiled Rust binaries. Useful if your application
# needs e.g. custom environment variables, in which case you can simply run
# `wrapProgram $out/bin/your-app-name` in here.
postInstall = attrs0.postInstall or false;
2020-02-07 11:11:32 +00:00
# Whether to use the `fromTOML` built-in or not. When set to `false` the
# python package `remarshal` is used instead (in a derivation) and the
# JSON output is read with `builtins.fromJSON`.
# This is a workaround for old versions of Nix. May be used safely from
# Nix 2.3 onwards where all bugs in `builtins.fromTOML` seem to have been
# fixed.
usePureFromTOML = attrs0.usePureFromTOML or true;
# When true, only run `cargo check`.
checkOnly = attrs0.checkOnly or false;
# When true, only run `cargo test`.
testOnly = attrs0.testOnly or false;
# When true, only run `cargo clippy`.
clippyOnly = attrs0.clippyOnly or false;
2020-02-07 11:11:32 +00:00
};
2019-12-01 22:25:35 +00:00
2019-11-18 13:49:27 +00:00
argIsAttrs =
if lib.isDerivation arg then false
else if lib.isString arg then false
else if builtins.typeOf arg == "path" then false
else if builtins.hasAttr "outPath" arg then false
else true;
2019-11-21 09:35:00 +00:00
# if the argument is not an attribute set, then assume it's the 'root'.
2019-12-01 22:25:35 +00:00
attrs =
if argIsAttrs
then mkAttrs arg
else mkAttrs { root = arg; };
2019-11-21 09:35:00 +00:00
userAttrs =
if argIsAttrs
then removeAttrs arg (builtins.attrNames attrs)
else {};
2019-11-21 09:35:00 +00:00
# we differentiate 'src' and 'root'. 'src' is used as source for the build;
# 'root' is used to find files like 'Cargo.toml'. As often as possible 'root'
# should be a "path" to avoid reading values from the nix-store.
# Below we try to come up with some good values for src and root if they're
# not defined.
sr =
let
2019-12-01 22:25:35 +00:00
hasRoot = ! isNull attrs.root;
hasSrc = ! isNull attrs.src;
2019-11-21 09:35:00 +00:00
isPath = x: builtins.typeOf x == "path";
root = attrs.root;
src = attrs.src;
in
2020-02-07 11:11:32 +00:00
# src: yes, root: no
if hasSrc && ! hasRoot then
if isPath src then
{ src = lib.cleanSource src; root = src; }
else { inherit src; root = src; }
# src: yes, root: yes
else if hasRoot && hasSrc then
{ inherit src root; }
# src: no, root: yes
else if hasRoot && ! hasSrc then
if isPath root then
{ inherit root; src = lib.cleanSource root; }
else
{ inherit root; src = root; }
# src: no, root: yes
else throw "please specify either 'src' or 'root'";
2019-11-21 09:35:00 +00:00
2019-12-01 22:25:35 +00:00
usePureFromTOML = attrs.usePureFromTOML;
2019-11-18 13:58:25 +00:00
readTOML = builtinz.readTOML usePureFromTOML;
cargoBuildOverwrite = let
inherit (lib.attrsets) optionalAttrs;
in (optionalAttrs attrs.checkOnly {
cargoBuild = ''cargo $cargo_options check $cargo_build_options >> $cargo_build_output_json'';
}) // (optionalAttrs attrs.testOnly {
cargoBuild = ''cargo $cargo_options test $cargo_test_options >> $cargo_build_output_json'';
}) // (optionalAttrs attrs.clippyOnly {
cargoBuild = ''cargo $cargo_options clippy $cargo_build_options -- -D warnings >> $cargo_build_output_json'';
});
# config used during build the prebuild and the final build
buildConfig = {
2020-02-06 17:31:34 +00:00
inherit (attrs)
nativeBuildInputs
2020-02-06 17:31:34 +00:00
buildInputs
release
override
cargoOptions
compressTarget
2020-02-06 17:31:34 +00:00
cargoBuild
cargoBuildOptions
2020-02-18 16:32:22 +00:00
remapPathPrefix
2020-02-06 17:31:34 +00:00
copyBins
copyBinsFilter
copyLibs
copyLibsFilter
2020-02-06 17:31:34 +00:00
copyTarget
2020-02-06 17:31:34 +00:00
cargoTestCommands
cargoTestOptions
2020-02-06 17:31:34 +00:00
doDoc
doDocFail
cargoDocCommands
cargoDocOptions
2020-02-06 17:31:34 +00:00
copyDocsToSeparateOutput
removeReferencesToSrcFromDocs
postInstall
2020-02-06 17:31:34 +00:00
;
# The list of _all_ crates (incl. transitive dependencies) with name,
# version and sha256 of the crate
# Example:
# [ { name = "wabt", version = "2.0.6", sha256 = "..." } ]
crateDependencies = libb.mkVersions buildPlanConfig.cargolock;
} // cargoBuildOverwrite;
# config used when planning the builds
buildPlanConfig = rec {
inherit userAttrs;
2019-11-21 09:35:00 +00:00
inherit (sr) src root;
inherit (attrs) overrideMain gitAllRefs gitSubmodules;
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
isSingleStep = attrs.singleStep;
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
# List of all the Cargo.tomls in the workspace.
#
# Note that the simplest thing here would be to read `workspace.members`,
# but somewhat unfortunately there's no requirement that all workspace
# crates should be listed there - for instance, some projects¹ do:
#
# ```
# [workspace]
# members = [ "crates/foo", "crates/bar" ]
#
# [dependencies]
# foo = { path = "crates/foo" }
# bar = { path = "crates/bar" }
# zar = { path = "crates/zar" }
# ```
#
# ... which Cargo allows and so should we.
#
# ¹ such as Nushell
cargotomls =
let
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
findCargoTomls = dir:
lib.mapAttrsToList
(name: type:
let
path = "${root}/${dir}/${name}";
in
if name == "Cargo.toml" then
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
[{ name = dir; toml = readTOML path; }]
else if type == "directory" then
findCargoTomls "${dir}/${name}"
else
[])
(builtins.readDir "${root}/${dir}");
2020-01-14 18:50:32 +00:00
2020-02-07 11:11:32 +00:00
in
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
lib.flatten (findCargoTomls ".");
2020-01-14 18:50:32 +00:00
# If `copySourcesFrom` is set, then it looks like the benefits brought by
# two-step caching break, for unclear reasons as of now. As such, do not set
# `copySourcesFrom` if there is no source to actually copy from.
copySourcesFrom = if copySources != [] then src else null;
copySources =
let
mkRelative = po:
if lib.hasPrefix "/" po.path
then throw "'${toString src}/Cargo.toml' contains the absolute path '${toString po.path}' which is not allowed under a [patch] section by naersk. Please make it relative to '${toString src}'"
else po.path;
in
arg.copySources or []
++
lib.optionals (builtins.hasAttr "patch" toplevelCargotoml)
(
map mkRelative
(
lib.collect (as: lib.isAttrs as && builtins.hasAttr "path" as)
toplevelCargotoml.patch
)
);
# Are we building a workspace (or is this a simple crate) ?
isWorkspace = builtins.hasAttr "workspace" toplevelCargotoml;
# The top level Cargo.toml, either a workspace or package
2019-11-21 09:35:00 +00:00
toplevelCargotoml = readTOML (root + "/Cargo.toml");
# The cargo lock
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
cargolock =
let
cargolock-file = root + "/Cargo.lock";
in
if builtins.pathExists cargolock-file then
readTOML (cargolock-file)
Improve Cargo.toml discovery algorithm Our current Cargo.toml discovery algorithm is based on reading the main Cargo.toml and looking at its `workspace.members` - unfortunately, as it turns out there's actually no requirement for all workspace crates to be listed there, and some applications do fancy things like: ```toml [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } # whoopsie ``` ... which doesn't play nice with Naersk's incremental builds (that is, the compilation fails unless `singleStep = true;` is turned on). This commit changes the logic to use a different approach: now we simply recursively scan the root directory, noting down all the Cargo.tomls we find; Crane seems to use the same algorithm. I think the only case where this approach falls short are spurious Cargo.tomls that are present somewhere in the source directory but are actually unused - for instance, imagine I'm writing a Cargo clone where I've got files like `src/tests/broken-manifest/Cargo.toml` - in cases like these, the current approach will cause the build to fail, because we will try to fixup a broken Cargo.toml, thinking it's used somewhere. We could try to handle cases like these by using an even different approach: instead of traversing the source tree, we could load the main Cargo.toml and traverse its `workspace.members` + all path-based dependencies recursively, noting down all the *explicitly present* Cargo.tomls. That is, given a top-level manifest of: ``` [workspace] members = [ "crates/foo" ] [dependencies] foo = { path = "crates/foo" } bar = { path = "crates/bar" } ``` ... we'd note down and load `crates/foo/Cargo.toml` + `crates/bar/Cargo.toml`, and then load _their_ path-based dependencies, to find all transitive Cargo.tomls that belong to this workspace. (that is, we have to scan the crates recursively, because `bar` might depend on `crates/zar` - and that's not an imaginary edge case, that's actually what happens inside Nushell, for instance.) I don't have enough time to implement this more extensive approach now, and IMO the improvement presented here is still an improvement, so I think it's safe to go with the new-but-still-subpar approach here and consider the better algorithm in the future :-) # Testing All tests pass, I've also made sure that incremental builds are still incremental. Closes https://github.com/nix-community/naersk/issues/274
2023-02-24 20:00:26 +00:00
else
2021-12-08 15:50:03 +00:00
throw "Naersk requires Cargo.lock to be available in root. Check that it is not in .gitignore and stage it when using git to filter sources (which flakes does)";
2019-12-01 22:25:35 +00:00
packageName =
if ! isNull attrs.name
then attrs.name
else toplevelCargotoml.package.name or
(if isWorkspace then "rust-workspace" else "rust-package");
2019-12-01 22:25:35 +00:00
packageVersion =
if ! isNull attrs.version
then attrs.version
else toplevelCargotoml.package.version or "unknown";
};
in
buildPlanConfig // { inherit buildConfig; }