mirror of
https://github.com/NixOS/nix.git
synced 2025-11-16 23:42:43 +01:00
Merge commit 'aa99005004' into ca-drv-exotic
This commit is contained in:
commit
f7f44f7c96
315 changed files with 6194 additions and 3610 deletions
|
|
@ -16,17 +16,33 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
|
|||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
|
||||
const Setting<std::string> compression{(StoreConfig*) this, "xz", "compression", "NAR compression method ('xz', 'bzip2', 'gzip', 'zstd', or 'none')"};
|
||||
const Setting<bool> writeNARListing{(StoreConfig*) this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"};
|
||||
const Setting<bool> writeDebugInfo{(StoreConfig*) this, false, "index-debug-info", "whether to index DWARF debug info files by build ID"};
|
||||
const Setting<Path> secretKeyFile{(StoreConfig*) this, "", "secret-key", "path to secret key used to sign the binary cache"};
|
||||
const Setting<Path> localNarCache{(StoreConfig*) this, "", "local-nar-cache", "path to a local cache of NARs"};
|
||||
const Setting<std::string> compression{(StoreConfig*) this, "xz", "compression",
|
||||
"NAR compression method (`xz`, `bzip2`, `gzip`, `zstd`, or `none`)."};
|
||||
|
||||
const Setting<bool> writeNARListing{(StoreConfig*) this, false, "write-nar-listing",
|
||||
"Whether to write a JSON file that lists the files in each NAR."};
|
||||
|
||||
const Setting<bool> writeDebugInfo{(StoreConfig*) this, false, "index-debug-info",
|
||||
R"(
|
||||
Whether to index DWARF debug info files by build ID. This allows [`dwarffs`](https://github.com/edolstra/dwarffs) to
|
||||
fetch debug info on demand
|
||||
)"};
|
||||
|
||||
const Setting<Path> secretKeyFile{(StoreConfig*) this, "", "secret-key",
|
||||
"Path to the secret key used to sign the binary cache."};
|
||||
|
||||
const Setting<Path> localNarCache{(StoreConfig*) this, "", "local-nar-cache",
|
||||
"Path to a local cache of NARs fetched from this binary cache, used by commands such as `nix store cat`."};
|
||||
|
||||
const Setting<bool> parallelCompression{(StoreConfig*) this, false, "parallel-compression",
|
||||
"enable multi-threading compression for NARs, available for xz and zstd only currently"};
|
||||
"Enable multi-threaded compression of NARs. This is currently only available for `xz` and `zstd`."};
|
||||
|
||||
const Setting<int> compressionLevel{(StoreConfig*) this, -1, "compression-level",
|
||||
"specify 'preset level' of compression to be used with NARs: "
|
||||
"meaning and accepted range of values depends on compression method selected, "
|
||||
"other than -1 which we reserve to indicate Nix defaults should be used"};
|
||||
R"(
|
||||
The *preset level* to be used when compressing NARs.
|
||||
The meaning and accepted values depend on the compression method selected.
|
||||
`-1` specifies that the default compression level should be used.
|
||||
)"};
|
||||
};
|
||||
|
||||
class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
|
||||
|
|
|
|||
|
|
@ -199,10 +199,10 @@ void DerivationGoal::haveDerivation()
|
|||
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
|
||||
|
||||
if (!drv->type().hasKnownOutputPaths())
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
||||
if (!drv->type().isPure()) {
|
||||
settings.requireExperimentalFeature(Xp::ImpureDerivations);
|
||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
|
||||
for (auto & [outputName, output] : drv->outputs) {
|
||||
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
|
||||
|
|
@ -336,7 +336,7 @@ void DerivationGoal::gaveUpOnSubstitution()
|
|||
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
|
||||
/* Ensure that pure, non-fixed-output derivations don't
|
||||
depend on impure derivations. */
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
|
||||
auto inputDrv = worker.evalStore.readDerivation(i.first);
|
||||
if (!inputDrv.type().isPure())
|
||||
throw Error("pure derivation '%s' depends on impure derivation '%s'",
|
||||
|
|
@ -477,7 +477,7 @@ void DerivationGoal::inputsRealised()
|
|||
ca.fixed
|
||||
/* Can optionally resolve if fixed, which is good
|
||||
for avoiding unnecessary rebuilds. */
|
||||
? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
|
||||
? experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
||||
/* Must resolve if floating and there are any inputs
|
||||
drvs. */
|
||||
: true);
|
||||
|
|
@ -488,7 +488,7 @@ void DerivationGoal::inputsRealised()
|
|||
}, drvType.raw());
|
||||
|
||||
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
||||
/* We are be able to resolve this derivation based on the
|
||||
now-known results of dependencies. If so, we become a
|
||||
|
|
@ -732,7 +732,7 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath)
|
|||
tmpPath (the replacement), so we have to move it out of the
|
||||
way first. We'd better not be interrupted here, because if
|
||||
we're repairing (say) Glibc, we end up with a broken system. */
|
||||
Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str();
|
||||
Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), random());
|
||||
if (pathExists(storePath))
|
||||
movePath(storePath, oldPath);
|
||||
|
||||
|
|
@ -1352,7 +1352,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
|
|||
};
|
||||
}
|
||||
auto drvOutput = DrvOutput{info.outputHash, i.first};
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
||||
info.known = {
|
||||
.path = real->outPath,
|
||||
|
|
|
|||
|
|
@ -61,20 +61,25 @@ void DrvOutputSubstitutionGoal::tryNext()
|
|||
|
||||
// FIXME: Make async
|
||||
// outputInfo = sub->queryRealisation(id);
|
||||
outPipe.create();
|
||||
promise = decltype(promise)();
|
||||
|
||||
/* The callback of the curl download below can outlive `this` (if
|
||||
some other error occurs), so it must not touch `this`. So put
|
||||
the shared state in a separate refcounted object. */
|
||||
downloadState = std::make_shared<DownloadState>();
|
||||
downloadState->outPipe.create();
|
||||
|
||||
sub->queryRealisation(
|
||||
id, { [&](std::future<std::shared_ptr<const Realisation>> res) {
|
||||
id,
|
||||
{ [downloadState(downloadState)](std::future<std::shared_ptr<const Realisation>> res) {
|
||||
try {
|
||||
Finally updateStats([this]() { outPipe.writeSide.close(); });
|
||||
promise.set_value(res.get());
|
||||
Finally updateStats([&]() { downloadState->outPipe.writeSide.close(); });
|
||||
downloadState->promise.set_value(res.get());
|
||||
} catch (...) {
|
||||
promise.set_exception(std::current_exception());
|
||||
downloadState->promise.set_exception(std::current_exception());
|
||||
}
|
||||
} });
|
||||
|
||||
worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false);
|
||||
worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false);
|
||||
|
||||
state = &DrvOutputSubstitutionGoal::realisationFetched;
|
||||
}
|
||||
|
|
@ -84,7 +89,7 @@ void DrvOutputSubstitutionGoal::realisationFetched()
|
|||
worker.childTerminated(this);
|
||||
|
||||
try {
|
||||
outputInfo = promise.get_future().get();
|
||||
outputInfo = downloadState->promise.get_future().get();
|
||||
} catch (std::exception & e) {
|
||||
printError(e.what());
|
||||
substituterFailed = true;
|
||||
|
|
@ -155,7 +160,7 @@ void DrvOutputSubstitutionGoal::work()
|
|||
|
||||
void DrvOutputSubstitutionGoal::handleEOF(int fd)
|
||||
{
|
||||
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
||||
if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Worker;
|
|||
// 2. Substitute the corresponding output path
|
||||
// 3. Register the output info
|
||||
class DrvOutputSubstitutionGoal : public Goal {
|
||||
private:
|
||||
|
||||
// The drv output we're trying to substitue
|
||||
DrvOutput id;
|
||||
|
||||
|
|
@ -30,9 +30,13 @@ private:
|
|||
/* The current substituter. */
|
||||
std::shared_ptr<Store> sub;
|
||||
|
||||
Pipe outPipe;
|
||||
std::thread thr;
|
||||
std::promise<std::shared_ptr<const Realisation>> promise;
|
||||
struct DownloadState
|
||||
{
|
||||
Pipe outPipe;
|
||||
std::promise<std::shared_ptr<const Realisation>> promise;
|
||||
};
|
||||
|
||||
std::shared_ptr<DownloadState> downloadState;
|
||||
|
||||
/* Whether a substituter failed. */
|
||||
bool substituterFailed = false;
|
||||
|
|
|
|||
|
|
@ -78,9 +78,9 @@ void Goal::amDone(ExitCode result, std::optional<Error> ex)
|
|||
}
|
||||
|
||||
|
||||
void Goal::trace(const FormatOrString & fs)
|
||||
void Goal::trace(std::string_view s)
|
||||
{
|
||||
debug("%1%: %2%", name, fs.s);
|
||||
debug("%1%: %2%", name, s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
|
|||
abort();
|
||||
}
|
||||
|
||||
void trace(const FormatOrString & fs);
|
||||
void trace(std::string_view s);
|
||||
|
||||
std::string getName()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@ HookInstance::HookInstance()
|
|||
/* Fork the hook. */
|
||||
pid = startProcess([&]() {
|
||||
|
||||
commonChildInit(fromHook);
|
||||
if (dup2(fromHook.writeSide.get(), STDERR_FILENO) == -1)
|
||||
throw SysError("cannot pipe standard error into log file");
|
||||
|
||||
commonChildInit();
|
||||
|
||||
if (chdir("/") == -1) throw SysError("changing into /");
|
||||
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ void LocalDerivationGoal::closeReadPipes()
|
|||
if (hook) {
|
||||
DerivationGoal::closeReadPipes();
|
||||
} else
|
||||
builderOut.readSide = -1;
|
||||
builderOut.close();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -413,7 +413,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
)
|
||||
{
|
||||
#if __linux__
|
||||
settings.requireExperimentalFeature(Xp::Cgroups);
|
||||
experimentalFeatureSettings.require(Xp::Cgroups);
|
||||
|
||||
auto cgroupFS = getCgroupFS();
|
||||
if (!cgroupFS)
|
||||
|
|
@ -650,7 +650,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
/* Clean up the chroot directory automatically. */
|
||||
autoDelChroot = std::make_shared<AutoDelete>(chrootRootDir);
|
||||
|
||||
printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % chrootRootDir);
|
||||
printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir);
|
||||
|
||||
// FIXME: make this 0700
|
||||
if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1)
|
||||
|
|
@ -753,8 +753,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
throw Error("home directory '%1%' exists; please remove it to assure purity of builds without sandboxing", homeDir);
|
||||
|
||||
if (useChroot && settings.preBuildHook != "" && dynamic_cast<Derivation *>(drv.get())) {
|
||||
printMsg(lvlChatty, format("executing pre-build hook '%1%'")
|
||||
% settings.preBuildHook);
|
||||
printMsg(lvlChatty, "executing pre-build hook '%1%'", settings.preBuildHook);
|
||||
auto args = useChroot ? Strings({worker.store.printStorePath(drvPath), chrootRootDir}) :
|
||||
Strings({ worker.store.printStorePath(drvPath) });
|
||||
enum BuildHookState {
|
||||
|
|
@ -803,15 +802,13 @@ void LocalDerivationGoal::startBuilder()
|
|||
/* Create the log file. */
|
||||
Path logFile = openLogFile();
|
||||
|
||||
/* Create a pipe to get the output of the builder. */
|
||||
//builderOut.create();
|
||||
|
||||
builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (!builderOut.readSide)
|
||||
/* Create a pseudoterminal to get the output of the builder. */
|
||||
builderOut = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (!builderOut)
|
||||
throw SysError("opening pseudoterminal master");
|
||||
|
||||
// FIXME: not thread-safe, use ptsname_r
|
||||
std::string slaveName(ptsname(builderOut.readSide.get()));
|
||||
std::string slaveName = ptsname(builderOut.get());
|
||||
|
||||
if (buildUser) {
|
||||
if (chmod(slaveName.c_str(), 0600))
|
||||
|
|
@ -822,34 +819,34 @@ void LocalDerivationGoal::startBuilder()
|
|||
}
|
||||
#if __APPLE__
|
||||
else {
|
||||
if (grantpt(builderOut.readSide.get()))
|
||||
if (grantpt(builderOut.get()))
|
||||
throw SysError("granting access to pseudoterminal slave");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Mount the pt in the sandbox so that the "tty" command works.
|
||||
// FIXME: this doesn't work with the new devpts in the sandbox.
|
||||
if (useChroot)
|
||||
dirsInChroot[slaveName] = {slaveName, false};
|
||||
#endif
|
||||
|
||||
if (unlockpt(builderOut.readSide.get()))
|
||||
if (unlockpt(builderOut.get()))
|
||||
throw SysError("unlocking pseudoterminal");
|
||||
|
||||
builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
|
||||
if (!builderOut.writeSide)
|
||||
throw SysError("opening pseudoterminal slave");
|
||||
/* Open the slave side of the pseudoterminal and use it as stderr. */
|
||||
auto openSlave = [&]()
|
||||
{
|
||||
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
|
||||
if (!builderOut)
|
||||
throw SysError("opening pseudoterminal slave");
|
||||
|
||||
// Put the pt into raw mode to prevent \n -> \r\n translation.
|
||||
struct termios term;
|
||||
if (tcgetattr(builderOut.writeSide.get(), &term))
|
||||
throw SysError("getting pseudoterminal attributes");
|
||||
// Put the pt into raw mode to prevent \n -> \r\n translation.
|
||||
struct termios term;
|
||||
if (tcgetattr(builderOut.get(), &term))
|
||||
throw SysError("getting pseudoterminal attributes");
|
||||
|
||||
cfmakeraw(&term);
|
||||
cfmakeraw(&term);
|
||||
|
||||
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
|
||||
throw SysError("putting pseudoterminal into raw mode");
|
||||
if (tcsetattr(builderOut.get(), TCSANOW, &term))
|
||||
throw SysError("putting pseudoterminal into raw mode");
|
||||
|
||||
if (dup2(builderOut.get(), STDERR_FILENO) == -1)
|
||||
throw SysError("cannot pipe standard error into log file");
|
||||
};
|
||||
|
||||
buildResult.startTime = time(0);
|
||||
|
||||
|
|
@ -898,7 +895,16 @@ void LocalDerivationGoal::startBuilder()
|
|||
|
||||
usingUserNamespace = userNamespacesSupported();
|
||||
|
||||
Pipe sendPid;
|
||||
sendPid.create();
|
||||
|
||||
Pid helper = startProcess([&]() {
|
||||
sendPid.readSide.close();
|
||||
|
||||
/* We need to open the slave early, before
|
||||
CLONE_NEWUSER. Otherwise we get EPERM when running as
|
||||
root. */
|
||||
openSlave();
|
||||
|
||||
/* Drop additional groups here because we can't do it
|
||||
after we've created the new user namespace. FIXME:
|
||||
|
|
@ -920,11 +926,12 @@ void LocalDerivationGoal::startBuilder()
|
|||
|
||||
pid_t child = startProcess([&]() { runChild(); }, options);
|
||||
|
||||
writeFull(builderOut.writeSide.get(),
|
||||
fmt("%d %d\n", usingUserNamespace, child));
|
||||
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||
_exit(0);
|
||||
});
|
||||
|
||||
sendPid.writeSide.close();
|
||||
|
||||
if (helper.wait() != 0)
|
||||
throw Error("unable to start build process");
|
||||
|
||||
|
|
@ -936,10 +943,9 @@ void LocalDerivationGoal::startBuilder()
|
|||
userNamespaceSync.writeSide = -1;
|
||||
});
|
||||
|
||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get()));
|
||||
assert(ss.size() == 2);
|
||||
usingUserNamespace = ss[0] == "1";
|
||||
pid = string2Int<pid_t>(ss[1]).value();
|
||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||
assert(ss.size() == 1);
|
||||
pid = string2Int<pid_t>(ss[0]).value();
|
||||
|
||||
if (usingUserNamespace) {
|
||||
/* Set the UID/GID mapping of the builder's user namespace
|
||||
|
|
@ -994,21 +1000,21 @@ void LocalDerivationGoal::startBuilder()
|
|||
#endif
|
||||
{
|
||||
pid = startProcess([&]() {
|
||||
openSlave();
|
||||
runChild();
|
||||
});
|
||||
}
|
||||
|
||||
/* parent */
|
||||
pid.setSeparatePG(true);
|
||||
builderOut.writeSide = -1;
|
||||
worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true, true);
|
||||
worker.childStarted(shared_from_this(), {builderOut.get()}, true, true);
|
||||
|
||||
/* Check if setting up the build environment failed. */
|
||||
std::vector<std::string> msgs;
|
||||
while (true) {
|
||||
std::string msg = [&]() {
|
||||
try {
|
||||
return readLine(builderOut.readSide.get());
|
||||
return readLine(builderOut.get());
|
||||
} catch (Error & e) {
|
||||
auto status = pid.wait();
|
||||
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
||||
|
|
@ -1020,7 +1026,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
}();
|
||||
if (msg.substr(0, 1) == "\2") break;
|
||||
if (msg.substr(0, 1) == "\1") {
|
||||
FdSource source(builderOut.readSide.get());
|
||||
FdSource source(builderOut.get());
|
||||
auto ex = readError(source);
|
||||
ex.addTrace({}, "while setting up the build environment");
|
||||
throw ex;
|
||||
|
|
@ -1104,7 +1110,7 @@ void LocalDerivationGoal::initEnv()
|
|||
env["NIX_STORE"] = worker.store.storeDir;
|
||||
|
||||
/* The maximum number of cores to utilize for parallel building. */
|
||||
env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
|
||||
env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores);
|
||||
|
||||
initTmpDir();
|
||||
|
||||
|
|
@ -1155,10 +1161,10 @@ void LocalDerivationGoal::writeStructuredAttrs()
|
|||
|
||||
writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites));
|
||||
chownToBuilder(tmpDir + "/.attrs.sh");
|
||||
env["NIX_ATTRS_SH_FILE"] = tmpDir + "/.attrs.sh";
|
||||
env["NIX_ATTRS_SH_FILE"] = tmpDirInSandbox + "/.attrs.sh";
|
||||
writeFile(tmpDir + "/.attrs.json", rewriteStrings(json.dump(), inputRewrites));
|
||||
chownToBuilder(tmpDir + "/.attrs.json");
|
||||
env["NIX_ATTRS_JSON_FILE"] = tmpDir + "/.attrs.json";
|
||||
env["NIX_ATTRS_JSON_FILE"] = tmpDirInSandbox + "/.attrs.json";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1414,7 +1420,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
|||
|
||||
void LocalDerivationGoal::startDaemon()
|
||||
{
|
||||
settings.requireExperimentalFeature(Xp::RecursiveNix);
|
||||
experimentalFeatureSettings.require(Xp::RecursiveNix);
|
||||
|
||||
Store::Params params;
|
||||
params["path-info-cache-size"] = "0";
|
||||
|
|
@ -1650,7 +1656,7 @@ void LocalDerivationGoal::runChild()
|
|||
|
||||
try { /* child */
|
||||
|
||||
commonChildInit(builderOut);
|
||||
commonChildInit();
|
||||
|
||||
try {
|
||||
setupSeccomp();
|
||||
|
|
@ -2063,7 +2069,7 @@ void LocalDerivationGoal::runChild()
|
|||
|
||||
/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
|
||||
to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
|
||||
Path globalTmpDir = canonPath(getEnv("TMPDIR").value_or("/tmp"), true);
|
||||
Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
|
||||
|
||||
/* They don't like trailing slashes on subpath directives */
|
||||
if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
|
||||
|
|
@ -2274,7 +2280,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
|||
bool discardReferences = false;
|
||||
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||
settings.requireExperimentalFeature(Xp::DiscardReferences);
|
||||
experimentalFeatureSettings.require(Xp::DiscardReferences);
|
||||
if (auto output = get(*udr, outputName)) {
|
||||
if (!output->is_boolean())
|
||||
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
|
||||
|
|
@ -2698,7 +2704,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
|||
},
|
||||
.outPath = newInfo.path
|
||||
};
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
||||
&& drv->type().isPure())
|
||||
{
|
||||
signRealisation(thisRealisation);
|
||||
|
|
@ -2896,7 +2902,7 @@ void LocalDerivationGoal::deleteTmpDir(bool force)
|
|||
bool LocalDerivationGoal::isReadDesc(int fd)
|
||||
{
|
||||
return (hook && DerivationGoal::isReadDesc(fd)) ||
|
||||
(!hook && fd == builderOut.readSide.get());
|
||||
(!hook && fd == builderOut.get());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
/* The path of the temporary directory in the sandbox. */
|
||||
Path tmpDirInSandbox;
|
||||
|
||||
/* Pipe for the builder's standard output/error. */
|
||||
Pipe builderOut;
|
||||
/* Master side of the pseudoterminal used for the builder's
|
||||
standard output/error. */
|
||||
AutoCloseFD builderOut;
|
||||
|
||||
/* Pipe for synchronising updates to the builder namespaces. */
|
||||
Pipe userNamespaceSync;
|
||||
|
|
|
|||
|
|
@ -92,13 +92,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
|||
if (S_ISLNK(dstSt.st_mode)) {
|
||||
auto prevPriority = state.priorities[dstFile];
|
||||
if (prevPriority == priority)
|
||||
throw Error(
|
||||
"files '%1%' and '%2%' have the same priority %3%; "
|
||||
"use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' "
|
||||
"or type 'nix profile install --help' if using 'nix profile' to find out how "
|
||||
"to change the priority of one of the conflicting packages"
|
||||
" (0 being the highest priority)",
|
||||
srcFile, readLink(dstFile), priority);
|
||||
throw BuildEnvFileConflictError(
|
||||
readLink(dstFile),
|
||||
srcFile,
|
||||
priority
|
||||
);
|
||||
if (prevPriority < priority)
|
||||
continue;
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,32 @@ struct Package {
|
|||
Package(const Path & path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
|
||||
};
|
||||
|
||||
class BuildEnvFileConflictError : public Error
|
||||
{
|
||||
public:
|
||||
const Path fileA;
|
||||
const Path fileB;
|
||||
int priority;
|
||||
|
||||
BuildEnvFileConflictError(
|
||||
const Path fileA,
|
||||
const Path fileB,
|
||||
int priority
|
||||
)
|
||||
: Error(
|
||||
"Unable to build profile. There is a conflict for the following files:\n"
|
||||
"\n"
|
||||
" %1%\n"
|
||||
" %2%",
|
||||
fileA,
|
||||
fileB
|
||||
)
|
||||
, fileA(fileA)
|
||||
, fileB(fileB)
|
||||
, priority(priority)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::vector<Package> Packages;
|
||||
|
||||
void buildProfile(const Path & out, Packages && pkgs);
|
||||
|
|
|
|||
|
|
@ -42,14 +42,6 @@ ContentAddressMethod parseContentAddressingPrefix(std::string_view & m)
|
|||
return method;
|
||||
}
|
||||
|
||||
|
||||
std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
|
||||
{
|
||||
return "fixed:"
|
||||
+ makeFileIngestionPrefix(method)
|
||||
+ hash.to_string(Base32, true);
|
||||
}
|
||||
|
||||
std::string renderContentAddress(ContentAddress ca)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
|
|
|
|||
|
|
@ -14,22 +14,46 @@ namespace nix {
|
|||
/* We only have one way to hash text with references, so this is a single-value
|
||||
type, mainly useful with std::variant.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The single way we can serialize "text" file system objects.
|
||||
*
|
||||
* Somewhat obscure, used by \ref Derivation derivations and
|
||||
* `builtins.toFile` currently.
|
||||
*/
|
||||
struct TextHashMethod : std::monostate { };
|
||||
|
||||
/**
|
||||
* An enumeration of the main ways we can serialize file system
|
||||
* objects.
|
||||
*/
|
||||
enum struct FileIngestionMethod : uint8_t {
|
||||
/**
|
||||
* Flat-file hashing. Directly ingest the contents of a single file
|
||||
*/
|
||||
Flat = false,
|
||||
/**
|
||||
* Recursive (or NAR) hashing. Serializes the file-system object in Nix
|
||||
* Archive format and ingest that
|
||||
*/
|
||||
Recursive = true
|
||||
};
|
||||
|
||||
/* Compute the prefix to the hash algorithm which indicates how the files were
|
||||
ingested. */
|
||||
/**
|
||||
* Compute the prefix to the hash algorithm which indicates how the
|
||||
* files were ingested.
|
||||
*/
|
||||
std::string makeFileIngestionPrefix(FileIngestionMethod m);
|
||||
|
||||
|
||||
/* Just the type of a content address. Combine with the hash itself, and we
|
||||
have a `ContentAddress` as defined below. Combine that, in turn, with info
|
||||
on references, and we have `ContentAddressWithReferences`, as defined
|
||||
further below. */
|
||||
/**
|
||||
* An enumeration of all the ways we can serialize file system objects.
|
||||
*
|
||||
* Just the type of a content address. Combine with the hash itself, and
|
||||
* we have a `ContentAddress` as defined below. Combine that, in turn,
|
||||
* with info on references, and we have `ContentAddressWithReferences`,
|
||||
* as defined further below.
|
||||
*/
|
||||
typedef std::variant<
|
||||
TextHashMethod,
|
||||
FileIngestionMethod
|
||||
|
|
@ -55,37 +79,58 @@ std::pair<ContentAddressMethod, HashType> parseContentAddressMethod(std::string_
|
|||
* Mini content address
|
||||
*/
|
||||
|
||||
/**
|
||||
* Somewhat obscure, used by \ref Derivation derivations and
|
||||
* `builtins.toFile` currently.
|
||||
*/
|
||||
struct TextHash {
|
||||
/**
|
||||
* Hash of the contents of the text/file.
|
||||
*/
|
||||
Hash hash;
|
||||
|
||||
GENERATE_CMP(TextHash, me->hash);
|
||||
};
|
||||
|
||||
/// Pair of a hash, and how the file system was ingested
|
||||
/**
|
||||
* Used by most store objects that are content-addressed.
|
||||
*/
|
||||
struct FixedOutputHash {
|
||||
/**
|
||||
* How the file system objects are serialized
|
||||
*/
|
||||
FileIngestionMethod method;
|
||||
/**
|
||||
* Hash of that serialization
|
||||
*/
|
||||
Hash hash;
|
||||
|
||||
std::string printMethodAlgo() const;
|
||||
|
||||
GENERATE_CMP(FixedOutputHash, me->method, me->hash);
|
||||
};
|
||||
|
||||
/*
|
||||
We've accumulated several types of content-addressed paths over the years;
|
||||
fixed-output derivations support multiple hash algorithms and serialisation
|
||||
methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms:
|
||||
|
||||
* ‘text:sha256:<sha256 hash of file contents>’: For paths
|
||||
computed by makeTextPath() / addTextToStore().
|
||||
|
||||
* ‘fixed:<r?>:<ht>:<h>’: For paths computed by
|
||||
makeFixedOutputPath() / addToStore().
|
||||
*/
|
||||
/**
|
||||
* We've accumulated several types of content-addressed paths over the
|
||||
* years; fixed-output derivations support multiple hash algorithms and
|
||||
* serialisation methods (flat file vs NAR). Thus, ‘ca’ has one of the
|
||||
* following forms:
|
||||
*
|
||||
* - ‘text:sha256:<sha256 hash of file contents>’: For paths
|
||||
* computed by Store::makeTextPath() / Store::addTextToStore().
|
||||
*
|
||||
* - ‘fixed:<r?>:<ht>:<h>’: For paths computed by
|
||||
* Store::makeFixedOutputPath() / Store::addToStore().
|
||||
*/
|
||||
typedef std::variant<
|
||||
TextHash, // for paths computed by makeTextPath() / addTextToStore
|
||||
FixedOutputHash // for path computed by makeFixedOutputPath
|
||||
TextHash,
|
||||
FixedOutputHash
|
||||
> ContentAddress;
|
||||
|
||||
/**
|
||||
* Compute the content-addressability assertion (ValidPathInfo::ca) for
|
||||
* paths created by Store::makeFixedOutputPath() / Store::addToStore().
|
||||
*/
|
||||
std::string renderContentAddress(ContentAddress ca);
|
||||
|
||||
std::string renderContentAddress(std::optional<ContentAddress> ca);
|
||||
|
|
@ -97,15 +142,33 @@ std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt);
|
|||
Hash getContentAddressHash(const ContentAddress & ca);
|
||||
|
||||
|
||||
/*
|
||||
* References set
|
||||
/**
|
||||
* A set of references to other store objects.
|
||||
*
|
||||
* References to other store objects are tracked with store paths, self
|
||||
* references however are tracked with a boolean.
|
||||
*/
|
||||
|
||||
struct StoreReferences {
|
||||
/**
|
||||
* References to other store objects
|
||||
*/
|
||||
StorePathSet others;
|
||||
|
||||
/**
|
||||
* Reference to this store object
|
||||
*/
|
||||
bool self = false;
|
||||
|
||||
/**
|
||||
* @return true iff no references, i.e. others is empty and self is
|
||||
* false.
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/**
|
||||
* Returns the numbers of references, i.e. the size of others + 1
|
||||
* iff self is true.
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
GENERATE_CMP(StoreReferences, me->self, me->others);
|
||||
|
|
@ -120,7 +183,10 @@ struct StoreReferences {
|
|||
// This matches the additional info that we need for makeTextPath
|
||||
struct TextInfo {
|
||||
TextHash hash;
|
||||
// References for the paths, self references disallowed
|
||||
/**
|
||||
* References to other store objects only; self references
|
||||
* disallowed
|
||||
*/
|
||||
StorePathSet references;
|
||||
|
||||
GENERATE_CMP(TextInfo, me->hash, me->references);
|
||||
|
|
@ -128,17 +194,28 @@ struct TextInfo {
|
|||
|
||||
struct FixedOutputInfo {
|
||||
FixedOutputHash hash;
|
||||
// References for the paths
|
||||
/**
|
||||
* References to other store objects or this one.
|
||||
*/
|
||||
StoreReferences references;
|
||||
|
||||
GENERATE_CMP(FixedOutputInfo, me->hash, me->references);
|
||||
};
|
||||
|
||||
/**
|
||||
* Ways of content addressing but not a complete ContentAddress.
|
||||
*
|
||||
* A ContentAddress without a Hash.
|
||||
*/
|
||||
typedef std::variant<
|
||||
TextInfo,
|
||||
FixedOutputInfo
|
||||
> ContentAddressWithReferences;
|
||||
|
||||
/**
|
||||
* Create a ContentAddressWithReferences from a mere ContentAddress, by
|
||||
* assuming no references in all cases.
|
||||
*/
|
||||
ContentAddressWithReferences caWithoutRefs(const ContentAddress &);
|
||||
|
||||
ContentAddressWithReferences contentAddressFromMethodHashAndRefs(
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ struct TunnelLogger : public Logger
|
|||
state->pendingMsgs.push_back(s);
|
||||
}
|
||||
|
||||
void log(Verbosity lvl, const FormatOrString & fs) override
|
||||
void log(Verbosity lvl, std::string_view s) override
|
||||
{
|
||||
if (lvl > verbosity) return;
|
||||
|
||||
StringSink buf;
|
||||
buf << STDERR_NEXT << (fs.s + "\n");
|
||||
buf << STDERR_NEXT << (s + "\n");
|
||||
enqueueMsg(buf.s);
|
||||
}
|
||||
|
||||
|
|
@ -231,10 +231,10 @@ struct ClientSettings
|
|||
try {
|
||||
if (name == "ssh-auth-sock") // obsolete
|
||||
;
|
||||
else if (name == settings.experimentalFeatures.name) {
|
||||
else if (name == experimentalFeatureSettings.experimentalFeatures.name) {
|
||||
// We don’t want to forward the experimental features to
|
||||
// the daemon, as that could cause some pretty weird stuff
|
||||
if (parseFeatures(tokenizeString<StringSet>(value)) != settings.experimentalFeatures.get())
|
||||
if (parseFeatures(tokenizeString<StringSet>(value)) != experimentalFeatureSettings.experimentalFeatures.get())
|
||||
debug("Ignoring the client-specified experimental features");
|
||||
} else if (name == settings.pluginFiles.name) {
|
||||
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
|||
ContentAddressMethod method = parseContentAddressingPrefix(hashAlgo);
|
||||
const auto hashType = parseHashType(hashAlgo);
|
||||
if (hashS == "impure") {
|
||||
settings.requireExperimentalFeature(Xp::ImpureDerivations);
|
||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
assert(pathS == "");
|
||||
return DerivationOutput::Impure {
|
||||
.method = std::move(method),
|
||||
|
|
@ -233,7 +233,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
|||
method, std::move(hash), {}),
|
||||
};
|
||||
} else {
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
assert(pathS == "");
|
||||
return DerivationOutput::CAFloating {
|
||||
.method = std::move(method),
|
||||
|
|
|
|||
|
|
@ -17,42 +17,72 @@ class Store;
|
|||
|
||||
/* Abstract syntax of derivations. */
|
||||
|
||||
/* The traditional non-fixed-output derivation type. */
|
||||
/**
|
||||
* The traditional non-fixed-output derivation type.
|
||||
*/
|
||||
struct DerivationOutputInputAddressed
|
||||
{
|
||||
StorePath path;
|
||||
};
|
||||
|
||||
/* Fixed-output derivations, whose output paths are content addressed
|
||||
according to that fixed output. */
|
||||
/**
|
||||
* Fixed-output derivations, whose output paths are content
|
||||
* addressed according to that fixed output.
|
||||
*/
|
||||
struct DerivationOutputCAFixed
|
||||
{
|
||||
/**
|
||||
* hash and refs used for expected hash computation
|
||||
*/
|
||||
ContentAddressWithReferences ca; /* hash and refs used for validating output */
|
||||
|
||||
/**
|
||||
* Return the \ref StorePath "store path" corresponding to this output
|
||||
*
|
||||
* @param drvName The name of the derivation this is an output of, without the `.drv`.
|
||||
* @param outputName The name of this output.
|
||||
*/
|
||||
StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;
|
||||
};
|
||||
|
||||
/* Floating-output derivations, whose output paths are content addressed, but
|
||||
not fixed, and so are dynamically calculated from whatever the output ends
|
||||
up being. */
|
||||
/**
|
||||
* Floating-output derivations, whose output paths are content
|
||||
* addressed, but not fixed, and so are dynamically calculated from
|
||||
* whatever the output ends up being.
|
||||
* */
|
||||
struct DerivationOutputCAFloating
|
||||
{
|
||||
/* information used for expected hash computation */
|
||||
/**
|
||||
* How the file system objects will be serialized for hashing
|
||||
*/
|
||||
ContentAddressMethod method;
|
||||
|
||||
/**
|
||||
* How the serialization will be hashed
|
||||
*/
|
||||
HashType hashType;
|
||||
};
|
||||
|
||||
/* Input-addressed output which depends on a (CA) derivation whose hash isn't
|
||||
* known yet.
|
||||
/**
|
||||
* Input-addressed output which depends on a (CA) derivation whose hash
|
||||
* isn't known yet.
|
||||
*/
|
||||
struct DerivationOutputDeferred {};
|
||||
|
||||
/* Impure output which is moved to a content-addressed location (like
|
||||
CAFloating) but isn't registered as a realization.
|
||||
/**
|
||||
* Impure output which is moved to a content-addressed location (like
|
||||
* CAFloating) but isn't registered as a realization.
|
||||
*/
|
||||
struct DerivationOutputImpure
|
||||
{
|
||||
/* information used for expected hash computation */
|
||||
/**
|
||||
* How the file system objects will be serialized for hashing
|
||||
*/
|
||||
ContentAddressMethod method;
|
||||
|
||||
/**
|
||||
* How the serialization will be hashed
|
||||
*/
|
||||
HashType hashType;
|
||||
};
|
||||
|
||||
|
|
@ -64,6 +94,9 @@ typedef std::variant<
|
|||
DerivationOutputImpure
|
||||
> _DerivationOutputRaw;
|
||||
|
||||
/**
|
||||
* A single output of a BasicDerivation (and Derivation).
|
||||
*/
|
||||
struct DerivationOutput : _DerivationOutputRaw
|
||||
{
|
||||
using Raw = _DerivationOutputRaw;
|
||||
|
|
@ -75,9 +108,12 @@ struct DerivationOutput : _DerivationOutputRaw
|
|||
using Deferred = DerivationOutputDeferred;
|
||||
using Impure = DerivationOutputImpure;
|
||||
|
||||
/* Note, when you use this function you should make sure that you're passing
|
||||
the right derivation name. When in doubt, you should use the safer
|
||||
interface provided by BasicDerivation::outputsAndOptPaths */
|
||||
/**
|
||||
* \note when you use this function you should make sure that you're
|
||||
* passing the right derivation name. When in doubt, you should use
|
||||
* the safer interface provided by
|
||||
* BasicDerivation::outputsAndOptPaths
|
||||
*/
|
||||
std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
|
|
@ -92,26 +128,61 @@ struct DerivationOutput : _DerivationOutputRaw
|
|||
|
||||
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
|
||||
|
||||
/* These are analogues to the previous DerivationOutputs data type, but they
|
||||
also contains, for each output, the (optional) store path in which it would
|
||||
be written. To calculate values of these types, see the corresponding
|
||||
functions in BasicDerivation */
|
||||
/**
|
||||
* These are analogues to the previous DerivationOutputs data type,
|
||||
* but they also contains, for each output, the (optional) store
|
||||
* path in which it would be written. To calculate values of these
|
||||
* types, see the corresponding functions in BasicDerivation.
|
||||
*/
|
||||
typedef std::map<std::string, std::pair<DerivationOutput, std::optional<StorePath>>>
|
||||
DerivationOutputsAndOptPaths;
|
||||
|
||||
/* For inputs that are sub-derivations, we specify exactly which
|
||||
output IDs we are interested in. */
|
||||
/**
|
||||
* For inputs that are sub-derivations, we specify exactly which
|
||||
* output IDs we are interested in.
|
||||
*/
|
||||
typedef std::map<StorePath, StringSet> DerivationInputs;
|
||||
|
||||
/**
|
||||
* Input-addressed derivation types
|
||||
*/
|
||||
struct DerivationType_InputAddressed {
|
||||
/**
|
||||
* True iff the derivation type can't be determined statically,
|
||||
* for instance because it (transitively) depends on a content-addressed
|
||||
* derivation.
|
||||
*/
|
||||
bool deferred;
|
||||
};
|
||||
|
||||
/**
|
||||
* Content-addressed derivation types
|
||||
*/
|
||||
struct DerivationType_ContentAddressed {
|
||||
/**
|
||||
* Whether the derivation should be built safely inside a sandbox.
|
||||
*/
|
||||
bool sandboxed;
|
||||
/**
|
||||
* Whether the derivation's outputs' content-addresses are "fixed"
|
||||
* or "floating.
|
||||
*
|
||||
* - Fixed: content-addresses are written down as part of the
|
||||
* derivation itself. If the outputs don't end up matching the
|
||||
* build fails.
|
||||
*
|
||||
* - Floating: content-addresses are not written down, we do not
|
||||
* know them until we perform the build.
|
||||
*/
|
||||
bool fixed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Impure derivation type
|
||||
*
|
||||
* This is similar at buil-time to the content addressed, not standboxed, not fixed
|
||||
* type, but has some restrictions on its usage.
|
||||
*/
|
||||
struct DerivationType_Impure {
|
||||
};
|
||||
|
||||
|
|
@ -128,30 +199,38 @@ struct DerivationType : _DerivationTypeRaw {
|
|||
using ContentAddressed = DerivationType_ContentAddressed;
|
||||
using Impure = DerivationType_Impure;
|
||||
|
||||
/* Do the outputs of the derivation have paths calculated from their content,
|
||||
or from the derivation itself? */
|
||||
/**
|
||||
* Do the outputs of the derivation have paths calculated from their
|
||||
* content, or from the derivation itself?
|
||||
*/
|
||||
bool isCA() const;
|
||||
|
||||
/* Is the content of the outputs fixed a-priori via a hash? Never true for
|
||||
non-CA derivations. */
|
||||
/**
|
||||
* Is the content of the outputs fixed <em>a priori</em> via a hash?
|
||||
* Never true for non-CA derivations.
|
||||
*/
|
||||
bool isFixed() const;
|
||||
|
||||
/* Whether the derivation is fully sandboxed. If false, the
|
||||
sandbox is opened up, e.g. the derivation has access to the
|
||||
network. Note that whether or not we actually sandbox the
|
||||
derivation is controlled separately. Always true for non-CA
|
||||
derivations. */
|
||||
/**
|
||||
* Whether the derivation is fully sandboxed. If false, the sandbox
|
||||
* is opened up, e.g. the derivation has access to the network. Note
|
||||
* that whether or not we actually sandbox the derivation is
|
||||
* controlled separately. Always true for non-CA derivations.
|
||||
*/
|
||||
bool isSandboxed() const;
|
||||
|
||||
/* Whether the derivation is expected to produce the same result
|
||||
every time, and therefore it only needs to be built once. This
|
||||
is only false for derivations that have the attribute '__impure
|
||||
= true'. */
|
||||
/**
|
||||
* Whether the derivation is expected to produce the same result
|
||||
* every time, and therefore it only needs to be built once. This is
|
||||
* only false for derivations that have the attribute '__impure =
|
||||
* true'.
|
||||
*/
|
||||
bool isPure() const;
|
||||
|
||||
/* Does the derivation knows its own output paths?
|
||||
Only true when there's no floating-ca derivation involved in the
|
||||
closure, or if fixed output.
|
||||
/**
|
||||
* Does the derivation knows its own output paths?
|
||||
* Only true when there's no floating-ca derivation involved in the
|
||||
* closure, or if fixed output.
|
||||
*/
|
||||
bool hasKnownOutputPaths() const;
|
||||
|
||||
|
|
@ -175,15 +254,21 @@ struct BasicDerivation
|
|||
|
||||
bool isBuiltin() const;
|
||||
|
||||
/* Return true iff this is a fixed-output derivation. */
|
||||
/**
|
||||
* Return true iff this is a fixed-output derivation.
|
||||
*/
|
||||
DerivationType type() const;
|
||||
|
||||
/* Return the output names of a derivation. */
|
||||
/**
|
||||
* Return the output names of a derivation.
|
||||
*/
|
||||
StringSet outputNames() const;
|
||||
|
||||
/* Calculates the maps that contains all the DerivationOutputs, but
|
||||
augmented with knowledge of the Store paths they would be written
|
||||
into. */
|
||||
/**
|
||||
* Calculates the maps that contains all the DerivationOutputs, but
|
||||
* augmented with knowledge of the Store paths they would be written
|
||||
* into.
|
||||
*/
|
||||
DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const;
|
||||
|
||||
static std::string_view nameFromPath(const StorePath & storePath);
|
||||
|
|
@ -191,23 +276,33 @@ struct BasicDerivation
|
|||
|
||||
struct Derivation : BasicDerivation
|
||||
{
|
||||
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
|
||||
/**
|
||||
* inputs that are sub-derivations
|
||||
*/
|
||||
DerivationInputs inputDrvs;
|
||||
|
||||
/* Print a derivation. */
|
||||
/**
|
||||
* Print a derivation.
|
||||
*/
|
||||
std::string unparse(const Store & store, bool maskOutputs,
|
||||
std::map<std::string, StringSet> * actualInputs = nullptr) const;
|
||||
|
||||
/* Return the underlying basic derivation but with these changes:
|
||||
|
||||
1. Input drvs are emptied, but the outputs of them that were used are
|
||||
added directly to input sources.
|
||||
|
||||
2. Input placeholders are replaced with realized input store paths. */
|
||||
/**
|
||||
* Return the underlying basic derivation but with these changes:
|
||||
*
|
||||
* 1. Input drvs are emptied, but the outputs of them that were used
|
||||
* are added directly to input sources.
|
||||
*
|
||||
* 2. Input placeholders are replaced with realized input store
|
||||
* paths.
|
||||
*/
|
||||
std::optional<BasicDerivation> tryResolve(Store & store) const;
|
||||
|
||||
/* Like the above, but instead of querying the Nix database for
|
||||
realisations, uses a given mapping from input derivation paths
|
||||
+ output names to actual output store paths. */
|
||||
/**
|
||||
* Like the above, but instead of querying the Nix database for
|
||||
* realisations, uses a given mapping from input derivation paths +
|
||||
* output names to actual output store paths.
|
||||
*/
|
||||
std::optional<BasicDerivation> tryResolve(
|
||||
Store & store,
|
||||
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const;
|
||||
|
|
@ -222,81 +317,108 @@ struct Derivation : BasicDerivation
|
|||
|
||||
class Store;
|
||||
|
||||
/* Write a derivation to the Nix store, and return its path. */
|
||||
/**
|
||||
* Write a derivation to the Nix store, and return its path.
|
||||
*/
|
||||
StorePath writeDerivation(Store & store,
|
||||
const Derivation & drv,
|
||||
RepairFlag repair = NoRepair,
|
||||
bool readOnly = false);
|
||||
|
||||
/* Read a derivation from a file. */
|
||||
/**
|
||||
* Read a derivation from a file.
|
||||
*/
|
||||
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
|
||||
|
||||
// FIXME: remove
|
||||
/**
|
||||
* \todo Remove.
|
||||
*
|
||||
* Use Path::isDerivation instead.
|
||||
*/
|
||||
bool isDerivation(std::string_view fileName);
|
||||
|
||||
/* Calculate the name that will be used for the store path for this
|
||||
output.
|
||||
|
||||
This is usually <drv-name>-<output-name>, but is just <drv-name> when
|
||||
the output name is "out". */
|
||||
/**
|
||||
* Calculate the name that will be used for the store path for this
|
||||
* output.
|
||||
*
|
||||
* This is usually <drv-name>-<output-name>, but is just <drv-name> when
|
||||
* the output name is "out".
|
||||
*/
|
||||
std::string outputPathName(std::string_view drvName, std::string_view outputName);
|
||||
|
||||
|
||||
// The hashes modulo of a derivation.
|
||||
//
|
||||
// Each output is given a hash, although in practice only the content-addressed
|
||||
// derivations (fixed-output or not) will have a different hash for each
|
||||
// output.
|
||||
/**
|
||||
* The hashes modulo of a derivation.
|
||||
*
|
||||
* Each output is given a hash, although in practice only the content-addressed
|
||||
* derivations (fixed-output or not) will have a different hash for each
|
||||
* output.
|
||||
*/
|
||||
struct DrvHash {
|
||||
/**
|
||||
* Map from output names to hashes
|
||||
*/
|
||||
std::map<std::string, Hash> hashes;
|
||||
|
||||
enum struct Kind : bool {
|
||||
// Statically determined derivations.
|
||||
// This hash will be directly used to compute the output paths
|
||||
/**
|
||||
* Statically determined derivations.
|
||||
* This hash will be directly used to compute the output paths
|
||||
*/
|
||||
Regular,
|
||||
// Floating-output derivations (and their reverse dependencies).
|
||||
|
||||
/**
|
||||
* Floating-output derivations (and their reverse dependencies).
|
||||
*/
|
||||
Deferred,
|
||||
};
|
||||
|
||||
/**
|
||||
* The kind of derivation this is, simplified for just "derivation hash
|
||||
* modulo" purposes.
|
||||
*/
|
||||
Kind kind;
|
||||
};
|
||||
|
||||
void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
|
||||
|
||||
/* Returns hashes with the details of fixed-output subderivations
|
||||
expunged.
|
||||
|
||||
A fixed-output derivation is a derivation whose outputs have a
|
||||
specified content hash and hash algorithm. (Currently they must have
|
||||
exactly one output (`out'), which is specified using the `outputHash'
|
||||
and `outputHashAlgo' attributes, but the algorithm doesn't assume
|
||||
this.) We don't want changes to such derivations to propagate upwards
|
||||
through the dependency graph, changing output paths everywhere.
|
||||
|
||||
For instance, if we change the url in a call to the `fetchurl'
|
||||
function, we do not want to rebuild everything depending on it---after
|
||||
all, (the hash of) the file being downloaded is unchanged. So the
|
||||
*output paths* should not change. On the other hand, the *derivation
|
||||
paths* should change to reflect the new dependency graph.
|
||||
|
||||
For fixed-output derivations, this returns a map from the name of
|
||||
each output to its hash, unique up to the output's contents.
|
||||
|
||||
For regular derivations, it returns a single hash of the derivation
|
||||
ATerm, after subderivations have been likewise expunged from that
|
||||
derivation.
|
||||
/**
|
||||
* Returns hashes with the details of fixed-output subderivations
|
||||
* expunged.
|
||||
*
|
||||
* A fixed-output derivation is a derivation whose outputs have a
|
||||
* specified content hash and hash algorithm. (Currently they must have
|
||||
* exactly one output (`out'), which is specified using the `outputHash'
|
||||
* and `outputHashAlgo' attributes, but the algorithm doesn't assume
|
||||
* this.) We don't want changes to such derivations to propagate upwards
|
||||
* through the dependency graph, changing output paths everywhere.
|
||||
*
|
||||
* For instance, if we change the url in a call to the `fetchurl'
|
||||
* function, we do not want to rebuild everything depending on it---after
|
||||
* all, (the hash of) the file being downloaded is unchanged. So the
|
||||
* *output paths* should not change. On the other hand, the *derivation
|
||||
* paths* should change to reflect the new dependency graph.
|
||||
*
|
||||
* For fixed-output derivations, this returns a map from the name of
|
||||
* each output to its hash, unique up to the output's contents.
|
||||
*
|
||||
* For regular derivations, it returns a single hash of the derivation
|
||||
* ATerm, after subderivations have been likewise expunged from that
|
||||
* derivation.
|
||||
*/
|
||||
DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
|
||||
|
||||
/*
|
||||
Return a map associating each output to a hash that uniquely identifies its
|
||||
derivation (modulo the self-references).
|
||||
|
||||
FIXME: what is the Hash in this map?
|
||||
/**
|
||||
* Return a map associating each output to a hash that uniquely identifies its
|
||||
* derivation (modulo the self-references).
|
||||
*
|
||||
* \todo What is the Hash in this map?
|
||||
*/
|
||||
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv);
|
||||
|
||||
/* Memoisation of hashDerivationModulo(). */
|
||||
/**
|
||||
* Memoisation of hashDerivationModulo().
|
||||
*/
|
||||
typedef std::map<StorePath, DrvHash> DrvHashes;
|
||||
|
||||
// FIXME: global, though at least thread-safe.
|
||||
|
|
@ -308,21 +430,25 @@ struct Sink;
|
|||
Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name);
|
||||
void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv);
|
||||
|
||||
/* This creates an opaque and almost certainly unique string
|
||||
deterministically from the output name.
|
||||
|
||||
It is used as a placeholder to allow derivations to refer to their
|
||||
own outputs without needing to use the hash of a derivation in
|
||||
itself, making the hash near-impossible to calculate. */
|
||||
/**
|
||||
* This creates an opaque and almost certainly unique string
|
||||
* deterministically from the output name.
|
||||
*
|
||||
* It is used as a placeholder to allow derivations to refer to their
|
||||
* own outputs without needing to use the hash of a derivation in
|
||||
* itself, making the hash near-impossible to calculate.
|
||||
*/
|
||||
std::string hashPlaceholder(const std::string_view outputName);
|
||||
|
||||
/* This creates an opaque and almost certainly unique string
|
||||
deterministically from a derivation path and output name.
|
||||
|
||||
It is used as a placeholder to allow derivations to refer to
|
||||
content-addressed paths whose content --- and thus the path
|
||||
themselves --- isn't yet known. This occurs when a derivation has a
|
||||
dependency which is a CA derivation. */
|
||||
/**
|
||||
* This creates an opaque and almost certainly unique string
|
||||
* deterministically from a derivation path and output name.
|
||||
*
|
||||
* It is used as a placeholder to allow derivations to refer to
|
||||
* content-addressed paths whose content --- and thus the path
|
||||
* themselves --- isn't yet known. This occurs when a derivation has a
|
||||
* dependency which is a CA derivation.
|
||||
*/
|
||||
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName);
|
||||
|
||||
extern const Hash impureOutputHash;
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
|||
auto drvHashes =
|
||||
staticOutputHashes(store, store.readDerivation(p.drvPath));
|
||||
for (auto& [outputName, outputPath] : p.outputs) {
|
||||
if (settings.isExperimentalFeatureEnabled(
|
||||
if (experimentalFeatureSettings.isEnabled(
|
||||
Xp::CaDerivations)) {
|
||||
auto drvOutput = get(drvHashes, outputName);
|
||||
if (!drvOutput)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ using _BuiltPathRaw = std::variant<
|
|||
>;
|
||||
|
||||
/**
|
||||
* A built path. Similar to a `DerivedPath`, but enriched with the corresponding
|
||||
* A built path. Similar to a DerivedPath, but enriched with the corresponding
|
||||
* output path(s).
|
||||
*/
|
||||
struct BuiltPath : _BuiltPathRaw {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ struct DummyStoreConfig : virtual StoreConfig {
|
|||
using StoreConfig::StoreConfig;
|
||||
|
||||
const std::string name() override { return "Dummy Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "dummy-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
struct DummyStore : public virtual DummyStoreConfig, public virtual Store
|
||||
|
|
|
|||
13
src/libstore/dummy-store.md
Normal file
13
src/libstore/dummy-store.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `dummy://`
|
||||
|
||||
This store type represents a store that contains no store paths and
|
||||
cannot be written to. It's useful when you want to use the Nix
|
||||
evaluator when no actual Nix store exists, e.g.
|
||||
|
||||
```console
|
||||
# nix eval --store dummy:// --expr '1 + 2'
|
||||
```
|
||||
|
||||
)"
|
||||
|
|
@ -16,7 +16,7 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink)
|
|||
//logger->incExpected(doneLabel, sorted.size());
|
||||
|
||||
for (auto & path : sorted) {
|
||||
//Activity act(*logger, lvlInfo, format("exporting path '%s'") % path);
|
||||
//Activity act(*logger, lvlInfo, "exporting path '%s'", path);
|
||||
sink << 1;
|
||||
exportPath(path, sink);
|
||||
//logger->incProgress(doneLabel);
|
||||
|
|
@ -71,7 +71,7 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
|||
|
||||
auto path = parseStorePath(readString(source));
|
||||
|
||||
//Activity act(*logger, lvlInfo, format("importing path '%s'") % info.path);
|
||||
//Activity act(*logger, lvlInfo, "importing path '%s'", info.path);
|
||||
|
||||
auto references = worker_proto::read(*this, source, Phantom<StorePathSet> {});
|
||||
auto deriver = readString(source);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@ struct curlFileTransfer : public FileTransfer
|
|||
{request.uri}, request.parentAct)
|
||||
, callback(std::move(callback))
|
||||
, finalSink([this](std::string_view data) {
|
||||
if (errorSink) {
|
||||
(*errorSink)(data);
|
||||
}
|
||||
|
||||
if (this->request.dataCallback) {
|
||||
auto httpStatus = getHTTPStatus();
|
||||
|
||||
|
|
@ -163,8 +167,6 @@ struct curlFileTransfer : public FileTransfer
|
|||
}
|
||||
}
|
||||
|
||||
if (errorSink)
|
||||
(*errorSink)({(char *) contents, realSize});
|
||||
(*decompressionSink)({(char *) contents, realSize});
|
||||
|
||||
return realSize;
|
||||
|
|
@ -183,7 +185,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
{
|
||||
size_t realSize = size * nmemb;
|
||||
std::string line((char *) contents, realSize);
|
||||
printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line));
|
||||
printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line));
|
||||
static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase);
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, statusLine)) {
|
||||
|
|
@ -207,7 +209,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
long httpStatus = 0;
|
||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
||||
if (result.etag == request.expectedETag && httpStatus == 200) {
|
||||
debug(format("shutting down on 200 HTTP response with expected ETag"));
|
||||
debug("shutting down on 200 HTTP response with expected ETag");
|
||||
return 0;
|
||||
}
|
||||
} else if (name == "content-encoding")
|
||||
|
|
@ -316,7 +318,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
|
||||
if (request.verifyTLS) {
|
||||
if (settings.caFile != "")
|
||||
curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str());
|
||||
curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.get().c_str());
|
||||
} else {
|
||||
curl_easy_setopt(req, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(req, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ static void makeSymlink(const Path & link, const Path & target)
|
|||
createDirs(dirOf(link));
|
||||
|
||||
/* Create the new symlink. */
|
||||
Path tempLink = (format("%1%.tmp-%2%-%3%")
|
||||
% link % getpid() % random()).str();
|
||||
Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), random());
|
||||
createSymlink(target, tempLink);
|
||||
|
||||
/* Atomically replace the old one. */
|
||||
|
|
@ -197,7 +196,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
|
|||
|
||||
pid_t pid = std::stoi(i.name);
|
||||
|
||||
debug(format("reading temporary root file '%1%'") % path);
|
||||
debug("reading temporary root file '%1%'", path);
|
||||
AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666));
|
||||
if (!fd) {
|
||||
/* It's okay if the file has disappeared. */
|
||||
|
|
@ -263,7 +262,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
|
|||
target = absPath(target, dirOf(path));
|
||||
if (!pathExists(target)) {
|
||||
if (isInDir(path, stateDir + "/" + gcRootsDir + "/auto")) {
|
||||
printInfo(format("removing stale link from '%1%' to '%2%'") % path % target);
|
||||
printInfo("removing stale link from '%1%' to '%2%'", path, target);
|
||||
unlink(path.c_str());
|
||||
}
|
||||
} else {
|
||||
|
|
@ -372,29 +371,29 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
|||
while (errno = 0, ent = readdir(procDir.get())) {
|
||||
checkInterrupt();
|
||||
if (std::regex_match(ent->d_name, digitsRegex)) {
|
||||
readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked);
|
||||
readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
|
||||
|
||||
auto fdStr = fmt("/proc/%s/fd", ent->d_name);
|
||||
auto fdDir = AutoCloseDir(opendir(fdStr.c_str()));
|
||||
if (!fdDir) {
|
||||
if (errno == ENOENT || errno == EACCES)
|
||||
continue;
|
||||
throw SysError("opening %1%", fdStr);
|
||||
}
|
||||
struct dirent * fd_ent;
|
||||
while (errno = 0, fd_ent = readdir(fdDir.get())) {
|
||||
if (fd_ent->d_name[0] != '.')
|
||||
readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked);
|
||||
}
|
||||
if (errno) {
|
||||
if (errno == ESRCH)
|
||||
continue;
|
||||
throw SysError("iterating /proc/%1%/fd", ent->d_name);
|
||||
}
|
||||
fdDir.reset();
|
||||
|
||||
try {
|
||||
readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked);
|
||||
readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
|
||||
|
||||
auto fdStr = fmt("/proc/%s/fd", ent->d_name);
|
||||
auto fdDir = AutoCloseDir(opendir(fdStr.c_str()));
|
||||
if (!fdDir) {
|
||||
if (errno == ENOENT || errno == EACCES)
|
||||
continue;
|
||||
throw SysError("opening %1%", fdStr);
|
||||
}
|
||||
struct dirent * fd_ent;
|
||||
while (errno = 0, fd_ent = readdir(fdDir.get())) {
|
||||
if (fd_ent->d_name[0] != '.')
|
||||
readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked);
|
||||
}
|
||||
if (errno) {
|
||||
if (errno == ESRCH)
|
||||
continue;
|
||||
throw SysError("iterating /proc/%1%/fd", ent->d_name);
|
||||
}
|
||||
fdDir.reset();
|
||||
|
||||
auto mapFile = fmt("/proc/%s/maps", ent->d_name);
|
||||
auto mapLines = tokenizeString<std::vector<std::string>>(readFile(mapFile), "\n");
|
||||
for (const auto & line : mapLines) {
|
||||
|
|
@ -863,7 +862,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||
continue;
|
||||
}
|
||||
|
||||
printMsg(lvlTalkative, format("deleting unused link '%1%'") % path);
|
||||
printMsg(lvlTalkative, "deleting unused link '%1%'", path);
|
||||
|
||||
if (unlink(path.c_str()) == -1)
|
||||
throw SysError("deleting '%1%'", path);
|
||||
|
|
|
|||
|
|
@ -30,28 +30,23 @@ static GlobalConfig::Register rSettings(&settings);
|
|||
|
||||
Settings::Settings()
|
||||
: nixPrefix(NIX_PREFIX)
|
||||
, nixStore(canonPath(getEnv("NIX_STORE_DIR").value_or(getEnv("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixDataDir(canonPath(getEnv("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
||||
, nixLogDir(canonPath(getEnv("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
||||
, nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||
, nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR)))
|
||||
, nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
||||
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
||||
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||
, nixConfDir(canonPath(getEnvNonEmpty("NIX_CONF_DIR").value_or(NIX_CONF_DIR)))
|
||||
, nixUserConfFiles(getUserConfigFiles())
|
||||
, nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
|
||||
, nixBinDir(canonPath(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
|
||||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
{
|
||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||
lockCPU = getEnv("NIX_AFFINITY_HACK") == "1";
|
||||
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||
|
||||
caFile = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||
if (caFile == "") {
|
||||
for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"})
|
||||
if (pathExists(fn)) {
|
||||
caFile = fn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||
if (sslOverride != "")
|
||||
caFile = sslOverride;
|
||||
|
||||
/* Backwards compatibility. */
|
||||
auto s = getEnv("NIX_REMOTE_SYSTEMS");
|
||||
|
|
@ -166,18 +161,6 @@ StringSet Settings::getDefaultExtraPlatforms()
|
|||
return extraPlatforms;
|
||||
}
|
||||
|
||||
bool Settings::isExperimentalFeatureEnabled(const ExperimentalFeature & feature)
|
||||
{
|
||||
auto & f = experimentalFeatures.get();
|
||||
return std::find(f.begin(), f.end(), feature) != f.end();
|
||||
}
|
||||
|
||||
void Settings::requireExperimentalFeature(const ExperimentalFeature & feature)
|
||||
{
|
||||
if (!isExperimentalFeatureEnabled(feature))
|
||||
throw MissingExperimentalFeature(feature);
|
||||
}
|
||||
|
||||
bool Settings::isWSL1()
|
||||
{
|
||||
struct utsname utsbuf;
|
||||
|
|
@ -187,6 +170,13 @@ bool Settings::isWSL1()
|
|||
return hasSuffix(utsbuf.release, "-Microsoft");
|
||||
}
|
||||
|
||||
Path Settings::getDefaultSSLCertFile()
|
||||
{
|
||||
for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"})
|
||||
if (pathExists(fn)) return fn;
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::string nixVersion = PACKAGE_VERSION;
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "types.hh"
|
||||
#include "config.hh"
|
||||
#include "util.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
||||
#include <map>
|
||||
#include <limits>
|
||||
|
|
@ -64,6 +63,8 @@ class Settings : public Config {
|
|||
|
||||
bool isWSL1();
|
||||
|
||||
Path getDefaultSSLCertFile();
|
||||
|
||||
public:
|
||||
|
||||
Settings();
|
||||
|
|
@ -97,7 +98,12 @@ public:
|
|||
Path nixDaemonSocketFile;
|
||||
|
||||
Setting<std::string> storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), "store",
|
||||
"The default Nix store to use."};
|
||||
R"(
|
||||
The [URL of the Nix store](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format)
|
||||
to use for most operations.
|
||||
See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md)
|
||||
for supported store types and settings.
|
||||
)"};
|
||||
|
||||
Setting<bool> keepFailed{this, false, "keep-failed",
|
||||
"Whether to keep temporary directories of failed builds."};
|
||||
|
|
@ -678,8 +684,9 @@ public:
|
|||
Strings{"https://cache.nixos.org/"},
|
||||
"substituters",
|
||||
R"(
|
||||
A list of URLs of substituters, separated by whitespace. Substituters
|
||||
are tried based on their Priority value, which each substituter can set
|
||||
A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format)
|
||||
to be used as substituters, separated by whitespace.
|
||||
Substituters are tried based on their Priority value, which each substituter can set
|
||||
independently. Lower value means higher priority.
|
||||
The default is `https://cache.nixos.org`, with a Priority of 40.
|
||||
|
||||
|
|
@ -697,7 +704,8 @@ public:
|
|||
Setting<StringSet> trustedSubstituters{
|
||||
this, {}, "trusted-substituters",
|
||||
R"(
|
||||
A list of URLs of substituters, separated by whitespace. These are
|
||||
A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format),
|
||||
separated by whitespace. These are
|
||||
not used by default, but can be enabled by users of the Nix daemon
|
||||
by specifying `--option substituters urls` on the command
|
||||
line. Unprivileged users are only allowed to pass a subset of the
|
||||
|
|
@ -826,8 +834,22 @@ public:
|
|||
> `.netrc`.
|
||||
)"};
|
||||
|
||||
/* Path to the SSL CA file used */
|
||||
Path caFile;
|
||||
Setting<Path> caFile{
|
||||
this, getDefaultSSLCertFile(), "ssl-cert-file",
|
||||
R"(
|
||||
The path of a file containing CA certificates used to
|
||||
authenticate `https://` downloads. Nix by default will use
|
||||
the first of the following files that exists:
|
||||
|
||||
1. `/etc/ssl/certs/ca-certificates.crt`
|
||||
2. `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt`
|
||||
|
||||
The path can be overridden by the following environment
|
||||
variables, in order of precedence:
|
||||
|
||||
1. `NIX_SSL_CERT_FILE`
|
||||
2. `SSL_CERT_FILE`
|
||||
)"};
|
||||
|
||||
#if __linux__
|
||||
Setting<bool> filterSyscalls{
|
||||
|
|
@ -932,13 +954,6 @@ public:
|
|||
are loaded as plugins (non-recursively).
|
||||
)"};
|
||||
|
||||
Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features",
|
||||
"Experimental Nix features to enable."};
|
||||
|
||||
bool isExperimentalFeatureEnabled(const ExperimentalFeature &);
|
||||
|
||||
void requireExperimentalFeature(const ExperimentalFeature &);
|
||||
|
||||
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
|
||||
"Maximum size of NARs before spilling them to disk."};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,14 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
|||
{
|
||||
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
|
||||
|
||||
const std::string name() override { return "Http Binary Cache Store"; }
|
||||
const std::string name() override { return "HTTP Binary Cache Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "http-binary-cache-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore
|
||||
|
|
|
|||
8
src/libstore/http-binary-cache-store.md
Normal file
8
src/libstore/http-binary-cache-store.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `http://...`, `https://...`
|
||||
|
||||
This store allows a binary cache to be accessed via the HTTP
|
||||
protocol.
|
||||
|
||||
)"
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "ssh-store-config.hh"
|
||||
#include "archive.hh"
|
||||
#include "pool.hh"
|
||||
#include "remote-store.hh"
|
||||
|
|
@ -12,17 +13,24 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
struct LegacySSHStoreConfig : virtual StoreConfig
|
||||
struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig
|
||||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
const Setting<int> maxConnections{(StoreConfig*) this, 1, "max-connections", "maximum number of concurrent SSH connections"};
|
||||
const Setting<Path> sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"};
|
||||
const Setting<std::string> sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"};
|
||||
const Setting<bool> compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"};
|
||||
const Setting<Path> remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"};
|
||||
const Setting<std::string> remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"};
|
||||
using CommonSSHStoreConfig::CommonSSHStoreConfig;
|
||||
|
||||
const std::string name() override { return "Legacy SSH Store"; }
|
||||
const Setting<Path> remoteProgram{(StoreConfig*) this, "nix-store", "remote-program",
|
||||
"Path to the `nix-store` executable on the remote machine."};
|
||||
|
||||
const Setting<int> maxConnections{(StoreConfig*) this, 1, "max-connections",
|
||||
"Maximum number of concurrent SSH connections."};
|
||||
|
||||
const std::string name() override { return "SSH Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "legacy-ssh-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store
|
||||
|
|
@ -51,6 +59,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
|
|||
|
||||
LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params)
|
||||
: StoreConfig(params)
|
||||
, CommonSSHStoreConfig(params)
|
||||
, LegacySSHStoreConfig(params)
|
||||
, Store(params)
|
||||
, host(host)
|
||||
|
|
|
|||
8
src/libstore/legacy-ssh-store.md
Normal file
8
src/libstore/legacy-ssh-store.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `ssh://[username@]hostname`
|
||||
|
||||
This store type allows limited access to a remote store on another
|
||||
machine via SSH.
|
||||
|
||||
)"
|
||||
|
|
@ -11,6 +11,13 @@ struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
|||
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
|
||||
|
||||
const std::string name() override { return "Local Binary Cache Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "local-binary-cache-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public virtual BinaryCacheStore
|
||||
|
|
|
|||
16
src/libstore/local-binary-cache-store.md
Normal file
16
src/libstore/local-binary-cache-store.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `file://`*path*
|
||||
|
||||
This store allows reading and writing a binary cache stored in *path*
|
||||
in the local filesystem. If *path* does not exist, it will be created.
|
||||
|
||||
For example, the following builds or downloads `nixpkgs#hello` into
|
||||
the local store and then copies it to the binary cache in
|
||||
`/tmp/binary-cache`:
|
||||
|
||||
```
|
||||
# nix copy --to file:///tmp/binary-cache nixpkgs#hello
|
||||
```
|
||||
|
||||
)"
|
||||
|
|
@ -9,20 +9,28 @@ namespace nix {
|
|||
struct LocalFSStoreConfig : virtual StoreConfig
|
||||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
|
||||
// FIXME: the (StoreConfig*) cast works around a bug in gcc that causes
|
||||
// it to omit the call to the Setting constructor. Clang works fine
|
||||
// either way.
|
||||
|
||||
const PathSetting rootDir{(StoreConfig*) this, true, "",
|
||||
"root", "directory prefixed to all other paths"};
|
||||
"root",
|
||||
"Directory prefixed to all other paths."};
|
||||
|
||||
const PathSetting stateDir{(StoreConfig*) this, false,
|
||||
rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir,
|
||||
"state", "directory where Nix will store state"};
|
||||
"state",
|
||||
"Directory where Nix will store state."};
|
||||
|
||||
const PathSetting logDir{(StoreConfig*) this, false,
|
||||
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
|
||||
"log", "directory where Nix will store state"};
|
||||
"log",
|
||||
"directory where Nix will store log files."};
|
||||
|
||||
const PathSetting realStoreDir{(StoreConfig*) this, false,
|
||||
rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
|
||||
"physical path to the Nix store"};
|
||||
"Physical path of the Nix store."};
|
||||
};
|
||||
|
||||
class LocalFSStore : public virtual LocalFSStoreConfig,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::string LocalStoreConfig::doc()
|
||||
{
|
||||
return
|
||||
#include "local-store.md"
|
||||
;
|
||||
}
|
||||
|
||||
struct LocalStore::State::Stmts {
|
||||
/* Some precompiled SQLite statements. */
|
||||
SQLiteStmt RegisterValidPath;
|
||||
|
|
@ -280,7 +287,7 @@ LocalStore::LocalStore(const Params & params)
|
|||
else if (curSchema == 0) { /* new store */
|
||||
curSchema = nixSchemaVersion;
|
||||
openDB(*state, true);
|
||||
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
|
||||
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true);
|
||||
}
|
||||
|
||||
else if (curSchema < nixSchemaVersion) {
|
||||
|
|
@ -329,14 +336,14 @@ LocalStore::LocalStore(const Params & params)
|
|||
txn.commit();
|
||||
}
|
||||
|
||||
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
|
||||
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true);
|
||||
|
||||
lockFile(globalLock.get(), ltRead, true);
|
||||
}
|
||||
|
||||
else openDB(*state, false);
|
||||
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +373,7 @@ LocalStore::LocalStore(const Params & params)
|
|||
state->stmts->QueryPathFromHashPart.create(state->db,
|
||||
"select path from ValidPaths where path >= ? limit 1;");
|
||||
state->stmts->QueryValidPaths.create(state->db, "select path from ValidPaths");
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
state->stmts->RegisterRealisedOutput.create(state->db,
|
||||
R"(
|
||||
insert into Realisations (drvPath, outputName, outputPath, signatures)
|
||||
|
|
@ -413,6 +420,13 @@ LocalStore::LocalStore(const Params & params)
|
|||
}
|
||||
|
||||
|
||||
LocalStore::LocalStore(std::string scheme, std::string path, const Params & params)
|
||||
: LocalStore(params)
|
||||
{
|
||||
throw UnimplementedError("LocalStore");
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD LocalStore::openGCLock()
|
||||
{
|
||||
Path fnGCLock = stateDir + "/gc.lock";
|
||||
|
|
@ -754,7 +768,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
|
|||
|
||||
void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs)
|
||||
{
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
|
||||
registerDrvOutput(info);
|
||||
else
|
||||
|
|
@ -763,7 +777,7 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
|
|||
|
||||
void LocalStore::registerDrvOutput(const Realisation & info)
|
||||
{
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
retrySQLite<void>([&]() {
|
||||
auto state(_state.lock());
|
||||
if (auto oldR = queryRealisation_(*state, info.id)) {
|
||||
|
|
@ -1052,7 +1066,7 @@ LocalStore::queryPartialDerivationOutputMap(const StorePath & path_)
|
|||
return outputs;
|
||||
});
|
||||
|
||||
if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
||||
if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||
return outputs;
|
||||
|
||||
auto drv = readInvalidDerivation(path);
|
||||
|
|
@ -1580,7 +1594,7 @@ void LocalStore::invalidatePathChecked(const StorePath & path)
|
|||
|
||||
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
{
|
||||
printInfo(format("reading the Nix store..."));
|
||||
printInfo("reading the Nix store...");
|
||||
|
||||
bool errors = false;
|
||||
|
||||
|
|
@ -1970,5 +1984,6 @@ std::optional<std::string> LocalStore::getVersion()
|
|||
return nixVersion;
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation<LocalStore, LocalStoreConfig> regLocalStore;
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
|
|||
|
||||
Setting<bool> requireSigs{(StoreConfig*) this,
|
||||
settings.requireSigs,
|
||||
"require-sigs", "whether store paths should have a trusted signature on import"};
|
||||
"require-sigs",
|
||||
"Whether store paths copied into this store should have a trusted signature."};
|
||||
|
||||
const std::string name() override { return "Local Store"; }
|
||||
};
|
||||
|
||||
std::string doc() override;
|
||||
};
|
||||
|
||||
class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore
|
||||
{
|
||||
|
|
@ -100,9 +102,13 @@ public:
|
|||
/* Initialise the local store, upgrading the schema if
|
||||
necessary. */
|
||||
LocalStore(const Params & params);
|
||||
LocalStore(std::string scheme, std::string path, const Params & params);
|
||||
|
||||
~LocalStore();
|
||||
|
||||
static std::set<std::string> uriSchemes()
|
||||
{ return {}; }
|
||||
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
std::string getUri() override;
|
||||
|
|
|
|||
39
src/libstore/local-store.md
Normal file
39
src/libstore/local-store.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `local`, *root*
|
||||
|
||||
This store type accesses a Nix store in the local filesystem directly
|
||||
(i.e. not via the Nix daemon). *root* is an absolute path that is
|
||||
prefixed to other directories such as the Nix store directory. The
|
||||
store pseudo-URL `local` denotes a store that uses `/` as its root
|
||||
directory.
|
||||
|
||||
A store that uses a *root* other than `/` is called a *chroot
|
||||
store*. With such stores, the store directory is "logically" still
|
||||
`/nix/store`, so programs stored in them can only be built and
|
||||
executed by `chroot`-ing into *root*. Chroot stores only support
|
||||
building and running on Linux when [`mount namespaces`](https://man7.org/linux/man-pages/man7/mount_namespaces.7.html) and [`user namespaces`](https://man7.org/linux/man-pages/man7/user_namespaces.7.html) are
|
||||
enabled.
|
||||
|
||||
For example, the following uses `/tmp/root` as the chroot environment
|
||||
to build or download `nixpkgs#hello` and then execute it:
|
||||
|
||||
```console
|
||||
# nix run --store /tmp/root nixpkgs#hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Here, the "physical" store location is `/tmp/root/nix/store`, and
|
||||
Nix's store metadata is in `/tmp/root/nix/var/nix/db`.
|
||||
|
||||
It is also possible, but not recommended, to change the "logical"
|
||||
location of the Nix store from its default of `/nix/store`. This makes
|
||||
it impossible to use default substituters such as
|
||||
`https://cache.nixos.org/`, and thus you may have to build everything
|
||||
locally. Here is an example:
|
||||
|
||||
```console
|
||||
# nix build --store 'local?store=/tmp/my-nix/store&state=/tmp/my-nix/state&log=/tmp/my-nix/log' nixpkgs#hello
|
||||
```
|
||||
|
||||
)"
|
||||
|
|
@ -129,7 +129,7 @@ struct AutoUserLock : UserLock
|
|||
useUserNamespace = false;
|
||||
#endif
|
||||
|
||||
settings.requireExperimentalFeature(Xp::AutoAllocateUids);
|
||||
experimentalFeatureSettings.require(Xp::AutoAllocateUids);
|
||||
assert(settings.startId > 0);
|
||||
assert(settings.uidCount % maxIdsPerBuild == 0);
|
||||
assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits<uid_t>::max());
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
|
|||
throw Error(
|
||||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
store.printStorePath(bfd.drvPath), output);
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
DrvOutput outputId { *outputHash, output };
|
||||
auto realisation = store.queryRealisation(outputId);
|
||||
if (!realisation)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
|||
}
|
||||
if (errno) throw SysError("reading directory '%1%'", linksDir);
|
||||
|
||||
printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
|
||||
printMsg(lvlTalkative, "loaded %1% hash inodes", inodeHash.size());
|
||||
|
||||
return inodeHash;
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
|||
checkInterrupt();
|
||||
|
||||
if (inodeHash.count(dirent->d_ino)) {
|
||||
debug(format("'%1%' is already linked") % dirent->d_name);
|
||||
debug("'%1%' is already linked", dirent->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
|
||||
if (std::regex_search(path, std::regex("\\.app/Contents/.+$")))
|
||||
{
|
||||
debug(format("'%1%' is not allowed to be linked in macOS") % path);
|
||||
debug("'%1%' is not allowed to be linked in macOS", path);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -146,7 +146,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
contents of the symlink (i.e. the result of readlink()), not
|
||||
the contents of the target (which may not even exist). */
|
||||
Hash hash = hashPath(htSHA256, path).first;
|
||||
debug(format("'%1%' has hash '%2%'") % path % hash.to_string(Base32, true));
|
||||
debug("'%1%' has hash '%2%'", path, hash.to_string(Base32, true));
|
||||
|
||||
/* Check if this is a known hash. */
|
||||
Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
|
||||
|
|
@ -196,11 +196,11 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
auto stLink = lstat(linkPath);
|
||||
|
||||
if (st.st_ino == stLink.st_ino) {
|
||||
debug(format("'%1%' is already linked to '%2%'") % path % linkPath);
|
||||
debug("'%1%' is already linked to '%2%'", path, linkPath);
|
||||
return;
|
||||
}
|
||||
|
||||
printMsg(lvlTalkative, format("linking '%1%' to '%2%'") % path % linkPath);
|
||||
printMsg(lvlTalkative, "linking '%1%' to '%2%'", path, linkPath);
|
||||
|
||||
/* Make the containing directory writable, but only if it's not
|
||||
the store itself (we don't want or need to mess with its
|
||||
|
|
@ -213,8 +213,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
its timestamp back to 0. */
|
||||
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
|
||||
|
||||
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
|
||||
% realStoreDir % getpid() % random()).str();
|
||||
Path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), random());
|
||||
|
||||
if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
|
||||
if (errno == EMLINK) {
|
||||
|
|
@ -222,7 +221,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
systems). This is likely to happen with empty files.
|
||||
Just shrug and ignore. */
|
||||
if (st.st_size)
|
||||
printInfo(format("'%1%' has maximum number of links") % linkPath);
|
||||
printInfo("'%1%' has maximum number of links", linkPath);
|
||||
return;
|
||||
}
|
||||
throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* A non-empty set of outputs, specified by name
|
||||
*/
|
||||
struct OutputNames : std::set<std::string> {
|
||||
using std::set<std::string>::set;
|
||||
|
||||
|
|
@ -18,6 +21,9 @@ struct OutputNames : std::set<std::string> {
|
|||
: std::set<std::string>(s)
|
||||
{ assert(!empty()); }
|
||||
|
||||
/**
|
||||
* Needs to be "inherited manually"
|
||||
*/
|
||||
OutputNames(std::set<std::string> && s)
|
||||
: std::set<std::string>(s)
|
||||
{ assert(!empty()); }
|
||||
|
|
@ -28,6 +34,9 @@ struct OutputNames : std::set<std::string> {
|
|||
OutputNames() = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* The set of all outputs, without needing to name them explicitly
|
||||
*/
|
||||
struct AllOutputs : std::monostate { };
|
||||
|
||||
typedef std::variant<AllOutputs, OutputNames> _OutputsSpecRaw;
|
||||
|
|
@ -36,7 +45,9 @@ struct OutputsSpec : _OutputsSpecRaw {
|
|||
using Raw = _OutputsSpecRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
/* Force choosing a variant */
|
||||
/**
|
||||
* Force choosing a variant
|
||||
*/
|
||||
OutputsSpec() = delete;
|
||||
|
||||
using Names = OutputNames;
|
||||
|
|
@ -52,14 +63,20 @@ struct OutputsSpec : _OutputsSpecRaw {
|
|||
|
||||
bool contains(const std::string & output) const;
|
||||
|
||||
/* Create a new OutputsSpec which is the union of this and that. */
|
||||
/**
|
||||
* Create a new OutputsSpec which is the union of this and that.
|
||||
*/
|
||||
OutputsSpec union_(const OutputsSpec & that) const;
|
||||
|
||||
/* Whether this OutputsSpec is a subset of that. */
|
||||
/**
|
||||
* Whether this OutputsSpec is a subset of that.
|
||||
*/
|
||||
bool isSubsetOf(const OutputsSpec & outputs) const;
|
||||
|
||||
/* Parse a string of the form 'output1,...outputN' or
|
||||
'*', returning the outputs spec. */
|
||||
/**
|
||||
* Parse a string of the form 'output1,...outputN' or '*', returning
|
||||
* the outputs spec.
|
||||
*/
|
||||
static OutputsSpec parse(std::string_view s);
|
||||
static std::optional<OutputsSpec> parseOpt(std::string_view s);
|
||||
|
||||
|
|
@ -81,8 +98,10 @@ struct ExtendedOutputsSpec : _ExtendedOutputsSpecRaw {
|
|||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
/* Parse a string of the form 'prefix^output1,...outputN' or
|
||||
'prefix^*', returning the prefix and the extended outputs spec. */
|
||||
/**
|
||||
* Parse a string of the form 'prefix^output1,...outputN' or
|
||||
* 'prefix^*', returning the prefix and the extended outputs spec.
|
||||
*/
|
||||
static std::pair<std::string_view, ExtendedOutputsSpec> parse(std::string_view s);
|
||||
static std::optional<std::pair<std::string_view, ExtendedOutputsSpec>> parseOpt(std::string_view s);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,22 @@ namespace nix {
|
|||
|
||||
struct Hash;
|
||||
|
||||
/**
|
||||
* \ref StorePath "Store path" is the fundamental reference type of Nix.
|
||||
* A store paths refers to a Store object.
|
||||
*
|
||||
* See glossary.html#gloss-store-path for more information on a
|
||||
* conceptual level.
|
||||
*/
|
||||
class StorePath
|
||||
{
|
||||
std::string baseName;
|
||||
|
||||
public:
|
||||
|
||||
/* Size of the hash part of store paths, in base-32 characters. */
|
||||
/**
|
||||
* Size of the hash part of store paths, in base-32 characters.
|
||||
*/
|
||||
constexpr static size_t HashLen = 32; // i.e. 160 bits
|
||||
|
||||
constexpr static size_t MaxPathLen = 211;
|
||||
|
|
@ -45,8 +54,9 @@ public:
|
|||
return baseName != other.baseName;
|
||||
}
|
||||
|
||||
/* Check whether a file name ends with the extension for
|
||||
derivations. */
|
||||
/**
|
||||
* Check whether a file name ends with the extension for derivations.
|
||||
*/
|
||||
bool isDerivation() const;
|
||||
|
||||
std::string_view name() const
|
||||
|
|
@ -67,7 +77,10 @@ public:
|
|||
typedef std::set<StorePath> StorePathSet;
|
||||
typedef std::vector<StorePath> StorePaths;
|
||||
|
||||
/* Extension of derivations in the Nix store. */
|
||||
/**
|
||||
* The file extension of \ref Derivation derivations when serialized
|
||||
* into store objects.
|
||||
*/
|
||||
const std::string drvExtension = ".drv";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
|
|||
checkInterrupt();
|
||||
Path lockPath = path + ".lock";
|
||||
|
||||
debug(format("locking path '%1%'") % path);
|
||||
debug("locking path '%1%'", path);
|
||||
|
||||
AutoCloseFD fd;
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
|
|||
}
|
||||
}
|
||||
|
||||
debug(format("lock acquired on '%1%'") % lockPath);
|
||||
debug("lock acquired on '%1%'", lockPath);
|
||||
|
||||
/* Check that the lock file hasn't become stale (i.e.,
|
||||
hasn't been unlinked). */
|
||||
|
|
@ -130,7 +130,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
|
|||
a lock on a deleted file. This means that other
|
||||
processes may create and acquire a lock on
|
||||
`lockPath', and proceed. So we must retry. */
|
||||
debug(format("open lock file '%1%' has become stale") % lockPath);
|
||||
debug("open lock file '%1%' has become stale", lockPath);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ void PathLocks::unlock()
|
|||
"error (ignored): cannot close lock file on '%1%'",
|
||||
i.second);
|
||||
|
||||
debug(format("lock released on '%1%'") % i.second);
|
||||
debug("lock released on '%1%'", i.second);
|
||||
}
|
||||
|
||||
fds.clear();
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path pro
|
|||
static void makeName(const Path & profile, GenerationNumber num,
|
||||
Path & outLink)
|
||||
{
|
||||
Path prefix = (format("%1%-%2%") % profile % num).str();
|
||||
Path prefix = fmt("%1%-%2%", profile, num);
|
||||
outLink = prefix + "-link";
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ void switchGeneration(
|
|||
|
||||
void lockProfile(PathLocks & lock, const Path & profile)
|
||||
{
|
||||
lock.lockPaths({profile}, (format("waiting for lock on profile '%1%'") % profile).str());
|
||||
lock.lockPaths({profile}, fmt("waiting for lock on profile '%1%'", profile));
|
||||
lock.setDeletion(true);
|
||||
}
|
||||
|
||||
|
|
@ -282,28 +282,48 @@ std::string optimisticLockProfile(const Path & profile)
|
|||
|
||||
Path profilesDir()
|
||||
{
|
||||
auto profileRoot = createNixStateDir() + "/profiles";
|
||||
auto profileRoot =
|
||||
(getuid() == 0)
|
||||
? rootProfilesDir()
|
||||
: createNixStateDir() + "/profiles";
|
||||
createDirs(profileRoot);
|
||||
return profileRoot;
|
||||
}
|
||||
|
||||
Path rootProfilesDir()
|
||||
{
|
||||
return settings.nixStateDir + "/profiles/per-user/root";
|
||||
}
|
||||
|
||||
|
||||
Path getDefaultProfile()
|
||||
{
|
||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||
try {
|
||||
auto profile =
|
||||
getuid() == 0
|
||||
? settings.nixStateDir + "/profiles/default"
|
||||
: profilesDir() + "/profile";
|
||||
auto profile = profilesDir() + "/profile";
|
||||
if (!pathExists(profileLink)) {
|
||||
replaceSymlink(profile, profileLink);
|
||||
}
|
||||
// Backwards compatibiliy measure: Make root's profile available as
|
||||
// `.../default` as it's what NixOS and most of the init scripts expect
|
||||
Path globalProfileLink = settings.nixStateDir + "/profiles/default";
|
||||
if (getuid() == 0 && !pathExists(globalProfileLink)) {
|
||||
replaceSymlink(profile, globalProfileLink);
|
||||
}
|
||||
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||
} catch (Error &) {
|
||||
return profileLink;
|
||||
}
|
||||
}
|
||||
|
||||
Path defaultChannelsDir()
|
||||
{
|
||||
return profilesDir() + "/channels";
|
||||
}
|
||||
|
||||
Path rootChannelsDir()
|
||||
{
|
||||
return rootProfilesDir() + "/channels";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,13 +68,32 @@ void lockProfile(PathLocks & lock, const Path & profile);
|
|||
rebuilt. */
|
||||
std::string optimisticLockProfile(const Path & profile);
|
||||
|
||||
/* Creates and returns the path to a directory suitable for storing the user’s
|
||||
profiles. */
|
||||
/**
|
||||
* Create and return the path to a directory suitable for storing the user’s
|
||||
* profiles.
|
||||
*/
|
||||
Path profilesDir();
|
||||
|
||||
/* Resolve the default profile (~/.nix-profile by default, $XDG_STATE_HOME/
|
||||
nix/profile if XDG Base Directory Support is enabled), and create if doesn't
|
||||
exist */
|
||||
/**
|
||||
* Return the path to the profile directory for root (but don't try creating it)
|
||||
*/
|
||||
Path rootProfilesDir();
|
||||
|
||||
/**
|
||||
* Create and return the path to the file used for storing the users's channels
|
||||
*/
|
||||
Path defaultChannelsDir();
|
||||
|
||||
/**
|
||||
* Return the path to the channel directory for root (but don't try creating it)
|
||||
*/
|
||||
Path rootChannelsDir();
|
||||
|
||||
/**
|
||||
* Resolve the default profile (~/.nix-profile by default,
|
||||
* $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled),
|
||||
* and create if doesn't exist
|
||||
*/
|
||||
Path getDefaultProfile();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ static void search(
|
|||
if (!match) continue;
|
||||
std::string ref(s.substr(i, refLength));
|
||||
if (hashes.erase(ref)) {
|
||||
debug(format("found reference to '%1%' at offset '%2%'")
|
||||
% ref % i);
|
||||
debug("found reference to '%1%' at offset '%2%'", ref, i);
|
||||
seen.insert(ref);
|
||||
}
|
||||
++i;
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ void RemoteStore::setOptions(Connection & conn)
|
|||
overrides.erase(settings.buildCores.name);
|
||||
overrides.erase(settings.useSubstitutes.name);
|
||||
overrides.erase(loggerSettings.showTrace.name);
|
||||
overrides.erase(settings.experimentalFeatures.name);
|
||||
overrides.erase(experimentalFeatureSettings.experimentalFeatures.name);
|
||||
overrides.erase(settings.pluginFiles.name);
|
||||
conn.to << overrides.size();
|
||||
for (auto & i : overrides)
|
||||
|
|
@ -880,7 +880,7 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
|
|||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
printStorePath(bfd.drvPath), output);
|
||||
auto outputId = DrvOutput{ *outputHash, output };
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
auto realisation =
|
||||
queryRealisation(outputId);
|
||||
if (!realisation)
|
||||
|
|
|
|||
|
|
@ -22,11 +22,13 @@ struct RemoteStoreConfig : virtual StoreConfig
|
|||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
|
||||
const Setting<int> maxConnections{(StoreConfig*) this, 1,
|
||||
"max-connections", "maximum number of concurrent connections to the Nix daemon"};
|
||||
const Setting<int> maxConnections{(StoreConfig*) this, 1, "max-connections",
|
||||
"Maximum number of concurrent connections to the Nix daemon."};
|
||||
|
||||
const Setting<unsigned int> maxConnectionAge{(StoreConfig*) this, std::numeric_limits<unsigned int>::max(),
|
||||
"max-connection-age", "number of seconds to reuse a connection"};
|
||||
const Setting<unsigned int> maxConnectionAge{(StoreConfig*) this,
|
||||
std::numeric_limits<unsigned int>::max(),
|
||||
"max-connection-age",
|
||||
"Maximum age of a connection before it is closed."};
|
||||
};
|
||||
|
||||
/* FIXME: RemoteStore is a misnomer - should be something like
|
||||
|
|
@ -38,8 +40,6 @@ class RemoteStore : public virtual RemoteStoreConfig,
|
|||
{
|
||||
public:
|
||||
|
||||
virtual bool sameMachine() = 0;
|
||||
|
||||
RemoteStore(const Params & params);
|
||||
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ struct S3Error : public Error
|
|||
/* Helper: given an Outcome<R, E>, return R in case of success, or
|
||||
throw an exception in case of an error. */
|
||||
template<typename R, typename E>
|
||||
R && checkAws(const FormatOrString & fs, Aws::Utils::Outcome<R, E> && outcome)
|
||||
R && checkAws(std::string_view s, Aws::Utils::Outcome<R, E> && outcome)
|
||||
{
|
||||
if (!outcome.IsSuccess())
|
||||
throw S3Error(
|
||||
outcome.GetError().GetErrorType(),
|
||||
fs.s + ": " + outcome.GetError().GetMessage());
|
||||
s + ": " + outcome.GetError().GetMessage());
|
||||
return outcome.GetResultWithOwnership();
|
||||
}
|
||||
|
||||
|
|
@ -192,19 +192,72 @@ S3BinaryCacheStore::S3BinaryCacheStore(const Params & params)
|
|||
struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
||||
{
|
||||
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
|
||||
const Setting<std::string> profile{(StoreConfig*) this, "", "profile", "The name of the AWS configuration profile to use."};
|
||||
const Setting<std::string> region{(StoreConfig*) this, Aws::Region::US_EAST_1, "region", {"aws-region"}};
|
||||
const Setting<std::string> scheme{(StoreConfig*) this, "", "scheme", "The scheme to use for S3 requests, https by default."};
|
||||
const Setting<std::string> endpoint{(StoreConfig*) this, "", "endpoint", "An optional override of the endpoint to use when talking to S3."};
|
||||
const Setting<std::string> narinfoCompression{(StoreConfig*) this, "", "narinfo-compression", "compression method for .narinfo files"};
|
||||
const Setting<std::string> lsCompression{(StoreConfig*) this, "", "ls-compression", "compression method for .ls files"};
|
||||
const Setting<std::string> logCompression{(StoreConfig*) this, "", "log-compression", "compression method for log/* files"};
|
||||
|
||||
const Setting<std::string> profile{(StoreConfig*) this, "", "profile",
|
||||
R"(
|
||||
The name of the AWS configuration profile to use. By default
|
||||
Nix will use the `default` profile.
|
||||
)"};
|
||||
|
||||
const Setting<std::string> region{(StoreConfig*) this, Aws::Region::US_EAST_1, "region",
|
||||
R"(
|
||||
The region of the S3 bucket. If your bucket is not in
|
||||
`us–east-1`, you should always explicitly specify the region
|
||||
parameter.
|
||||
)"};
|
||||
|
||||
const Setting<std::string> scheme{(StoreConfig*) this, "", "scheme",
|
||||
R"(
|
||||
The scheme used for S3 requests, `https` (default) or `http`. This
|
||||
option allows you to disable HTTPS for binary caches which don't
|
||||
support it.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> HTTPS should be used if the cache might contain sensitive
|
||||
> information.
|
||||
)"};
|
||||
|
||||
const Setting<std::string> endpoint{(StoreConfig*) this, "", "endpoint",
|
||||
R"(
|
||||
The URL of the endpoint of an S3-compatible service such as MinIO.
|
||||
Do not specify this setting if you're using Amazon S3.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This endpoint must support HTTPS and will use path-based
|
||||
> addressing instead of virtual host based addressing.
|
||||
)"};
|
||||
|
||||
const Setting<std::string> narinfoCompression{(StoreConfig*) this, "", "narinfo-compression",
|
||||
"Compression method for `.narinfo` files."};
|
||||
|
||||
const Setting<std::string> lsCompression{(StoreConfig*) this, "", "ls-compression",
|
||||
"Compression method for `.ls` files."};
|
||||
|
||||
const Setting<std::string> logCompression{(StoreConfig*) this, "", "log-compression",
|
||||
R"(
|
||||
Compression method for `log/*` files. It is recommended to
|
||||
use a compression method supported by most web browsers
|
||||
(e.g. `brotli`).
|
||||
)"};
|
||||
|
||||
const Setting<bool> multipartUpload{
|
||||
(StoreConfig*) this, false, "multipart-upload", "whether to use multi-part uploads"};
|
||||
(StoreConfig*) this, false, "multipart-upload",
|
||||
"Whether to use multi-part uploads."};
|
||||
|
||||
const Setting<uint64_t> bufferSize{
|
||||
(StoreConfig*) this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"};
|
||||
(StoreConfig*) this, 5 * 1024 * 1024, "buffer-size",
|
||||
"Size (in bytes) of each part in multi-part uploads."};
|
||||
|
||||
const std::string name() override { return "S3 Binary Cache Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "s3-binary-cache-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore
|
||||
|
|
@ -430,9 +483,9 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
|
|||
std::string marker;
|
||||
|
||||
do {
|
||||
debug(format("listing bucket 's3://%s' from key '%s'...") % bucketName % marker);
|
||||
debug("listing bucket 's3://%s' from key '%s'...", bucketName, marker);
|
||||
|
||||
auto res = checkAws(format("AWS error listing bucket '%s'") % bucketName,
|
||||
auto res = checkAws(fmt("AWS error listing bucket '%s'", bucketName),
|
||||
s3Helper.client->ListObjects(
|
||||
Aws::S3::Model::ListObjectsRequest()
|
||||
.WithBucket(bucketName)
|
||||
|
|
@ -441,8 +494,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
|
|||
|
||||
auto & contents = res.GetContents();
|
||||
|
||||
debug(format("got %d keys, next marker '%s'")
|
||||
% contents.size() % res.GetNextMarker());
|
||||
debug("got %d keys, next marker '%s'",
|
||||
contents.size(), res.GetNextMarker());
|
||||
|
||||
for (auto object : contents) {
|
||||
auto & key = object.GetKey();
|
||||
|
|
|
|||
8
src/libstore/s3-binary-cache-store.md
Normal file
8
src/libstore/s3-binary-cache-store.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `s3://`*bucket-name*
|
||||
|
||||
This store allows reading and writing a binary cache stored in an AWS
|
||||
S3 bucket.
|
||||
|
||||
)"
|
||||
26
src/libstore/ssh-store-config.hh
Normal file
26
src/libstore/ssh-store-config.hh
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "store-api.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct CommonSSHStoreConfig : virtual StoreConfig
|
||||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
|
||||
const Setting<Path> sshKey{(StoreConfig*) this, "", "ssh-key",
|
||||
"Path to the SSH private key used to authenticate to the remote machine."};
|
||||
|
||||
const Setting<std::string> sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key",
|
||||
"The public host key of the remote machine."};
|
||||
|
||||
const Setting<bool> compress{(StoreConfig*) this, false, "compress",
|
||||
"Whether to enable SSH compression."};
|
||||
|
||||
const Setting<std::string> remoteStore{(StoreConfig*) this, "", "remote-store",
|
||||
R"(
|
||||
[Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format)
|
||||
to be used on the remote machine. The default is `auto`
|
||||
(i.e. use the Nix daemon or `/nix/store` directly).
|
||||
)"};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "ssh-store-config.hh"
|
||||
#include "store-api.hh"
|
||||
#include "remote-store.hh"
|
||||
#include "remote-fs-accessor.hh"
|
||||
|
|
@ -8,17 +9,22 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
struct SSHStoreConfig : virtual RemoteStoreConfig
|
||||
struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig
|
||||
{
|
||||
using RemoteStoreConfig::RemoteStoreConfig;
|
||||
using CommonSSHStoreConfig::CommonSSHStoreConfig;
|
||||
|
||||
const Setting<Path> sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"};
|
||||
const Setting<std::string> sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"};
|
||||
const Setting<bool> compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"};
|
||||
const Setting<Path> remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"};
|
||||
const Setting<std::string> remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"};
|
||||
const Setting<Path> remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program",
|
||||
"Path to the `nix-daemon` executable on the remote machine."};
|
||||
|
||||
const std::string name() override { return "SSH Store"; }
|
||||
const std::string name() override { return "Experimental SSH Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "ssh-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore
|
||||
|
|
@ -28,6 +34,7 @@ public:
|
|||
SSHStore(const std::string & scheme, const std::string & host, const Params & params)
|
||||
: StoreConfig(params)
|
||||
, RemoteStoreConfig(params)
|
||||
, CommonSSHStoreConfig(params)
|
||||
, SSHStoreConfig(params)
|
||||
, Store(params)
|
||||
, RemoteStore(params)
|
||||
|
|
@ -49,9 +56,6 @@ public:
|
|||
return *uriSchemes().begin() + "://" + host;
|
||||
}
|
||||
|
||||
bool sameMachine() override
|
||||
{ return false; }
|
||||
|
||||
// FIXME extend daemon protocol, move implementation to RemoteStore
|
||||
std::optional<std::string> getBuildLogExact(const StorePath & path) override
|
||||
{ unsupported("getBuildLogExact"); }
|
||||
|
|
|
|||
8
src/libstore/ssh-store.md
Normal file
8
src/libstore/ssh-store.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `ssh-ng://[username@]hostname`
|
||||
|
||||
Experimental store type that allows full access to a Nix store on a
|
||||
remote machine.
|
||||
|
||||
)"
|
||||
|
|
@ -465,10 +465,10 @@ StringSet StoreConfig::getDefaultSystemFeatures()
|
|||
{
|
||||
auto res = settings.systemFeatures.get();
|
||||
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||
res.insert("ca-derivations");
|
||||
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix))
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::RecursiveNix))
|
||||
res.insert("recursive-nix");
|
||||
|
||||
return res;
|
||||
|
|
@ -810,13 +810,13 @@ std::string Store::makeValidityRegistration(const StorePathSet & paths,
|
|||
|
||||
if (showHash) {
|
||||
s += info->narHash.to_string(Base16, false) + "\n";
|
||||
s += (format("%1%\n") % info->narSize).str();
|
||||
s += fmt("%1%\n", info->narSize);
|
||||
}
|
||||
|
||||
auto deriver = showDerivers && info->deriver ? printStorePath(*info->deriver) : "";
|
||||
s += deriver + "\n";
|
||||
|
||||
s += (format("%1%\n") % info->references.size()).str();
|
||||
s += fmt("%1%\n", info->references.size());
|
||||
|
||||
for (auto & j : info->references)
|
||||
s += printStorePath(j) + "\n";
|
||||
|
|
@ -875,6 +875,7 @@ json Store::pathInfoToJSON(const StorePathSet & storePaths,
|
|||
auto info = queryPathInfo(storePath);
|
||||
|
||||
jsonPath["path"] = printStorePath(info->path);
|
||||
jsonPath["valid"] = true;
|
||||
jsonPath["narHash"] = info->narHash.to_string(hashBase, true);
|
||||
jsonPath["narSize"] = info->narSize;
|
||||
|
||||
|
|
@ -1038,7 +1039,7 @@ std::map<StorePath, StorePath> copyPaths(
|
|||
for (auto & path : paths) {
|
||||
storePaths.insert(path.path());
|
||||
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
toplevelRealisations.insert(*realisation);
|
||||
}
|
||||
}
|
||||
|
|
@ -1124,6 +1125,8 @@ std::map<StorePath, StorePath> copyPaths(
|
|||
return storePathForDst;
|
||||
};
|
||||
|
||||
uint64_t total = 0;
|
||||
|
||||
for (auto & missingPath : sortedMissing) {
|
||||
auto info = srcStore.queryPathInfo(missingPath);
|
||||
|
||||
|
|
@ -1144,7 +1147,13 @@ std::map<StorePath, StorePath> copyPaths(
|
|||
{storePathS, srcUri, dstUri});
|
||||
PushActivity pact(act.id);
|
||||
|
||||
srcStore.narFromPath(missingPath, sink);
|
||||
LambdaSink progressSink([&](std::string_view data) {
|
||||
total += data.size();
|
||||
act.progress(total, info->narSize);
|
||||
});
|
||||
TeeSink tee { sink, progressSink };
|
||||
|
||||
srcStore.narFromPath(missingPath, tee);
|
||||
});
|
||||
pathsToCopy.push_back(std::pair{infoForDst, std::move(source)});
|
||||
}
|
||||
|
|
@ -1265,7 +1274,7 @@ std::optional<StorePath> Store::getBuildDerivationPath(const StorePath & path)
|
|||
}
|
||||
}
|
||||
|
||||
if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations) || !isValidPath(path))
|
||||
if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations) || !isValidPath(path))
|
||||
return path;
|
||||
|
||||
auto drv = readDerivation(path);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -26,9 +26,9 @@ UDSRemoteStore::UDSRemoteStore(const Params & params)
|
|||
|
||||
|
||||
UDSRemoteStore::UDSRemoteStore(
|
||||
const std::string scheme,
|
||||
std::string socket_path,
|
||||
const Params & params)
|
||||
const std::string scheme,
|
||||
std::string socket_path,
|
||||
const Params & params)
|
||||
: UDSRemoteStore(params)
|
||||
{
|
||||
path.emplace(socket_path);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon
|
|||
}
|
||||
|
||||
const std::string name() override { return "Local Daemon Store"; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "uds-remote-store.md"
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
class UDSRemoteStore : public virtual UDSRemoteStoreConfig, public virtual LocalFSStore, public virtual RemoteStore
|
||||
|
|
@ -29,9 +36,6 @@ public:
|
|||
static std::set<std::string> uriSchemes()
|
||||
{ return {"unix"}; }
|
||||
|
||||
bool sameMachine() override
|
||||
{ return true; }
|
||||
|
||||
ref<FSAccessor> getFSAccessor() override
|
||||
{ return LocalFSStore::getFSAccessor(); }
|
||||
|
||||
|
|
|
|||
9
src/libstore/uds-remote-store.md
Normal file
9
src/libstore/uds-remote-store.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
R"(
|
||||
|
||||
**Store URL format**: `daemon`, `unix://`*path*
|
||||
|
||||
This store type accesses a Nix store by talking to a Nix daemon
|
||||
listening on the Unix domain socket *path*. The store pseudo-URL
|
||||
`daemon` is equivalent to `unix:///nix/var/nix/daemon-socket/socket`.
|
||||
|
||||
)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue