1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-07 14:21:48 +02:00

Merge remote-tracking branch 'upstream/master' into lfs

This commit is contained in:
Leandro Reina 2025-02-03 19:07:20 +01:00
commit 134530a534
32 changed files with 324 additions and 196 deletions

View file

@ -613,12 +613,8 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st
context->last_err_code = NIX_OK;
try {
auto & v = check_value_in(value);
nix::NixStringContext stringContext;
auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned();
nix::StorePathSet storePaths;
auto rewrites = state->state.realiseContext(stringContext, &storePaths);
auto s = nix::rewriteStrings(rawStr, rewrites);
auto s = state->state.realiseString(v, &storePaths, isIFD);
// Convert to the C API StorePath type and convert to vector for index-based access
std::vector<StorePath> vec;

View file

@ -820,6 +820,15 @@ public:
*/
[[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
/**
* Realise the given string with context, and return the string with outputs instead of downstream output placeholders.
* @param[in] str the string to realise
* @param[out] paths all referenced store paths will be added to this set
* @return the realised string
* @throw EvalError if the value is not a string, path or derivation (see `coerceToString`)
*/
std::string realiseString(Value & str, StorePathSet * storePathsOutMaybe, bool isIFD = true, const PosIdx pos = noPos);
/* Call the binary path filter predicate used builtins.path etc. */
bool callPathFilter(
Value * filterFun,

View file

@ -24,6 +24,7 @@ deps_public_maybe_subproject = [
dependency('nix-fetchers'),
]
subdir('nix-meson-build-support/subprojects')
subdir('nix-meson-build-support/big-objs')
boost = dependency(
'boost',

View file

@ -47,6 +47,15 @@ static inline Value * mkString(EvalState & state, const std::csub_match & match)
return v;
}
std::string EvalState::realiseString(Value & s, StorePathSet * storePathsOutMaybe, bool isIFD, const PosIdx pos)
{
nix::NixStringContext stringContext;
auto rawStr = coerceToString(pos, s, stringContext, "while realising a string").toOwned();
auto rewrites = realiseContext(stringContext, storePathsOutMaybe, isIFD);
return nix::rewriteStrings(rawStr, rewrites);
}
StringMap EvalState::realiseContext(const NixStringContext & context, StorePathSet * maybePathsOut, bool isIFD)
{
std::vector<DerivedPath::Built> drvs;

View file

@ -90,24 +90,26 @@ static void fetchTree(
fetchers::Input input { state.fetchSettings };
NixStringContext context;
std::optional<std::string> type;
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
if (params.isFetchGit) type = "git";
state.forceValue(*args[0], pos);
if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchTree");
state.forceAttrs(*args[0], pos, fmt("while evaluating the argument passed to '%s'", fetcher));
fetchers::Attrs attrs;
if (auto aType = args[0]->attrs()->get(state.sType)) {
if (type)
state.error<EvalError>(
"unexpected attribute 'type'"
"unexpected argument 'type'"
).atPos(pos).debugThrow();
type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree");
type = state.forceStringNoCtx(*aType->value, aType->pos,
fmt("while evaluating the `type` argument passed to '%s'", fetcher));
} else if (!type)
state.error<EvalError>(
"attribute 'type' is missing in call to 'fetchTree'"
"argument 'type' is missing in call to '%s'", fetcher
).atPos(pos).debugThrow();
attrs.emplace("type", type.value());
@ -127,9 +129,8 @@ static void fetchTree(
else if (attr.value->type() == nInt) {
auto intValue = attr.value->integer().value;
if (intValue < 0) {
state.error<EvalError>("negative value given for fetchTree attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow();
}
if (intValue < 0)
state.error<EvalError>("negative value given for '%s' argument '%s': %d", fetcher, state.symbols[attr.name], intValue).atPos(pos).debugThrow();
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
} else if (state.symbols[attr.name] == "publicKeys") {
@ -137,8 +138,8 @@ static void fetchTree(
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
}
else
state.error<TypeError>("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name], showType(*attr.value)).debugThrow();
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name], fetcher, showType(*attr.value)).debugThrow();
}
if (params.isFetchGit && !attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
@ -153,14 +154,14 @@ static void fetchTree(
if (!params.allowNameArgument)
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
state.error<EvalError>(
"attribute 'name' isnt supported in call to 'fetchTree'"
"argument 'name' isnt supported in call to '%s'", fetcher
).atPos(pos).debugThrow();
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else {
auto url = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to the fetcher",
false, false).toOwned();
fmt("while evaluating the first argument passed to '%s'", fetcher),
false, false).toOwned();
if (params.isFetchGit) {
fetchers::Attrs attrs;
@ -173,7 +174,7 @@ static void fetchTree(
} else {
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
state.error<EvalError>(
"passing a string argument to 'fetchTree' requires the 'flakes' experimental feature"
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher
).atPos(pos).debugThrow();
input = fetchers::Input::fromURL(state.fetchSettings, url);
}
@ -182,15 +183,16 @@ static void fetchTree(
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
input = lookupInRegistries(state.store, input).first;
if (state.settings.pureEval && !input.isConsideredLocked(state.fetchSettings)) {
auto fetcher = "fetchTree";
if (params.isFetchGit)
fetcher = "fetchGit";
state.error<EvalError>(
"in pure evaluation mode, '%s' will not fetch unlocked input '%s'",
fetcher, input.to_string()
).atPos(pos).debugThrow();
if (state.settings.pureEval && !input.isLocked()) {
if (input.getNarHash())
warn(
"Input '%s' is unlocked (e.g. lacks a Git revision) but does have a NAR hash. "
"This is deprecated since such inputs are verifiable but may not be reproducible.",
input.to_string());
else
state.error<EvalError>(
"in pure evaluation mode, '%s' will not fetch unlocked input '%s'",
fetcher, input.to_string()).atPos(pos).debugThrow();
}
state.checkURI(input.toURLString());

View file

@ -155,12 +155,6 @@ bool Input::isLocked() const
return scheme && scheme->isLocked(*this);
}
bool Input::isConsideredLocked(
const Settings & settings) const
{
return isLocked() || (settings.allowDirtyLocks && getNarHash());
}
bool Input::isFinal() const
{
return maybeGetBoolAttr(attrs, "__final").value_or(false);

View file

@ -90,15 +90,6 @@ public:
*/
bool isLocked() const;
/**
* Return whether the input is either locked, or, if
* `allow-dirty-locks` is enabled, it has a NAR hash. In the
* latter case, we can verify the input but we may not be able to
* fetch it from anywhere.
*/
bool isConsideredLocked(
const Settings & settings) const;
/**
* Only for relative path flakes, i.e. 'path:./foo', returns the
* relative path, i.e. './foo'.

View file

@ -509,7 +509,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
*/
ref<GitSourceAccessor> getRawAccessor(const Hash & rev, bool smudgeLfs);
ref<SourceAccessor> getAccessor(const Hash & rev, bool exportIgnore, bool smudgeLfs) override;
ref<SourceAccessor> getAccessor(
const Hash & rev,
bool exportIgnore,
std::string displayPrefix,
bool smudgeLfs) override;
ref<SourceAccessor> getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override;
@ -628,7 +632,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
Hash treeHashToNarHash(const Hash & treeHash) override
{
auto accessor = getAccessor(treeHash, false, false);
auto accessor = getAccessor(treeHash, false, "", false);
fetchers::Cache::Key cacheKey{"treeHashToNarHash", {{"treeHash", treeHash.gitRev()}}};
@ -1212,16 +1216,19 @@ ref<GitSourceAccessor> GitRepoImpl::getRawAccessor(const Hash & rev, bool smudge
return make_ref<GitSourceAccessor>(self, rev, smudgeLfs);
}
ref<SourceAccessor> GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore, bool smudgeLfs)
ref<SourceAccessor> GitRepoImpl::getAccessor(
const Hash & rev,
bool exportIgnore,
std::string displayPrefix,
bool smudgeLfs)
{
auto self = ref<GitRepoImpl>(shared_from_this());
ref<GitSourceAccessor> rawGitAccessor = getRawAccessor(rev, smudgeLfs);
if (exportIgnore) {
rawGitAccessor->setPathDisplay(std::move(displayPrefix));
if (exportIgnore)
return make_ref<GitExportIgnoreSourceAccessor>(self, rawGitAccessor, rev);
}
else {
else
return rawGitAccessor;
}
}
ref<SourceAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
@ -1254,7 +1261,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
/* Read the .gitmodules files from this revision. */
CanonPath modulesFile(".gitmodules");
auto accessor = getAccessor(rev, exportIgnore, false);
auto accessor = getAccessor(rev, exportIgnore, "", false);
if (!accessor->pathExists(modulesFile)) return {};
/* Parse it and get the revision of each submodule. */

View file

@ -86,7 +86,11 @@ struct GitRepo
virtual bool hasObject(const Hash & oid) = 0;
virtual ref<SourceAccessor> getAccessor(const Hash & rev, bool exportIgnore, bool smudgeLfs) = 0;
virtual ref<SourceAccessor> getAccessor(
const Hash & rev,
bool exportIgnore,
std::string displayPrefix,
bool smudgeLfs) = 0;
virtual ref<SourceAccessor> getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0;

View file

@ -681,9 +681,7 @@ struct GitInputScheme : InputScheme
bool exportIgnore = getExportIgnoreAttr(input);
bool smudgeLfs = getLfsAttr(input);
auto accessor = repo->getAccessor(rev, exportIgnore, smudgeLfs);
accessor->setPathDisplay("«" + input.to_string() + "»");
auto accessor = repo->getAccessor(rev, exportIgnore, "«" + input.to_string() + "»", smudgeLfs);
/* If the repo has submodules, fetch them and return a mounted
input accessor consisting of the accessor for the top-level
@ -747,8 +745,6 @@ struct GitInputScheme : InputScheme
exportIgnore,
makeNotAllowedError(repoInfo.locationToArg()));
accessor->setPathDisplay(repoInfo.locationToArg());
/* If the repo has submodules, return a mounted input accessor
consisting of the accessor for the top-level repo and the
accessors for the submodule workdirs. */

View file

@ -294,9 +294,11 @@ struct GitArchiveInputScheme : InputScheme
#endif
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false, false);
accessor->setPathDisplay("«" + input.to_string() + "»");
auto accessor = getTarballCache()->getAccessor(
tarballInfo.treeHash,
false,
"«" + input.to_string() + "»",
false);
return {accessor, input};
}

View file

@ -105,7 +105,8 @@ DownloadFileResult downloadFile(
static DownloadTarballResult downloadTarball_(
const std::string & url,
const Headers & headers)
const Headers & headers,
const std::string & displayPrefix)
{
Cache::Key cacheKey{"tarball", {{"url", url}}};
@ -118,7 +119,7 @@ static DownloadTarballResult downloadTarball_(
.treeHash = treeHash,
.lastModified = (time_t) getIntAttr(infoAttrs, "lastModified"),
.immutableUrl = maybeGetStrAttr(infoAttrs, "immutableUrl"),
.accessor = getTarballCache()->getAccessor(treeHash, false, false),
.accessor = getTarballCache()->getAccessor(treeHash, false, displayPrefix, false),
};
};
@ -371,9 +372,10 @@ struct TarballInputScheme : CurlInputScheme
{
auto input(_input);
auto result = downloadTarball_(getStrAttr(input.attrs, "url"), {});
result.accessor->setPathDisplay("«" + input.to_string() + "»");
auto result = downloadTarball_(
getStrAttr(input.attrs, "url"),
{},
"«" + input.to_string() + "»");
if (result.immutableUrl) {
auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl);

View file

@ -107,7 +107,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
path = absPath(path, baseDir);
path = absPath(path, baseDir, true);
if (isFlake) {

View file

@ -1,7 +1,10 @@
#include <unordered_set>
#include "fetch-settings.hh"
#include "flake/settings.hh"
#include "lockfile.hh"
#include "store-api.hh"
#include "strings.hh"
#include <algorithm>
#include <iomanip>
@ -9,8 +12,6 @@
#include <iterator>
#include <nlohmann/json.hpp>
#include "strings.hh"
#include "flake/settings.hh"
namespace nix::flake {
@ -45,9 +46,16 @@ LockedNode::LockedNode(
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
, parentInputAttrPath(json.find("parent") != json.end() ? (std::optional<InputAttrPath>) json["parent"] : std::nullopt)
{
if (!lockedRef.input.isConsideredLocked(fetchSettings) && !lockedRef.input.isRelative())
throw Error("Lock file contains unlocked input '%s'. Use '--allow-dirty-locks' to accept this lock file.",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
if (!lockedRef.input.isLocked() && !lockedRef.input.isRelative()) {
if (lockedRef.input.getNarHash())
warn(
"Lock file entry '%s' is unlocked (e.g. lacks a Git revision) but does have a NAR hash. "
"This is deprecated since such inputs are verifiable but may not be reproducible.",
lockedRef.to_string());
else
throw Error("Lock file contains unlocked input '%s'. Use '--allow-dirty-locks' to accept this lock file.",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
}
// For backward compatibility, lock file entries are implicitly final.
assert(!lockedRef.input.attrs.contains("__final"));
@ -248,11 +256,20 @@ std::optional<FlakeRef> LockFile::isUnlocked(const fetchers::Settings & fetchSet
visit(root);
/* Return whether the input is either locked, or, if
`allow-dirty-locks` is enabled, it has a NAR hash. In the
latter case, we can verify the input but we may not be able to
fetch it from anywhere. */
auto isConsideredLocked = [&](const fetchers::Input & input)
{
return input.isLocked() || (fetchSettings.allowDirtyLocks && input.getNarHash());
};
for (auto & i : nodes) {
if (i == ref<const Node>(root)) continue;
auto node = i.dynamic_pointer_cast<const LockedNode>();
if (node
&& (!node->lockedRef.input.isConsideredLocked(fetchSettings)
&& (!isConsideredLocked(node->lockedRef.input)
|| !node->lockedRef.input.isFinal())
&& !node->lockedRef.input.isRelative())
return node->lockedRef;

View file

@ -167,16 +167,15 @@ LocalStore::LocalStore(
/* Ensure that the store and its parents are not symlinks. */
if (!settings.allowSymlinkedStore) {
Path path = realStoreDir;
struct stat st;
while (path != "/") {
st = lstat(path);
if (S_ISLNK(st.st_mode))
std::filesystem::path path = realStoreDir.get();
std::filesystem::path root = path.root_path();
while (path != root) {
if (std::filesystem::is_symlink(path))
throw Error(
"the path '%1%' is a symlink; "
"this is not allowed for the Nix store and its parent directories",
path);
path = dirOf(path);
path = path.parent_path();
}
}

View file

@ -19,10 +19,6 @@
# include "namespaces.hh"
#endif
#ifndef _WIN32
# include <sys/resource.h>
#endif
namespace nix {
unsigned int getMaxCPU()
@ -55,11 +51,11 @@ unsigned int getMaxCPU()
//////////////////////////////////////////////////////////////////////
#ifndef _WIN32
size_t savedStackSize = 0;
void setStackSize(size_t stackSize)
{
#ifndef _WIN32
struct rlimit limit;
if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) {
savedStackSize = limit.rlim_cur;
@ -77,31 +73,8 @@ void setStackSize(size_t stackSize)
);
}
}
#else
ULONG_PTR stackLow, stackHigh;
GetCurrentThreadStackLimits(&stackLow, &stackHigh);
ULONG maxStackSize = stackHigh - stackLow;
ULONG currStackSize = 0;
// This retrieves the current promised stack size
SetThreadStackGuarantee(&currStackSize);
if (currStackSize < stackSize) {
savedStackSize = currStackSize;
ULONG newStackSize = std::min(static_cast<ULONG>(stackSize), maxStackSize);
if (SetThreadStackGuarantee(&newStackSize) == 0) {
logger->log(
lvlError,
HintFmt(
"Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%",
savedStackSize,
stackSize,
maxStackSize,
std::to_string(GetLastError())
).str()
);
}
}
#endif
}
#endif
void restoreProcessContext(bool restoreMounts)
{

View file

@ -17,10 +17,13 @@ namespace nix {
*/
unsigned int getMaxCPU();
// It does not seem possible to dynamically change stack size on Windows.
#ifndef _WIN32
/**
* Change the stack size.
*/
void setStackSize(size_t stackSize);
#endif
/**
* Restore the original inherited Unix process context (such as signal

View file

@ -557,9 +557,11 @@ void mainWrapped(int argc, char * * argv)
int main(int argc, char * * argv)
{
#ifndef _WIN32
// Increase the default stack size for the evaluator and for
// libstdc++'s std::regex.
nix::setStackSize(64 * 1024 * 1024);
#endif
return nix::handleExceptions(argv[0], [&]() {
nix::mainWrapped(argc, argv);

View file

@ -103,6 +103,7 @@ mkMesonExecutable (finalAttrs: {
];
meta = {
mainProgram = "nix";
platforms = lib.platforms.unix ++ lib.platforms.windows;
};