* Previously we would manually copy over any Cargo.lock file from the
source to the dummified source
* Now, `mkDummySrc` will implicitly copy the file if it is present, or
ignore it if missing, so this check is a bit overzealous now
* Loosening this check also allows callers to sneak in their own
Cargo.lock file (e.g. through a patch phase) while still avoiding IFD
when trying to use crane with a non local src (eg, not `./.`) we get the error
```
$ nix flake show
<...>
error: access to absolute path '/Cargo.toml' is forbidden in pure eval mode (use '--impure' to override)
(use '--show-trace' to show detailed location information)
```
To fix this, we quote the relative paths so to properly append them to the base path.
https://nixos.wiki/wiki/Nix_Expression_Language#Coercing_a_relative_path_with_interpolated_variables_to_an_absolute_path_.28for_imports.29
## Reproduction flake
```
{
description = "Build a cargo project without extra checks";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane = {
url = "github:ipetkov/crane";
inputs.nixpkgs.follows = "nixpkgs";
};
external-crate-source = {
url = "github:ray-kast/empress";
flake = false;
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, external-crate-source, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
test-crate = crane.lib.${system}.buildPackage {
src = external-crate-source.outPath;
};
in
{
packages.default = test-crate;
});
}
```
* When Nix fetches a git repo it will only look for the specified
revision only starting from the main branch (apparently fetching
arbitrary revisions from a repository has some security implications)
* If a ref (i.e. branch or tag) is not specified, Nix will only fetch
the repo's main branch
* To remedy this we will supply Nix with the branch or tag (if specified
in the Cargo.lock) to help it find the specified revision
* If cargo does not specify a branch or tag for us, we'll set `allRefs =
true` so that Nix can try fetching all possible branches and tags
before trying to check out the locked revision
* Previously all build hooks were instantiated in a single
`callPackages` call which led to several issues:
- changes via `lib.overrideScope'` were ignored because the build
hooks were only ever instantiated with the nixpkgs instance of our
flake (not whatever overlays the caller may have made)
- the pkgs splicing was not done quite right, meaning when we pull in
the build hooks as nativeBuildInputs the splicing wasn't picking up
the overridden versions of `cargo` but was instead trying to
recompile the nixpkgs version each time
* Seems like Nix can get unhappy if a path fragment is evaluated too
eagerly, giving errors like
`error: access to absolute path '/Cargo.toml' is forbidden in pure eval mode (use '--impure' to override)`
* Changing to using string manipulation seems to resolve the issue
* Rather than recursively look up all paths in a given project and then
filtering for cargo-specific files, we now use our own customized
traversal method to only capture the results we want.
* Previously the check was overzealous and it was impossible to only
specify `cargoToml` (since both other parameters would be null)
* Instead, we take advantage of Nix's laziness and only throw when
needed
* Similarly rename `installCargoTargetDirHook` to
`installCargoArtifactsHook`
* The intention is to highlight that "install" implies "copy to output"
and not anywhere else
* Also avoids the potential confusion of "cargo target dir" (location of
cargo's artifacts) with "cargo target" (which is the target
architecture/platform we want cargo to build for)
* There are some edge cases where cargo will allow a lib.rs file to be
present but not a main.rs (e.g. a binary cannot be called "examples",
which will happen if the crate is called that and a main.rs file is
present)
* By default we pass everything through to the actual derivation itself,
but some internal parameters don't show up as environment variables
directly (or rather do not need to)
* In an effort to keep the build environment as lean as possible, we can
do some clean up (e.g. to avoid invalidating builds if some parameter
changes but is completely ignored/overridden elsewhere)
* This hook was never a great implementation to begin with since it
would simply search the cargo artifacts directory for binaries and
libraries to install
* This isn't really great since if we have multiple builds (say one with
debug artifacts, one with release artifacts) it isn't exactly clear
which artifacts would get installed (or which will get clobbered).
* Now that we have installFromCargoBuildLogHook we can simplify the
options a bit and only have one main installation method. The caller
can always provide their own if they wish
* The intention here is to split up different "responsibilities" into
smaller parts which can be composed as a DAG rather than mutually
recursive functions. Specifically:
* `mkCargoDerivation` represents a lower-level thin wrapper around
`stdenv.mkDerivation` which will
- set up hooks
- require the caller to define the variables needed by the hooks (like
vendor dir, or artifacts to inherit)
- ensure that build/check/install phases can be configured by the
caller without having them remember to call pre/post hooks
* This allows `buildDepsOnly` to only focus on setting some default
values (like good default commands to build all artifacts, setting the
derivation name, etc.) and delegating the rest to `mkCargoDerivation`
* Lastly, the responsibility of `buildWithCargo` ends up ensuring that
`cargoArtifacts` and `cargoVendorDir` are defined if the caller does
not pass them in
* Copying the cargo artifacts to their own separate output is a good
idea *in theory* where each derivation produces bins/libs so that
other things can depend on them without pulling in the cargo artifacts
as well
* In practice, it's much more likely that a derivation will produce
cargo artifacts (to be reused in other build/test steps) XOR produce
the final binaries
* Therefore _not_ separating the outputs will produce less friction when
forming dependency trees in the general path (e.g. no more forgetting
to specify `drv.target`)
* If a caller really wants to install cargo artifacts in a separate
output, they can easily add the customization themselves
* Some tools may try to inspect crate types (e.g. wasm-pack) so it may
be useful to retain the attribute
* In general, the crate-type can influence the project's "structure" so
we should remain faithful to that
* Turns out the `required-features` attribute is only used from hiding
targets from cargo's selection (e.g. do not build unless feature is
enabled) and does not actually influence what features cargo will
enable/build for dependencies
* Reducing the parameter surface area for good measure, zstd compression
works pretty well and it seems redundant to support multiple ways of
copying the target directory around
* If a derivation is created without a name *and* we cannot infer a name
from a Cargo.toml file, we'll throw a descriptive error message which
hints towards the remediation
* Otherwise nix can throw a pretty obtuse "derivation has no name" error
with no error trace
* Hopefully the updated name should be more clear that we are inhering
some existing cargo artifacts and NOT inheriting something like
CARGO_BUILD_TARGET which refers to the host target being built
* This should make it a bit easier to tell at a glance what inputs are
significant for the derivation without having to look at the entire
source (and corresponding hooks)
* The intention is to make it more clear that this flag controls copying
the target directory so that *other* derivations can use it, and DOES
NOT control whether we use an *existing* target directory from another
derivation
* If the source root contains a Cargo.lock file it will be used to
create a vendor directory
* Otherwise a (descriptive) error will be thrown describing how to
remediate
* The caller can, of course, manually specify their own `cargoVendorDir`
which will skip the default vendoring
* We can simplify the configuration by removing the
`doCopyTargetToSeparateOutput` parameter
* If a caller wants to copy target artifacts into any other output, it
is pretty trivial for them to add their own logic for it