mirror of
https://github.com/NixOS/nix
synced 2025-06-24 22:11:15 +02:00
Merge remote-tracking branch 'upstream/master' into lfs
This commit is contained in:
commit
40a3007b7c
89 changed files with 904 additions and 459 deletions
|
@ -27,6 +27,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.18-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.19
|
||||
|
@ -37,6 +38,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.19-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.20
|
||||
|
@ -47,6 +49,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.20-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.21
|
||||
|
@ -57,6 +60,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.21-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.22
|
||||
|
@ -67,6 +71,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.22-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.23
|
||||
|
@ -77,6 +82,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- 2.23-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.24
|
||||
|
@ -87,6 +93,7 @@ pull_request_rules:
|
|||
branches:
|
||||
- "2.24-maintenance"
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.25
|
||||
|
@ -97,4 +104,5 @@ pull_request_rules:
|
|||
branches:
|
||||
- "2.25-maintenance"
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
|
12
doc/manual/rl-next/relative-path-flakes.md
Normal file
12
doc/manual/rl-next/relative-path-flakes.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
synopsis: "Support for relative path inputs"
|
||||
prs: [10089]
|
||||
---
|
||||
|
||||
Flakes can now refer to other flakes in the same repository using relative paths, e.g.
|
||||
```nix
|
||||
inputs.foo.url = "path:./foo";
|
||||
```
|
||||
uses the flake in the `foo` subdirectory of the referring flake. For more information, see the documentation on [the `path` flake input type](@docroot@/command-ref/new-cli/nix3-flake.md#path-fetcher).
|
||||
|
||||
This feature required a change to the lock file format. Previous Nix versions will not be able to use lock files that have locks for relative path inputs in them.
|
|
@ -11,6 +11,7 @@
|
|||
[`--from-profile` *path*]
|
||||
[`--preserve-installed` | `-P`]
|
||||
[`--remove-all` | `-r`]
|
||||
[`--priority` *priority*]
|
||||
|
||||
# Description
|
||||
|
||||
|
@ -61,6 +62,10 @@ The arguments *args* map to store paths in a number of possible ways:
|
|||
The derivations returned by those function calls are installed.
|
||||
This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name.
|
||||
|
||||
- If `--priority` *priority* is given, the priority of the derivations being installed is set to *priority*.
|
||||
This can be used to override the priority of the derivations being installed.
|
||||
This is useful if *args* are [store paths], which don't have any priority information.
|
||||
|
||||
- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed.
|
||||
|
||||
- If *args* are [store paths] that are not store derivations, then these are [realised] and installed.
|
||||
|
@ -235,4 +240,3 @@ channel:
|
|||
```console
|
||||
$ nix-env --file https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz --install --attr firefox
|
||||
```
|
||||
|
||||
|
|
|
@ -160,6 +160,6 @@ which you may remove.
|
|||
To remove a [single-user installation](./installing-binary.md#single-user-installation) of Nix, run:
|
||||
|
||||
```console
|
||||
$ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
|
||||
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
|
||||
```
|
||||
You might also want to manually remove references to Nix from your `~/.profile`.
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
in {
|
||||
inherit stdenvs native;
|
||||
static = native.pkgsStatic;
|
||||
llvm = native.pkgsLLVM;
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
|
@ -282,6 +283,7 @@
|
|||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName};
|
||||
"${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName};
|
||||
"${pkgName}-llvm" = nixpkgsFor.${system}.llvm.nixComponents.${pkgName};
|
||||
}
|
||||
// lib.optionalAttrs supportsCross (flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: {
|
||||
# These attributes go right into `packages.<system>`.
|
||||
|
@ -321,6 +323,9 @@
|
|||
prefixAttrs "static" (forAllStdenvs (stdenvName: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsStatic;
|
||||
})) //
|
||||
prefixAttrs "llvm" (forAllStdenvs (stdenvName: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsLLVM;
|
||||
})) //
|
||||
prefixAttrs "cross" (forAllCrossSystems (crossSystem: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.cross.${crossSystem};
|
||||
}))
|
||||
|
|
|
@ -10,6 +10,27 @@
|
|||
# https://flake.parts/options/git-hooks-nix#options
|
||||
pre-commit.settings = {
|
||||
hooks = {
|
||||
# Conflicts are usually found by other checks, but not those in docs,
|
||||
# and potentially other places.
|
||||
check-merge-conflicts.enable = true;
|
||||
# built-in check-merge-conflicts seems ineffective against those produced by mergify backports
|
||||
check-merge-conflicts-2 = {
|
||||
enable = true;
|
||||
entry = "${pkgs.writeScript "check-merge-conflicts" ''
|
||||
#!${pkgs.runtimeShell}
|
||||
conflicts=false
|
||||
for file in "$@"; do
|
||||
if grep --with-filename --line-number -E '^>>>>>>> ' -- "$file"; then
|
||||
conflicts=true
|
||||
fi
|
||||
done
|
||||
if $conflicts; then
|
||||
echo "ERROR: found merge/patch conflicts in files"
|
||||
exit 1
|
||||
fi
|
||||
touch $out
|
||||
''}";
|
||||
};
|
||||
clang-format = {
|
||||
enable = true;
|
||||
# https://github.com/cachix/git-hooks.nix/pull/532
|
||||
|
|
|
@ -75,7 +75,11 @@ let
|
|||
# Users who are debugging Nix builds are expected to set the environment variable `mesonBuildType`, per the
|
||||
# guidance in https://github.com/NixOS/nix/blob/8a3fc27f1b63a08ac983ee46435a56cf49ebaf4a/doc/manual/source/development/debugging.md?plain=1#L10.
|
||||
# For this reason, we don't want to refer to `finalAttrs.mesonBuildType` here, but rather use the environment variable.
|
||||
preConfigure = prevAttrs.preConfigure or "" + ''
|
||||
preConfigure = prevAttrs.preConfigure or "" + lib.optionalString (
|
||||
!stdenv.hostPlatform.isWindows
|
||||
# build failure
|
||||
&& !stdenv.hostPlatform.isStatic
|
||||
) ''
|
||||
case "$mesonBuildType" in
|
||||
release|minsize) appendToVar mesonFlags "-Db_lto=true" ;;
|
||||
*) appendToVar mesonFlags "-Db_lto=false" ;;
|
||||
|
@ -97,6 +101,12 @@ let
|
|||
];
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic;
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
env = prevAttrs.env or {}
|
||||
// lib.optionalAttrs
|
||||
(stdenv.isLinux
|
||||
&& !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")
|
||||
&& !(stdenv.hostPlatform.useLLVM or false))
|
||||
{ LDFLAGS = "-fuse-ld=gold"; };
|
||||
};
|
||||
|
||||
mesonLibraryLayer = finalAttrs: prevAttrs:
|
||||
|
|
|
@ -42,27 +42,35 @@
|
|||
}:
|
||||
|
||||
let
|
||||
libs = {
|
||||
inherit
|
||||
nix-util
|
||||
nix-util-c
|
||||
nix-store
|
||||
nix-store-c
|
||||
nix-fetchers
|
||||
nix-expr
|
||||
nix-expr-c
|
||||
nix-flake
|
||||
nix-flake-c
|
||||
nix-main
|
||||
nix-main-c
|
||||
nix-cmd
|
||||
;
|
||||
} // lib.optionalAttrs (!stdenv.hostPlatform.isStatic && stdenv.buildPlatform.canExecute stdenv.hostPlatform) {
|
||||
# Currently fails in static build
|
||||
inherit
|
||||
nix-perl-bindings
|
||||
;
|
||||
};
|
||||
|
||||
dev = stdenv.mkDerivation (finalAttrs: {
|
||||
name = "nix-${nix-cli.version}-dev";
|
||||
pname = "nix";
|
||||
version = nix-cli.version;
|
||||
dontUnpack = true;
|
||||
dontBuild = true;
|
||||
libs = map lib.getDev [
|
||||
nix-cmd
|
||||
nix-expr
|
||||
nix-expr-c
|
||||
nix-fetchers
|
||||
nix-flake
|
||||
nix-flake-c
|
||||
nix-main
|
||||
nix-main-c
|
||||
nix-store
|
||||
nix-store-c
|
||||
nix-util
|
||||
nix-util-c
|
||||
nix-perl-bindings
|
||||
];
|
||||
libs = map lib.getDev (lib.attrValues libs);
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support
|
||||
echo $libs >> $out/nix-support/propagated-build-inputs
|
||||
|
@ -127,20 +135,16 @@ in
|
|||
nix-fetchers-tests.tests.run
|
||||
nix-flake-tests.tests.run
|
||||
|
||||
# Make sure the functional tests have passed
|
||||
nix-functional-tests
|
||||
|
||||
# dev bundle is ok
|
||||
# (checkInputs must be empty paths??)
|
||||
(runCommand "check-pkg-config" { checked = dev.tests.pkg-config; } "mkdir $out")
|
||||
] ++
|
||||
(if stdenv.buildPlatform.canExecute stdenv.hostPlatform
|
||||
then [
|
||||
# TODO: add perl.tests
|
||||
nix-perl-bindings
|
||||
]
|
||||
else [
|
||||
nix-perl-bindings
|
||||
]);
|
||||
installCheckInputs = [
|
||||
nix-functional-tests
|
||||
] ++ lib.optionals (!stdenv.hostPlatform.isStatic && stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
|
||||
# Perl currently fails in static build
|
||||
# TODO: Split out tests into a separate derivation?
|
||||
nix-perl-bindings
|
||||
];
|
||||
passthru = prevAttrs.passthru // {
|
||||
inherit (nix-cli) version;
|
||||
|
@ -162,21 +166,7 @@ in
|
|||
disallowedReferences = nix.all;
|
||||
```
|
||||
*/
|
||||
libs = {
|
||||
inherit
|
||||
nix-util
|
||||
nix-util-c
|
||||
nix-store
|
||||
nix-store-c
|
||||
nix-fetchers
|
||||
nix-expr
|
||||
nix-expr-c
|
||||
nix-flake
|
||||
nix-flake-c
|
||||
nix-main
|
||||
nix-main-c
|
||||
;
|
||||
};
|
||||
inherit libs;
|
||||
|
||||
tests = prevAttrs.passthru.tests or {} // {
|
||||
# TODO: create a proper fixpoint and:
|
||||
|
|
|
@ -145,13 +145,28 @@ poly_user_id_get() {
|
|||
dsclattr "/Users/$1" "UniqueID"
|
||||
}
|
||||
|
||||
dscl_create() {
|
||||
# workaround a bug in dscl where it sometimes fails with eNotYetImplemented:
|
||||
# https://github.com/NixOS/nix/issues/12140
|
||||
while ! _sudo "$1" /usr/bin/dscl . -create "$2" "$3" "$4" 2> "$SCRATCH/dscl.err"; do
|
||||
local err=$?
|
||||
if [[ $err -eq 140 ]] && grep -q "-14988 (eNotYetImplemented)" "$SCRATCH/dscl.err"; then
|
||||
echo "dscl failed with eNotYetImplemented, retrying..."
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
cat "$SCRATCH/dscl.err"
|
||||
return $err
|
||||
done
|
||||
}
|
||||
|
||||
poly_user_hidden_get() {
|
||||
dsclattr "/Users/$1" "IsHidden"
|
||||
}
|
||||
|
||||
poly_user_hidden_set() {
|
||||
_sudo "in order to make $1 a hidden user" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
|
||||
dscl_create "in order to make $1 a hidden user" \
|
||||
"/Users/$1" "IsHidden" "1"
|
||||
}
|
||||
|
||||
poly_user_home_get() {
|
||||
|
@ -161,8 +176,8 @@ poly_user_home_get() {
|
|||
poly_user_home_set() {
|
||||
# This can trigger a permission prompt now:
|
||||
# "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings.
|
||||
_sudo "in order to give $1 a safe home directory" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
|
||||
dscl_create "in order to give $1 a safe home directory" \
|
||||
"/Users/$1" "NFSHomeDirectory" "$2"
|
||||
}
|
||||
|
||||
poly_user_note_get() {
|
||||
|
@ -170,8 +185,8 @@ poly_user_note_get() {
|
|||
}
|
||||
|
||||
poly_user_note_set() {
|
||||
_sudo "in order to give $username a useful note" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
|
||||
dscl_create "in order to give $1 a useful note" \
|
||||
"/Users/$1" "RealName" "$2"
|
||||
}
|
||||
|
||||
poly_user_shell_get() {
|
||||
|
@ -179,8 +194,8 @@ poly_user_shell_get() {
|
|||
}
|
||||
|
||||
poly_user_shell_set() {
|
||||
_sudo "in order to give $1 a safe shell" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
|
||||
dscl_create "in order to give $1 a safe shell" \
|
||||
"/Users/$1" "UserShell" "$2"
|
||||
}
|
||||
|
||||
poly_user_in_group_check() {
|
||||
|
|
|
@ -562,7 +562,7 @@ create_build_user_for_core() {
|
|||
if [ "$actual_uid" != "$uid" ]; then
|
||||
failure <<EOF
|
||||
It seems the build user $username already exists, but with the UID
|
||||
with the UID '$actual_uid'. This script can't really handle that right
|
||||
'$actual_uid'. This script can't really handle that right
|
||||
now, so I'm going to give up.
|
||||
|
||||
If you already created the users and you know they start from
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
set -eo pipefail
|
||||
|
||||
# stock path to avoid unexpected command versions
|
||||
PATH="$(/usr/bin/getconf PATH)"
|
||||
|
||||
((NEW_NIX_FIRST_BUILD_UID=351))
|
||||
((TEMP_NIX_FIRST_BUILD_UID=31000))
|
||||
|
||||
|
|
|
@ -450,7 +450,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
|||
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||
{
|
||||
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
|
||||
? lockedFlake->getFingerprint(state.store)
|
||||
? lockedFlake->getFingerprint(state.store, state.fetchSettings)
|
||||
: std::nullopt;
|
||||
auto rootLoader = [&state, lockedFlake]()
|
||||
{
|
||||
|
|
|
@ -76,10 +76,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
(lib.mesonOption "readline-flavor" readlineFlavor)
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-c
|
||||
|
@ -47,10 +46,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-test-support
|
||||
|
@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -56,10 +56,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
|
|
@ -41,10 +41,17 @@ let
|
|||
(key: node:
|
||||
let
|
||||
|
||||
parentNode = allNodes.${getInputByPath lockFile.root node.parent};
|
||||
|
||||
sourceInfo =
|
||||
if overrides ? ${key}
|
||||
then
|
||||
overrides.${key}.sourceInfo
|
||||
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/"
|
||||
then
|
||||
parentNode.sourceInfo // {
|
||||
outPath = parentNode.outPath + ("/" + node.locked.path);
|
||||
}
|
||||
else
|
||||
# FIXME: remove obsolete node.info.
|
||||
# Note: lock file entries are always final.
|
||||
|
|
|
@ -406,7 +406,7 @@ void EvalState::checkURI(const std::string & uri)
|
|||
|
||||
/* If the URI is a path, then check it against allowedPaths as
|
||||
well. */
|
||||
if (hasPrefix(uri, "/")) {
|
||||
if (isAbsolute(uri)) {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(uri));
|
||||
return;
|
||||
|
|
|
@ -96,8 +96,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
} // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
|
|
@ -182,7 +182,7 @@ static void fetchTree(
|
|||
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
input = lookupInRegistries(state.store, input).first;
|
||||
|
||||
if (state.settings.pureEval && !input.isLocked()) {
|
||||
if (state.settings.pureEval && !input.isConsideredLocked(state.fetchSettings)) {
|
||||
auto fetcher = "fetchTree";
|
||||
if (params.isFetchGit)
|
||||
fetcher = "fetchGit";
|
||||
|
|
|
@ -54,10 +54,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
|
|
@ -70,6 +70,22 @@ struct Settings : public Config
|
|||
Setting<bool> warnDirty{this, true, "warn-dirty",
|
||||
"Whether to warn about dirty Git/Mercurial trees."};
|
||||
|
||||
Setting<bool> allowDirtyLocks{
|
||||
this,
|
||||
false,
|
||||
"allow-dirty-locks",
|
||||
R"(
|
||||
Whether to allow dirty inputs (such as dirty Git workdirs)
|
||||
to be locked via their NAR hash. This is generally bad
|
||||
practice since Nix has no way to obtain such inputs if they
|
||||
are subsequently modified. Therefore lock files with dirty
|
||||
locks should generally only be used for local testing, and
|
||||
should not be pushed to other users.
|
||||
)",
|
||||
{},
|
||||
true,
|
||||
Xp::Flakes};
|
||||
|
||||
Setting<bool> trustTarballsFromGitForges{
|
||||
this, true, "trust-tarballs-from-git-forges",
|
||||
R"(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "fetch-to-store.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "store-path-accessor.hh"
|
||||
#include "fetch-settings.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -154,11 +155,23 @@ bool Input::isLocked() const
|
|||
return scheme && scheme->isLocked(*this);
|
||||
}
|
||||
|
||||
bool Input::isConsideredLocked(
|
||||
const Settings & settings) const
|
||||
{
|
||||
return isLocked() || (settings.allowDirtyLocks && getNarHash());
|
||||
}
|
||||
|
||||
bool Input::isFinal() const
|
||||
{
|
||||
return maybeGetBoolAttr(attrs, "__final").value_or(false);
|
||||
}
|
||||
|
||||
std::optional<std::string> Input::isRelative() const
|
||||
{
|
||||
assert(scheme);
|
||||
return scheme->isRelative(*this);
|
||||
}
|
||||
|
||||
Attrs Input::toAttrs() const
|
||||
{
|
||||
return attrs;
|
||||
|
@ -345,7 +358,7 @@ void Input::clone(const Path & destDir) const
|
|||
scheme->clone(*this, destDir);
|
||||
}
|
||||
|
||||
std::optional<Path> Input::getSourcePath() const
|
||||
std::optional<std::filesystem::path> Input::getSourcePath() const
|
||||
{
|
||||
assert(scheme);
|
||||
return scheme->getSourcePath(*this);
|
||||
|
@ -448,7 +461,7 @@ Input InputScheme::applyOverrides(
|
|||
return input;
|
||||
}
|
||||
|
||||
std::optional<Path> InputScheme::getSourcePath(const Input & input) const
|
||||
std::optional<std::filesystem::path> InputScheme::getSourcePath(const Input & input) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -41,11 +41,6 @@ struct Input
|
|||
std::shared_ptr<InputScheme> scheme; // note: can be null
|
||||
Attrs attrs;
|
||||
|
||||
/**
|
||||
* path of the parent of this input, used for relative path resolution
|
||||
*/
|
||||
std::optional<Path> parent;
|
||||
|
||||
/**
|
||||
* Cached result of getFingerprint().
|
||||
*/
|
||||
|
@ -95,6 +90,21 @@ public:
|
|||
*/
|
||||
bool isLocked() const;
|
||||
|
||||
/**
|
||||
* Return whether the input is either locked, or, if
|
||||
* `allow-dirty-locks` is enabled, it has a NAR hash. In the
|
||||
* latter case, we can verify the input but we may not be able to
|
||||
* fetch it from anywhere.
|
||||
*/
|
||||
bool isConsideredLocked(
|
||||
const Settings & settings) const;
|
||||
|
||||
/**
|
||||
* Only for relative path flakes, i.e. 'path:./foo', returns the
|
||||
* relative path, i.e. './foo'.
|
||||
*/
|
||||
std::optional<std::string> isRelative() const;
|
||||
|
||||
/**
|
||||
* Return whether this is a "final" input, meaning that fetching
|
||||
* it will not add, remove or change any attributes. (See
|
||||
|
@ -154,7 +164,7 @@ public:
|
|||
|
||||
void clone(const Path & destDir) const;
|
||||
|
||||
std::optional<Path> getSourcePath() const;
|
||||
std::optional<std::filesystem::path> getSourcePath() const;
|
||||
|
||||
/**
|
||||
* Write a file to this input, for input types that support
|
||||
|
@ -237,7 +247,7 @@ struct InputScheme
|
|||
|
||||
virtual void clone(const Input & input, const Path & destDir) const;
|
||||
|
||||
virtual std::optional<Path> getSourcePath(const Input & input) const;
|
||||
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
|
||||
|
||||
virtual void putFile(
|
||||
const Input & input,
|
||||
|
@ -260,6 +270,9 @@ struct InputScheme
|
|||
|
||||
virtual bool isLocked(const Input & input) const
|
||||
{ return false; }
|
||||
|
||||
virtual std::optional<std::string> isRelative(const Input & input) const
|
||||
{ return std::nullopt; }
|
||||
};
|
||||
|
||||
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
|
||||
|
|
|
@ -300,7 +300,7 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
Strings args = {"clone"};
|
||||
|
||||
args.push_back(repoInfo.url);
|
||||
args.push_back(repoInfo.locationToArg());
|
||||
|
||||
if (auto ref = input.getRef()) {
|
||||
args.push_back("--branch");
|
||||
|
@ -314,11 +314,9 @@ struct GitInputScheme : InputScheme
|
|||
runProgram("git", true, args, {}, true);
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (repoInfo.isLocal) return repoInfo.url;
|
||||
return std::nullopt;
|
||||
return getRepoInfo(input).getPath();
|
||||
}
|
||||
|
||||
void putFile(
|
||||
|
@ -328,14 +326,15 @@ struct GitInputScheme : InputScheme
|
|||
std::optional<std::string> commitMsg) const override
|
||||
{
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (!repoInfo.isLocal)
|
||||
auto repoPath = repoInfo.getPath();
|
||||
if (!repoPath)
|
||||
throw Error("cannot commit '%s' to Git repository '%s' because it's not a working tree", path, input.to_string());
|
||||
|
||||
writeFile((CanonPath(repoInfo.url) / path).abs(), contents);
|
||||
writeFile(*repoPath / path.rel(), contents);
|
||||
|
||||
auto result = runProgram(RunOptions {
|
||||
.program = "git",
|
||||
.args = {"-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())},
|
||||
.args = {"-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())},
|
||||
});
|
||||
auto exitCode =
|
||||
#ifndef WIN32 // TODO abstract over exit status handling on Windows
|
||||
|
@ -348,7 +347,7 @@ struct GitInputScheme : InputScheme
|
|||
if (exitCode != 0) {
|
||||
// The path is not `.gitignore`d, we can add the file.
|
||||
runProgram("git", true,
|
||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "add", "--intent-to-add", "--", std::string(path.rel()) });
|
||||
{ "-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "add", "--intent-to-add", "--", std::string(path.rel()) });
|
||||
|
||||
|
||||
if (commitMsg) {
|
||||
|
@ -356,7 +355,7 @@ struct GitInputScheme : InputScheme
|
|||
logger->pause();
|
||||
Finally restoreLogger([]() { logger->resume(); });
|
||||
runProgram("git", true,
|
||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" },
|
||||
{ "-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" },
|
||||
*commitMsg);
|
||||
}
|
||||
}
|
||||
|
@ -364,24 +363,41 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
struct RepoInfo
|
||||
{
|
||||
/* Whether this is a local, non-bare repository. */
|
||||
bool isLocal = false;
|
||||
/* Either the path of the repo (for local, non-bare repos), or
|
||||
the URL (which is never a `file` URL). */
|
||||
std::variant<std::filesystem::path, ParsedURL> location;
|
||||
|
||||
/* Working directory info: the complete list of files, and
|
||||
whether the working directory is dirty compared to HEAD. */
|
||||
GitRepo::WorkdirInfo workdirInfo;
|
||||
|
||||
/* URL of the repo, or its path if isLocal. Never a `file` URL. */
|
||||
std::string url;
|
||||
std::string locationToArg() const
|
||||
{
|
||||
return std::visit(
|
||||
overloaded {
|
||||
[&](const std::filesystem::path & path)
|
||||
{ return path.string(); },
|
||||
[&](const ParsedURL & url)
|
||||
{ return url.to_string(); }
|
||||
}, location);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const
|
||||
{
|
||||
if (auto path = std::get_if<std::filesystem::path>(&location))
|
||||
return *path;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void warnDirty(const Settings & settings) const
|
||||
{
|
||||
if (workdirInfo.isDirty) {
|
||||
if (!settings.allowDirty)
|
||||
throw Error("Git tree '%s' is dirty", url);
|
||||
throw Error("Git tree '%s' is dirty", locationToArg());
|
||||
|
||||
if (settings.warnDirty)
|
||||
warn("Git tree '%s' is dirty", url);
|
||||
warn("Git tree '%s' is dirty", locationToArg());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,18 +449,36 @@ struct GitInputScheme : InputScheme
|
|||
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; // for testing
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
bool isBareRepository = url.scheme == "file" && !pathExists(url.path + "/.git");
|
||||
repoInfo.isLocal = url.scheme == "file" && !forceHttp && !isBareRepository;
|
||||
repoInfo.url = repoInfo.isLocal ? url.path : url.to_string();
|
||||
//
|
||||
// FIXME: here we turn a possibly relative path into an absolute path.
|
||||
// This allows relative git flake inputs to be resolved against the
|
||||
// **current working directory** (as in POSIX), which tends to work out
|
||||
// ok in the context of flakes, but is the wrong behavior,
|
||||
// as it should resolve against the flake.nix base directory instead.
|
||||
//
|
||||
// See: https://discourse.nixos.org/t/57783 and #9708
|
||||
//
|
||||
if (url.scheme == "file" && !forceHttp && !isBareRepository) {
|
||||
if (!isAbsolute(url.path)) {
|
||||
warn(
|
||||
"Fetching Git repository '%s', which uses a path relative to the current directory. "
|
||||
"This is not supported and will stop working in a future release. "
|
||||
"See https://github.com/NixOS/nix/issues/12281 for details.",
|
||||
url);
|
||||
}
|
||||
repoInfo.location = std::filesystem::absolute(url.path);
|
||||
} else
|
||||
repoInfo.location = url;
|
||||
|
||||
// If this is a local directory and no ref or revision is
|
||||
// given, then allow the use of an unclean working tree.
|
||||
if (!input.getRef() && !input.getRev() && repoInfo.isLocal)
|
||||
repoInfo.workdirInfo = GitRepo::getCachedWorkdirInfo(repoInfo.url);
|
||||
if (auto repoPath = repoInfo.getPath(); !input.getRef() && !input.getRev() && repoPath)
|
||||
repoInfo.workdirInfo = GitRepo::getCachedWorkdirInfo(*repoPath);
|
||||
|
||||
return repoInfo;
|
||||
}
|
||||
|
||||
uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const
|
||||
uint64_t getLastModified(const RepoInfo & repoInfo, const std::filesystem::path & repoDir, const Hash & rev) const
|
||||
{
|
||||
Cache::Key key{"gitLastModified", {{"rev", rev.gitRev()}}};
|
||||
|
||||
|
@ -460,7 +494,7 @@ struct GitInputScheme : InputScheme
|
|||
return lastModified;
|
||||
}
|
||||
|
||||
uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const
|
||||
uint64_t getRevCount(const RepoInfo & repoInfo, const std::filesystem::path & repoDir, const Hash & rev) const
|
||||
{
|
||||
Cache::Key key{"gitRevCount", {{"rev", rev.gitRev()}}};
|
||||
|
||||
|
@ -469,7 +503,7 @@ struct GitInputScheme : InputScheme
|
|||
if (auto revCountAttrs = cache->lookup(key))
|
||||
return getIntAttr(*revCountAttrs, "revCount");
|
||||
|
||||
Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url));
|
||||
Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.locationToArg()));
|
||||
|
||||
auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev);
|
||||
|
||||
|
@ -480,11 +514,15 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
std::string getDefaultRef(const RepoInfo & repoInfo) const
|
||||
{
|
||||
auto head = repoInfo.isLocal
|
||||
? GitRepo::openRepo(repoInfo.url)->getWorkdirRef()
|
||||
: readHeadCached(repoInfo.url);
|
||||
auto head = std::visit(
|
||||
overloaded {
|
||||
[&](const std::filesystem::path & path)
|
||||
{ return GitRepo::openRepo(path)->getWorkdirRef(); },
|
||||
[&](const ParsedURL & url)
|
||||
{ return readHeadCached(url.to_string()); }
|
||||
}, repoInfo.location);
|
||||
if (!head) {
|
||||
warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.url);
|
||||
warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.locationToArg());
|
||||
return "master";
|
||||
}
|
||||
return *head;
|
||||
|
@ -527,29 +565,30 @@ struct GitInputScheme : InputScheme
|
|||
auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo);
|
||||
input.attrs.insert_or_assign("ref", ref);
|
||||
|
||||
Path repoDir;
|
||||
std::filesystem::path repoDir;
|
||||
|
||||
if (repoInfo.isLocal) {
|
||||
repoDir = repoInfo.url;
|
||||
if (auto repoPath = repoInfo.getPath()) {
|
||||
repoDir = *repoPath;
|
||||
if (!input.getRev())
|
||||
input.attrs.insert_or_assign("rev", GitRepo::openRepo(repoDir)->resolveRef(ref).gitRev());
|
||||
} else {
|
||||
Path cacheDir = getCachePath(repoInfo.url, getShallowAttr(input));
|
||||
auto repoUrl = std::get<ParsedURL>(repoInfo.location);
|
||||
std::filesystem::path cacheDir = getCachePath(repoUrl.to_string(), getShallowAttr(input));
|
||||
repoDir = cacheDir;
|
||||
repoInfo.gitDir = ".";
|
||||
|
||||
createDirs(dirOf(cacheDir));
|
||||
PathLocks cacheDirLock({cacheDir});
|
||||
std::filesystem::create_directories(cacheDir.parent_path());
|
||||
PathLocks cacheDirLock({cacheDir.string()});
|
||||
|
||||
auto repo = GitRepo::openRepo(cacheDir, true, true);
|
||||
|
||||
// We need to set the origin so resolving submodule URLs works
|
||||
repo->setRemote("origin", repoInfo.url);
|
||||
repo->setRemote("origin", repoUrl.to_string());
|
||||
|
||||
Path localRefFile =
|
||||
auto localRefFile =
|
||||
ref.compare(0, 5, "refs/") == 0
|
||||
? cacheDir + "/" + ref
|
||||
: cacheDir + "/refs/heads/" + ref;
|
||||
? cacheDir / ref
|
||||
: cacheDir / "refs/heads" / ref;
|
||||
|
||||
bool doFetch;
|
||||
time_t now = time(0);
|
||||
|
@ -565,7 +604,7 @@ struct GitInputScheme : InputScheme
|
|||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
|
||||
doFetch = stat(localRefFile.string().c_str(), &st) != 0 ||
|
||||
!isCacheFileWithinTtl(now, st);
|
||||
}
|
||||
}
|
||||
|
@ -583,11 +622,11 @@ struct GitInputScheme : InputScheme
|
|||
? ref
|
||||
: "refs/heads/" + ref;
|
||||
|
||||
repo->fetch(repoInfo.url, fmt("%s:%s", fetchRef, fetchRef), getShallowAttr(input));
|
||||
repo->fetch(repoUrl.to_string(), fmt("%s:%s", fetchRef, fetchRef), getShallowAttr(input));
|
||||
} catch (Error & e) {
|
||||
if (!pathExists(localRefFile)) throw;
|
||||
if (!std::filesystem::exists(localRefFile)) throw;
|
||||
logError(e.info());
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.url);
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.locationToArg());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -596,8 +635,8 @@ struct GitInputScheme : InputScheme
|
|||
} catch (Error & e) {
|
||||
warn("could not update mtime for file '%s': %s", localRefFile, e.info().msg);
|
||||
}
|
||||
if (!originalRef && !storeCachedHead(repoInfo.url, ref))
|
||||
warn("could not update cached head '%s' for '%s'", ref, repoInfo.url);
|
||||
if (!originalRef && !storeCachedHead(repoUrl.to_string(), ref))
|
||||
warn("could not update cached head '%s' for '%s'", ref, repoInfo.locationToArg());
|
||||
}
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
|
@ -609,8 +648,7 @@ struct GitInputScheme : InputScheme
|
|||
"allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD "fetchGit" ANSI_NORMAL ".",
|
||||
rev->gitRev(),
|
||||
ref,
|
||||
repoInfo.url
|
||||
);
|
||||
repoInfo.locationToArg());
|
||||
} else
|
||||
input.attrs.insert_or_assign("rev", repo->resolveRef(ref).gitRev());
|
||||
|
||||
|
@ -622,7 +660,7 @@ struct GitInputScheme : InputScheme
|
|||
auto isShallow = repo->isShallow();
|
||||
|
||||
if (isShallow && !getShallowAttr(input))
|
||||
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.url);
|
||||
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.locationToArg());
|
||||
|
||||
// FIXME: check whether rev is an ancestor of ref?
|
||||
|
||||
|
@ -637,7 +675,7 @@ struct GitInputScheme : InputScheme
|
|||
infoAttrs.insert_or_assign("revCount",
|
||||
getRevCount(repoInfo, repoDir, rev));
|
||||
|
||||
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.url);
|
||||
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg());
|
||||
|
||||
verifyCommit(input, repo);
|
||||
|
||||
|
@ -693,21 +731,23 @@ struct GitInputScheme : InputScheme
|
|||
RepoInfo & repoInfo,
|
||||
Input && input) const
|
||||
{
|
||||
auto repoPath = repoInfo.getPath().value();
|
||||
|
||||
if (getSubmodulesAttr(input))
|
||||
/* Create mountpoints for the submodules. */
|
||||
for (auto & submodule : repoInfo.workdirInfo.submodules)
|
||||
repoInfo.workdirInfo.files.insert(submodule.path);
|
||||
|
||||
auto repo = GitRepo::openRepo(repoInfo.url, false, false);
|
||||
auto repo = GitRepo::openRepo(repoPath, false, false);
|
||||
|
||||
auto exportIgnore = getExportIgnoreAttr(input);
|
||||
|
||||
ref<SourceAccessor> accessor =
|
||||
repo->getAccessor(repoInfo.workdirInfo,
|
||||
exportIgnore,
|
||||
makeNotAllowedError(repoInfo.url));
|
||||
makeNotAllowedError(repoInfo.locationToArg()));
|
||||
|
||||
accessor->setPathDisplay(repoInfo.url);
|
||||
accessor->setPathDisplay(repoInfo.locationToArg());
|
||||
|
||||
/* If the repo has submodules, return a mounted input accessor
|
||||
consisting of the accessor for the top-level repo and the
|
||||
|
@ -716,10 +756,10 @@ struct GitInputScheme : InputScheme
|
|||
std::map<CanonPath, nix::ref<SourceAccessor>> mounts;
|
||||
|
||||
for (auto & submodule : repoInfo.workdirInfo.submodules) {
|
||||
auto submodulePath = CanonPath(repoInfo.url) / submodule.path;
|
||||
auto submodulePath = repoPath / submodule.path.rel();
|
||||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "git");
|
||||
attrs.insert_or_assign("url", submodulePath.abs());
|
||||
attrs.insert_or_assign("url", submodulePath.string());
|
||||
attrs.insert_or_assign("exportIgnore", Explicit<bool>{ exportIgnore });
|
||||
attrs.insert_or_assign("submodules", Explicit<bool>{ true });
|
||||
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
|
||||
|
@ -743,7 +783,7 @@ struct GitInputScheme : InputScheme
|
|||
}
|
||||
|
||||
if (!repoInfo.workdirInfo.isDirty) {
|
||||
auto repo = GitRepo::openRepo(repoInfo.url);
|
||||
auto repo = GitRepo::openRepo(repoPath);
|
||||
|
||||
if (auto ref = repo->getWorkdirRef())
|
||||
input.attrs.insert_or_assign("ref", *ref);
|
||||
|
@ -753,7 +793,7 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
input.attrs.insert_or_assign("rev", rev.gitRev());
|
||||
input.attrs.insert_or_assign("revCount",
|
||||
rev == nullRev ? 0 : getRevCount(repoInfo, repoInfo.url, rev));
|
||||
rev == nullRev ? 0 : getRevCount(repoInfo, repoPath, rev));
|
||||
|
||||
verifyCommit(input, repo);
|
||||
} else {
|
||||
|
@ -772,7 +812,7 @@ struct GitInputScheme : InputScheme
|
|||
input.attrs.insert_or_assign(
|
||||
"lastModified",
|
||||
repoInfo.workdirInfo.headRev
|
||||
? getLastModified(repoInfo, repoInfo.url, *repoInfo.workdirInfo.headRev)
|
||||
? getLastModified(repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
|
||||
: 0);
|
||||
|
||||
return {accessor, std::move(input)};
|
||||
|
@ -795,7 +835,7 @@ struct GitInputScheme : InputScheme
|
|||
}
|
||||
|
||||
auto [accessor, final] =
|
||||
input.getRef() || input.getRev() || !repoInfo.isLocal
|
||||
input.getRef() || input.getRev() || !repoInfo.getPath()
|
||||
? getAccessorFromCommit(store, repoInfo, std::move(input))
|
||||
: getAccessorFromWorkdir(store, repoInfo, std::move(input));
|
||||
|
||||
|
@ -813,14 +853,14 @@ struct GitInputScheme : InputScheme
|
|||
return makeFingerprint(*rev);
|
||||
else {
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (repoInfo.isLocal && repoInfo.workdirInfo.headRev && repoInfo.workdirInfo.submodules.empty()) {
|
||||
if (auto repoPath = repoInfo.getPath(); repoPath && repoInfo.workdirInfo.headRev && repoInfo.workdirInfo.submodules.empty()) {
|
||||
/* Calculate a fingerprint that takes into account the
|
||||
deleted and modified/added files. */
|
||||
HashSink hashSink{HashAlgorithm::SHA512};
|
||||
for (auto & file : repoInfo.workdirInfo.dirtyFiles) {
|
||||
writeString("modified:", hashSink);
|
||||
writeString(file.abs(), hashSink);
|
||||
dumpPath(repoInfo.url + "/" + file.abs(), hashSink);
|
||||
dumpPath((*repoPath / file.rel()).string(), hashSink);
|
||||
}
|
||||
for (auto & file : repoInfo.workdirInfo.deletedFiles) {
|
||||
writeString("deleted:", hashSink);
|
||||
|
|
|
@ -126,7 +126,7 @@ struct MercurialInputScheme : InputScheme
|
|||
return res;
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
if (url.scheme == "file" && !input.getRef() && !input.getRev())
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
|
@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -80,9 +80,9 @@ struct PathInputScheme : InputScheme
|
|||
};
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
return getStrAttr(input.attrs, "path");
|
||||
return getAbsPath(input);
|
||||
}
|
||||
|
||||
void putFile(
|
||||
|
@ -91,13 +91,13 @@ struct PathInputScheme : InputScheme
|
|||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const override
|
||||
{
|
||||
writeFile((CanonPath(getAbsPath(input)) / path).abs(), contents);
|
||||
writeFile(getAbsPath(input) / path.rel(), contents);
|
||||
}
|
||||
|
||||
std::optional<std::string> isRelative(const Input & input) const
|
||||
std::optional<std::string> isRelative(const Input & input) const override
|
||||
{
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
if (hasPrefix(path, "/"))
|
||||
if (isAbsolute(path))
|
||||
return std::nullopt;
|
||||
else
|
||||
return path;
|
||||
|
@ -108,12 +108,12 @@ struct PathInputScheme : InputScheme
|
|||
return (bool) input.getNarHash();
|
||||
}
|
||||
|
||||
CanonPath getAbsPath(const Input & input) const
|
||||
std::filesystem::path getAbsPath(const Input & input) const
|
||||
{
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] == '/')
|
||||
return CanonPath(path);
|
||||
if (isAbsolute(path))
|
||||
return canonPath(path);
|
||||
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
}
|
||||
|
@ -121,31 +121,14 @@ struct PathInputScheme : InputScheme
|
|||
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
|
||||
{
|
||||
Input input(_input);
|
||||
std::string absPath;
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] != '/') {
|
||||
if (!input.parent)
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
auto absPath = getAbsPath(input);
|
||||
|
||||
auto parent = canonPath(*input.parent);
|
||||
|
||||
// the path isn't relative, prefix it
|
||||
absPath = nix::absPath(path, parent);
|
||||
|
||||
// for security, ensure that if the parent is a store path, it's inside it
|
||||
if (store->isInStore(parent)) {
|
||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
||||
if (!isDirOrInDir(absPath, storePath))
|
||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||
}
|
||||
} else
|
||||
absPath = path;
|
||||
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to the store", absPath));
|
||||
|
||||
// FIXME: check whether access to 'path' is allowed.
|
||||
auto storePath = store->maybeParseStorePath(absPath);
|
||||
auto storePath = store->maybeParseStorePath(absPath.string());
|
||||
|
||||
if (storePath)
|
||||
store->addTempRoot(*storePath);
|
||||
|
@ -154,7 +137,7 @@ struct PathInputScheme : InputScheme
|
|||
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
|
||||
// FIXME: try to substitute storePath.
|
||||
auto src = sinkToSource([&](Sink & sink) {
|
||||
mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
|
||||
mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter);
|
||||
});
|
||||
storePath = store->addToStoreFromDump(*src, "source");
|
||||
}
|
||||
|
@ -176,7 +159,7 @@ struct PathInputScheme : InputScheme
|
|||
store object and the subpath. */
|
||||
auto path = getAbsPath(input);
|
||||
try {
|
||||
auto [storePath, subPath] = store->toStorePath(path.abs());
|
||||
auto [storePath, subPath] = store->toStorePath(path.string());
|
||||
auto info = store->queryPathInfo(storePath);
|
||||
return fmt("path:%s:%s", info->narHash.to_string(HashFormat::Base16, false), subPath);
|
||||
} catch (Error &) {
|
||||
|
|
|
@ -153,7 +153,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, re
|
|||
return std::make_shared<Registry>(settings, Registry::Global); // empty registry
|
||||
}
|
||||
|
||||
if (!hasPrefix(path, "/")) {
|
||||
if (!isAbsolute(path)) {
|
||||
auto storePath = downloadFile(store, path, "flake-registry.json").storePath;
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-c
|
||||
|
@ -49,10 +48,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -56,10 +56,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
|
|
@ -43,7 +43,7 @@ static std::optional<FetchedFlake> lookupInFlakeCache(
|
|||
static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
EvalState & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
bool useRegistries,
|
||||
FlakeCache & flakeCache)
|
||||
{
|
||||
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
|
||||
|
@ -54,7 +54,7 @@ static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
|||
auto [storePath, lockedRef] = originalRef.fetchTree(state.store);
|
||||
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .storePath = storePath});
|
||||
} else {
|
||||
if (allowLookup) {
|
||||
if (useRegistries) {
|
||||
resolvedRef = originalRef.resolve(
|
||||
state.store,
|
||||
[](fetchers::Registry::RegistryType type) {
|
||||
|
@ -102,12 +102,19 @@ static void expectType(EvalState & state, ValueType type,
|
|||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir);
|
||||
|
||||
static FlakeInput parseFlakeInput(EvalState & state,
|
||||
std::string_view inputName, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||
static FlakeInput parseFlakeInput(
|
||||
EvalState & state,
|
||||
std::string_view inputName,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir)
|
||||
{
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
|
@ -124,14 +131,25 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
for (auto & attr : *value->attrs()) {
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
url = attr.value->string_view();
|
||||
forceTrivialValue(state, *attr.value, pos);
|
||||
if (attr.value->type() == nString)
|
||||
url = attr.value->string_view();
|
||||
else if (attr.value->type() == nPath) {
|
||||
auto path = attr.value->path();
|
||||
if (path.accessor != flakeDir.accessor)
|
||||
throw Error("input path '%s' at %s must be in the same source tree as %s",
|
||||
path, state.positions[attr.pos], flakeDir);
|
||||
url = "path:" + flakeDir.path.makeRelative(path.path);
|
||||
}
|
||||
else
|
||||
throw Error("expected a string or a path but got %s at %s",
|
||||
showType(attr.value->type()), state.positions[attr.pos]);
|
||||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, nBool, *attr.value, attr.pos);
|
||||
input.isFlake = attr.value->boolean();
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
|
||||
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, lockRootPath, flakeDir);
|
||||
} else if (attr.name == sFollows) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
auto follows(parseInputPath(attr.value->c_str()));
|
||||
|
@ -189,7 +207,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
if (!attrs.empty())
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
||||
if (url)
|
||||
input.ref = parseFlakeRef(state.fetchSettings, *url, baseDir, true, input.isFlake);
|
||||
input.ref = parseFlakeRef(state.fetchSettings, *url, {}, true, input.isFlake, true);
|
||||
}
|
||||
|
||||
if (!input.follows && !input.ref)
|
||||
|
@ -199,8 +217,11 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir)
|
||||
{
|
||||
std::map<FlakeId, FlakeInput> inputs;
|
||||
|
||||
|
@ -212,8 +233,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|||
state.symbols[inputAttr.name],
|
||||
inputAttr.value,
|
||||
inputAttr.pos,
|
||||
baseDir,
|
||||
lockRootPath));
|
||||
lockRootPath,
|
||||
flakeDir));
|
||||
}
|
||||
|
||||
return inputs;
|
||||
|
@ -227,7 +248,8 @@ static Flake readFlake(
|
|||
const SourcePath & rootDir,
|
||||
const InputPath & lockRootPath)
|
||||
{
|
||||
auto flakePath = rootDir / CanonPath(resolvedRef.subdir) / "flake.nix";
|
||||
auto flakeDir = rootDir / CanonPath(resolvedRef.subdir);
|
||||
auto flakePath = flakeDir / "flake.nix";
|
||||
|
||||
// NOTE evalFile forces vInfo to be an attrset because mustBeTrivial is true.
|
||||
Value vInfo;
|
||||
|
@ -248,7 +270,7 @@ static Flake readFlake(
|
|||
auto sInputs = state.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs()->get(sInputs))
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, lockRootPath, flakeDir);
|
||||
|
||||
auto sOutputs = state.symbols.create("outputs");
|
||||
|
||||
|
@ -323,25 +345,20 @@ static Flake readFlake(
|
|||
static Flake getFlake(
|
||||
EvalState & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
bool useRegistries,
|
||||
FlakeCache & flakeCache,
|
||||
InputPath lockRootPath)
|
||||
const InputPath & lockRootPath)
|
||||
{
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
state, originalRef, useRegistries, flakeCache);
|
||||
|
||||
return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootPath);
|
||||
}
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
||||
{
|
||||
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
||||
}
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
|
||||
{
|
||||
FlakeCache flakeCache;
|
||||
return getFlake(state, originalRef, allowLookup, flakeCache);
|
||||
return getFlake(state, originalRef, useRegistries, flakeCache, {});
|
||||
}
|
||||
|
||||
static LockFile readLockFile(
|
||||
|
@ -367,7 +384,7 @@ LockedFlake lockFlake(
|
|||
|
||||
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
|
||||
|
||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
|
||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});
|
||||
|
||||
if (lockFlags.applyNixConfig) {
|
||||
flake.config.apply(settings);
|
||||
|
@ -386,13 +403,29 @@ LockedFlake lockFlake(
|
|||
|
||||
debug("old lock file: %s", oldLockFile);
|
||||
|
||||
std::map<InputPath, FlakeInput> overrides;
|
||||
struct OverrideTarget
|
||||
{
|
||||
FlakeInput input;
|
||||
SourcePath sourcePath;
|
||||
std::optional<InputPath> parentInputPath; // FIXME: rename to inputPathPrefix?
|
||||
};
|
||||
|
||||
std::map<InputPath, OverrideTarget> overrides;
|
||||
std::set<InputPath> explicitCliOverrides;
|
||||
std::set<InputPath> overridesUsed, updatesUsed;
|
||||
std::map<ref<Node>, SourcePath> nodePaths;
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides) {
|
||||
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
|
||||
overrides.emplace(
|
||||
i.first,
|
||||
OverrideTarget {
|
||||
.input = FlakeInput { .ref = i.second },
|
||||
/* Note: any relative overrides
|
||||
(e.g. `--override-input B/C "path:./foo/bar"`)
|
||||
are interpreted relative to the top-level
|
||||
flake. */
|
||||
.sourcePath = flake.path,
|
||||
});
|
||||
explicitCliOverrides.insert(i.first);
|
||||
}
|
||||
|
||||
|
@ -405,8 +438,8 @@ LockedFlake lockFlake(
|
|||
ref<Node> node,
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const InputPath & lockRootPath,
|
||||
const Path & parentPath,
|
||||
const InputPath & followsPrefix,
|
||||
const SourcePath & sourcePath,
|
||||
bool trustLock)>
|
||||
computeLocks;
|
||||
|
||||
|
@ -421,8 +454,13 @@ LockedFlake lockFlake(
|
|||
/* The old node, if any, from which locks can be
|
||||
copied. */
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const InputPath & lockRootPath,
|
||||
const Path & parentPath,
|
||||
/* The prefix relative to which 'follows' should be
|
||||
interpreted. When a node is initially locked, it's
|
||||
relative to the node's flake; when it's already locked,
|
||||
it's relative to the root of the lock file. */
|
||||
const InputPath & followsPrefix,
|
||||
/* The source path of this node's flake. */
|
||||
const SourcePath & sourcePath,
|
||||
bool trustLock)
|
||||
{
|
||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||
|
@ -434,7 +472,12 @@ LockedFlake lockFlake(
|
|||
auto inputPath(inputPathPrefix);
|
||||
inputPath.push_back(id);
|
||||
inputPath.push_back(idOverride);
|
||||
overrides.insert_or_assign(inputPath, inputOverride);
|
||||
overrides.emplace(inputPath,
|
||||
OverrideTarget {
|
||||
.input = inputOverride,
|
||||
.sourcePath = sourcePath,
|
||||
.parentInputPath = inputPathPrefix
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,13 +509,18 @@ LockedFlake lockFlake(
|
|||
auto i = overrides.find(inputPath);
|
||||
bool hasOverride = i != overrides.end();
|
||||
bool hasCliOverride = explicitCliOverrides.contains(inputPath);
|
||||
if (hasOverride) {
|
||||
if (hasOverride)
|
||||
overridesUsed.insert(inputPath);
|
||||
// Respect the “flakeness” of the input even if we
|
||||
// override it
|
||||
i->second.isFlake = input2.isFlake;
|
||||
}
|
||||
auto & input = hasOverride ? i->second : input2;
|
||||
auto input = hasOverride ? i->second.input : input2;
|
||||
|
||||
/* Resolve relative 'path:' inputs relative to
|
||||
the source path of the overrider. */
|
||||
auto overridenSourcePath = hasOverride ? i->second.sourcePath : sourcePath;
|
||||
|
||||
/* Respect the "flakeness" of the input even if we
|
||||
override it. */
|
||||
if (hasOverride)
|
||||
input.isFlake = input2.isFlake;
|
||||
|
||||
/* Resolve 'follows' later (since it may refer to an input
|
||||
path we haven't processed yet. */
|
||||
|
@ -488,6 +536,33 @@ LockedFlake lockFlake(
|
|||
|
||||
assert(input.ref);
|
||||
|
||||
auto overridenParentPath =
|
||||
input.ref->input.isRelative()
|
||||
? std::optional<InputPath>(hasOverride ? i->second.parentInputPath : inputPathPrefix)
|
||||
: std::nullopt;
|
||||
|
||||
auto resolveRelativePath = [&]() -> std::optional<SourcePath>
|
||||
{
|
||||
if (auto relativePath = input.ref->input.isRelative()) {
|
||||
return SourcePath {
|
||||
overridenSourcePath.accessor,
|
||||
CanonPath(*relativePath, overridenSourcePath.path.parent().value())
|
||||
};
|
||||
} else
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
/* Get the input flake, resolve 'path:./...'
|
||||
flakerefs relative to the parent flake. */
|
||||
auto getInputFlake = [&]()
|
||||
{
|
||||
if (auto resolvedPath = resolveRelativePath()) {
|
||||
return readFlake(state, *input.ref, *input.ref, *input.ref, *resolvedPath, inputPath);
|
||||
} else {
|
||||
return getFlake(state, *input.ref, useRegistries, flakeCache, inputPath);
|
||||
}
|
||||
};
|
||||
|
||||
/* Do we have an entry in the existing lock file?
|
||||
And the input is not in updateInputs? */
|
||||
std::shared_ptr<LockedNode> oldLock;
|
||||
|
@ -501,6 +576,7 @@ LockedFlake lockFlake(
|
|||
|
||||
if (oldLock
|
||||
&& oldLock->originalRef == *input.ref
|
||||
&& oldLock->parentPath == overridenParentPath
|
||||
&& !hasCliOverride)
|
||||
{
|
||||
debug("keeping existing input '%s'", inputPathS);
|
||||
|
@ -509,7 +585,10 @@ LockedFlake lockFlake(
|
|||
didn't change and there is no override from a
|
||||
higher level flake. */
|
||||
auto childNode = make_ref<LockedNode>(
|
||||
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake);
|
||||
oldLock->lockedRef,
|
||||
oldLock->originalRef,
|
||||
oldLock->isFlake,
|
||||
oldLock->parentPath);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
|
@ -551,7 +630,7 @@ LockedFlake lockFlake(
|
|||
break;
|
||||
}
|
||||
}
|
||||
auto absoluteFollows(lockRootPath);
|
||||
auto absoluteFollows(followsPrefix);
|
||||
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
||||
fakeInputs.emplace(i.first, FlakeInput {
|
||||
.follows = absoluteFollows,
|
||||
|
@ -561,11 +640,12 @@ LockedFlake lockFlake(
|
|||
}
|
||||
|
||||
if (mustRefetch) {
|
||||
auto inputFlake = getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath);
|
||||
auto inputFlake = getInputFlake();
|
||||
nodePaths.emplace(childNode, inputFlake.path.parent());
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, lockRootPath, parentPath, false);
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, followsPrefix,
|
||||
inputFlake.path, false);
|
||||
} else {
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, lockRootPath, parentPath, true);
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, followsPrefix, sourcePath, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -573,7 +653,9 @@ LockedFlake lockFlake(
|
|||
this input. */
|
||||
debug("creating new input '%s'", inputPathS);
|
||||
|
||||
if (!lockFlags.allowUnlocked && !input.ref->input.isLocked())
|
||||
if (!lockFlags.allowUnlocked
|
||||
&& !input.ref->input.isLocked()
|
||||
&& !input.ref->input.isRelative())
|
||||
throw Error("cannot update unlocked flake input '%s' in pure mode", inputPathS);
|
||||
|
||||
/* Note: in case of an --override-input, we use
|
||||
|
@ -586,17 +668,13 @@ LockedFlake lockFlake(
|
|||
auto ref = (input2.ref && explicitCliOverrides.contains(inputPath)) ? *input2.ref : *input.ref;
|
||||
|
||||
if (input.isFlake) {
|
||||
Path localPath = parentPath;
|
||||
FlakeRef localRef = *input.ref;
|
||||
auto inputFlake = getInputFlake();
|
||||
|
||||
// If this input is a path, recurse it down.
|
||||
// This allows us to resolve path inputs relative to the current flake.
|
||||
if (localRef.input.getType() == "path")
|
||||
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
||||
|
||||
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
|
||||
|
||||
auto childNode = make_ref<LockedNode>(inputFlake.lockedRef, ref);
|
||||
auto childNode = make_ref<LockedNode>(
|
||||
inputFlake.lockedRef,
|
||||
ref,
|
||||
true,
|
||||
overridenParentPath);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
|
@ -617,18 +695,27 @@ LockedFlake lockFlake(
|
|||
oldLock
|
||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||
: readLockFile(state.fetchSettings, inputFlake.lockFilePath()).root.get_ptr(),
|
||||
oldLock ? lockRootPath : inputPath,
|
||||
localPath,
|
||||
oldLock ? followsPrefix : inputPath,
|
||||
inputFlake.path,
|
||||
false);
|
||||
}
|
||||
|
||||
else {
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
auto [path, lockedRef] = [&]() -> std::tuple<SourcePath, FlakeRef>
|
||||
{
|
||||
// Handle non-flake 'path:./...' inputs.
|
||||
if (auto resolvedPath = resolveRelativePath()) {
|
||||
return {*resolvedPath, *input.ref};
|
||||
} else {
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
return {state.rootPath(state.store->toRealPath(storePath)), lockedRef};
|
||||
}
|
||||
}();
|
||||
|
||||
auto childNode = make_ref<LockedNode>(lockedRef, ref, false);
|
||||
auto childNode = make_ref<LockedNode>(lockedRef, ref, false, overridenParentPath);
|
||||
|
||||
nodePaths.emplace(childNode, state.rootPath(state.store->toRealPath(storePath)));
|
||||
nodePaths.emplace(childNode, path);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
}
|
||||
|
@ -641,9 +728,6 @@ LockedFlake lockFlake(
|
|||
}
|
||||
};
|
||||
|
||||
// Bring in the current ref for relative path resolution if we have it
|
||||
auto parentPath = flake.path.parent().path.abs();
|
||||
|
||||
nodePaths.emplace(newLockFile.root, flake.path.parent());
|
||||
|
||||
computeLocks(
|
||||
|
@ -652,7 +736,7 @@ LockedFlake lockFlake(
|
|||
{},
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root.get_ptr(),
|
||||
{},
|
||||
parentPath,
|
||||
flake.path,
|
||||
false);
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides)
|
||||
|
@ -678,9 +762,11 @@ LockedFlake lockFlake(
|
|||
|
||||
if (lockFlags.writeLockFile) {
|
||||
if (sourcePath || lockFlags.outputLockFilePath) {
|
||||
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
||||
if (auto unlockedInput = newLockFile.isUnlocked(state.fetchSettings)) {
|
||||
if (lockFlags.failOnUnlocked)
|
||||
throw Error("cannot write lock file of flake '%s' because it has an unlocked input ('%s').\n", topRef, *unlockedInput);
|
||||
throw Error(
|
||||
"Will not write lock file of flake '%s' because it has an unlocked input ('%s'). "
|
||||
"Use '--allow-dirty-locks' to allow this anyway.", topRef, *unlockedInput);
|
||||
if (state.fetchSettings.warnDirty)
|
||||
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
||||
} else {
|
||||
|
@ -695,9 +781,9 @@ LockedFlake lockFlake(
|
|||
writeFile(*lockFlags.outputLockFilePath, newLockFileS);
|
||||
} else {
|
||||
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
||||
auto outputLockFilePath = *sourcePath + "/" + relPath;
|
||||
auto outputLockFilePath = *sourcePath / relPath;
|
||||
|
||||
bool lockFileExists = pathExists(outputLockFilePath);
|
||||
bool lockFileExists = fs::symlink_exists(outputLockFilePath);
|
||||
|
||||
auto s = chomp(diff);
|
||||
if (lockFileExists) {
|
||||
|
@ -733,8 +819,7 @@ LockedFlake lockFlake(
|
|||
repo, so we should re-read it. FIXME: we could
|
||||
also just clear the 'rev' field... */
|
||||
auto prevLockedRef = flake.lockedRef;
|
||||
FlakeCache dummyCache;
|
||||
flake = getFlake(state, topRef, useRegistries, dummyCache);
|
||||
flake = getFlake(state, topRef, useRegistries);
|
||||
|
||||
if (lockFlags.commitLockFile &&
|
||||
flake.lockedRef.input.getRev() &&
|
||||
|
@ -979,9 +1064,11 @@ static RegisterPrimOp r4({
|
|||
|
||||
}
|
||||
|
||||
std::optional<Fingerprint> LockedFlake::getFingerprint(ref<Store> store) const
|
||||
std::optional<Fingerprint> LockedFlake::getFingerprint(
|
||||
ref<Store> store,
|
||||
const fetchers::Settings & fetchSettings) const
|
||||
{
|
||||
if (lockFile.isUnlocked()) return std::nullopt;
|
||||
if (lockFile.isUnlocked(fetchSettings)) return std::nullopt;
|
||||
|
||||
auto fingerprint = flake.lockedRef.input.getFingerprint(store);
|
||||
if (!fingerprint) return std::nullopt;
|
||||
|
|
|
@ -110,7 +110,7 @@ struct Flake
|
|||
}
|
||||
};
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup);
|
||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool useRegistries);
|
||||
|
||||
/**
|
||||
* Fingerprint of a locked flake; used as a cache key.
|
||||
|
@ -129,7 +129,9 @@ struct LockedFlake
|
|||
*/
|
||||
std::map<ref<Node>, SourcePath> nodePaths;
|
||||
|
||||
std::optional<Fingerprint> getFingerprint(ref<Store> store) const;
|
||||
std::optional<Fingerprint> getFingerprint(
|
||||
ref<Store> store,
|
||||
const fetchers::Settings & fetchSettings) const;
|
||||
};
|
||||
|
||||
struct LockFlags
|
||||
|
|
|
@ -48,9 +48,10 @@ FlakeRef parseFlakeRef(
|
|||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake, preserveRelativePaths);
|
||||
if (fragment != "")
|
||||
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
||||
return flakeRef;
|
||||
|
@ -87,7 +88,8 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
|||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
static std::regex pathFlakeRegex(
|
||||
R"(([^?#]*)(\?([^#]*))?(#(.*))?)",
|
||||
|
@ -102,8 +104,8 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
|||
|
||||
if (baseDir) {
|
||||
/* Check if 'url' is a path (either absolute or relative
|
||||
to 'baseDir'). If so, search upward to the root of the
|
||||
repo (i.e. the directory containing .git). */
|
||||
to 'baseDir'). If so, search upward to the root of the
|
||||
repo (i.e. the directory containing .git). */
|
||||
|
||||
path = absPath(path, baseDir);
|
||||
|
||||
|
@ -178,9 +180,8 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
|||
}
|
||||
|
||||
} else {
|
||||
if (!hasPrefix(path, "/"))
|
||||
if (!preserveRelativePaths && !isAbsolute(path))
|
||||
throw BadURL("flake reference '%s' is not an absolute path", url);
|
||||
path = canonPath(path + "/" + getOr(query, "dir", ""));
|
||||
}
|
||||
|
||||
return fromParsedURL(fetchSettings, {
|
||||
|
@ -199,8 +200,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
|||
static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
|
||||
const fetchers::Settings & fetchSettings,
|
||||
const std::string & url,
|
||||
bool isFlake
|
||||
)
|
||||
bool isFlake)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
|
@ -228,11 +228,15 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
|
|||
const fetchers::Settings & fetchSettings,
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool isFlake
|
||||
)
|
||||
bool isFlake)
|
||||
{
|
||||
try {
|
||||
return fromParsedURL(fetchSettings, parseURL(url), isFlake);
|
||||
auto parsed = parseURL(url);
|
||||
if (baseDir
|
||||
&& (parsed.scheme == "path" || parsed.scheme == "git+file")
|
||||
&& !isAbsolute(parsed.path))
|
||||
parsed.path = absPath(parsed.path, *baseDir);
|
||||
return fromParsedURL(fetchSettings, std::move(parsed), isFlake);
|
||||
} catch (BadURL &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -243,7 +247,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
|||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
using namespace fetchers;
|
||||
|
||||
|
@ -252,7 +257,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
|||
} else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) {
|
||||
return *res;
|
||||
} else {
|
||||
return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
|
||||
return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake, preserveRelativePaths);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,8 @@ FlakeRef parseFlakeRef(
|
|||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
bool isFlake = true,
|
||||
bool preserveRelativePaths = false);
|
||||
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
|
||||
|
@ -102,7 +103,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
|||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
bool isFlake = true,
|
||||
bool preserveRelativePaths = false);
|
||||
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "strings.hh"
|
||||
#include "flake/settings.hh"
|
||||
|
||||
namespace nix::flake {
|
||||
|
||||
|
@ -42,9 +43,10 @@ LockedNode::LockedNode(
|
|||
: lockedRef(getFlakeRef(fetchSettings, json, "locked", "info")) // FIXME: remove "info"
|
||||
, originalRef(getFlakeRef(fetchSettings, json, "original", nullptr))
|
||||
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
|
||||
, parentPath(json.find("parent") != json.end() ? (std::optional<InputPath>) json["parent"] : std::nullopt)
|
||||
{
|
||||
if (!lockedRef.input.isLocked())
|
||||
throw Error("lock file contains unlocked input '%s'",
|
||||
if (!lockedRef.input.isConsideredLocked(fetchSettings) && !lockedRef.input.isRelative())
|
||||
throw Error("Lock file contains unlocked input '%s'. Use '--allow-dirty-locks' to accept this lock file.",
|
||||
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
|
||||
|
||||
// For backward compatibility, lock file entries are implicitly final.
|
||||
|
@ -197,10 +199,12 @@ std::pair<nlohmann::json, LockFile::KeyMap> LockFile::toJSON() const
|
|||
/* For backward compatibility, omit the "__final"
|
||||
attribute. We never allow non-final inputs in lock files
|
||||
anyway. */
|
||||
assert(lockedNode->lockedRef.input.isFinal());
|
||||
assert(lockedNode->lockedRef.input.isFinal() || lockedNode->lockedRef.input.isRelative());
|
||||
n["locked"].erase("__final");
|
||||
if (!lockedNode->isFlake)
|
||||
n["flake"] = false;
|
||||
if (lockedNode->parentPath)
|
||||
n["parent"] = *lockedNode->parentPath;
|
||||
}
|
||||
|
||||
nodes[key] = std::move(n);
|
||||
|
@ -228,7 +232,7 @@ std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
|||
return stream;
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> LockFile::isUnlocked() const
|
||||
std::optional<FlakeRef> LockFile::isUnlocked(const fetchers::Settings & fetchSettings) const
|
||||
{
|
||||
std::set<ref<const Node>> nodes;
|
||||
|
||||
|
@ -247,7 +251,10 @@ std::optional<FlakeRef> LockFile::isUnlocked() const
|
|||
for (auto & i : nodes) {
|
||||
if (i == ref<const Node>(root)) continue;
|
||||
auto node = i.dynamic_pointer_cast<const LockedNode>();
|
||||
if (node && (!node->lockedRef.input.isLocked() || !node->lockedRef.input.isFinal()))
|
||||
if (node
|
||||
&& (!node->lockedRef.input.isConsideredLocked(fetchSettings)
|
||||
|| !node->lockedRef.input.isFinal())
|
||||
&& !node->lockedRef.input.isRelative())
|
||||
return node->lockedRef;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,19 @@ struct LockedNode : Node
|
|||
FlakeRef lockedRef, originalRef;
|
||||
bool isFlake = true;
|
||||
|
||||
/* The node relative to which relative source paths
|
||||
(e.g. 'path:../foo') are interpreted. */
|
||||
std::optional<InputPath> parentPath;
|
||||
|
||||
LockedNode(
|
||||
const FlakeRef & lockedRef,
|
||||
const FlakeRef & originalRef,
|
||||
bool isFlake = true)
|
||||
: lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake)
|
||||
bool isFlake = true,
|
||||
std::optional<InputPath> parentPath = {})
|
||||
: lockedRef(lockedRef)
|
||||
, originalRef(originalRef)
|
||||
, isFlake(isFlake)
|
||||
, parentPath(parentPath)
|
||||
{ }
|
||||
|
||||
LockedNode(
|
||||
|
@ -71,7 +79,7 @@ struct LockFile
|
|||
* Check whether this lock file has any unlocked or non-final
|
||||
* inputs. If so, return one.
|
||||
*/
|
||||
std::optional<FlakeRef> isUnlocked() const;
|
||||
std::optional<FlakeRef> isUnlocked(const fetchers::Settings & fetchSettings) const;
|
||||
|
||||
bool operator ==(const LockFile & other) const;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ struct Settings : public Config
|
|||
this,
|
||||
false,
|
||||
"accept-flake-config",
|
||||
"Whether to accept nix configuration from a flake without prompting.",
|
||||
"Whether to accept Nix configuration settings from a flake without prompting.",
|
||||
{},
|
||||
true,
|
||||
Xp::Flakes};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
|
@ -48,10 +47,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util-c
|
||||
|
@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, openssl
|
||||
|
@ -45,10 +44,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -287,23 +287,21 @@ public:
|
|||
|
||||
else if (type == resBuildLogLine || type == resPostBuildLogLine) {
|
||||
auto lastLine = chomp(getS(fields, 0));
|
||||
if (!lastLine.empty()) {
|
||||
auto i = state->its.find(act);
|
||||
assert(i != state->its.end());
|
||||
ActInfo info = *i->second;
|
||||
if (printBuildLogs) {
|
||||
auto suffix = "> ";
|
||||
if (type == resPostBuildLogLine) {
|
||||
suffix = " (post)> ";
|
||||
}
|
||||
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
||||
} else {
|
||||
state->activities.erase(i->second);
|
||||
info.lastLine = lastLine;
|
||||
state->activities.emplace_back(info);
|
||||
i->second = std::prev(state->activities.end());
|
||||
update(*state);
|
||||
auto i = state->its.find(act);
|
||||
assert(i != state->its.end());
|
||||
ActInfo info = *i->second;
|
||||
if (printBuildLogs) {
|
||||
auto suffix = "> ";
|
||||
if (type == resPostBuildLogLine) {
|
||||
suffix = " (post)> ";
|
||||
}
|
||||
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
||||
} else {
|
||||
state->activities.erase(i->second);
|
||||
info.lastLine = lastLine;
|
||||
state->activities.emplace_back(info);
|
||||
i->second = std::prev(state->activities.end());
|
||||
update(*state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util-c
|
||||
|
@ -47,10 +46,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util-test-support
|
||||
|
@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -62,10 +62,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = let
|
||||
|
|
|
@ -50,8 +50,9 @@ struct FileTransferSettings : Config
|
|||
|
||||
Setting<size_t> downloadBufferSize{this, 64 * 1024 * 1024, "download-buffer-size",
|
||||
R"(
|
||||
The size of Nix's internal download buffer during `curl` transfers. If data is
|
||||
The size of Nix's internal download buffer in bytes during `curl` transfers. If data is
|
||||
not processed quickly enough to exceed the size of this buffer, downloads may stall.
|
||||
The default is 67108864 (64 MiB).
|
||||
)"};
|
||||
};
|
||||
|
||||
|
|
|
@ -455,7 +455,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||
bool gcKeepOutputs = settings.gcKeepOutputs;
|
||||
bool gcKeepDerivations = settings.gcKeepDerivations;
|
||||
|
||||
StorePathSet roots, dead, alive;
|
||||
std::unordered_set<StorePath> roots, dead, alive;
|
||||
|
||||
struct Shared
|
||||
{
|
||||
|
@ -661,7 +661,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||
}
|
||||
};
|
||||
|
||||
std::map<StorePath, StorePathSet> referrersCache;
|
||||
std::unordered_map<StorePath, StorePathSet> referrersCache;
|
||||
|
||||
/* Helper function that visits all paths reachable from `start`
|
||||
via the referrers edges and optionally derivers and derivation
|
||||
|
|
|
@ -291,7 +291,11 @@ json listNar(ref<SourceAccessor> accessor, const CanonPath & path, bool recurse)
|
|||
obj["type"] = "symlink";
|
||||
obj["target"] = accessor->readLink(path);
|
||||
break;
|
||||
case SourceAccessor::Type::tMisc:
|
||||
case SourceAccessor::Type::tBlock:
|
||||
case SourceAccessor::Type::tChar:
|
||||
case SourceAccessor::Type::tSocket:
|
||||
case SourceAccessor::Type::tFifo:
|
||||
case SourceAccessor::Type::tUnknown:
|
||||
assert(false); // cannot happen for NARs
|
||||
}
|
||||
return obj;
|
||||
|
|
|
@ -87,8 +87,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
} // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
|
|
@ -534,14 +534,17 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
|||
|
||||
|
||||
void RemoteStore::addMultipleToStore(
|
||||
PathsSource & pathsToCopy,
|
||||
PathsSource && pathsToCopy,
|
||||
Activity & act,
|
||||
RepairFlag repair,
|
||||
CheckSigsFlag checkSigs)
|
||||
{
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
sink << pathsToCopy.size();
|
||||
for (auto & [pathInfo, pathSource] : pathsToCopy) {
|
||||
// Reverse, so we can release memory at the original start
|
||||
std::reverse(pathsToCopy.begin(), pathsToCopy.end());
|
||||
while (!pathsToCopy.empty()) {
|
||||
auto & [pathInfo, pathSource] = pathsToCopy.back();
|
||||
WorkerProto::Serialise<ValidPathInfo>::write(*this,
|
||||
WorkerProto::WriteConn {
|
||||
.to = sink,
|
||||
|
@ -549,6 +552,7 @@ void RemoteStore::addMultipleToStore(
|
|||
},
|
||||
pathInfo);
|
||||
pathSource->drainInto(sink);
|
||||
pathsToCopy.pop_back();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
CheckSigsFlag checkSigs) override;
|
||||
|
||||
void addMultipleToStore(
|
||||
PathsSource & pathsToCopy,
|
||||
PathsSource && pathsToCopy,
|
||||
Activity & act,
|
||||
RepairFlag repair,
|
||||
CheckSigsFlag checkSigs) override;
|
||||
|
|
|
@ -223,7 +223,7 @@ StorePath Store::addToStore(
|
|||
}
|
||||
|
||||
void Store::addMultipleToStore(
|
||||
PathsSource & pathsToCopy,
|
||||
PathsSource && pathsToCopy,
|
||||
Activity & act,
|
||||
RepairFlag repair,
|
||||
CheckSigsFlag checkSigs)
|
||||
|
@ -246,9 +246,7 @@ void Store::addMultipleToStore(
|
|||
act.progress(nrDone, pathsToCopy.size(), nrRunning, nrFailed);
|
||||
};
|
||||
|
||||
ThreadPool pool;
|
||||
|
||||
processGraph<StorePath>(pool,
|
||||
processGraph<StorePath>(
|
||||
storePathsToAdd,
|
||||
|
||||
[&](const StorePath & path) {
|
||||
|
@ -1028,12 +1026,10 @@ std::map<StorePath, StorePath> copyPaths(
|
|||
}
|
||||
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
||||
|
||||
ThreadPool pool;
|
||||
|
||||
try {
|
||||
// Copy the realisation closure
|
||||
processGraph<Realisation>(
|
||||
pool, Realisation::closure(srcStore, toplevelRealisations),
|
||||
Realisation::closure(srcStore, toplevelRealisations),
|
||||
[&](const Realisation & current) -> std::set<Realisation> {
|
||||
std::set<Realisation> children;
|
||||
for (const auto & [drvOutput, _] : current.dependentRealisations) {
|
||||
|
@ -1142,7 +1138,7 @@ std::map<StorePath, StorePath> copyPaths(
|
|||
pathsToCopy.push_back(std::pair{infoForDst, std::move(source)});
|
||||
}
|
||||
|
||||
dstStore.addMultipleToStore(pathsToCopy, act, repair, checkSigs);
|
||||
dstStore.addMultipleToStore(std::move(pathsToCopy), act, repair, checkSigs);
|
||||
|
||||
return pathsMap;
|
||||
}
|
||||
|
|
|
@ -425,7 +425,7 @@ public:
|
|||
CheckSigsFlag checkSigs = CheckSigs);
|
||||
|
||||
virtual void addMultipleToStore(
|
||||
PathsSource & pathsToCopy,
|
||||
PathsSource && pathsToCopy,
|
||||
Activity & act,
|
||||
RepairFlag repair = NoRepair,
|
||||
CheckSigsFlag checkSigs = CheckSigs);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
|
@ -45,10 +44,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
|
@ -49,10 +48,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -55,10 +55,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
|
|
@ -31,12 +31,7 @@ namespace nix {
|
|||
|
||||
namespace fs { using namespace std::filesystem; }
|
||||
|
||||
/**
|
||||
* Treat the string as possibly an absolute path, by inspecting the
|
||||
* start of it. Return whether it was probably intended to be
|
||||
* absolute.
|
||||
*/
|
||||
static bool isAbsolute(PathView path)
|
||||
bool isAbsolute(PathView path)
|
||||
{
|
||||
return fs::path { path }.is_absolute();
|
||||
}
|
||||
|
@ -648,7 +643,7 @@ void setWriteTime(
|
|||
// doesn't support access time just modification time.
|
||||
//
|
||||
// System clock vs File clock issues also make that annoying.
|
||||
warn("Changing file times is not yet implemented on Windows, path is '%s'", path);
|
||||
warn("Changing file times is not yet implemented on Windows, path is %s", path);
|
||||
#elif HAVE_UTIMENSAT && HAVE_DECL_AT_SYMLINK_NOFOLLOW
|
||||
struct timespec times[2] = {
|
||||
{
|
||||
|
@ -661,7 +656,7 @@ void setWriteTime(
|
|||
},
|
||||
};
|
||||
if (utimensat(AT_FDCWD, path.c_str(), times, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
throw SysError("changing modification time of '%s' (using `utimensat`)", path);
|
||||
throw SysError("changing modification time of %s (using `utimensat`)", path);
|
||||
#else
|
||||
struct timeval times[2] = {
|
||||
{
|
||||
|
@ -675,7 +670,7 @@ void setWriteTime(
|
|||
};
|
||||
#if HAVE_LUTIMES
|
||||
if (lutimes(path.c_str(), times) == -1)
|
||||
throw SysError("changing modification time of '%s'", path);
|
||||
throw SysError("changing modification time of %s", path);
|
||||
#else
|
||||
bool isSymlink = optIsSymlink
|
||||
? *optIsSymlink
|
||||
|
@ -683,9 +678,9 @@ void setWriteTime(
|
|||
|
||||
if (!isSymlink) {
|
||||
if (utimes(path.c_str(), times) == -1)
|
||||
throw SysError("changing modification time of '%s' (not a symlink)", path);
|
||||
throw SysError("changing modification time of %s (not a symlink)", path);
|
||||
} else {
|
||||
throw Error("Cannot modification time of symlink '%s'", path);
|
||||
throw Error("Cannot modification time of symlink %s", path);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -714,7 +709,7 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete)
|
|||
copyFile(entry, to / entry.path().filename(), andDelete);
|
||||
}
|
||||
} else {
|
||||
throw Error("file '%s' has an unsupported type", from);
|
||||
throw Error("file %s has an unsupported type", from);
|
||||
}
|
||||
|
||||
setWriteTime(to, lstat(from.string().c_str()));
|
||||
|
@ -741,7 +736,7 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
auto tempCopyTarget = temp / "copy-target";
|
||||
if (e.code().value() == EXDEV) {
|
||||
fs::remove(newPath);
|
||||
warn("Can’t rename %s as %s, copying instead", oldName, newName);
|
||||
warn("can’t rename %s as %s, copying instead", oldName, newName);
|
||||
copyFile(oldPath, tempCopyTarget, true);
|
||||
std::filesystem::rename(
|
||||
os_string_to_string(PathViewNG { tempCopyTarget }),
|
||||
|
|
|
@ -42,6 +42,11 @@ namespace nix {
|
|||
struct Sink;
|
||||
struct Source;
|
||||
|
||||
/**
|
||||
* Return whether the path denotes an absolute path.
|
||||
*/
|
||||
bool isAbsolute(PathView path);
|
||||
|
||||
/**
|
||||
* @return An absolutized path, resolving paths relative to the
|
||||
* specified directory, or the current directory otherwise. The path
|
||||
|
|
|
@ -49,11 +49,13 @@ void copyRecursive(
|
|||
break;
|
||||
}
|
||||
|
||||
case SourceAccessor::tMisc:
|
||||
throw Error("file '%1%' has an unsupported type", from);
|
||||
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo:
|
||||
case SourceAccessor::tUnknown:
|
||||
default:
|
||||
unreachable();
|
||||
throw Error("file '%1%' has an unsupported type of %2%", from, stat.typeString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +112,7 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
|
|||
crf.startFsync = startFsync;
|
||||
crf.fd =
|
||||
#ifdef _WIN32
|
||||
CreateFileW(p.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
CreateFileW(p.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
#else
|
||||
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)
|
||||
#endif
|
||||
|
|
|
@ -200,7 +200,11 @@ std::optional<Mode> convertMode(SourceAccessor::Type type)
|
|||
case SourceAccessor::tSymlink: return Mode::Symlink;
|
||||
case SourceAccessor::tRegular: return Mode::Regular;
|
||||
case SourceAccessor::tDirectory: return Mode::Directory;
|
||||
case SourceAccessor::tMisc: return std::nullopt;
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo: return std::nullopt;
|
||||
case SourceAccessor::tUnknown:
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
|
@ -314,9 +318,13 @@ Mode dump(
|
|||
return Mode::Symlink;
|
||||
}
|
||||
|
||||
case SourceAccessor::tMisc:
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo:
|
||||
case SourceAccessor::tUnknown:
|
||||
default:
|
||||
throw Error("file '%1%' has an unsupported type", path);
|
||||
throw Error("file '%1%' has an unsupported type of %2%", path, st.typeString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,8 +72,6 @@ mkMesonLibrary (finalAttrs: {
|
|||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
} // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
|
|
@ -122,7 +122,13 @@ std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonP
|
|||
S_ISREG(st->st_mode) ? tRegular :
|
||||
S_ISDIR(st->st_mode) ? tDirectory :
|
||||
S_ISLNK(st->st_mode) ? tSymlink :
|
||||
tMisc,
|
||||
S_ISCHR(st->st_mode) ? tChar :
|
||||
S_ISBLK(st->st_mode) ? tBlock :
|
||||
#ifdef S_ISSOCK
|
||||
S_ISSOCK(st->st_mode) ? tSocket :
|
||||
#endif
|
||||
S_ISFIFO(st->st_mode) ? tFifo :
|
||||
tUnknown,
|
||||
.fileSize = S_ISREG(st->st_mode) ? std::optional<uint64_t>(st->st_size) : std::nullopt,
|
||||
.isExecutable = S_ISREG(st->st_mode) && st->st_mode & S_IXUSR,
|
||||
};
|
||||
|
@ -156,7 +162,11 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
case std::filesystem::file_type::regular: return Type::tRegular; break;
|
||||
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
|
||||
case std::filesystem::file_type::directory: return Type::tDirectory; break;
|
||||
default: return tMisc;
|
||||
case std::filesystem::file_type::character: return Type::tChar; break;
|
||||
case std::filesystem::file_type::block: return Type::tBlock; break;
|
||||
case std::filesystem::file_type::fifo: return Type::tFifo; break;
|
||||
case std::filesystem::file_type::socket: return Type::tSocket; break;
|
||||
default: return tUnknown;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}();
|
||||
|
|
|
@ -5,6 +5,26 @@ namespace nix {
|
|||
|
||||
static std::atomic<size_t> nextNumber{0};
|
||||
|
||||
bool SourceAccessor::Stat::isNotNARSerialisable()
|
||||
{
|
||||
return this->type != tRegular && this->type != tSymlink && this->type != tDirectory;
|
||||
}
|
||||
|
||||
std::string SourceAccessor::Stat::typeString() {
|
||||
switch (this->type) {
|
||||
case tRegular: return "regular";
|
||||
case tSymlink: return "symlink";
|
||||
case tDirectory: return "directory";
|
||||
case tChar: return "character device";
|
||||
case tBlock: return "block device";
|
||||
case tSocket: return "socket";
|
||||
case tFifo: return "fifo";
|
||||
case tUnknown:
|
||||
default: return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
SourceAccessor::SourceAccessor()
|
||||
: number(++nextNumber)
|
||||
, displayPrefix{"«unknown»"}
|
||||
|
@ -95,7 +115,7 @@ CanonPath SourceAccessor::resolveSymlinks(
|
|||
throw Error("infinite symlink recursion in path '%s'", showPath(path));
|
||||
auto target = readLink(res);
|
||||
res.pop();
|
||||
if (hasPrefix(target, "/"))
|
||||
if (isAbsolute(target))
|
||||
res = CanonPath::root;
|
||||
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
||||
}
|
||||
|
|
|
@ -88,12 +88,13 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
|
||||
Unlike `DT_UNKNOWN`, this must not be used for deferring the lookup of types.
|
||||
*/
|
||||
tMisc
|
||||
tChar, tBlock, tSocket, tFifo,
|
||||
tUnknown
|
||||
};
|
||||
|
||||
struct Stat
|
||||
{
|
||||
Type type = tMisc;
|
||||
Type type = tUnknown;
|
||||
|
||||
/**
|
||||
* For regular files only: the size of the file. Not all
|
||||
|
@ -112,6 +113,9 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* file in the NAR. Only returned by NAR accessors.
|
||||
*/
|
||||
std::optional<uint64_t> narOffset;
|
||||
|
||||
bool isNotNARSerialisable();
|
||||
std::string typeString();
|
||||
};
|
||||
|
||||
Stat lstat(const CanonPath & path);
|
||||
|
|
|
@ -83,7 +83,6 @@ private:
|
|||
*/
|
||||
template<typename T>
|
||||
void processGraph(
|
||||
ThreadPool & pool,
|
||||
const std::set<T> & nodes,
|
||||
std::function<std::set<T>(const T &)> getEdges,
|
||||
std::function<void(const T &)> processNode)
|
||||
|
@ -97,6 +96,10 @@ void processGraph(
|
|||
|
||||
std::function<void(const T &)> worker;
|
||||
|
||||
/* Create pool last to ensure threads are stopped before other destructors
|
||||
* run */
|
||||
ThreadPool pool;
|
||||
|
||||
worker = [&](const T & node) {
|
||||
|
||||
{
|
||||
|
@ -147,8 +150,16 @@ void processGraph(
|
|||
}
|
||||
};
|
||||
|
||||
for (auto & node : nodes)
|
||||
pool.enqueue(std::bind(worker, std::ref(node)));
|
||||
for (auto & node : nodes) {
|
||||
try {
|
||||
pool.enqueue(std::bind(worker, std::ref(node)));
|
||||
} catch (ThreadPoolShutDown &) {
|
||||
/* Stop if the thread pool is shutting down. It means a
|
||||
previous work item threw an exception, so process()
|
||||
below will rethrow it. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pool.process();
|
||||
|
||||
|
|
|
@ -501,9 +501,17 @@ static bool keep(PackageInfo & drv)
|
|||
return drv.queryMetaBool("keep", false);
|
||||
}
|
||||
|
||||
static void setMetaFlag(EvalState & state, PackageInfo & drv,
|
||||
const std::string & name, const std::string & value)
|
||||
{
|
||||
auto v = state.allocValue();
|
||||
v->mkString(value);
|
||||
drv.setMeta(name, v);
|
||||
}
|
||||
|
||||
|
||||
static void installDerivations(Globals & globals,
|
||||
const Strings & args, const Path & profile)
|
||||
const Strings & args, const Path & profile, std::optional<int> priority)
|
||||
{
|
||||
debug("installing derivations");
|
||||
|
||||
|
@ -527,6 +535,11 @@ static void installDerivations(Globals & globals,
|
|||
newNames.insert(DrvName(i.queryName()).name);
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
for (auto & drv : newElems) {
|
||||
setMetaFlag(*globals.state, drv, "priority", std::to_string((priority.value())));
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto lockToken = optimisticLockProfile(profile);
|
||||
|
@ -564,6 +577,7 @@ static void installDerivations(Globals & globals,
|
|||
|
||||
static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
{
|
||||
std::optional<int> priority;
|
||||
for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
|
||||
auto arg = *i++;
|
||||
if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
|
||||
|
@ -571,10 +585,17 @@ static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
|
|||
globals.preserveInstalled = true;
|
||||
else if (arg == "--remove-all" || arg == "-r")
|
||||
globals.removeAll = true;
|
||||
else if (arg == "--priority") {
|
||||
if (i == opFlags.end())
|
||||
throw UsageError("'%1%' requires an argument", arg);
|
||||
priority = string2Int<int>(*i++);
|
||||
if (!priority)
|
||||
throw UsageError("'--priority' requires an integer argument");
|
||||
}
|
||||
else throw UsageError("unknown flag '%1%'", arg);
|
||||
}
|
||||
|
||||
installDerivations(globals, opArgs, globals.profile);
|
||||
installDerivations(globals, opArgs, globals.profile, priority);
|
||||
}
|
||||
|
||||
|
||||
|
@ -689,15 +710,6 @@ static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs)
|
|||
}
|
||||
|
||||
|
||||
static void setMetaFlag(EvalState & state, PackageInfo & drv,
|
||||
const std::string & name, const std::string & value)
|
||||
{
|
||||
auto v = state.allocValue();
|
||||
v->mkString(value);
|
||||
drv.setMeta(name, v);
|
||||
}
|
||||
|
||||
|
||||
static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (opFlags.size() > 0)
|
||||
|
@ -1507,7 +1519,8 @@ static int main_nix_env(int argc, char * * argv)
|
|||
opFlags.push_back(*arg);
|
||||
/* FIXME: hacky */
|
||||
if (*arg == "--from-profile" ||
|
||||
(op == opQuery && (*arg == "--attr" || *arg == "-A")))
|
||||
(op == opQuery && (*arg == "--attr" || *arg == "-A")) ||
|
||||
(op == opInstall && (*arg == "--priority")))
|
||||
opFlags.push_back(getArg(*arg, arg, end));
|
||||
}
|
||||
else
|
||||
|
|
|
@ -696,7 +696,7 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
auto sourcePath = installableFlake->getLockedFlake()->flake.resolvedRef.input.getSourcePath();
|
||||
if (sourcePath) {
|
||||
if (chdir(sourcePath->c_str()) == -1) {
|
||||
throw SysError("chdir to '%s' failed", *sourcePath);
|
||||
throw SysError("chdir to %s failed", *sourcePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
|
|||
j["lastModified"] = *lastModified;
|
||||
j["path"] = storePath;
|
||||
j["locks"] = lockedFlake.lockFile.toJSON().first;
|
||||
if (auto fingerprint = lockedFlake.getFingerprint(store))
|
||||
if (auto fingerprint = lockedFlake.getFingerprint(store, fetchSettings))
|
||||
j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false);
|
||||
logger->cout("%s", j.dump());
|
||||
} else {
|
||||
|
@ -272,7 +272,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
|
|||
logger->cout(
|
||||
ANSI_BOLD "Last modified:" ANSI_NORMAL " %s",
|
||||
std::put_time(std::localtime(&*lastModified), "%F %T"));
|
||||
if (auto fingerprint = lockedFlake.getFingerprint(store))
|
||||
if (auto fingerprint = lockedFlake.getFingerprint(store, fetchSettings))
|
||||
logger->cout(
|
||||
ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s",
|
||||
fingerprint->to_string(HashFormat::Base16, false));
|
||||
|
@ -941,7 +941,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|||
createSymlink(target, os_string_to_string(PathViewNG { to2 }));
|
||||
}
|
||||
else
|
||||
throw Error("file '%s' has unsupported type", from2);
|
||||
throw Error("path '%s' needs to be a symlink, file, or directory but instead is a %s", from2, st.typeString());
|
||||
changedFiles.push_back(to2);
|
||||
notice("wrote: %s", to2);
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ Currently the `type` attribute can be one of the following:
|
|||
* `nixpkgs/nixos-unstable/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293`
|
||||
* `sub/dir` (if a flake named `sub` is in the registry)
|
||||
|
||||
* `path`: arbitrary local directories. The required attribute `path`
|
||||
* <a name="path-fetcher"></a>`path`: arbitrary local directories. The required attribute `path`
|
||||
specifies the path of the flake. The URL form is
|
||||
|
||||
```
|
||||
|
@ -200,18 +200,38 @@ Currently the `type` attribute can be one of the following:
|
|||
If the flake at *path* is not inside a git repository, the `path:`
|
||||
prefix is implied and can be omitted.
|
||||
|
||||
*path* generally must be an absolute path. However, on the command
|
||||
line, it can be a relative path (e.g. `.` or `./foo`) which is
|
||||
interpreted as relative to the current directory. In this case, it
|
||||
must start with `.` to avoid ambiguity with registry lookups
|
||||
(e.g. `nixpkgs` is a registry lookup; `./nixpkgs` is a relative
|
||||
path).
|
||||
If *path* is a relative path (i.e. if it does not start with `/`),
|
||||
it is interpreted as follows:
|
||||
|
||||
- If *path* is a command line argument, it is interpreted relative
|
||||
to the current directory.
|
||||
|
||||
- If *path* is used in a `flake.nix`, it is interpreted relative to
|
||||
the directory containing that `flake.nix`. However, the resolved
|
||||
path must be in the same tree. For instance, a `flake.nix` in the
|
||||
root of a tree can use `path:./foo` to access the flake in
|
||||
subdirectory `foo`, but `path:../bar` is illegal. On the other
|
||||
hand, a flake in the `/foo` directory of a tree can use
|
||||
`path:../bar` to refer to the flake in `/bar`.
|
||||
|
||||
Path inputs can be specified with path values in `flake.nix`. Path values are a syntax for `path` inputs, and they are converted by
|
||||
1. resolving them into relative paths, relative to the base directory of `flake.nix`
|
||||
2. escaping URL characters (refer to IETF RFC?)
|
||||
3. prepending `path:`
|
||||
|
||||
Note that the allowed syntax for path values in flake `inputs` may be more restrictive than general Nix, so you may need to use `path:` if your path contains certain special characters. See [Path literals](@docroot@/language/syntax.md#path-literal)
|
||||
|
||||
Note that if you omit `path:`, relative paths must start with `.` to
|
||||
avoid ambiguity with registry lookups (e.g. `nixpkgs` is a registry
|
||||
lookup; `./nixpkgs` is a relative path).
|
||||
|
||||
For example, these are valid path flake references:
|
||||
|
||||
* `path:/home/user/sub/dir`
|
||||
* `/home/user/sub/dir` (if `dir/flake.nix` is *not* in a git repository)
|
||||
* `./sub/dir` (when used on the command line and `dir/flake.nix` is *not* in a git repository)
|
||||
* `path:sub/dir`
|
||||
* `./sub/dir`
|
||||
* `path:../parent`
|
||||
|
||||
* `git`: Git repositories. The location of the repository is specified
|
||||
by the attribute `url`.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonExecutable
|
||||
|
||||
, nix-store
|
||||
|
@ -99,10 +98,6 @@ mkMesonExecutable (finalAttrs: {
|
|||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ using namespace nix;
|
|||
|
||||
struct CmdUpgradeNix : MixDryRun, StoreCommand
|
||||
{
|
||||
Path profileDir;
|
||||
std::filesystem::path profileDir;
|
||||
|
||||
CmdUpgradeNix()
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
|||
if (profileDir == "")
|
||||
profileDir = getProfileDir(store);
|
||||
|
||||
printInfo("upgrading Nix in profile '%s'", profileDir);
|
||||
printInfo("upgrading Nix in profile %s", profileDir);
|
||||
|
||||
auto storePath = getLatestNix(store);
|
||||
|
||||
|
@ -93,40 +93,44 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
|||
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown,
|
||||
fmt("installing '%s' into profile '%s'...", store->printStorePath(storePath), profileDir));
|
||||
fmt("installing '%s' into profile %s...", store->printStorePath(storePath), profileDir));
|
||||
|
||||
// FIXME: don't call an external process.
|
||||
runProgram(getNixBin("nix-env").string(), false,
|
||||
{"--profile", profileDir, "-i", store->printStorePath(storePath), "--no-sandbox"});
|
||||
{"--profile", profileDir.string(), "-i", store->printStorePath(storePath), "--no-sandbox"});
|
||||
}
|
||||
|
||||
printInfo(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version);
|
||||
}
|
||||
|
||||
/* Return the profile in which Nix is installed. */
|
||||
Path getProfileDir(ref<Store> store)
|
||||
std::filesystem::path getProfileDir(ref<Store> store)
|
||||
{
|
||||
auto whereOpt = ExecutablePath::load().findName(OS_STR("nix-env"));
|
||||
if (!whereOpt)
|
||||
throw Error("couldn't figure out how Nix is installed, so I can't upgrade it");
|
||||
const auto & where = whereOpt->parent_path();
|
||||
|
||||
printInfo("found Nix in '%s'", where);
|
||||
printInfo("found Nix in %s", where);
|
||||
|
||||
if (hasPrefix(where.string(), "/run/current-system"))
|
||||
throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'");
|
||||
|
||||
Path profileDir = where.parent_path().string();
|
||||
auto profileDir = where.parent_path();
|
||||
|
||||
// Resolve profile to /nix/var/nix/profiles/<name> link.
|
||||
while (canonPath(profileDir).find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir))
|
||||
profileDir = readLink(profileDir);
|
||||
while (canonPath(profileDir.string()).find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir))
|
||||
profileDir = readLink(profileDir.string());
|
||||
|
||||
printInfo("found profile '%s'", profileDir);
|
||||
printInfo("found profile %s", profileDir);
|
||||
|
||||
Path userEnv = canonPath(profileDir, true);
|
||||
Path userEnv = canonPath(profileDir.string(), true);
|
||||
|
||||
if (where.filename() != "bin" ||
|
||||
!hasSuffix(userEnv, "user-environment"))
|
||||
throw Error("directory %s does not appear to be part of a Nix profile", where);
|
||||
if (std::filesystem::exists(profileDir / "manifest.json"))
|
||||
throw Error("directory %s is managed by 'nix profile' and currently cannot be upgraded by 'nix upgrade-nix'", profileDir);
|
||||
|
||||
if (!std::filesystem::exists(profileDir / "manifest.nix"))
|
||||
throw Error("directory %s does not appear to be part of a Nix profile", profileDir);
|
||||
|
||||
if (!store->isValidPath(store->parseStorePath(userEnv)))
|
||||
throw Error("directory '%s' is not in the Nix store", userEnv);
|
||||
|
|
|
@ -82,6 +82,9 @@ nix_store_dep = dependency('nix-store')
|
|||
# pkgconfig available, are not in a standard location,
|
||||
# and are installed into a version folder. Use the
|
||||
# Perl binary to give hints about perl include dir.
|
||||
#
|
||||
# Note that until we have a better solution for this, cross-compiling
|
||||
# the perl bindings does not appear to be possible.
|
||||
#-------------------------------------------------
|
||||
perl_archname = run_command(
|
||||
perl, '-e', 'use Config; print $Config{archname};', check: true).stdout()
|
||||
|
|
|
@ -76,3 +76,21 @@ git -C "$rootRepo" commit -m "Add flake.nix"
|
|||
|
||||
storePath=$(nix flake metadata --json "$rootRepo?submodules=1" | jq -r .path)
|
||||
[[ -e "$storePath/submodule" ]]
|
||||
|
||||
# The root repo may use the submodule repo as an input
|
||||
# through the relative path. This may change in the future;
|
||||
# see: https://discourse.nixos.org/t/57783 and #9708.
|
||||
cat > "$rootRepo"/flake.nix <<EOF
|
||||
{
|
||||
inputs.subRepo.url = "git+file:./submodule";
|
||||
outputs = { ... }: { };
|
||||
}
|
||||
EOF
|
||||
git -C "$rootRepo" add flake.nix
|
||||
git -C "$rootRepo" commit -m "Add subRepo input"
|
||||
(
|
||||
cd "$rootRepo"
|
||||
# The submodule must be locked to the relative path,
|
||||
# _not_ the absolute path:
|
||||
[[ $(nix flake metadata --json | jq -r .locks.nodes.subRepo.locked.url) = "file:./submodule" ]]
|
||||
)
|
||||
|
|
|
@ -97,6 +97,9 @@ nix build -o "$TEST_ROOT/result" flake1
|
|||
|
||||
nix build -o "$TEST_ROOT/result" "$flake1Dir"
|
||||
nix build -o "$TEST_ROOT/result" "git+file://$flake1Dir"
|
||||
(cd "$flake1Dir" && nix build -o "$TEST_ROOT/result" ".")
|
||||
(cd "$flake1Dir" && nix build -o "$TEST_ROOT/result" "path:.")
|
||||
(cd "$flake1Dir" && nix build -o "$TEST_ROOT/result" "git+file:.")
|
||||
|
||||
# Test explicit packages.default.
|
||||
nix build -o "$TEST_ROOT/result" "$flake1Dir#default"
|
||||
|
@ -106,6 +109,15 @@ nix build -o "$TEST_ROOT/result" "git+file://$flake1Dir#default"
|
|||
nix build -o "$TEST_ROOT/result" "$flake1Dir?ref=HEAD#default"
|
||||
nix build -o "$TEST_ROOT/result" "git+file://$flake1Dir?ref=HEAD#default"
|
||||
|
||||
# Check that relative paths are allowed for git flakes.
|
||||
# This may change in the future once git submodule support is refined.
|
||||
# See: https://discourse.nixos.org/t/57783 and #9708.
|
||||
(
|
||||
# This `cd` should not be required and is indicative of aforementioned bug.
|
||||
cd "$flake1Dir/.."
|
||||
nix build -o "$TEST_ROOT/result" "git+file:./$(basename "$flake1Dir")"
|
||||
)
|
||||
|
||||
# Check that store symlinks inside a flake are not interpreted as flakes.
|
||||
nix build -o "$flake1Dir/result" "git+file://$flake1Dir"
|
||||
nix path-info "$flake1Dir/result"
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
source ./common.sh
|
||||
|
||||
# FIXME: this test is disabled because relative path flakes are broken. Re-enable this in #10089.
|
||||
exit 0
|
||||
|
||||
requireGit
|
||||
|
||||
flakeFollowsA=$TEST_ROOT/follows/flakeA
|
||||
|
@ -120,7 +117,7 @@ nix flake lock $flakeFollowsA
|
|||
[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]]
|
||||
jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$"
|
||||
|
||||
# Ensure a relative path is not allowed to go outside the store path
|
||||
# Check that path: inputs cannot escape from their root.
|
||||
cat > $flakeFollowsA/flake.nix <<EOF
|
||||
{
|
||||
description = "Flake A";
|
||||
|
@ -133,7 +130,28 @@ EOF
|
|||
|
||||
git -C $flakeFollowsA add flake.nix
|
||||
|
||||
expect 1 nix flake lock $flakeFollowsA 2>&1 | grep 'points outside'
|
||||
expect 1 nix flake lock $flakeFollowsA 2>&1 | grep '/flakeB.*is forbidden in pure evaluation mode'
|
||||
expect 1 nix flake lock --impure $flakeFollowsA 2>&1 | grep '/flakeB.*does not exist'
|
||||
|
||||
# Test relative non-flake inputs.
|
||||
cat > $flakeFollowsA/flake.nix <<EOF
|
||||
{
|
||||
description = "Flake A";
|
||||
inputs = {
|
||||
E.flake = false;
|
||||
E.url = "./foo.nix"; # test relative paths without 'path:'
|
||||
};
|
||||
outputs = { E, ... }: { e = import E; };
|
||||
}
|
||||
EOF
|
||||
|
||||
echo 123 > $flakeFollowsA/foo.nix
|
||||
|
||||
git -C $flakeFollowsA add flake.nix foo.nix
|
||||
|
||||
nix flake lock $flakeFollowsA
|
||||
|
||||
[[ $(nix eval --json $flakeFollowsA#e) = 123 ]]
|
||||
|
||||
# Non-existant follows should print a warning.
|
||||
cat >$flakeFollowsA/flake.nix <<EOF
|
||||
|
@ -338,6 +356,6 @@ json=$(nix flake metadata "$flakeFollowsCustomUrlA" --json)
|
|||
rm "$flakeFollowsCustomUrlA"/flake.lock
|
||||
|
||||
# if override-input is specified, lock "original" entry should contain original url
|
||||
json=$(nix flake metadata "$flakeFollowsCustomUrlA" --override-input B/C "path:./flakeB/flakeD" --json)
|
||||
json=$(nix flake metadata "$flakeFollowsCustomUrlA" --override-input B/C "$flakeFollowsCustomUrlD" --json)
|
||||
echo "$json" | jq .locks.nodes.C.original
|
||||
[[ $(echo "$json" | jq -r .locks.nodes.C.original.path) = './flakeC' ]]
|
||||
|
|
|
@ -27,6 +27,7 @@ suites += {
|
|||
'shebang.sh',
|
||||
'commit-lock-file-summary.sh',
|
||||
'non-flake-inputs.sh',
|
||||
'relative-paths.sh',
|
||||
],
|
||||
'workdir': meson.current_source_dir(),
|
||||
}
|
||||
|
|
97
tests/functional/flakes/relative-paths.sh
Normal file
97
tests/functional/flakes/relative-paths.sh
Normal file
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ./common.sh
|
||||
|
||||
requireGit
|
||||
|
||||
rootFlake="$TEST_ROOT/flake1"
|
||||
subflake0="$rootFlake/sub0"
|
||||
subflake1="$rootFlake/sub1"
|
||||
subflake2="$rootFlake/sub2"
|
||||
|
||||
rm -rf "$rootFlake"
|
||||
mkdir -p "$rootFlake" "$subflake0" "$subflake1" "$subflake2"
|
||||
|
||||
cat > "$rootFlake/flake.nix" <<EOF
|
||||
{
|
||||
inputs.sub0.url = ./sub0;
|
||||
outputs = { self, sub0 }: {
|
||||
x = 2;
|
||||
y = self.x * sub0.x;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "$subflake0/flake.nix" <<EOF
|
||||
{
|
||||
outputs = { self }: {
|
||||
x = 7;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
[[ $(nix eval "$rootFlake#x") = 2 ]]
|
||||
[[ $(nix eval "$rootFlake#y") = 14 ]]
|
||||
|
||||
cat > "$subflake1/flake.nix" <<EOF
|
||||
{
|
||||
inputs.root.url = "../";
|
||||
outputs = { self, root }: {
|
||||
x = 3;
|
||||
y = self.x * root.x;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
[[ $(nix eval "$rootFlake?dir=sub1#y") = 6 ]]
|
||||
|
||||
git init "$rootFlake"
|
||||
git -C "$rootFlake" add flake.nix sub0/flake.nix sub1/flake.nix
|
||||
|
||||
[[ $(nix eval "$subflake1#y") = 6 ]]
|
||||
|
||||
cat > "$subflake2/flake.nix" <<EOF
|
||||
{
|
||||
inputs.root.url = ./..;
|
||||
inputs.sub1.url = "../sub1";
|
||||
outputs = { self, root, sub1 }: {
|
||||
x = 5;
|
||||
y = self.x * sub1.x;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
git -C "$rootFlake" add flake.nix sub2/flake.nix
|
||||
|
||||
[[ $(nix eval "$subflake2#y") = 15 ]]
|
||||
|
||||
# Make sure that this still works after commiting the lock file.
|
||||
git -C "$rootFlake" add sub2/flake.lock
|
||||
[[ $(nix eval "$subflake2#y") = 15 ]]
|
||||
|
||||
# Make sure there are no content locks for relative path flakes.
|
||||
(! grep "$TEST_ROOT" "$subflake2/flake.lock")
|
||||
if ! isTestOnNixOS; then
|
||||
(! grep "$NIX_STORE_DIR" "$subflake2/flake.lock")
|
||||
fi
|
||||
(! grep narHash "$subflake2/flake.lock")
|
||||
|
||||
# Test circular relative path flakes. FIXME: doesn't work at the moment.
|
||||
if false; then
|
||||
|
||||
cat > "$rootFlake/flake.nix" <<EOF
|
||||
{
|
||||
inputs.sub1.url = "./sub1";
|
||||
inputs.sub2.url = "./sub1";
|
||||
outputs = { self, sub1, sub2 }: {
|
||||
x = 2;
|
||||
y = self.x * sub1.x * sub2.x;
|
||||
z = sub1.y * sub2.y;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
[[ $(nix eval "$rootFlake#x") = 30 ]]
|
||||
[[ $(nix eval "$rootFlake#z") = 90 ]]
|
||||
|
||||
fi
|
|
@ -31,5 +31,14 @@ echo 456 > "$flake1Dir"/x.nix
|
|||
|
||||
[[ $(nix eval --json "$flake2Dir#x" --override-input flake1 "$TEST_ROOT/flake1") = 456 ]]
|
||||
|
||||
# Dirty overrides require --allow-dirty-locks.
|
||||
expectStderr 1 nix flake lock "$flake2Dir" --override-input flake1 "$TEST_ROOT/flake1" |
|
||||
grepQuiet "cannot write lock file.*because it has an unlocked input"
|
||||
grepQuiet "Will not write lock file.*because it has an unlocked input"
|
||||
|
||||
nix flake lock "$flake2Dir" --override-input flake1 "$TEST_ROOT/flake1" --allow-dirty-locks
|
||||
|
||||
# Using a lock file with a dirty lock requires --allow-dirty-locks as well.
|
||||
expectStderr 1 nix eval "$flake2Dir#x" |
|
||||
grepQuiet "Lock file contains unlocked input"
|
||||
|
||||
[[ $(nix eval "$flake2Dir#x" --allow-dirty-locks) = 456 ]]
|
||||
|
|
|
@ -173,13 +173,21 @@ nix-env -q '*' | grepQuiet bar-0.1.1
|
|||
|
||||
# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it
|
||||
# should be possible to install both without a collision. Also test
|
||||
# ‘--set-flag priority’ to manually override the declared priorities.
|
||||
# '-i --priority' and '--set-flag priority' to manually override the
|
||||
# declared priorities.
|
||||
nix-env -e '*'
|
||||
nix-env -i foo-0.1 foo-1.0
|
||||
[ "$($profiles/test/bin/foo)" = "foo-1.0" ]
|
||||
nix-env --set-flag priority 1 foo-0.1
|
||||
[ "$($profiles/test/bin/foo)" = "foo-0.1" ]
|
||||
|
||||
# Priorities can be overridden with the --priority flag
|
||||
nix-env -e '*'
|
||||
nix-env -i foo-1.0
|
||||
[ "$($profiles/test/bin/foo)" = "foo-1.0" ]
|
||||
nix-env -i --priority 1 foo-0.1
|
||||
[ "$($profiles/test/bin/foo)" = "foo-0.1" ]
|
||||
|
||||
# Test nix-env --set.
|
||||
nix-env --set $outPath10
|
||||
[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ]
|
||||
|
|
|
@ -39,11 +39,14 @@
|
|||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the builders.
|
||||
client.wait_for_unit("network.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
|
||||
remote.succeed("mkdir -p -m 700 /root/.ssh")
|
||||
remote.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
remote.wait_for_unit("sshd")
|
||||
remote.wait_for_unit("multi-user.target")
|
||||
remote.wait_for_unit("network-online.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.succeed(f"ssh -o StrictHostKeyChecking=no {remote.name} 'echo hello world'")
|
||||
|
||||
remote.succeed("""
|
||||
|
|
|
@ -161,7 +161,9 @@ in
|
|||
github.succeed("cat /var/log/httpd/*.log >&2")
|
||||
|
||||
github.wait_for_unit("httpd.service")
|
||||
github.wait_for_unit("network-online.target")
|
||||
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.succeed("curl -v https://github.com/ >&2")
|
||||
out = client.succeed("nix registry list")
|
||||
print(out)
|
||||
|
|
|
@ -48,7 +48,10 @@ in {
|
|||
server.succeed("mkdir -m 700 /root/.ssh")
|
||||
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
server.wait_for_unit("sshd")
|
||||
client.wait_for_unit("network.target")
|
||||
server.wait_for_unit("multi-user.target")
|
||||
server.wait_for_unit("network-online.target")
|
||||
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'")
|
||||
|
||||
# Copy the closure of package A from the client to the server.
|
||||
|
|
|
@ -56,7 +56,10 @@ in {
|
|||
start_all()
|
||||
|
||||
server.wait_for_unit("sshd")
|
||||
client.wait_for_unit("network.target")
|
||||
server.wait_for_unit("multi-user.target")
|
||||
server.wait_for_unit("network-online.target")
|
||||
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.wait_for_unit("getty@tty1.service")
|
||||
# Either the prompt: ]#
|
||||
# or an OCR misreading of it: 1#
|
||||
|
|
|
@ -37,6 +37,7 @@ in {
|
|||
|
||||
testScript = { nodes }: ''
|
||||
cache.wait_for_unit("harmonia.service")
|
||||
cache.wait_for_unit("network-online.target")
|
||||
|
||||
machine.succeed("mkdir -p /etc/containers")
|
||||
machine.succeed("""echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json""")
|
||||
|
|
|
@ -102,6 +102,7 @@ in
|
|||
};
|
||||
|
||||
testScript = { nodes, ... }: ''
|
||||
http_dns.wait_for_unit("network-online.target")
|
||||
http_dns.wait_for_unit("nginx")
|
||||
http_dns.wait_for_open_port(80)
|
||||
http_dns.wait_for_unit("unbound")
|
||||
|
@ -109,6 +110,7 @@ in
|
|||
|
||||
client.start()
|
||||
client.wait_for_unit('multi-user.target')
|
||||
client.wait_for_unit('network-online.target')
|
||||
|
||||
with subtest("can fetch data from a remote server outside sandbox"):
|
||||
client.succeed("nix --version >&2")
|
||||
|
|
|
@ -89,10 +89,13 @@ in
|
|||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the builder.
|
||||
client.wait_for_unit("network.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
builder.succeed("mkdir -p -m 700 /root/.ssh")
|
||||
builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
builder.wait_for_unit("sshd")
|
||||
builder.wait_for_unit("multi-user.target")
|
||||
builder.wait_for_unit("network-online.target")
|
||||
|
||||
client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'")
|
||||
|
||||
# Perform a build
|
||||
|
|
|
@ -112,11 +112,12 @@ in
|
|||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the builders.
|
||||
client.wait_for_unit("network.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
for builder in [builder1, builder2]:
|
||||
builder.succeed("mkdir -p -m 700 /root/.ssh")
|
||||
builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
builder.wait_for_unit("sshd")
|
||||
builder.wait_for_unit("network-online.target")
|
||||
# Make sure the builder can handle our login correctly
|
||||
builder.wait_for_unit("multi-user.target")
|
||||
# Make sure there's no funny business on the client either
|
||||
|
|
|
@ -52,12 +52,15 @@ in {
|
|||
|
||||
# Create a binary cache.
|
||||
server.wait_for_unit("minio")
|
||||
server.wait_for_unit("network-online.target")
|
||||
|
||||
server.succeed("mc config host add minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4")
|
||||
server.succeed("mc mb minio/my-cache")
|
||||
|
||||
server.succeed("${env} nix copy --to '${storeUrl}' ${pkgA}")
|
||||
|
||||
client.wait_for_unit("network-online.target")
|
||||
|
||||
# Test fetchurl on s3:// URLs while we're at it.
|
||||
client.succeed("${env} nix eval --impure --expr 'builtins.fetchurl { name = \"foo\"; url = \"s3://my-cache/nix-cache-info?endpoint=http://server:9000®ion=eu-west-1\"; }'")
|
||||
|
||||
|
|
|
@ -122,6 +122,8 @@ in
|
|||
start_all()
|
||||
|
||||
sourcehut.wait_for_unit("httpd.service")
|
||||
sourcehut.wait_for_unit("network-online.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
|
||||
client.succeed("curl -v https://git.sr.ht/ >&2")
|
||||
client.succeed("nix registry list | grep nixpkgs")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue