1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-23 18:59:35 +01:00

Merge commit '6636202356' into progress-bar

This commit is contained in:
John Ericson 2023-03-11 17:04:22 -05:00
commit 5109b5e467
81 changed files with 870 additions and 590 deletions

View file

@ -14,9 +14,27 @@ if [ -t 1 ]; then
yellow="" yellow=""
normal="" normal=""
fi fi
(cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)" run_test () {
status=$? (cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
status=$?
}
run_test "$1"
# Hack: Retry the test if it fails with “unexpected EOF reading a line” as these
# appear randomly without anyone knowing why.
# See https://github.com/NixOS/nix/issues/3605 for more info
if [[ $status -ne 0 && $status -ne 99 && \
"$(uname)" == "Darwin" && \
"$log" =~ "unexpected EOF reading a line" \
]]; then
echo "$post_run_msg [${yellow}FAIL$normal] (possibly flaky, so will be retried)"
echo "$log" | sed 's/^/ /'
run_test "$1"
fi
if [ $status -eq 0 ]; then if [ $status -eq 0 ]; then
echo "$post_run_msg [${green}PASS$normal]" echo "$post_run_msg [${green}PASS$normal]"
elif [ $status -eq 99 ]; then elif [ $status -eq 99 ]; then

View file

@ -246,7 +246,8 @@ get_volume_pass() {
verify_volume_pass() { verify_volume_pass() {
local volume_special="$1" # (i.e., disk1s7) local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2" local volume_uuid="$2"
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid" _sudo "to confirm the password actually unlocks the volume" \
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
} }
volume_pass_works() { volume_pass_works() {
@ -685,22 +686,27 @@ encrypt_volume() {
local volume_uuid="$1" local volume_uuid="$1"
local volume_label="$2" local volume_label="$2"
local password local password
task "Encrypt the Nix volume" >&2
# Note: mount/unmount are late additions to support the right order # Note: mount/unmount are late additions to support the right order
# of operations for creating the volume and then baking its uuid into # of operations for creating the volume and then baking its uuid into
# other artifacts; not as well-trod wrt to potential errors, race # other artifacts; not as well-trod wrt to potential errors, race
# conditions, etc. # conditions, etc.
/usr/sbin/diskutil mount "$volume_label" _sudo "to mount your Nix volume for encrypting" \
/usr/sbin/diskutil mount "$volume_label"
password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)" password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)"
_sudo "to add your Nix volume's password to Keychain" \ _sudo "to add your Nix volume's password to Keychain" \
/usr/bin/security -i <<EOF /usr/bin/security -i <<EOF
add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain" add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain"
EOF EOF
builtin printf "%s" "$password" | _sudo "to encrypt your Nix volume" \ builtin printf "%s" "$password" | _sudo "to actually encrypt your Nix volume" \
/usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase /usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase
/usr/sbin/diskutil unmount force "$volume_label" _sudo "to unmount the encrypted volume" \
/usr/sbin/diskutil unmount force "$volume_label"
} }
create_volume() { create_volume() {

View file

@ -23,10 +23,10 @@ readonly RED='\033[31m'
# installer allows overriding build user count to speed up installation # installer allows overriding build user count to speed up installation
# as creating each user takes non-trivial amount of time on macos # as creating each user takes non-trivial amount of time on macos
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32} readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
readonly NIX_BUILD_GROUP_ID="30000" readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
readonly NIX_BUILD_GROUP_NAME="nixbld" readonly NIX_BUILD_GROUP_NAME="nixbld"
# darwin installer needs to override these # darwin installer needs to override these
NIX_FIRST_BUILD_UID="30001" NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d" NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
# Please don't change this. We don't support it, because the # Please don't change this. We don't support it, because the
# default shell profile that comes with Nix doesn't support it. # default shell profile that comes with Nix doesn't support it.

View file

@ -14,6 +14,7 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "globals.hh" #include "globals.hh"
#include "serialise.hh" #include "serialise.hh"
#include "build-result.hh"
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "local-store.hh" #include "local-store.hh"

View file

@ -153,7 +153,7 @@ void BuiltPathsCommand::run(ref<Store> store)
for (auto & p : store->queryAllValidPaths()) for (auto & p : store->queryAllValidPaths())
paths.push_back(BuiltPath::Opaque{p}); paths.push_back(BuiltPath::Opaque{p});
} else { } else {
paths = toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables); paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) { if (recursive) {
// XXX: This only computes the store path closure, ignoring // XXX: This only computes the store path closure, ignoring
// intermediate realisations // intermediate realisations

View file

@ -5,7 +5,6 @@
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "path.hh" #include "path.hh"
#include "flake/lockfile.hh" #include "flake/lockfile.hh"
#include "store-api.hh"
#include <optional> #include <optional>
@ -82,14 +81,6 @@ struct MixFlakeOptions : virtual Args, EvalCommand
{ return {}; } { return {}; }
}; };
/* How to handle derivations in commands that operate on store paths. */
enum class OperateOn {
/* Operate on the output path. */
Output,
/* Operate on the .drv path. */
Derivation
};
struct SourceExprCommand : virtual Args, MixFlakeOptions struct SourceExprCommand : virtual Args, MixFlakeOptions
{ {
std::optional<Path> file; std::optional<Path> file;
@ -113,19 +104,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
void completeInstallable(std::string_view prefix); void completeInstallable(std::string_view prefix);
}; };
enum class Realise {
/* Build the derivation. Postcondition: the
derivation outputs exist. */
Outputs,
/* Don't build the derivation. Postcondition: the store derivation
exists. */
Derivation,
/* Evaluate in dry-run mode. Postcondition: nothing. */
// FIXME: currently unused, but could be revived if we can
// evaluate derivations in-memory.
Nothing
};
/* A command that operates on a list of "installables", which can be /* A command that operates on a list of "installables", which can be
store paths, attribute paths, Nix expressions, etc. */ store paths, attribute paths, Nix expressions, etc. */
struct InstallablesCommand : virtual Args, SourceExprCommand struct InstallablesCommand : virtual Args, SourceExprCommand
@ -238,38 +216,6 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); }); return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
} }
BuiltPaths build(
ref<Store> evalStore,
ref<Store> store, Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
std::set<StorePath> toStorePaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
StorePath toStorePath(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
std::shared_ptr<Installable> installable);
std::set<StorePath> toDerivations(
ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver = false);
BuiltPaths toBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
/* Helper function to generate args that invoke $EDITOR on /* Helper function to generate args that invoke $EDITOR on
filename:lineno. */ filename:lineno. */
Strings editorFor(const Pos & pos); Strings editorFor(const Pos & pos);

View file

@ -468,11 +468,10 @@ std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations
std::vector<DerivationInfo> res; std::vector<DerivationInfo> res;
for (auto & drvInfo : drvInfos) { for (auto & drvInfo : drvInfos) {
res.push_back({ auto drvPath = drvInfo.queryDrvPath();
state->store->parseStorePath(drvInfo.queryDrvPath()), if (!drvPath)
state->store->maybeParseStorePath(drvInfo.queryOutPath()), throw Error("'%s' is not a derivation", what());
drvInfo.queryOutputName() res.push_back({ *drvPath, drvInfo.queryOutputName() });
});
} }
return res; return res;
@ -586,9 +585,8 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
auto drvPath = attr->forceDerivation(); auto drvPath = attr->forceDerivation();
auto drvInfo = DerivationInfo{ auto drvInfo = DerivationInfo {
std::move(drvPath), std::move(drvPath),
state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()),
attr->getAttr(state->sOutputName)->getString() attr->getAttr(state->sOutputName)->getString()
}; };
@ -789,7 +787,7 @@ BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPa
return res; return res;
} }
BuiltPaths build( BuiltPaths Installable::build(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
@ -814,7 +812,7 @@ BuiltPaths build(
return getBuiltPaths(evalStore, store, pathsToBuild); return getBuiltPaths(evalStore, store, pathsToBuild);
} }
BuiltPaths toBuiltPaths( BuiltPaths Installable::toBuiltPaths(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
@ -822,19 +820,19 @@ BuiltPaths toBuiltPaths(
const std::vector<std::shared_ptr<Installable>> & installables) const std::vector<std::shared_ptr<Installable>> & installables)
{ {
if (operateOn == OperateOn::Output) if (operateOn == OperateOn::Output)
return build(evalStore, store, mode, installables); return Installable::build(evalStore, store, mode, installables);
else { else {
if (mode == Realise::Nothing) if (mode == Realise::Nothing)
settings.readOnlyMode = true; settings.readOnlyMode = true;
BuiltPaths res; BuiltPaths res;
for (auto & drvPath : toDerivations(store, installables, true)) for (auto & drvPath : Installable::toDerivations(store, installables, true))
res.push_back(BuiltPath::Opaque{drvPath}); res.push_back(BuiltPath::Opaque{drvPath});
return res; return res;
} }
} }
StorePathSet toStorePaths( StorePathSet Installable::toStorePaths(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, OperateOn operateOn, Realise mode, OperateOn operateOn,
@ -848,7 +846,7 @@ StorePathSet toStorePaths(
return outPaths; return outPaths;
} }
StorePath toStorePath( StorePath Installable::toStorePath(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, OperateOn operateOn, Realise mode, OperateOn operateOn,
@ -862,7 +860,7 @@ StorePath toStorePath(
return *paths.begin(); return *paths.begin();
} }
StorePathSet toDerivations( StorePathSet Installable::toDerivations(
ref<Store> store, ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables, const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver) bool useDeriver)

View file

@ -5,6 +5,7 @@
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "derived-path.hh" #include "derived-path.hh"
#include "eval.hh" #include "eval.hh"
#include "store-api.hh"
#include "flake/flake.hh" #include "flake/flake.hh"
#include <optional> #include <optional>
@ -29,6 +30,27 @@ struct UnresolvedApp
App resolve(ref<Store> evalStore, ref<Store> store); App resolve(ref<Store> evalStore, ref<Store> store);
}; };
enum class Realise {
/* Build the derivation. Postcondition: the
derivation outputs exist. */
Outputs,
/* Don't build the derivation. Postcondition: the store derivation
exists. */
Derivation,
/* Evaluate in dry-run mode. Postcondition: nothing. */
// FIXME: currently unused, but could be revived if we can
// evaluate derivations in-memory.
Nothing
};
/* How to handle derivations in commands that operate on store paths. */
enum class OperateOn {
/* Operate on the output path. */
Output,
/* Operate on the .drv path. */
Derivation
};
struct Installable struct Installable
{ {
virtual ~Installable() { } virtual ~Installable() { }
@ -68,6 +90,39 @@ struct Installable
{ {
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
} }
static BuiltPaths build(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
static std::set<StorePath> toStorePaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
static StorePath toStorePath(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
std::shared_ptr<Installable> installable);
static std::set<StorePath> toDerivations(
ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver = false);
static BuiltPaths toBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
}; };
struct InstallableValue : Installable struct InstallableValue : Installable
@ -79,7 +134,6 @@ struct InstallableValue : Installable
struct DerivationInfo struct DerivationInfo
{ {
StorePath drvPath; StorePath drvPath;
std::optional<StorePath> outPath;
std::string outputName; std::string outputName;
}; };
@ -131,4 +185,9 @@ ref<eval_cache::EvalCache> openEvalCache(
EvalState & state, EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake); std::shared_ptr<flake::LockedFlake> lockedFlake);
BuiltPaths getBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
const DerivedPaths & hopefullyBuiltPaths);
} }

View file

@ -86,15 +86,10 @@ RootValue allocRootValue(Value * v)
} }
void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v) void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v)
{ {
checkInterrupt(); checkInterrupt();
if (!active.insert(&v).second) {
str << "<CYCLE>";
return;
}
switch (v.internalType) { switch (v.internalType) {
case tInt: case tInt:
str << v.integer; str << v.integer;
@ -120,24 +115,32 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
str << "null"; str << "null";
break; break;
case tAttrs: { case tAttrs: {
str << "{ "; if (!v.attrs->empty() && !seen.insert(v.attrs).second)
for (auto & i : v.attrs->lexicographicOrder()) { str << "<REPEAT>";
str << i->name << " = "; else {
printValue(str, active, *i->value); str << "{ ";
str << "; "; for (auto & i : v.attrs->lexicographicOrder()) {
str << i->name << " = ";
printValue(str, seen, *i->value);
str << "; ";
}
str << "}";
} }
str << "}";
break; break;
} }
case tList1: case tList1:
case tList2: case tList2:
case tListN: case tListN:
str << "[ "; if (v.listSize() && !seen.insert(v.listElems()).second)
for (auto v2 : v.listItems()) { str << "<REPEAT>";
printValue(str, active, *v2); else {
str << " "; str << "[ ";
for (auto v2 : v.listItems()) {
printValue(str, seen, *v2);
str << " ";
}
str << "]";
} }
str << "]";
break; break;
case tThunk: case tThunk:
case tApp: case tApp:
@ -161,15 +164,13 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
default: default:
abort(); abort();
} }
active.erase(&v);
} }
std::ostream & operator << (std::ostream & str, const Value & v) std::ostream & operator << (std::ostream & str, const Value & v)
{ {
std::set<const Value *> active; std::set<const void *> seen;
printValue(str, active, v); printValue(str, seen, v);
return str; return str;
} }
@ -517,6 +518,14 @@ void EvalState::allowPath(const StorePath & storePath)
allowedPaths->insert(store->toRealPath(storePath)); allowedPaths->insert(store->toRealPath(storePath));
} }
void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
{
allowPath(storePath);
auto path = store->printStorePath(storePath);
v.mkString(path, PathSet({path}));
}
Path EvalState::checkSourcePath(const Path & path_) Path EvalState::checkSourcePath(const Path & path_)
{ {
if (!allowedPaths) return path_; if (!allowedPaths) return path_;
@ -2050,6 +2059,18 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
} }
StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
return *storePath;
throw EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = pos
});
}
bool EvalState::eqValues(Value & v1, Value & v2) bool EvalState::eqValues(Value & v1, Value & v2)
{ {
forceValue(v1, noPos); forceValue(v1, noPos);

View file

@ -161,6 +161,9 @@ public:
the real store path if `store` is a chroot store. */ the real store path if `store` is a chroot store. */
void allowPath(const StorePath & storePath); void allowPath(const StorePath & storePath);
/* Allow access to a store path and return it as a string. */
void allowAndSetStorePathString(const StorePath & storePath, Value & v);
/* Check whether access to a path is allowed and throw an error if /* Check whether access to a path is allowed and throw an error if
not. Otherwise return the canonicalised path. */ not. Otherwise return the canonicalised path. */
Path checkSourcePath(const Path & path); Path checkSourcePath(const Path & path);
@ -269,6 +272,9 @@ public:
path. Nothing is copied to the store. */ path. Nothing is copied to the store. */
Path coerceToPath(const Pos & pos, Value & v, PathSet & context); Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */
StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
public: public:
/* The base environment, containing the builtin functions and /* The base environment, containing the builtin functions and

View file

@ -1,5 +1,6 @@
#include "flake.hh" #include "flake.hh"
#include "globals.hh" #include "globals.hh"
#include "fetch-settings.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -53,7 +54,7 @@ void ConfigFile::apply()
auto trustedList = readTrustedList(); auto trustedList = readTrustedList();
bool trusted = false; bool trusted = false;
if (nix::settings.acceptFlakeConfig){ if (nix::fetchSettings.acceptFlakeConfig){
trusted = true; trusted = true;
} else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) { } else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
trusted = *saved; trusted = *saved;

View file

@ -6,6 +6,7 @@
#include "store-api.hh" #include "store-api.hh"
#include "fetchers.hh" #include "fetchers.hh"
#include "finally.hh" #include "finally.hh"
#include "fetch-settings.hh"
namespace nix { namespace nix {
@ -317,7 +318,7 @@ LockedFlake lockFlake(
FlakeCache flakeCache; FlakeCache flakeCache;
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
auto flake = getFlake(state, topRef, useRegistries, flakeCache); auto flake = getFlake(state, topRef, useRegistries, flakeCache);
@ -593,7 +594,7 @@ LockedFlake lockFlake(
if (lockFlags.writeLockFile) { if (lockFlags.writeLockFile) {
if (auto sourcePath = topRef.input.getSourcePath()) { if (auto sourcePath = topRef.input.getSourcePath()) {
if (!newLockFile.isImmutable()) { if (!newLockFile.isImmutable()) {
if (settings.warnDirty) if (fetchSettings.warnDirty)
warn("will not write lock file of flake '%s' because it has a mutable input", topRef); warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
} else { } else {
if (!lockFlags.updateLockFile) if (!lockFlags.updateLockFile)
@ -620,7 +621,7 @@ LockedFlake lockFlake(
if (lockFlags.commitLockFile) { if (lockFlags.commitLockFile) {
std::string cm; std::string cm;
cm = settings.commitLockFileSummary.get(); cm = fetchSettings.commitLockFileSummary.get();
if (cm == "") { if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
@ -718,7 +719,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
lockFlake(state, flakeRef, lockFlake(state, flakeRef,
LockFlags { LockFlags {
.updateLockFile = false, .updateLockFile = false,
.useRegistries = !evalSettings.pureEval && settings.useRegistries, .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries,
.allowMutable = !evalSettings.pureEval, .allowMutable = !evalSettings.pureEval,
}), }),
v); v);

View file

@ -98,7 +98,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
if (std::regex_match(url, match, flakeRegex)) { if (std::regex_match(url, match, flakeRegex)) {
auto parsedURL = ParsedURL{ auto parsedURL = ParsedURL{
.url = url, .url = url,
.base = "flake:" + std::string(match[1]), .base = "flake:" + match.str(1),
.scheme = "flake", .scheme = "flake",
.authority = "", .authority = "",
.path = match[1], .path = match[1],
@ -106,12 +106,12 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
return std::make_pair( return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), ""), FlakeRef(Input::fromURL(parsedURL), ""),
percentDecode(std::string(match[6]))); percentDecode(match.str(6)));
} }
else if (std::regex_match(url, match, pathUrlRegex)) { else if (std::regex_match(url, match, pathUrlRegex)) {
std::string path = match[1]; std::string path = match[1];
std::string fragment = percentDecode(std::string(match[3])); std::string fragment = percentDecode(match.str(3));
if (baseDir) { if (baseDir) {
/* Check if 'url' is a path (either absolute or relative /* Check if 'url' is a path (either absolute or relative

View file

@ -22,7 +22,7 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
{ {
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs); auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
this->drvPath = store->printStorePath(drvPath); this->drvPath = drvPath;
auto drv = store->derivationFromPath(drvPath); auto drv = store->derivationFromPath(drvPath);
@ -41,9 +41,7 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName); throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName);
auto & [outputName, output] = *i; auto & [outputName, output] = *i;
auto optStorePath = output.path(*store, drv.name, outputName); outPath = {output.path(*store, drv.name, outputName)};
if (optStorePath)
outPath = store->printStorePath(*optStorePath);
} }
@ -68,24 +66,35 @@ std::string DrvInfo::querySystem() const
} }
std::string DrvInfo::queryDrvPath() const std::optional<StorePath> DrvInfo::queryDrvPath() const
{ {
if (drvPath == "" && attrs) { if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath); Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context; PathSet context;
drvPath = i != attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : ""; if (i == attrs->end())
drvPath = {std::nullopt};
else
drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
} }
return drvPath; return drvPath.value_or(std::nullopt);
} }
std::string DrvInfo::queryOutPath() const StorePath DrvInfo::requireDrvPath() const
{
if (auto drvPath = queryDrvPath())
return *drvPath;
throw Error("derivation does not contain a 'drvPath' attribute");
}
StorePath DrvInfo::queryOutPath() const
{ {
if (!outPath && attrs) { if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath); Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context; PathSet context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToPath(*i->pos, *i->value, context); outPath = state->coerceToStorePath(*i->pos, *i->value, context);
} }
if (!outPath) if (!outPath)
throw UnimplementedError("CA derivations are not yet supported"); throw UnimplementedError("CA derivations are not yet supported");
@ -113,10 +122,10 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context; PathSet context;
outputs[name] = state->coerceToPath(*outPath->pos, *outPath->value, context); outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
} }
} else } else
outputs["out"] = queryOutPath(); outputs.emplace("out", queryOutPath());
} }
if (!onlyOutputsToInstall || !attrs) if (!onlyOutputsToInstall || !attrs)
return outputs; return outputs;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "eval.hh" #include "eval.hh"
#include "path.hh"
#include <string> #include <string>
#include <map> #include <map>
@ -12,15 +13,15 @@ namespace nix {
struct DrvInfo struct DrvInfo
{ {
public: public:
typedef std::map<std::string, Path> Outputs; typedef std::map<std::string, StorePath> Outputs;
private: private:
EvalState * state; EvalState * state;
mutable std::string name; mutable std::string name;
mutable std::string system; mutable std::string system;
mutable std::string drvPath; mutable std::optional<std::optional<StorePath>> drvPath;
mutable std::optional<std::string> outPath; mutable std::optional<StorePath> outPath;
mutable std::string outputName; mutable std::string outputName;
Outputs outputs; Outputs outputs;
@ -41,8 +42,9 @@ public:
std::string queryName() const; std::string queryName() const;
std::string querySystem() const; std::string querySystem() const;
std::string queryDrvPath() const; std::optional<StorePath> queryDrvPath() const;
std::string queryOutPath() const; StorePath requireDrvPath() const;
StorePath queryOutPath() const;
std::string queryOutputName() const; std::string queryOutputName() const;
/** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */ /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false); Outputs queryOutputs(bool onlyOutputsToInstall = false);
@ -61,8 +63,8 @@ public:
*/ */
void setName(const std::string & s) { name = s; } void setName(const std::string & s) { name = s; }
void setDrvPath(const std::string & s) { drvPath = s; } void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; }
void setOutPath(const std::string & s) { outPath = s; } void setOutPath(StorePath path) { outPath = {{std::move(path)}}; }
void setFailed() { failed = true; }; void setFailed() { failed = true; };
bool hasFailed() { return failed; }; bool hasFailed() { return failed; };

View file

@ -1919,20 +1919,15 @@ static void addPath(
if (expectedHash) if (expectedHash)
expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name); expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
Path dstPath;
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
dstPath = state.store->printStorePath(settings.readOnlyMode StorePath dstPath = settings.readOnlyMode
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first ? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs)); : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs);
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath)) if (expectedHash && expectedStorePath != dstPath)
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path); throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
state.allowAndSetStorePathString(dstPath, v);
} else } else
dstPath = state.store->printStorePath(*expectedStorePath); state.allowAndSetStorePathString(*expectedStorePath, v);
v.mkString(dstPath, {dstPath});
state.allowPath(dstPath);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, "while adding path '%s'", path); e.addTrace(pos, "while adding path '%s'", path);
throw; throw;

View file

@ -230,6 +230,21 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
if (evalSettings.pureEval && !expectedHash) if (evalSettings.pureEval && !expectedHash)
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who); throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
// early exit if pinned and already in the store
if (expectedHash && expectedHash->type == htSHA256) {
auto expectedPath =
unpack
? state.store->makeFixedOutputPath(FileIngestionMethod::Recursive, *expectedHash, name, {})
: state.store->makeFixedOutputPath(FileIngestionMethod::Flat, *expectedHash, name, {});
if (state.store->isValidPath(expectedPath)) {
state.allowAndSetStorePathString(expectedPath, v);
return;
}
}
// TODO: fetching may fail, yet the path may be substitutable.
// https://github.com/NixOS/nix/issues/4313
auto storePath = auto storePath =
unpack unpack
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
@ -244,10 +259,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true)); *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
} }
state.allowPath(storePath); state.allowAndSetStorePathString(storePath, v);
auto path = state.store->printStorePath(storePath);
v.mkString(path, PathSet({path}));
} }
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)

View file

@ -114,8 +114,8 @@ struct Value
private: private:
InternalType internalType; InternalType internalType;
friend std::string showType(const Value & v); friend std::string showType(const Value & v);
friend void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v); friend void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v);
public: public:

View file

@ -0,0 +1,13 @@
#include "fetch-settings.hh"
namespace nix {
FetchSettings::FetchSettings()
{
}
FetchSettings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings);
}

View file

@ -0,0 +1,93 @@
#pragma once
#include "types.hh"
#include "config.hh"
#include "util.hh"
#include <map>
#include <limits>
#include <sys/types.h>
namespace nix {
struct FetchSettings : public Config
{
FetchSettings();
Setting<StringMap> accessTokens{this, {}, "access-tokens",
R"(
Access tokens used to access protected GitHub, GitLab, or
other locations requiring token-based authentication.
Access tokens are specified as a string made up of
space-separated `host=token` values. The specific token
used is selected by matching the `host` portion against the
"host" specification of the input. The actual use of the
`token` value is determined by the type of resource being
accessed:
* Github: the token value is the OAUTH-TOKEN string obtained
as the Personal Access Token from the Github server (see
https://docs.github.com/en/developers/apps/authorizing-oath-apps).
* Gitlab: the token value is either the OAuth2 token or the
Personal Access Token (these are different types tokens
for gitlab, see
https://docs.gitlab.com/12.10/ee/api/README.html#authentication).
The `token` value should be `type:tokenstring` where
`type` is either `OAuth2` or `PAT` to indicate which type
of token is being specified.
Example `~/.config/nix/nix.conf`:
```
access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk
```
Example `~/code/flake.nix`:
```nix
input.foo = {
type = "gitlab";
host = "gitlab.mycompany.com";
owner = "mycompany";
repo = "pro";
};
```
This example specifies three tokens, one each for accessing
github.com, gitlab.mycompany.com, and sourceforge.net.
The `input.foo` uses the "gitlab" fetcher, which might
requires specifying the token type along with the token
value.
)"};
Setting<bool> allowDirty{this, true, "allow-dirty",
"Whether to allow dirty Git/Mercurial trees."};
Setting<bool> warnDirty{this, true, "warn-dirty",
"Whether to warn about dirty Git/Mercurial trees."};
Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
"Path or URI of the global flake registry."};
Setting<bool> useRegistries{this, true, "use-registries",
"Whether to use flake registries to resolve flake references."};
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
"Whether to accept nix configuration from a flake without prompting."};
Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary",
R"(
The commit summary to use when committing changed flake lock files. If
empty, the summary is generated based on the action performed.
)"};
};
// FIXME: don't use a global variable.
extern FetchSettings fetchSettings;
}

View file

@ -6,6 +6,8 @@
#include "url-parts.hh" #include "url-parts.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#include "fetch-settings.hh"
#include <sys/time.h> #include <sys/time.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -246,10 +248,10 @@ struct GitInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked files. */ /* This is an unclean working tree. So copy all tracked files. */
if (!settings.allowDirty) if (!fetchSettings.allowDirty)
throw Error("Git tree '%s' is dirty", actualUrl); throw Error("Git tree '%s' is dirty", actualUrl);
if (settings.warnDirty) if (fetchSettings.warnDirty)
warn("Git tree '%s' is dirty", actualUrl); warn("Git tree '%s' is dirty", actualUrl);
auto gitOpts = Strings({ "-C", actualUrl, "ls-files", "-z" }); auto gitOpts = Strings({ "-C", actualUrl, "ls-files", "-z" });

View file

@ -1,11 +1,13 @@
#include "filetransfer.hh" #include "filetransfer.hh"
#include "cache.hh" #include "cache.hh"
#include "fetchers.hh"
#include "globals.hh" #include "globals.hh"
#include "store-api.hh" #include "store-api.hh"
#include "types.hh" #include "types.hh"
#include "url-parts.hh" #include "url-parts.hh"
#include "fetchers.hh"
#include "fetch-settings.hh"
#include <optional> #include <optional>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <fstream> #include <fstream>
@ -157,7 +159,7 @@ struct GitArchiveInputScheme : InputScheme
std::optional<std::string> getAccessToken(const std::string & host) const std::optional<std::string> getAccessToken(const std::string & host) const
{ {
auto tokens = settings.accessTokens.get(); auto tokens = fetchSettings.accessTokens.get();
if (auto token = get(tokens, host)) if (auto token = get(tokens, host))
return *token; return *token;
return {}; return {};

View file

@ -5,6 +5,8 @@
#include "store-api.hh" #include "store-api.hh"
#include "url-parts.hh" #include "url-parts.hh"
#include "fetch-settings.hh"
#include <sys/time.h> #include <sys/time.h>
using namespace std::string_literals; using namespace std::string_literals;
@ -165,10 +167,10 @@ struct MercurialInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked /* This is an unclean working tree. So copy all tracked
files. */ files. */
if (!settings.allowDirty) if (!fetchSettings.allowDirty)
throw Error("Mercurial tree '%s' is unclean", actualUrl); throw Error("Mercurial tree '%s' is unclean", actualUrl);
if (settings.warnDirty) if (fetchSettings.warnDirty)
warn("Mercurial tree '%s' is unclean", actualUrl); warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl }))); input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));

View file

@ -5,6 +5,8 @@
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "fetch-settings.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace nix::fetchers { namespace nix::fetchers {
@ -150,7 +152,7 @@ void overrideRegistry(
static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store) static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
{ {
static auto reg = [&]() { static auto reg = [&]() {
auto path = settings.flakeRegistry.get(); auto path = fetchSettings.flakeRegistry.get();
if (!hasPrefix(path, "/")) { if (!hasPrefix(path, "/")) {
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath; auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;

View file

@ -1,6 +1,7 @@
#include "globals.hh" #include "globals.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
#include "util.hh" #include "util.hh"
#include "loggers.hh" #include "loggers.hh"
@ -227,8 +228,6 @@ LegacyArgs::LegacyArgs(const std::string & programName,
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg) std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
: MixCommonArgs(programName), parseArg(parseArg) : MixCommonArgs(programName), parseArg(parseArg)
{ {
printError("FOO %s", programName);
addFlag({ addFlag({
.longName = "no-build-output", .longName = "no-build-output",
.shortName = 'Q', .shortName = 'Q',

View file

@ -0,0 +1,77 @@
#pragma once
#include "realisation.hh"
#include <string>
#include <chrono>
namespace nix {
struct BuildResult
{
/* Note: don't remove status codes, and only add new status codes
at the end of the list, to prevent client/server
incompatibilities in the nix-store --serve protocol. */
enum Status {
Built = 0,
Substituted,
AlreadyValid,
PermanentFailure,
InputRejected,
OutputRejected,
TransientFailure, // possibly transient
CachedFailure, // no longer used
TimedOut,
MiscFailure,
DependencyFailed,
LogLimitExceeded,
NotDeterministic,
ResolvesToAlreadyValid,
} status = MiscFailure;
std::string errorMsg;
std::string toString() const {
auto strStatus = [&]() {
switch (status) {
case Built: return "Built";
case Substituted: return "Substituted";
case AlreadyValid: return "AlreadyValid";
case PermanentFailure: return "PermanentFailure";
case InputRejected: return "InputRejected";
case OutputRejected: return "OutputRejected";
case TransientFailure: return "TransientFailure";
case CachedFailure: return "CachedFailure";
case TimedOut: return "TimedOut";
case MiscFailure: return "MiscFailure";
case DependencyFailed: return "DependencyFailed";
case LogLimitExceeded: return "LogLimitExceeded";
case NotDeterministic: return "NotDeterministic";
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
default: return "Unknown";
};
}();
return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
}
/* How many times this build was performed. */
unsigned int timesBuilt = 0;
/* If timesBuilt > 1, whether some builds did not produce the same
result. (Note that 'isNonDeterministic = false' does not mean
the build is deterministic, just that we don't have evidence of
non-determinism.) */
bool isNonDeterministic = false;
DrvOutputs builtOutputs;
/* The start/stop times of the build (or one of the rounds, if it
was repeated). */
time_t startTime = 0, stopTime = 0;
bool success() {
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
}
};
}

View file

@ -2,6 +2,7 @@
#include "parsed-derivations.hh" #include "parsed-derivations.hh"
#include "lock.hh" #include "lock.hh"
#include "build-result.hh"
#include "store-api.hh" #include "store-api.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#include "goal.hh" #include "goal.hh"

View file

@ -1,4 +1,5 @@
#include "local-derivation-goal.hh" #include "local-derivation-goal.hh"
#include "gc-store.hh"
#include "hook-instance.hh" #include "hook-instance.hh"
#include "worker.hh" #include "worker.hh"
#include "builtins.hh" #include "builtins.hh"
@ -1128,7 +1129,7 @@ struct RestrictedStoreConfig : virtual LocalFSStoreConfig
/* A wrapper around LocalStore that only allows building/querying of /* A wrapper around LocalStore that only allows building/querying of
paths that are in the input closures of the build or were added via paths that are in the input closures of the build or were added via
recursive Nix calls. */ recursive Nix calls. */
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore, public virtual GcStore
{ {
ref<LocalStore> next; ref<LocalStore> next;
@ -1934,7 +1935,7 @@ void LocalDerivationGoal::runChild()
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin", "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
i.first, i.second.source); i.first, i.second.source);
string path = i.first; std::string path = i.first;
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) { if (lstat(path.c_str(), &st)) {
if (i.second.optional && errno == ENOENT) if (i.second.optional && errno == ENOENT)
@ -1986,7 +1987,7 @@ void LocalDerivationGoal::runChild()
args.push_back("IMPORT_DIR=" + settings.nixDataDir + "/nix/sandbox/"); args.push_back("IMPORT_DIR=" + settings.nixDataDir + "/nix/sandbox/");
if (allowLocalNetworking) { if (allowLocalNetworking) {
args.push_back("-D"); args.push_back("-D");
args.push_back(string("_ALLOW_LOCAL_NETWORKING=1")); args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
} }
args.push_back(drv->builder); args.push_back(drv->builder);
} else { } else {
@ -2384,14 +2385,10 @@ void LocalDerivationGoal::registerOutputs()
[&](DerivationOutputCAFloating dof) { [&](DerivationOutputCAFloating dof) {
return newInfoFromCA(dof); return newInfoFromCA(dof);
}, },
[&](DerivationOutputDeferred) { [&](DerivationOutputDeferred) -> ValidPathInfo {
// No derivation should reach that point without having been // No derivation should reach that point without having been
// rewritten first // rewritten first
assert(false); assert(false);
// Ugly, but the compiler insists on having this return a value
// of type `ValidPathInfo` despite the `assert(false)`, so
// let's provide it
return *(ValidPathInfo*)0;
}, },
}, output.output); }, output.output);

View file

@ -62,7 +62,7 @@ struct LocalDerivationGoal : public DerivationGoal
Environment env; Environment env;
#if __APPLE__ #if __APPLE__
typedef string SandboxProfile; typedef std::string SandboxProfile;
SandboxProfile additionalSandboxProfile; SandboxProfile additionalSandboxProfile;
#endif #endif

View file

@ -1,7 +1,9 @@
#include "daemon.hh" #include "daemon.hh"
#include "monitor-fd.hh" #include "monitor-fd.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "build-result.hh"
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "finally.hh" #include "finally.hh"
#include "archive.hh" #include "archive.hh"
@ -622,9 +624,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopAddIndirectRoot: { case wopAddIndirectRoot: {
Path path = absPath(readString(from)); Path path = absPath(readString(from));
logger->startWork(); logger->startWork();
store->addIndirectRoot(path); auto & gcStore = requireGcStore(*store);
gcStore.addIndirectRoot(path);
logger->stopWork(); logger->stopWork();
to << 1; to << 1;
break; break;
} }
@ -639,7 +644,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopFindRoots: { case wopFindRoots: {
logger->startWork(); logger->startWork();
Roots roots = store->findRoots(!trusted); auto & gcStore = requireGcStore(*store);
Roots roots = gcStore.findRoots(!trusted);
logger->stopWork(); logger->stopWork();
size_t size = 0; size_t size = 0;
@ -670,7 +676,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork(); logger->startWork();
if (options.ignoreLiveness) if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness"); throw Error("you are not allowed to ignore liveness");
store->collectGarbage(options, results); auto & gcStore = requireGcStore(*store);
gcStore.collectGarbage(options, results);
logger->stopWork(); logger->stopWork();
to << results.paths << results.bytesFreed << 0 /* obsolete */; to << results.paths << results.bytesFreed << 0 /* obsolete */;

View file

@ -197,7 +197,7 @@ struct curlFileTransfer : public FileTransfer
result.etag = ""; result.etag = "";
result.data.clear(); result.data.clear();
result.bodySize = 0; result.bodySize = 0;
statusMsg = trim((std::string &) match[1]); statusMsg = trim(match.str(1));
acceptRanges = false; acceptRanges = false;
encoding = ""; encoding = "";
} else { } else {

13
src/libstore/gc-store.cc Normal file
View file

@ -0,0 +1,13 @@
#include "gc-store.hh"
namespace nix {
GcStore & requireGcStore(Store & store)
{
auto * gcStore = dynamic_cast<GcStore *>(&store);
if (!gcStore)
throw UsageError("Garbage collection not supported by this store");
return *gcStore;
}
}

84
src/libstore/gc-store.hh Normal file
View file

@ -0,0 +1,84 @@
#pragma once
#include "store-api.hh"
namespace nix {
typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
struct GCOptions
{
/* Garbage collector operation:
- `gcReturnLive': return the set of paths reachable from
(i.e. in the closure of) the roots.
- `gcReturnDead': return the set of paths not reachable from
the roots.
- `gcDeleteDead': actually delete the latter set.
- `gcDeleteSpecific': delete the paths listed in
`pathsToDelete', insofar as they are not reachable.
*/
typedef enum {
gcReturnLive,
gcReturnDead,
gcDeleteDead,
gcDeleteSpecific,
} GCAction;
GCAction action{gcDeleteDead};
/* If `ignoreLiveness' is set, then reachability from the roots is
ignored (dangerous!). However, the paths must still be
unreferenced *within* the store (i.e., there can be no other
store paths that depend on them). */
bool ignoreLiveness{false};
/* For `gcDeleteSpecific', the paths to delete. */
StorePathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */
uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
};
struct GCResults
{
/* Depending on the action, the GC roots, or the paths that would
be or have been deleted. */
PathSet paths;
/* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
number of bytes that would be or was freed. */
uint64_t bytesFreed = 0;
};
struct GcStore : public virtual Store
{
/* Add an indirect root, which is merely a symlink to `path' from
/nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
to be a symlink to a store path. The garbage collector will
automatically remove the indirect root when it finds that
`path' has disappeared. */
virtual void addIndirectRoot(const Path & path) = 0;
/* Find the roots of the garbage collector. Each root is a pair
(link, storepath) where `link' is the path of the symlink
outside of the Nix store that point to `storePath'. If
'censor' is true, privacy-sensitive information about roots
found in /proc is censored. */
virtual Roots findRoots(bool censor) = 0;
/* Perform a garbage collection. */
virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0;
};
GcStore & requireGcStore(Store & store);
}

View file

@ -413,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
try { try {
std::regex lsofRegex(R"(^n(/.*)$)"); std::regex lsofRegex(R"(^n(/.*)$)");
auto lsofLines = auto lsofLines =
tokenizeString<std::vector<string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n"); tokenizeString<std::vector<std::string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n");
for (const auto & line : lsofLines) { for (const auto & line : lsofLines) {
std::smatch match; std::smatch match;
if (std::regex_match(line, match, lsofRegex)) if (std::regex_match(line, match, lsofRegex))

View file

@ -880,55 +880,6 @@ public:
are loaded as plugins (non-recursively). are loaded as plugins (non-recursively).
)"}; )"};
Setting<StringMap> accessTokens{this, {}, "access-tokens",
R"(
Access tokens used to access protected GitHub, GitLab, or
other locations requiring token-based authentication.
Access tokens are specified as a string made up of
space-separated `host=token` values. The specific token
used is selected by matching the `host` portion against the
"host" specification of the input. The actual use of the
`token` value is determined by the type of resource being
accessed:
* Github: the token value is the OAUTH-TOKEN string obtained
as the Personal Access Token from the Github server (see
https://docs.github.com/en/developers/apps/authorizing-oath-apps).
* Gitlab: the token value is either the OAuth2 token or the
Personal Access Token (these are different types tokens
for gitlab, see
https://docs.gitlab.com/12.10/ee/api/README.html#authentication).
The `token` value should be `type:tokenstring` where
`type` is either `OAuth2` or `PAT` to indicate which type
of token is being specified.
Example `~/.config/nix/nix.conf`:
```
access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk
```
Example `~/code/flake.nix`:
```nix
input.foo = {
type = "gitlab";
host = "gitlab.mycompany.com";
owner = "mycompany";
repo = "pro";
};
```
This example specifies three tokens, one each for accessing
github.com, gitlab.mycompany.com, and sourceforge.net.
The `input.foo` uses the "gitlab" fetcher, which might
requires specifying the token type along with the token
value.
)"};
Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features", Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features",
"Experimental Nix features to enable."}; "Experimental Nix features to enable."};
@ -936,18 +887,9 @@ public:
void requireExperimentalFeature(const ExperimentalFeature &); void requireExperimentalFeature(const ExperimentalFeature &);
Setting<bool> allowDirty{this, true, "allow-dirty",
"Whether to allow dirty Git/Mercurial trees."};
Setting<bool> warnDirty{this, true, "warn-dirty",
"Whether to warn about dirty Git/Mercurial trees."};
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
"Maximum size of NARs before spilling them to disk."}; "Maximum size of NARs before spilling them to disk."};
Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
"Path or URI of the global flake registry."};
Setting<bool> allowSymlinkedStore{ Setting<bool> allowSymlinkedStore{
this, false, "allow-symlinked-store", this, false, "allow-symlinked-store",
R"( R"(
@ -960,19 +902,6 @@ public:
resolves to a different location from that of the build machine. You resolves to a different location from that of the build machine. You
can enable this setting if you are sure you're not going to do that. can enable this setting if you are sure you're not going to do that.
)"}; )"};
Setting<bool> useRegistries{this, true, "use-registries",
"Whether to use flake registries to resolve flake references."};
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
"Whether to accept nix configuration from a flake without prompting."};
Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary",
R"(
The commit summary to use when committing changed flake lock files. If
empty, the summary is generated based on the action performed.
)"};
}; };

View file

@ -2,6 +2,7 @@
#include "pool.hh" #include "pool.hh"
#include "remote-store.hh" #include "remote-store.hh"
#include "serve-protocol.hh" #include "serve-protocol.hh"
#include "build-result.hh"
#include "store-api.hh" #include "store-api.hh"
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
namespace nix { namespace nix {
@ -23,7 +24,7 @@ struct LocalFSStoreConfig : virtual StoreConfig
"physical path to the Nix store"}; "physical path to the Nix store"};
}; };
class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store, virtual GcStore
{ {
public: public:

View file

@ -5,6 +5,7 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "gc-store.hh"
#include "sync.hh" #include "sync.hh"
#include "util.hh" #include "util.hh"
@ -43,7 +44,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
}; };
class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore
{ {
private: private:

View file

@ -22,9 +22,9 @@ DerivedPath StorePathWithOutputs::toDerivedPath() const
std::vector<DerivedPath> toDerivedPaths(const std::vector<StorePathWithOutputs> ss) std::vector<DerivedPath> toDerivedPaths(const std::vector<StorePathWithOutputs> ss)
{ {
std::vector<DerivedPath> reqs; std::vector<DerivedPath> reqs;
for (auto & s : ss) reqs.push_back(s.toDerivedPath()); for (auto & s : ss) reqs.push_back(s.toDerivedPath());
return reqs; return reqs;
} }

View file

@ -1,7 +1,9 @@
#include "serialise.hh" #include "serialise.hh"
#include "util.hh" #include "util.hh"
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "gc-store.hh"
#include "remote-fs-accessor.hh" #include "remote-fs-accessor.hh"
#include "build-result.hh"
#include "remote-store.hh" #include "remote-store.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "archive.hh" #include "archive.hh"

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
namespace nix { namespace nix {
@ -29,7 +30,7 @@ struct RemoteStoreConfig : virtual StoreConfig
/* FIXME: RemoteStore is a misnomer - should be something like /* FIXME: RemoteStore is a misnomer - should be something like
DaemonStore. */ DaemonStore. */
class RemoteStore : public virtual RemoteStoreConfig, public virtual Store class RemoteStore : public virtual RemoteStoreConfig, public virtual Store, public virtual GcStore
{ {
public: public:

View file

@ -76,127 +76,10 @@ enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true };
const uint32_t exportMagic = 0x4558494e; const uint32_t exportMagic = 0x4558494e;
typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
struct GCOptions
{
/* Garbage collector operation:
- `gcReturnLive': return the set of paths reachable from
(i.e. in the closure of) the roots.
- `gcReturnDead': return the set of paths not reachable from
the roots.
- `gcDeleteDead': actually delete the latter set.
- `gcDeleteSpecific': delete the paths listed in
`pathsToDelete', insofar as they are not reachable.
*/
typedef enum {
gcReturnLive,
gcReturnDead,
gcDeleteDead,
gcDeleteSpecific,
} GCAction;
GCAction action{gcDeleteDead};
/* If `ignoreLiveness' is set, then reachability from the roots is
ignored (dangerous!). However, the paths must still be
unreferenced *within* the store (i.e., there can be no other
store paths that depend on them). */
bool ignoreLiveness{false};
/* For `gcDeleteSpecific', the paths to delete. */
StorePathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */
uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
};
struct GCResults
{
/* Depending on the action, the GC roots, or the paths that would
be or have been deleted. */
PathSet paths;
/* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
number of bytes that would be or was freed. */
uint64_t bytesFreed = 0;
};
enum BuildMode { bmNormal, bmRepair, bmCheck }; enum BuildMode { bmNormal, bmRepair, bmCheck };
struct BuildResult;
struct BuildResult
{
/* Note: don't remove status codes, and only add new status codes
at the end of the list, to prevent client/server
incompatibilities in the nix-store --serve protocol. */
enum Status {
Built = 0,
Substituted,
AlreadyValid,
PermanentFailure,
InputRejected,
OutputRejected,
TransientFailure, // possibly transient
CachedFailure, // no longer used
TimedOut,
MiscFailure,
DependencyFailed,
LogLimitExceeded,
NotDeterministic,
ResolvesToAlreadyValid,
} status = MiscFailure;
std::string errorMsg;
std::string toString() const {
auto strStatus = [&]() {
switch (status) {
case Built: return "Built";
case Substituted: return "Substituted";
case AlreadyValid: return "AlreadyValid";
case PermanentFailure: return "PermanentFailure";
case InputRejected: return "InputRejected";
case OutputRejected: return "OutputRejected";
case TransientFailure: return "TransientFailure";
case CachedFailure: return "CachedFailure";
case TimedOut: return "TimedOut";
case MiscFailure: return "MiscFailure";
case DependencyFailed: return "DependencyFailed";
case LogLimitExceeded: return "LogLimitExceeded";
case NotDeterministic: return "NotDeterministic";
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
default: return "Unknown";
};
}();
return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
}
/* How many times this build was performed. */
unsigned int timesBuilt = 0;
/* If timesBuilt > 1, whether some builds did not produce the same
result. (Note that 'isNonDeterministic = false' does not mean
the build is deterministic, just that we don't have evidence of
non-determinism.) */
bool isNonDeterministic = false;
DrvOutputs builtOutputs;
/* The start/stop times of the build (or one of the rounds, if it
was repeated). */
time_t startTime = 0, stopTime = 0;
bool success() {
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
}
};
struct StoreConfig : public Config struct StoreConfig : public Config
{ {
@ -595,26 +478,6 @@ public:
virtual void addTempRoot(const StorePath & path) virtual void addTempRoot(const StorePath & path)
{ debug("not creating temporary root, store doesn't support GC"); } { debug("not creating temporary root, store doesn't support GC"); }
/* Add an indirect root, which is merely a symlink to `path' from
/nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
to be a symlink to a store path. The garbage collector will
automatically remove the indirect root when it finds that
`path' has disappeared. */
virtual void addIndirectRoot(const Path & path)
{ unsupported("addIndirectRoot"); }
/* Find the roots of the garbage collector. Each root is a pair
(link, storepath) where `link' is the path of the symlink
outside of the Nix store that point to `storePath'. If
'censor' is true, privacy-sensitive information about roots
found in /proc is censored. */
virtual Roots findRoots(bool censor)
{ unsupported("findRoots"); }
/* Perform a garbage collection. */
virtual void collectGarbage(const GCOptions & options, GCResults & results)
{ unsupported("collectGarbage"); }
/* Return a string representing information about the path that /* Return a string representing information about the path that
can be loaded into the database using `nix-store --load-db' or can be loaded into the database using `nix-store --load-db' or
`nix-store --register-validity'. */ `nix-store --register-validity'. */

View file

@ -359,7 +359,7 @@ namespace nix {
// constructing without access violation. // constructing without access violation.
ErrPos ep(invalid); ErrPos ep(invalid);
// assignment without access violation. // assignment without access violation.
ep = invalid; ep = invalid;

View file

@ -1263,9 +1263,9 @@ std::string chomp(std::string_view s)
std::string trim(std::string_view s, std::string_view whitespace) std::string trim(std::string_view s, std::string_view whitespace)
{ {
auto i = s.find_first_not_of(whitespace); auto i = s.find_first_not_of(whitespace);
if (i == std::string_view::npos) return ""; if (i == s.npos) return "";
auto j = s.find_last_not_of(whitespace); auto j = s.find_last_not_of(whitespace);
return std::string(s, i, j == std::string::npos ? j : j - i + 1); return std::string(s, i, j == s.npos ? j : j - i + 1);
} }
@ -1412,7 +1412,7 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
} }
} }
else if (*i == '\r') else if (*i == '\r' || *i == '\a')
// do nothing for now // do nothing for now
i++; i++;

View file

@ -346,7 +346,7 @@ static void main_nix_build(int argc, char * * argv)
throw UsageError("nix-shell requires a single derivation"); throw UsageError("nix-shell requires a single derivation");
auto & drvInfo = drvs.front(); auto & drvInfo = drvs.front();
auto drv = evalStore->derivationFromPath(evalStore->parseStorePath(drvInfo.queryDrvPath())); auto drv = evalStore->derivationFromPath(drvInfo.requireDrvPath());
std::vector<StorePathWithOutputs> pathsToBuild; std::vector<StorePathWithOutputs> pathsToBuild;
RealisedPath::Set pathsToCopy; RealisedPath::Set pathsToCopy;
@ -369,7 +369,7 @@ static void main_nix_build(int argc, char * * argv)
if (!drv) if (!drv)
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation"); throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
auto bashDrv = store->parseStorePath(drv->queryDrvPath()); auto bashDrv = drv->requireDrvPath();
pathsToBuild.push_back({bashDrv}); pathsToBuild.push_back({bashDrv});
pathsToCopy.insert(bashDrv); pathsToCopy.insert(bashDrv);
shellDrv = bashDrv; shellDrv = bashDrv;
@ -458,10 +458,7 @@ static void main_nix_build(int argc, char * * argv)
} }
} }
ParsedDerivation parsedDrv( ParsedDerivation parsedDrv(drvInfo.requireDrvPath(), drv);
StorePath(store->parseStorePath(drvInfo.queryDrvPath())),
drv
);
if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, inputs)) { if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, inputs)) {
auto json = structAttrs.value(); auto json = structAttrs.value();
@ -553,7 +550,7 @@ static void main_nix_build(int argc, char * * argv)
std::map<StorePath, std::pair<size_t, StringSet>> drvMap; std::map<StorePath, std::pair<size_t, StringSet>> drvMap;
for (auto & drvInfo : drvs) { for (auto & drvInfo : drvs) {
auto drvPath = store->parseStorePath(drvInfo.queryDrvPath()); auto drvPath = drvInfo.requireDrvPath();
auto outputName = drvInfo.queryOutputName(); auto outputName = drvInfo.queryOutputName();
if (outputName == "") if (outputName == "")

View file

@ -96,7 +96,7 @@ static void update(const StringSet & channelNames)
std::smatch match; std::smatch match;
auto urlBase = std::string(baseNameOf(url)); auto urlBase = std::string(baseNameOf(url));
if (std::regex_search(urlBase, match, std::regex("(-\\d.*)$"))) if (std::regex_search(urlBase, match, std::regex("(-\\d.*)$")))
cname = cname + (std::string) match[1]; cname = cname + match.str(1);
std::string extraAttrs; std::string extraAttrs;

View file

@ -1,4 +1,5 @@
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
#include "profiles.hh" #include "profiles.hh"
#include "shared.hh" #include "shared.hh"
#include "globals.hh" #include "globals.hh"
@ -80,10 +81,11 @@ static int main_nix_collect_garbage(int argc, char * * argv)
// Run the actual garbage collector. // Run the actual garbage collector.
if (!dryRun) { if (!dryRun) {
auto store = openStore(); auto store = openStore();
auto & gcStore = requireGcStore(*store);
options.action = GCOptions::gcDeleteDead; options.action = GCOptions::gcDeleteDead;
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);
store->collectGarbage(options, results); gcStore.collectGarbage(options, results);
} }
return 0; return 0;

View file

@ -211,7 +211,7 @@ static long comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2)
// at a time. // at a time.
static bool isPrebuilt(EvalState & state, DrvInfo & elem) static bool isPrebuilt(EvalState & state, DrvInfo & elem)
{ {
auto path = state.store->parseStorePath(elem.queryOutPath()); auto path = elem.queryOutPath();
if (state.store->isValidPath(path)) return true; if (state.store->isValidPath(path)) return true;
return state.store->querySubstitutablePaths({path}).count(path); return state.store->querySubstitutablePaths({path}).count(path);
} }
@ -429,14 +429,15 @@ static void queryInstSources(EvalState & state,
elem.setName(name); elem.setName(name);
if (path.isDerivation()) { if (path.isDerivation()) {
elem.setDrvPath(state.store->printStorePath(path)); elem.setDrvPath(path);
auto outputs = state.store->queryDerivationOutputMap(path); auto outputs = state.store->queryDerivationOutputMap(path);
elem.setOutPath(state.store->printStorePath(outputs.at("out"))); elem.setOutPath(outputs.at("out"));
if (name.size() >= drvExtension.size() && if (name.size() >= drvExtension.size() &&
std::string(name, name.size() - drvExtension.size()) == drvExtension) std::string(name, name.size() - drvExtension.size()) == drvExtension)
name = name.substr(0, name.size() - drvExtension.size()); name = name.substr(0, name.size() - drvExtension.size());
} }
else elem.setOutPath(state.store->printStorePath(path)); else
elem.setOutPath(path);
elems.push_back(elem); elems.push_back(elem);
} }
@ -470,13 +471,11 @@ static void queryInstSources(EvalState & state,
static void printMissing(EvalState & state, DrvInfos & elems) static void printMissing(EvalState & state, DrvInfos & elems)
{ {
std::vector<DerivedPath> targets; std::vector<DerivedPath> targets;
for (auto & i : elems) { for (auto & i : elems)
Path drvPath = i.queryDrvPath(); if (auto drvPath = i.queryDrvPath())
if (drvPath != "") targets.push_back(DerivedPath::Built{*drvPath});
targets.push_back(DerivedPath::Built{state.store->parseStorePath(drvPath)});
else else
targets.push_back(DerivedPath::Opaque{state.store->parseStorePath(i.queryOutPath())}); targets.push_back(DerivedPath::Opaque{i.queryOutPath()});
}
printMissing(state.store, targets); printMissing(state.store, targets);
} }
@ -744,14 +743,11 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
if (globals.forceName != "") if (globals.forceName != "")
drv.setName(globals.forceName); drv.setName(globals.forceName);
auto drvPath = drv.queryDrvPath();
std::vector<DerivedPath> paths { std::vector<DerivedPath> paths {
(drv.queryDrvPath() != "") drvPath
? (DerivedPath) (DerivedPath::Built { ? (DerivedPath) (DerivedPath::Built { *drvPath })
globals.state->store->parseStorePath(drv.queryDrvPath()) : (DerivedPath) (DerivedPath::Opaque { drv.queryOutPath() }),
})
: (DerivedPath) (DerivedPath::Opaque {
globals.state->store->parseStorePath(drv.queryOutPath())
}),
}; };
printMissing(globals.state->store, paths); printMissing(globals.state->store, paths);
if (globals.dryRun) return; if (globals.dryRun) return;
@ -759,8 +755,9 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
debug(format("switching to new user environment")); debug(format("switching to new user environment"));
Path generation = createGeneration( Path generation = createGeneration(
ref<LocalFSStore>(store2), globals.profile, ref<LocalFSStore>(store2),
store2->parseStorePath(drv.queryOutPath())); globals.profile,
drv.queryOutPath());
switchLink(globals.profile, generation); switchLink(globals.profile, generation);
} }
@ -780,7 +777,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
split = std::partition( split = std::partition(
workingElems.begin(), workingElems.end(), workingElems.begin(), workingElems.end(),
[&selectorStorePath, globals](auto &elem) { [&selectorStorePath, globals](auto &elem) {
return selectorStorePath != globals.state->store->parseStorePath(elem.queryOutPath()); return selectorStorePath != elem.queryOutPath();
} }
); );
} else { } else {
@ -925,9 +922,8 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
if (printOutPath) { if (printOutPath) {
DrvInfo::Outputs outputs = i.queryOutputs(); DrvInfo::Outputs outputs = i.queryOutputs();
JSONObject outputObj = pkgObj.object("outputs"); JSONObject outputObj = pkgObj.object("outputs");
for (auto & j : outputs) { for (auto & j : outputs)
outputObj.attr(j.first, j.second); outputObj.attr(j.first, globals.state->store->printStorePath(j.second));
}
} }
if (printMeta) { if (printMeta) {
@ -957,6 +953,8 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
{ {
auto & store { *globals.state->store };
Strings remaining; Strings remaining;
std::string attrPath; std::string attrPath;
@ -1027,12 +1025,11 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
/* We only need to know the installed paths when we are querying /* We only need to know the installed paths when we are querying
the status of the derivation. */ the status of the derivation. */
PathSet installed; /* installed paths */ StorePathSet installed; /* installed paths */
if (printStatus) { if (printStatus)
for (auto & i : installedElems) for (auto & i : installedElems)
installed.insert(i.queryOutPath()); installed.insert(i.queryOutPath());
}
/* Query which paths have substitutes. */ /* Query which paths have substitutes. */
@ -1042,13 +1039,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
StorePathSet paths; StorePathSet paths;
for (auto & i : elems) for (auto & i : elems)
try { try {
paths.insert(globals.state->store->parseStorePath(i.queryOutPath())); paths.insert(i.queryOutPath());
} catch (AssertionError & e) { } catch (AssertionError & e) {
printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName()); printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName());
i.setFailed(); i.setFailed();
} }
validPaths = globals.state->store->queryValidPaths(paths); validPaths = store.queryValidPaths(paths);
substitutablePaths = globals.state->store->querySubstitutablePaths(paths); substitutablePaths = store.querySubstitutablePaths(paths);
} }
@ -1073,8 +1070,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
//Activity act(*logger, lvlDebug, format("outputting query result '%1%'") % i.attrPath); //Activity act(*logger, lvlDebug, format("outputting query result '%1%'") % i.attrPath);
if (globals.prebuiltOnly && if (globals.prebuiltOnly &&
!validPaths.count(globals.state->store->parseStorePath(i.queryOutPath())) && !validPaths.count(i.queryOutPath()) &&
!substitutablePaths.count(globals.state->store->parseStorePath(i.queryOutPath()))) !substitutablePaths.count(i.queryOutPath()))
continue; continue;
/* For table output. */ /* For table output. */
@ -1084,10 +1081,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
XMLAttrs attrs; XMLAttrs attrs;
if (printStatus) { if (printStatus) {
Path outPath = i.queryOutPath(); auto outPath = i.queryOutPath();
bool hasSubs = substitutablePaths.count(globals.state->store->parseStorePath(outPath)); bool hasSubs = substitutablePaths.count(outPath);
bool isInstalled = installed.find(outPath) != installed.end(); bool isInstalled = installed.count(outPath);
bool isValid = validPaths.count(globals.state->store->parseStorePath(outPath)); bool isValid = validPaths.count(outPath);
if (xmlOutput) { if (xmlOutput) {
attrs["installed"] = isInstalled ? "1" : "0"; attrs["installed"] = isInstalled ? "1" : "0";
attrs["valid"] = isValid ? "1" : "0"; attrs["valid"] = isValid ? "1" : "0";
@ -1152,9 +1149,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
if (printDrvPath) { if (printDrvPath) {
auto drvPath = i.queryDrvPath(); auto drvPath = i.queryDrvPath();
if (xmlOutput) { if (xmlOutput) {
if (drvPath != "") attrs["drvPath"] = drvPath; if (drvPath) attrs["drvPath"] = store.printStorePath(*drvPath);
} else } else
columns.push_back(drvPath == "" ? "-" : drvPath); columns.push_back(drvPath ? store.printStorePath(*drvPath) : "-");
} }
if (printOutPath && !xmlOutput) { if (printOutPath && !xmlOutput) {
@ -1163,7 +1160,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
for (auto & j : outputs) { for (auto & j : outputs) {
if (!s.empty()) s += ';'; if (!s.empty()) s += ';';
if (j.first != "out") { s += j.first; s += "="; } if (j.first != "out") { s += j.first; s += "="; }
s += j.second; s += store.printStorePath(j.second);
} }
columns.push_back(s); columns.push_back(s);
} }
@ -1184,7 +1181,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
for (auto & j : outputs) { for (auto & j : outputs) {
XMLAttrs attrs2; XMLAttrs attrs2;
attrs2["name"] = j.first; attrs2["name"] = j.first;
attrs2["path"] = j.second; attrs2["path"] = store.printStorePath(j.second);
xml.writeEmptyElement("output", attrs2); xml.writeEmptyElement("output", attrs2);
} }
} }

View file

@ -38,8 +38,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
exist already. */ exist already. */
std::vector<StorePathWithOutputs> drvsToBuild; std::vector<StorePathWithOutputs> drvsToBuild;
for (auto & i : elems) for (auto & i : elems)
if (i.queryDrvPath() != "") if (auto drvPath = i.queryDrvPath())
drvsToBuild.push_back({state.store->parseStorePath(i.queryDrvPath())}); drvsToBuild.push_back({*drvPath});
debug(format("building user environment dependencies")); debug(format("building user environment dependencies"));
state.store->buildPaths( state.store->buildPaths(
@ -55,7 +55,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Create a pseudo-derivation containing the name, system, /* Create a pseudo-derivation containing the name, system,
output paths, and optionally the derivation path, as well output paths, and optionally the derivation path, as well
as the meta attributes. */ as the meta attributes. */
Path drvPath = keepDerivations ? i.queryDrvPath() : ""; std::optional<StorePath> drvPath = keepDerivations ? i.queryDrvPath() : std::nullopt;
DrvInfo::Outputs outputs = i.queryOutputs(true); DrvInfo::Outputs outputs = i.queryOutputs(true);
StringSet metaNames = i.queryMetaNames(); StringSet metaNames = i.queryMetaNames();
@ -66,9 +66,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
auto system = i.querySystem(); auto system = i.querySystem();
if (!system.empty()) if (!system.empty())
attrs.alloc(state.sSystem).mkString(system); attrs.alloc(state.sSystem).mkString(system);
attrs.alloc(state.sOutPath).mkString(i.queryOutPath()); attrs.alloc(state.sOutPath).mkString(state.store->printStorePath(i.queryOutPath()));
if (drvPath != "") if (drvPath)
attrs.alloc(state.sDrvPath).mkString(i.queryDrvPath()); attrs.alloc(state.sDrvPath).mkString(state.store->printStorePath(*drvPath));
// Copy each output meant for installation. // Copy each output meant for installation.
auto & vOutputs = attrs.alloc(state.sOutputs); auto & vOutputs = attrs.alloc(state.sOutputs);
@ -76,15 +76,15 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
for (const auto & [m, j] : enumerate(outputs)) { for (const auto & [m, j] : enumerate(outputs)) {
(vOutputs.listElems()[m] = state.allocValue())->mkString(j.first); (vOutputs.listElems()[m] = state.allocValue())->mkString(j.first);
auto outputAttrs = state.buildBindings(2); auto outputAttrs = state.buildBindings(2);
outputAttrs.alloc(state.sOutPath).mkString(j.second); outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(j.second));
attrs.alloc(j.first).mkAttrs(outputAttrs); attrs.alloc(j.first).mkAttrs(outputAttrs);
/* This is only necessary when installing store paths, e.g., /* This is only necessary when installing store paths, e.g.,
`nix-env -i /nix/store/abcd...-foo'. */ `nix-env -i /nix/store/abcd...-foo'. */
state.store->addTempRoot(state.store->parseStorePath(j.second)); state.store->addTempRoot(j.second);
state.store->ensurePath(state.store->parseStorePath(j.second)); state.store->ensurePath(j.second);
references.insert(state.store->parseStorePath(j.second)); references.insert(j.second);
} }
// Copy the meta attributes. // Copy the meta attributes.
@ -99,7 +99,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
(manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs); (manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs);
if (drvPath != "") references.insert(state.store->parseStorePath(drvPath)); if (drvPath) references.insert(*drvPath);
} }
/* Also write a copy of the list of user environment elements to /* Also write a copy of the list of user environment elements to
@ -132,9 +132,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); }); state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
PathSet context; PathSet context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
auto topLevelDrv = state.store->parseStorePath(state.coerceToPath(*aDrvPath.pos, *aDrvPath.value, context)); auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context);
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
Path topLevelOut = state.coerceToPath(*aOutPath.pos, *aOutPath.value, context); auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context);
/* Realise the resulting store expression. */ /* Realise the resulting store expression. */
debug("building user environment"); debug("building user environment");
@ -158,8 +158,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
} }
debug(format("switching to new user environment")); debug(format("switching to new user environment"));
Path generation = createGeneration(ref<LocalFSStore>(store2), profile, Path generation = createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut);
store2->parseStorePath(topLevelOut));
switchLink(profile, generation); switchLink(profile, generation);
} }

View file

@ -61,12 +61,13 @@ void processExpr(EvalState & state, const Strings & attrPaths,
DrvInfos drvs; DrvInfos drvs;
getDerivations(state, v, "", autoArgs, drvs, false); getDerivations(state, v, "", autoArgs, drvs, false);
for (auto & i : drvs) { for (auto & i : drvs) {
Path drvPath = i.queryDrvPath(); auto drvPath = i.requireDrvPath();
auto drvPathS = state.store->printStorePath(drvPath);
/* What output do we want? */ /* What output do we want? */
std::string outputName = i.queryOutputName(); std::string outputName = i.queryOutputName();
if (outputName == "") if (outputName == "")
throw Error("derivation '%1%' lacks an 'outputName' attribute ", drvPath); throw Error("derivation '%1%' lacks an 'outputName' attribute", drvPathS);
if (gcRoot == "") if (gcRoot == "")
printGCWarning(); printGCWarning();
@ -75,9 +76,9 @@ void processExpr(EvalState & state, const Strings & attrPaths,
if (++rootNr > 1) rootName += "-" + std::to_string(rootNr); if (++rootNr > 1) rootName += "-" + std::to_string(rootNr);
auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>(); auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
if (store2) if (store2)
drvPath = store2->addPermRoot(store2->parseStorePath(drvPath), rootName); drvPathS = store2->addPermRoot(drvPath, rootName);
} }
std::cout << fmt("%s%s\n", drvPath, (outputName != "out" ? "!" + outputName : "")); std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : ""));
} }
} }
} }

View file

@ -2,6 +2,8 @@
#include "derivations.hh" #include "derivations.hh"
#include "dotgraph.hh" #include "dotgraph.hh"
#include "globals.hh" #include "globals.hh"
#include "build-result.hh"
#include "gc-store.hh"
#include "local-store.hh" #include "local-store.hh"
#include "monitor-fd.hh" #include "monitor-fd.hh"
#include "serve-protocol.hh" #include "serve-protocol.hh"
@ -427,11 +429,12 @@ static void opQuery(Strings opFlags, Strings opArgs)
store->computeFSClosure( store->computeFSClosure(
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations);
Roots roots = store->findRoots(false); auto & gcStore = requireGcStore(*store);
Roots roots = gcStore.findRoots(false);
for (auto & [target, links] : roots) for (auto & [target, links] : roots)
if (referrers.find(target) != referrers.end()) if (referrers.find(target) != referrers.end())
for (auto & link : links) for (auto & link : links)
cout << fmt("%1% -> %2%\n", link, store->printStorePath(target)); cout << fmt("%1% -> %2%\n", link, gcStore.printStorePath(target));
break; break;
} }
@ -587,20 +590,22 @@ static void opGC(Strings opFlags, Strings opArgs)
if (!opArgs.empty()) throw UsageError("no arguments expected"); if (!opArgs.empty()) throw UsageError("no arguments expected");
auto & gcStore = requireGcStore(*store);
if (printRoots) { if (printRoots) {
Roots roots = store->findRoots(false); Roots roots = gcStore.findRoots(false);
std::set<std::pair<Path, StorePath>> roots2; std::set<std::pair<Path, StorePath>> roots2;
// Transpose and sort the roots. // Transpose and sort the roots.
for (auto & [target, links] : roots) for (auto & [target, links] : roots)
for (auto & link : links) for (auto & link : links)
roots2.emplace(link, target); roots2.emplace(link, target);
for (auto & [link, target] : roots2) for (auto & [link, target] : roots2)
std::cout << link << " -> " << store->printStorePath(target) << "\n"; std::cout << link << " -> " << gcStore.printStorePath(target) << "\n";
} }
else { else {
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results); PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
store->collectGarbage(options, results); gcStore.collectGarbage(options, results);
if (options.action != GCOptions::gcDeleteDead) if (options.action != GCOptions::gcDeleteDead)
for (auto & i : results.paths) for (auto & i : results.paths)
@ -624,9 +629,11 @@ static void opDelete(Strings opFlags, Strings opArgs)
for (auto & i : opArgs) for (auto & i : opArgs)
options.pathsToDelete.insert(store->followLinksToStorePath(i)); options.pathsToDelete.insert(store->followLinksToStorePath(i));
auto & gcStore = requireGcStore(*store);
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);
store->collectGarbage(options, results); gcStore.collectGarbage(options, results);
} }

View file

@ -114,7 +114,7 @@ App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
installableContext.push_back( installableContext.push_back(
std::make_shared<InstallableDerivedPath>(store, ctxElt.toDerivedPath())); std::make_shared<InstallableDerivedPath>(store, ctxElt.toDerivedPath()));
auto builtContext = build(evalStore, store, Realise::Outputs, installableContext); auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
res.program = resolveString(*store, unresolved.program, builtContext); res.program = resolveString(*store, unresolved.program, builtContext);
if (!store->isInStore(res.program)) if (!store->isInStore(res.program))
throw Error("app program '%s' is not in the Nix store", res.program); throw Error("app program '%s' is not in the Nix store", res.program);

View file

@ -52,7 +52,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto buildables = build( auto buildables = Installable::build(
getEvalStore(), store, getEvalStore(), store,
dryRun ? Realise::Derivation : Realise::Outputs, dryRun ? Realise::Derivation : Realise::Outputs,
installables, buildMode); installables, buildMode);

View file

@ -97,13 +97,13 @@ struct CmdBundle : InstallableCommand
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
PathSet context2; PathSet context2;
StorePath drvPath = store->parseStorePath(evalState->coerceToPath(*attr1->pos, *attr1->value, context2)); auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2);
auto attr2 = vRes->attrs->get(evalState->sOutPath); auto attr2 = vRes->attrs->get(evalState->sOutPath);
if (!attr2) if (!attr2)
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
StorePath outPath = store->parseStorePath(evalState->coerceToPath(*attr2->pos, *attr2->value, context2)); auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2);
store->buildPaths({ DerivedPath::Built { drvPath } }); store->buildPaths({ DerivedPath::Built { drvPath } });

View file

@ -307,7 +307,7 @@ struct Common : InstallableCommand, MixProfile
for (auto & [installable_, dir_] : redirects) { for (auto & [installable_, dir_] : redirects) {
auto dir = absPath(dir_); auto dir = absPath(dir_);
auto installable = parseInstallable(store, installable_); auto installable = parseInstallable(store, installable_);
auto builtPaths = toStorePaths( auto builtPaths = Installable::toStorePaths(
getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable}); getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable});
for (auto & path: builtPaths) { for (auto & path: builtPaths) {
auto from = store->printStorePath(path); auto from = store->printStorePath(path);
@ -347,7 +347,7 @@ struct Common : InstallableCommand, MixProfile
if (path && hasSuffix(path->to_string(), "-env")) if (path && hasSuffix(path->to_string(), "-env"))
return *path; return *path;
else { else {
auto drvs = toDerivations(store, {installable}); auto drvs = Installable::toDerivations(store, {installable});
if (drvs.size() != 1) if (drvs.size() != 1)
throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations",
@ -511,7 +511,8 @@ struct CmdDevelop : Common, MixEnvironment
nixpkgsLockFlags); nixpkgsLockFlags);
shell = store->printStorePath( shell = store->printStorePath(
toStorePath(getEvalStore(), store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash"; Installable::toStorePath(getEvalStore(), store, Realise::Outputs, OperateOn::Output, bashInstallable))
+ "/bin/bash";
} catch (Error &) { } catch (Error &) {
ignoreException(); ignoreException();
} }

View file

@ -131,9 +131,9 @@ struct CmdDiffClosures : SourceExprCommand
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto before = parseInstallable(store, _before); auto before = parseInstallable(store, _before);
auto beforePath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, before); auto beforePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, before);
auto after = parseInstallable(store, _after); auto after = parseInstallable(store, _after);
auto afterPath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after); auto afterPath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after);
printClosureDiff(store, beforePath, afterPath, ""); printClosureDiff(store, beforePath, afterPath, "");
} }
}; };

View file

@ -327,7 +327,7 @@ struct CmdFlakeCheck : FlakeCommand
if (!drvInfo) if (!drvInfo)
throw Error("flake attribute '%s' is not a derivation", attrPath); throw Error("flake attribute '%s' is not a derivation", attrPath);
// FIXME: check meta attributes // FIXME: check meta attributes
return std::make_optional(store->parseStorePath(drvInfo->queryDrvPath())); return drvInfo->queryDrvPath();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath)); e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
reportError(e); reportError(e);

View file

@ -61,6 +61,27 @@ struct ProfileElement
{ {
return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths); return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
} }
void updateStorePaths(ref<Store> evalStore, ref<Store> store, Installable & installable)
{
// FIXME: respect meta.outputsToInstall
storePaths.clear();
for (auto & buildable : getBuiltPaths(evalStore, store, installable.toDerivedPaths())) {
std::visit(overloaded {
[&](const BuiltPath::Opaque & bo) {
storePaths.insert(bo.path);
},
[&](const BuiltPath::Built & bfd) {
// TODO: Why are we querying if we know the output
// names already? Is it just to figure out what the
// default one is?
for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) {
storePaths.insert(output.second);
}
},
}, buildable.raw());
}
}
}; };
struct ProfileManifest struct ProfileManifest
@ -105,7 +126,7 @@ struct ProfileManifest
for (auto & drvInfo : drvInfos) { for (auto & drvInfo : drvInfos) {
ProfileElement element; ProfileElement element;
element.storePaths = {state.store->parseStorePath(drvInfo.queryOutPath())}; element.storePaths = {drvInfo.queryOutPath()};
elements.emplace_back(std::move(element)); elements.emplace_back(std::move(element));
} }
} }
@ -232,53 +253,25 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{ {
ProfileManifest manifest(*getEvalState(), *profile); ProfileManifest manifest(*getEvalState(), *profile);
std::vector<DerivedPath> pathsToBuild; auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
for (auto & installable : installables) { for (auto & installable : installables) {
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) { ProfileElement element;
auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
ProfileElement element; if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
if (!drv.outPath) // FIXME: make build() return this?
throw UnimplementedError("CA derivations are not yet supported by 'nix profile'"); auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{ element.source = ProfileElementSource{
installable2->flakeRef, installable2->flakeRef,
resolvedRef, resolvedRef,
attrPath, attrPath,
}; };
pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, StringSet{drv.outputName}});
manifest.elements.emplace_back(std::move(element));
} else {
auto buildables = build(getEvalStore(), store, Realise::Outputs, {installable}, bmNormal);
for (auto & buildable : buildables) {
ProfileElement element;
std::visit(overloaded {
[&](const BuiltPath::Opaque & bo) {
pathsToBuild.push_back(bo);
element.storePaths.insert(bo.path);
},
[&](const BuiltPath::Built & bfd) {
// TODO: Why are we querying if we know the output
// names already? Is it just to figure out what the
// default one is?
for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) {
pathsToBuild.push_back(DerivedPath::Built{bfd.drvPath, {output.first}});
element.storePaths.insert(output.second);
}
},
}, buildable.raw());
manifest.elements.emplace_back(std::move(element));
}
} }
}
store->buildPaths(pathsToBuild); element.updateStorePaths(getEvalStore(), store, *installable);
manifest.elements.push_back(std::move(element));
}
updateProfile(manifest.build(store)); updateProfile(manifest.build(store));
} }
@ -373,15 +366,15 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
if (removedCount == 0) { if (removedCount == 0) {
for (auto matcher: matchers) { for (auto matcher: matchers) {
if (const size_t* index = std::get_if<size_t>(&matcher)){ if (const size_t * index = std::get_if<size_t>(&matcher)){
warn("'%d' is not a valid index in profile", *index); warn("'%d' is not a valid index", *index);
} else if (const Path* path = std::get_if<Path>(&matcher)){ } else if (const Path * path = std::get_if<Path>(&matcher)){
warn("'%s' does not match any paths in profile", *path); warn("'%s' does not match any paths", *path);
} else if (const RegexPattern* regex = std::get_if<RegexPattern>(&matcher)){ } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
warn("'%s' does not match any packages in profile", regex->pattern); warn("'%s' does not match any packages", regex->pattern);
} }
} }
warn ("Try `nix profile list` to see the current profile."); warn ("Use 'nix profile list' to see the current profile.");
} }
updateProfile(newManifest.build(store)); updateProfile(newManifest.build(store));
} }
@ -407,8 +400,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
auto matchers = getMatchers(store); auto matchers = getMatchers(store);
// FIXME: code duplication std::vector<std::shared_ptr<Installable>> installables;
std::vector<DerivedPath> pathsToBuild; std::vector<size_t> indices;
auto upgradedCount = 0; auto upgradedCount = 0;
@ -423,49 +416,53 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking '%s' for updates", element.source->attrPath)); fmt("checking '%s' for updates", element.source->attrPath));
InstallableFlake installable( auto installable = std::make_shared<InstallableFlake>(
this, this,
getEvalState(), getEvalState(),
FlakeRef(element.source->originalRef), FlakeRef(element.source->originalRef),
"", "",
{element.source->attrPath}, Strings{element.source->attrPath},
{}, Strings{},
lockFlags); lockFlags);
auto [attrPath, resolvedRef, drv] = installable.toDerivation(); auto [attrPath, resolvedRef, drv] = installable->toDerivation();
if (element.source->resolvedRef == resolvedRef) continue; if (element.source->resolvedRef == resolvedRef) continue;
printInfo("upgrading '%s' from flake '%s' to '%s'", printInfo("upgrading '%s' from flake '%s' to '%s'",
element.source->attrPath, element.source->resolvedRef, resolvedRef); element.source->attrPath, element.source->resolvedRef, resolvedRef);
if (!drv.outPath)
throw UnimplementedError("CA derivations are not yet supported by 'nix profile'");
element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{ element.source = ProfileElementSource{
installable.flakeRef, installable->flakeRef,
resolvedRef, resolvedRef,
attrPath, attrPath,
}; };
pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, {drv.outputName}}); installables.push_back(installable);
indices.push_back(i);
} }
} }
if (upgradedCount == 0) { if (upgradedCount == 0) {
for (auto & matcher : matchers) { for (auto & matcher : matchers) {
if (const size_t* index = std::get_if<size_t>(&matcher)){ if (const size_t * index = std::get_if<size_t>(&matcher)){
warn("'%d' is not a valid index in profile", *index); warn("'%d' is not a valid index", *index);
} else if (const Path* path = std::get_if<Path>(&matcher)){ } else if (const Path * path = std::get_if<Path>(&matcher)){
warn("'%s' does not match any paths in profile", *path); warn("'%s' does not match any paths", *path);
} else if (const RegexPattern* regex = std::get_if<RegexPattern>(&matcher)){ } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
warn("'%s' does not match any packages in profile", regex->pattern); warn("'%s' does not match any packages", regex->pattern);
} }
} }
warn ("Use 'nix profile list' to see the current profile."); warn ("Use 'nix profile list' to see the current profile.");
} }
store->buildPaths(pathsToBuild); auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
for (size_t i = 0; i < installables.size(); ++i) {
auto & installable = installables.at(i);
auto & element = manifest.elements[indices.at(i)];
element.updateStorePaths(getEvalStore(), store, *installable);
}
updateProfile(manifest.build(store)); updateProfile(manifest.build(store));
} }

View file

@ -384,13 +384,12 @@ StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(*state, v, false); auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo) if (!drvInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it"); throw Error("expression does not evaluate to a derivation, so I can't build it");
Path drvPathRaw = drvInfo->queryDrvPath(); auto drvPath = drvInfo->queryDrvPath();
if (drvPathRaw == "") if (!drvPath)
throw Error("expression did not evaluate to a valid derivation (no drv path)"); throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
StorePath drvPath = state->store->parseStorePath(drvPathRaw); if (!state->store->isValidPath(*drvPath))
if (!state->store->isValidPath(drvPath)) throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
throw Error("expression did not evaluate to a valid derivation (invalid drv path)"); return *drvPath;
return drvPath;
} }
@ -780,8 +779,11 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "«derivation "; str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath); Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context; PathSet context;
Path drvPath = i != v.attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : "???"; if (i != v.attrs->end())
str << drvPath << "»"; str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
else
str << "???";
str << "»";
} }
else if (maxDepth > 0) { else if (maxDepth > 0) {
@ -798,7 +800,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
else else
printStringValue(str, i.first.c_str()); printStringValue(str, i.first.c_str());
str << " = "; str << " = ";
if (seen.find(i.second) != seen.end()) if (seen.count(i.second))
str << "«repeated»"; str << "«repeated»";
else else
try { try {

View file

@ -91,7 +91,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto outPaths = toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
auto accessor = store->getFSAccessor(); auto accessor = store->getFSAccessor();

View file

@ -40,7 +40,7 @@ struct CmdShowDerivation : InstallablesCommand
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto drvPaths = toDerivations(store, installables, true); auto drvPaths = Installable::toDerivations(store, installables, true);
if (recursive) { if (recursive) {
StorePathSet closure; StorePathSet closure;

View file

@ -2,6 +2,7 @@
#include "common-args.hh" #include "common-args.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
using namespace nix; using namespace nix;
@ -32,12 +33,14 @@ struct CmdStoreDelete : StorePathsCommand
void run(ref<Store> store, std::vector<StorePath> && storePaths) override void run(ref<Store> store, std::vector<StorePath> && storePaths) override
{ {
auto & gcStore = requireGcStore(*store);
for (auto & path : storePaths) for (auto & path : storePaths)
options.pathsToDelete.insert(path); options.pathsToDelete.insert(path);
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);
store->collectGarbage(options, results); gcStore.collectGarbage(options, results);
} }
}; };

View file

@ -2,6 +2,7 @@
#include "common-args.hh" #include "common-args.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh"
using namespace nix; using namespace nix;
@ -33,10 +34,12 @@ struct CmdStoreGC : StoreCommand, MixDryRun
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto & gcStore = requireGcStore(*store);
options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead; options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead;
GCResults results; GCResults results;
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results); PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
store->collectGarbage(options, results); gcStore.collectGarbage(options, results);
} }
}; };

View file

@ -82,9 +82,9 @@ struct CmdWhyDepends : SourceExprCommand
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto package = parseInstallable(store, _package); auto package = parseInstallable(store, _package);
auto packagePath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package); auto packagePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package);
auto dependency = parseInstallable(store, _dependency); auto dependency = parseInstallable(store, _dependency);
auto dependencyPath = toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency); auto dependencyPath = Installable::toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency);
auto dependencyPathHash = dependencyPath.hashPart(); auto dependencyPathHash = dependencyPath.hashPart();
StorePathSet closure; StorePathSet closure;
@ -171,12 +171,6 @@ struct CmdWhyDepends : SourceExprCommand
node.visited ? "\e[38;5;244m" : "", node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? "" : "", firstPad != "" ? "" : "",
pathS); pathS);
} else {
logger->cout("%s%s%s%s" ANSI_NORMAL,
firstPad,
node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? treeLast : "",
pathS);
} }
if (node.path == dependencyPath && !all if (node.path == dependencyPath && !all
@ -184,7 +178,7 @@ struct CmdWhyDepends : SourceExprCommand
throw BailOut(); throw BailOut();
if (node.visited) return; if (node.visited) return;
node.visited = true; if (precise) node.visited = true;
/* Sort the references by distance to `dependency` to /* Sort the references by distance to `dependency` to
ensure that the shortest path is printed first. */ ensure that the shortest path is printed first. */
@ -267,6 +261,16 @@ struct CmdWhyDepends : SourceExprCommand
if (!all) break; if (!all) break;
} }
if (!precise) {
auto pathS = store->printStorePath(ref.second->path);
logger->cout("%s%s%s%s" ANSI_NORMAL,
firstPad,
ref.second->visited ? "\e[38;5;244m" : "",
last ? treeLast : treeConn,
pathS);
node.visited = true;
}
printNode(*ref.second, printNode(*ref.second,
tailPad + (last ? treeNull : treeLine), tailPad + (last ? treeNull : treeLine),
tailPad + (last ? treeNull : treeLine)); tailPad + (last ? treeNull : treeLine));
@ -275,6 +279,9 @@ struct CmdWhyDepends : SourceExprCommand
RunPager pager; RunPager pager;
try { try {
if (!precise) {
logger->cout("%s", store->printStorePath(graph.at(packagePath).path));
}
printNode(graph.at(packagePath), "", ""); printNode(graph.at(packagePath), "", "");
} catch (BailOut & ) { } } catch (BailOut & ) { }
} }

9
tests/bash-profile.sh Normal file
View file

@ -0,0 +1,9 @@
source common.sh
sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh
user=$(whoami)
rm -rf $TEST_HOME $TEST_ROOT/profile-var
mkdir -p $TEST_HOME
USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set"
USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency

View file

@ -2,7 +2,7 @@ source common.sh
file=build-hook-ca-floating.nix file=build-hook-ca-floating.nix
sed -i 's/experimental-features .*/& ca-derivations/' "$NIX_CONF_DIR"/nix.conf enableFeatures "ca-derivations ca-references"
CONTENT_ADDRESSED=true CONTENT_ADDRESSED=true

View file

@ -37,7 +37,7 @@ testCutoffFor () {
} }
testCutoff () { testCutoff () {
# Don't directly build depenentCA, that way we'll make sure we dodn't rely on # Don't directly build dependentCA, that way we'll make sure we don't rely on
# dependent derivations always being already built. # dependent derivations always being already built.
#testDerivation dependentCA #testDerivation dependentCA
testCutoffFor transitivelyDependentCA testCutoffFor transitivelyDependentCA

View file

@ -1,5 +1,5 @@
source ../common.sh source ../common.sh
sed -i 's/experimental-features .*/& ca-derivations ca-references/' "$NIX_CONF_DIR"/nix.conf enableFeatures "ca-derivations ca-references"
restartDaemon restartDaemon

View file

@ -2,8 +2,6 @@ source ./common.sh
requireDaemonNewerThan "2.4pre20210625" requireDaemonNewerThan "2.4pre20210625"
sed -i 's/experimental-features .*/& ca-derivations ca-references/' "$NIX_CONF_DIR"/nix.conf
export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE_DIR="$TEST_ROOT/remote_store"
export REMOTE_STORE="file://$REMOTE_STORE_DIR" export REMOTE_STORE="file://$REMOTE_STORE_DIR"

View file

@ -2,9 +2,6 @@
source common.sh source common.sh
# Globally enable the ca derivations experimental flag
sed -i 's/experimental-features = .*/& ca-derivations ca-references/' "$NIX_CONF_DIR/nix.conf"
export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE_DIR="$TEST_ROOT/remote_store"
export REMOTE_STORE="file://$REMOTE_STORE_DIR" export REMOTE_STORE="file://$REMOTE_STORE_DIR"

View file

@ -2,8 +2,6 @@
source common.sh source common.sh
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
FLAKE_PATH=path:$PWD FLAKE_PATH=path:$PWD
nix run --no-write-lock-file $FLAKE_PATH#runnable nix run --no-write-lock-file $FLAKE_PATH#runnable

View file

@ -2,8 +2,6 @@
source common.sh source common.sh
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
CONTENT_ADDRESSED=true CONTENT_ADDRESSED=true
cd .. cd ..
source ./nix-shell.sh source ./nix-shell.sh

View file

@ -4,8 +4,6 @@ source common.sh
requireDaemonNewerThan "2.4pre20210626" requireDaemonNewerThan "2.4pre20210626"
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
export NIX_TESTS_CA_BY_DEFAULT=1 export NIX_TESTS_CA_BY_DEFAULT=1
cd .. cd ..
source ./post-hook.sh source ./post-hook.sh

View file

@ -4,8 +4,6 @@ source common.sh
requireDaemonNewerThan "2.4pre20210623" requireDaemonNewerThan "2.4pre20210623"
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
export NIX_TESTS_CA_BY_DEFAULT=1 export NIX_TESTS_CA_BY_DEFAULT=1
cd .. cd ..
source ./recursive.sh source ./recursive.sh

View file

@ -1,8 +1,5 @@
source common.sh source common.sh
# Globally enable the ca derivations experimental flag
sed -i 's/experimental-features = .*/& ca-derivations ca-references/' "$NIX_CONF_DIR/nix.conf"
clearStore clearStore
clearCache clearCache

View file

@ -87,15 +87,20 @@ startDaemon() {
if [[ "$NIX_REMOTE" == daemon ]]; then if [[ "$NIX_REMOTE" == daemon ]]; then
return return
fi fi
# Start the daemon, wait for the socket to appear. !!! # Start the daemon, wait for the socket to appear.
# nix-daemon should have an option to fork into the background.
rm -f $NIX_DAEMON_SOCKET_PATH rm -f $NIX_DAEMON_SOCKET_PATH
PATH=$DAEMON_PATH nix daemon & PATH=$DAEMON_PATH nix-daemon&
pidDaemon=$!
for ((i = 0; i < 300; i++)); do for ((i = 0; i < 300; i++)); do
if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then
DAEMON_STARTED=1
break;
fi
sleep 0.1 sleep 0.1
done done
pidDaemon=$! if [[ -z ${DAEMON_STARTED+x} ]]; then
fail "Didnt manage to start the daemon"
fi
trap "killDaemon" EXIT trap "killDaemon" EXIT
export NIX_REMOTE=daemon export NIX_REMOTE=daemon
} }
@ -103,10 +108,10 @@ startDaemon() {
killDaemon() { killDaemon() {
kill $pidDaemon kill $pidDaemon
for i in {0..100}; do for i in {0..100}; do
kill -0 $pidDaemon || break kill -0 $pidDaemon 2> /dev/null || break
sleep 0.1 sleep 0.1
done done
kill -9 $pidDaemon || true kill -9 $pidDaemon 2> /dev/null || true
wait $pidDaemon || true wait $pidDaemon || true
trap "" EXIT trap "" EXIT
} }
@ -167,10 +172,15 @@ needLocalStore() {
} }
# Just to make it easy to find which tests should be fixed # Just to make it easy to find which tests should be fixed
buggyNeedLocalStore () { buggyNeedLocalStore() {
needLocalStore needLocalStore
} }
enableFeatures() {
local features="$1"
sed -i 's/experimental-features .*/& '"$features"'/' "$NIX_CONF_DIR"/nix.conf
}
set -x set -x
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then

View file

@ -9,6 +9,10 @@ outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file:
cmp $outPath fetchurl.sh cmp $outPath fetchurl.sh
# Do not re-fetch paths already present.
outPath2=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file:///does-not-exist/must-remain-unused/fetchurl.sh --argstr sha256 $hash --no-out-link)
test "$outPath" == "$outPath2"
# Now using a base-64 hash. # Now using a base-64 hash.
clearStore clearStore

View file

@ -89,10 +89,11 @@ nix_tests = \
build.sh \ build.sh \
ca/nix-run.sh \ ca/nix-run.sh \
db-migration.sh \ db-migration.sh \
nix-profile.sh \ bash-profile.sh \
pass-as-file.sh \ pass-as-file.sh \
describe-stores.sh \ describe-stores.sh \
store-ping.sh store-ping.sh \
nix-profile.sh
ifeq ($(HAVE_LIBCPUID), 1) ifeq ($(HAVE_LIBCPUID), 1)
nix_tests += compute-levels.sh nix_tests += compute-levels.sh

View file

@ -1,9 +1,97 @@
source common.sh source common.sh
sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh clearStore
clearProfiles
user=$(whoami) enableFeatures "ca-derivations ca-references"
rm -rf $TEST_HOME $TEST_ROOT/profile-var restartDaemon
mkdir -p $TEST_HOME
USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set" # Make a flake.
USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency flake1Dir=$TEST_ROOT/flake1
mkdir -p $flake1Dir
cat > $flake1Dir/flake.nix <<EOF
{
description = "Bla bla";
outputs = { self }: with import ./config.nix; rec {
packages.$system.default = mkDerivation {
name = "profile-test-\${builtins.readFile ./version}";
builder = builtins.toFile "builder.sh"
''
mkdir -p \$out/bin
cat > \$out/bin/hello <<EOF
#! ${shell}
echo Hello \${builtins.readFile ./who}
EOF
chmod +x \$out/bin/hello
echo DONE
'';
__contentAddressed = import ./ca.nix;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
};
};
}
EOF
printf World > $flake1Dir/who
printf 1.0 > $flake1Dir/version
printf false > $flake1Dir/ca.nix
cp ./config.nix $flake1Dir/
# Test upgrading from nix-env.
nix-env -f ./user-envs.nix -i foo-1.0
nix profile list | grep '0 - - .*-foo-1.0'
nix profile install $flake1Dir -L
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
nix profile history
nix profile history | grep "packages.$system.default: ∅ -> 1.0"
nix profile diff-closures | grep 'env-manifest.nix: ε → ∅'
# Test upgrading a package.
printf NixOS > $flake1Dir/who
printf 2.0 > $flake1Dir/version
nix profile upgrade 1
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]]
nix profile history | grep "packages.$system.default: 1.0 -> 2.0"
# Test 'history', 'diff-closures'.
nix profile diff-closures
# Test rollback.
nix profile rollback
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
# Test uninstall.
[ -e $TEST_HOME/.nix-profile/bin/foo ]
nix profile remove 0
(! [ -e $TEST_HOME/.nix-profile/bin/foo ])
nix profile history | grep 'foo: 1.0 -> ∅'
nix profile diff-closures | grep 'Version 3 -> 4'
# Test installing a non-flake package.
nix profile install --file ./simple.nix ''
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
nix profile remove 1
nix profile install $(nix-build --no-out-link ./simple.nix)
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
# Test wipe-history.
nix profile wipe-history
[[ $(nix profile history | grep Version | wc -l) -eq 1 ]]
# Test upgrade to CA package.
printf true > $flake1Dir/ca.nix
printf 3.0 > $flake1Dir/version
nix profile upgrade 0
nix profile history | grep "packages.$system.default: 1.0 -> 3.0"
# Test new install of CA package.
nix profile remove 0
printf 4.0 > $flake1Dir/version
printf Utrecht > $flake1Dir/who
nix profile install $flake1Dir
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]]

View file

@ -26,10 +26,14 @@ test_tarball() {
nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball
nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)" nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)"
# Do not re-fetch paths already present
nix-build -o $TEST_ROOT/result -E "import (fetchTarball { url = file:///does-not-exist/must-remain-unused/$tarball; sha256 = \"$hash\"; })"
nix-build -o $TEST_ROOT/result -E "import (fetchTree file://$tarball)" nix-build -o $TEST_ROOT/result -E "import (fetchTree file://$tarball)"
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; })" nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; })"
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })" nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })"
# Do not re-fetch paths already present
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file:///does-not-exist/must-remain-unused/$tarball; narHash = \"$hash\"; })"
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" 2>&1 | grep 'NAR hash mismatch in input' nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" 2>&1 | grep 'NAR hash mismatch in input'
nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" >&2 nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" >&2

View file

@ -9,7 +9,6 @@ clearProfiles
# Query installed: should be empty. # Query installed: should be empty.
test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
mkdir -p $TEST_HOME
nix-env --switch-profile $profiles/test nix-env --switch-profile $profiles/test
# Query available: should contain several. # Query available: should contain several.