mirror of
https://github.com/NixOS/nix
synced 2025-06-25 06:31:14 +02:00
Merge remote-tracking branch 'origin/master' into finish-value
This commit is contained in:
commit
6a3ecdaa39
271 changed files with 7672 additions and 1195 deletions
|
@ -892,7 +892,6 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
|
|||
mkString(s, encodeContext(context));
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
{
|
||||
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
|
||||
|
@ -1660,7 +1659,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
try {
|
||||
fn->fun(*this, vCur.determinePos(noPos), args, vCur);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
if (fn->addTrace)
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -1708,7 +1708,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
// so the debugger allows to inspect the wrong parameters passed to the builtin.
|
||||
fn->fun(*this, vCur.determinePos(noPos), vArgs, vCur);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
if (fn->addTrace)
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -69,10 +70,17 @@ struct PrimOp
|
|||
*/
|
||||
const char * doc = nullptr;
|
||||
|
||||
/**
|
||||
* Add a trace item, `while calling the '<name>' builtin`
|
||||
*
|
||||
* This is used to remove the redundant item for `builtins.addErrorContext`.
|
||||
*/
|
||||
bool addTrace = true;
|
||||
|
||||
/**
|
||||
* Implementation of the primop.
|
||||
*/
|
||||
PrimOpFun fun;
|
||||
std::function<std::remove_pointer<PrimOpFun>::type> fun;
|
||||
|
||||
/**
|
||||
* Optional experimental for this to be gated on.
|
||||
|
@ -722,10 +730,12 @@ public:
|
|||
bool fullGC();
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
* used to construct the associated value to their final store path
|
||||
* Realise the given context
|
||||
* @param[in] context the context to realise
|
||||
* @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set
|
||||
* @return a mapping from the placeholders used to construct the associated value to their final store path.
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
|
||||
/* Call the binary path filter predicate used builtins.path etc. */
|
||||
bool callPathFilter(
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "finally.hh"
|
||||
#include "fetch-settings.hh"
|
||||
#include "value-to-json.hh"
|
||||
#include "local-fs-store.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -755,7 +756,17 @@ void callFlake(EvalState & state,
|
|||
|
||||
auto lockedNode = node.dynamic_pointer_cast<const LockedNode>();
|
||||
|
||||
auto [storePath, subdir] = state.store->toStorePath(sourcePath.path.abs());
|
||||
// FIXME: This is a hack to support chroot stores. Remove this
|
||||
// once we can pass a sourcePath rather than a storePath to
|
||||
// call-flake.nix.
|
||||
auto path = sourcePath.path.abs();
|
||||
if (auto store = state.store.dynamic_pointer_cast<LocalFSStore>()) {
|
||||
auto realStoreDir = store->getRealStoreDir();
|
||||
if (isInDir(path, realStoreDir))
|
||||
path = store->storeDir + path.substr(realStoreDir.size());
|
||||
}
|
||||
|
||||
auto [storePath, subdir] = state.store->toStorePath(path);
|
||||
|
||||
emitTreeAttrs(
|
||||
state,
|
||||
|
@ -857,10 +868,13 @@ static RegisterPrimOp r3({
|
|||
Parse a flake reference, and return its exploded form.
|
||||
|
||||
For example:
|
||||
|
||||
```nix
|
||||
builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib"
|
||||
```
|
||||
|
||||
evaluates to:
|
||||
|
||||
```nix
|
||||
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
|
||||
```
|
||||
|
@ -909,12 +923,15 @@ static RegisterPrimOp r4({
|
|||
Convert a flake reference from attribute set format to URL format.
|
||||
|
||||
For example:
|
||||
|
||||
```nix
|
||||
builtins.flakeRefToString {
|
||||
dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github";
|
||||
}
|
||||
```
|
||||
|
||||
evaluates to
|
||||
|
||||
```nix
|
||||
"github:NixOS/nixpkgs/23.05?dir=lib"
|
||||
```
|
||||
|
|
|
@ -11,8 +11,11 @@ libexpr_SOURCES := \
|
|||
$(wildcard $(d)/flake/*.cc) \
|
||||
$(d)/lexer-tab.cc \
|
||||
$(d)/parser-tab.cc
|
||||
# Not just for this library itself, but also for downstream libraries using this library
|
||||
|
||||
libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libmain -I src/libexpr
|
||||
INCLUDE_libexpr := -I $(d)
|
||||
|
||||
libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr)
|
||||
|
||||
libexpr_LIBS = libutil libstore libfetchers
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace nix {
|
|||
* Miscellaneous
|
||||
*************************************************************/
|
||||
|
||||
StringMap EvalState::realiseContext(const NixStringContext & context)
|
||||
StringMap EvalState::realiseContext(const NixStringContext & context, StorePathSet * maybePathsOut, bool isIFD)
|
||||
{
|
||||
std::vector<DerivedPath::Built> drvs;
|
||||
StringMap res;
|
||||
|
@ -59,21 +59,23 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
},
|
||||
[&](const NixStringContextElem::Opaque & o) {
|
||||
auto ctxS = store->printStorePath(o.path);
|
||||
res.insert_or_assign(ctxS, ctxS);
|
||||
ensureValid(o.path);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(o.path);
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
/* Treat same as Opaque */
|
||||
auto ctxS = store->printStorePath(d.drvPath);
|
||||
res.insert_or_assign(ctxS, ctxS);
|
||||
ensureValid(d.drvPath);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(d.drvPath);
|
||||
},
|
||||
}, c.raw);
|
||||
}
|
||||
|
||||
if (drvs.empty()) return {};
|
||||
|
||||
if (!evalSettings.enableImportFromDerivation)
|
||||
if (isIFD && !evalSettings.enableImportFromDerivation)
|
||||
error<EvalError>(
|
||||
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
||||
drvs.begin()->to_string(*store)
|
||||
|
@ -90,6 +92,8 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
auto outputs = resolveDerivedPath(*buildStore, drv, &*store);
|
||||
for (auto & [outputName, outputPath] : outputs) {
|
||||
outputsToCopyAndAllow.insert(outputPath);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(outputPath);
|
||||
|
||||
/* Get all the output paths corresponding to the placeholders we had */
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
|
@ -106,10 +110,13 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
}
|
||||
|
||||
if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow);
|
||||
for (auto & outputPath : outputsToCopyAndAllow) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
allowPath(outputPath);
|
||||
|
||||
if (isIFD) {
|
||||
for (auto & outputPath : outputsToCopyAndAllow) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
allowPath(outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -826,7 +833,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
|||
auto message = state.coerceToString(pos, *args[0], context,
|
||||
"while evaluating the error message passed to builtins.addErrorContext",
|
||||
false, false).toOwned();
|
||||
e.addTrace(nullptr, HintFmt(message));
|
||||
e.addTrace(nullptr, HintFmt(message), TracePrint::Always);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -834,6 +841,8 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
|||
static RegisterPrimOp primop_addErrorContext(PrimOp {
|
||||
.name = "__addErrorContext",
|
||||
.arity = 2,
|
||||
// The normal trace item is redundant
|
||||
.addTrace = false,
|
||||
.fun = prim_addErrorContext,
|
||||
});
|
||||
|
||||
|
@ -1132,7 +1141,7 @@ static void derivationStrictInternal(
|
|||
bool contentAddressed = false;
|
||||
bool isImpure = false;
|
||||
std::optional<std::string> outputHash;
|
||||
std::string outputHashAlgo;
|
||||
std::optional<HashAlgorithm> outputHashAlgo;
|
||||
std::optional<ContentAddressMethod> ingestionMethod;
|
||||
|
||||
StringSet outputs;
|
||||
|
@ -1144,18 +1153,20 @@ static void derivationStrictInternal(
|
|||
vomit("processing attribute '%1%'", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string_view s) {
|
||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||
else if (s == "git") {
|
||||
experimentalFeatureSettings.require(Xp::GitHashing);
|
||||
ingestionMethod = FileIngestionMethod::Git;
|
||||
} else if (s == "text") {
|
||||
experimentalFeatureSettings.require(Xp::DynamicDerivations);
|
||||
ingestionMethod = TextIngestionMethod {};
|
||||
} else
|
||||
if (s == "recursive") {
|
||||
// back compat, new name is "nar"
|
||||
ingestionMethod = FileIngestionMethod::Recursive;
|
||||
} else try {
|
||||
ingestionMethod = ContentAddressMethod::parse(s);
|
||||
} catch (UsageError &) {
|
||||
state.error<EvalError>(
|
||||
"invalid value '%s' for 'outputHashMode' attribute", s
|
||||
).atPos(v).debugThrow();
|
||||
}
|
||||
if (ingestionMethod == TextIngestionMethod {})
|
||||
experimentalFeatureSettings.require(Xp::DynamicDerivations);
|
||||
if (ingestionMethod == FileIngestionMethod::Git)
|
||||
experimentalFeatureSettings.require(Xp::GitHashing);
|
||||
};
|
||||
|
||||
auto handleOutputs = [&](const Strings & ss) {
|
||||
|
@ -1231,7 +1242,7 @@ static void derivationStrictInternal(
|
|||
else if (i->name == state.sOutputHash)
|
||||
outputHash = state.forceStringNoCtx(*i->value, pos, context_below);
|
||||
else if (i->name == state.sOutputHashAlgo)
|
||||
outputHashAlgo = state.forceStringNoCtx(*i->value, pos, context_below);
|
||||
outputHashAlgo = parseHashAlgoOpt(state.forceStringNoCtx(*i->value, pos, context_below));
|
||||
else if (i->name == state.sOutputHashMode)
|
||||
handleHashMode(state.forceStringNoCtx(*i->value, pos, context_below));
|
||||
else if (i->name == state.sOutputs) {
|
||||
|
@ -1249,7 +1260,7 @@ static void derivationStrictInternal(
|
|||
if (i->name == state.sBuilder) drv.builder = std::move(s);
|
||||
else if (i->name == state.sSystem) drv.platform = std::move(s);
|
||||
else if (i->name == state.sOutputHash) outputHash = std::move(s);
|
||||
else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s);
|
||||
else if (i->name == state.sOutputHashAlgo) outputHashAlgo = parseHashAlgoOpt(s);
|
||||
else if (i->name == state.sOutputHashMode) handleHashMode(s);
|
||||
else if (i->name == state.sOutputs)
|
||||
handleOutputs(tokenizeString<Strings>(s));
|
||||
|
@ -1332,7 +1343,7 @@ static void derivationStrictInternal(
|
|||
"multiple outputs are not supported in fixed-output derivations"
|
||||
).atPos(v).debugThrow();
|
||||
|
||||
auto h = newHashAllowEmpty(*outputHash, parseHashAlgoOpt(outputHashAlgo));
|
||||
auto h = newHashAllowEmpty(*outputHash, outputHashAlgo);
|
||||
|
||||
auto method = ingestionMethod.value_or(FileIngestionMethod::Flat);
|
||||
|
||||
|
@ -1352,7 +1363,7 @@ static void derivationStrictInternal(
|
|||
state.error<EvalError>("derivation cannot be both content-addressed and impure")
|
||||
.atPos(v).debugThrow();
|
||||
|
||||
auto ha = parseHashAlgoOpt(outputHashAlgo).value_or(HashAlgorithm::SHA256);
|
||||
auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256);
|
||||
auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive);
|
||||
|
||||
for (auto & i : outputs) {
|
||||
|
@ -1575,23 +1586,50 @@ static RegisterPrimOp primop_pathExists({
|
|||
.fun = prim_pathExists,
|
||||
});
|
||||
|
||||
// Ideally, all trailing slashes should have been removed, but it's been like this for
|
||||
// almost a decade as of writing. Changing it will affect reproducibility.
|
||||
static std::string_view legacyBaseNameOf(std::string_view path)
|
||||
{
|
||||
if (path.empty())
|
||||
return "";
|
||||
|
||||
auto last = path.size() - 1;
|
||||
if (path[last] == '/' && last > 0)
|
||||
last -= 1;
|
||||
|
||||
auto pos = path.rfind('/', last);
|
||||
if (pos == path.npos)
|
||||
pos = 0;
|
||||
else
|
||||
pos += 1;
|
||||
|
||||
return path.substr(pos, last - pos + 1);
|
||||
}
|
||||
|
||||
/* Return the base name of the given string, i.e., everything
|
||||
following the last slash. */
|
||||
static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context,
|
||||
v.mkString(legacyBaseNameOf(*state.coerceToString(pos, *args[0], context,
|
||||
"while evaluating the first argument passed to builtins.baseNameOf",
|
||||
false, false)), context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_baseNameOf({
|
||||
.name = "baseNameOf",
|
||||
.args = {"s"},
|
||||
.args = {"x"},
|
||||
.doc = R"(
|
||||
Return the *base name* of the string *s*, that is, everything
|
||||
following the final slash in the string. This is similar to the GNU
|
||||
`basename` command.
|
||||
Return the *base name* of either a [path value](@docroot@/language/values.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules.
|
||||
|
||||
For a path value, the *base name* is considered to be the part of the path after the last directory separator, including any file extensions.
|
||||
This is the simple case, as path values don't have trailing slashes.
|
||||
|
||||
When the argument is a string, a more involved logic applies. If the string ends with a `/`, only this one final slash is removed.
|
||||
|
||||
After this, the *base name* is returned as previously described, assuming `/` as the directory separator. (Note that evaluation must be platform independent.)
|
||||
|
||||
This is somewhat similar to the [GNU `basename`](https://www.gnu.org/software/coreutils/manual/html_node/basename-invocation.html) command, but GNU `basename` will strip any number of trailing slashes.
|
||||
)",
|
||||
.fun = prim_baseNameOf,
|
||||
});
|
||||
|
@ -1893,11 +1931,13 @@ static RegisterPrimOp primop_outputOf({
|
|||
*`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`.
|
||||
This primop can be chained arbitrarily deeply.
|
||||
For instance,
|
||||
|
||||
```nix
|
||||
builtins.outputOf
|
||||
(builtins.outputOf myDrv "out")
|
||||
"out"
|
||||
```
|
||||
|
||||
will return a placeholder for the output of the output of `myDrv`.
|
||||
|
||||
This primop corresponds to the `^` sigil for derivable paths, e.g. as part of installable syntax on the command line.
|
||||
|
@ -3379,10 +3419,11 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
auto comparator = [&](Value * a, Value * b) {
|
||||
/* Optimization: if the comparator is lessThan, bypass
|
||||
callFunction. */
|
||||
/* TODO: (layus) this is absurd. An optimisation like this
|
||||
should be outside the lambda creation */
|
||||
if (args[0]->isPrimOp() && args[0]->primOp()->fun == prim_lessThan)
|
||||
return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b);
|
||||
if (args[0]->isPrimOp()) {
|
||||
auto ptr = args[0]->primOp()->fun.target<decltype(&prim_lessThan)>();
|
||||
if (ptr && *ptr == prim_lessThan)
|
||||
return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b);
|
||||
}
|
||||
|
||||
Value * vs[] = {a, b};
|
||||
Value vBool;
|
||||
|
@ -3838,7 +3879,7 @@ static RegisterPrimOp primop_stringLength({
|
|||
.name = "__stringLength",
|
||||
.args = {"e"},
|
||||
.doc = R"(
|
||||
Return the length of the string *e*. If *e* is not a string,
|
||||
Return the number of bytes of the string *e*. If *e* is not a string,
|
||||
evaluation is aborted.
|
||||
)",
|
||||
.fun = prim_stringLength,
|
||||
|
|
|
@ -473,7 +473,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
auto storePath =
|
||||
unpack
|
||||
? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name)
|
||||
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
||||
: fetchers::downloadFile(state.store, *url, name).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack
|
||||
|
@ -650,12 +650,14 @@ static RegisterPrimOp primop_fetchGit({
|
|||
|
||||
The public keys against which `rev` is verified if `verifyCommit` is enabled.
|
||||
Must be given as a list of attribute sets with the following form:
|
||||
|
||||
```nix
|
||||
{
|
||||
key = "<public key>";
|
||||
type = "<key type>"; # optional, default: "ssh-ed25519"
|
||||
}
|
||||
```
|
||||
|
||||
Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem)
|
|||
}
|
||||
|
||||
|
||||
SearchPath parseSearchPath(const Strings & rawElems)
|
||||
SearchPath SearchPath::parse(const Strings & rawElems)
|
||||
{
|
||||
SearchPath res;
|
||||
for (auto & rawElem : rawElems)
|
||||
|
|
|
@ -321,6 +321,7 @@ public:
|
|||
}
|
||||
|
||||
void mkPath(const SourcePath & path);
|
||||
void mkPath(std::string_view path);
|
||||
|
||||
inline void mkPath(InputAccessor * accessor, const char * path)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue