mirror of
https://github.com/NixOS/nix.git
synced 2024-11-22 06:42:28 +00:00
Merge remote-tracking branch 'origin/master' into overlayfs-store
This commit is contained in:
commit
c0e6466a1e
@ -320,16 +320,6 @@ Derivations can declare some infrequently used optional attributes.
|
||||
```
|
||||
|
||||
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
|
||||
> **Warning**
|
||||
> This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||
>
|
||||
> To use this attribute, you must enable the
|
||||
> [`discard-references`](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) experimental feature.
|
||||
> For example, in [nix.conf](../command-ref/conf-file.md) you could add:
|
||||
>
|
||||
> ```
|
||||
> extra-experimental-features = discard-references
|
||||
> ```
|
||||
|
||||
When using [structured attributes](#adv-attr-structuredAttrs), the
|
||||
attribute `unsafeDiscardReferences` is an attribute set with a boolean value for each output name.
|
||||
|
@ -8,3 +8,11 @@
|
||||
These functions are useful for converting between flake references encoded as attribute sets and URLs.
|
||||
|
||||
- [`builtins.toJSON`](@docroot@/language/builtins.md#builtins-parseFlakeRef) now prints [--show-trace](@docroot@/command-ref/conf-file.html#conf-show-trace) items for the path in which it finds an evaluation error.
|
||||
|
||||
- Error messages regarding malformed input to [`derivation add`](@docroot@/command-ref/new-cli/nix3-derivation-add.md) are now clearer and more detailed.
|
||||
|
||||
- The `discard-references` feature has been stabilized.
|
||||
This means that the
|
||||
[unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references)
|
||||
attribute is no longer guarded by an experimental flag and can be used
|
||||
freely.
|
||||
|
@ -1031,14 +1031,15 @@ void EvalState::mkOutputString(
|
||||
Value & value,
|
||||
const StorePath & drvPath,
|
||||
const std::string outputName,
|
||||
std::optional<StorePath> optOutputPath)
|
||||
std::optional<StorePath> optOutputPath,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
value.mkString(
|
||||
optOutputPath
|
||||
? store->printStorePath(*std::move(optOutputPath))
|
||||
/* Downstream we would substitute this for an actual path once
|
||||
we build the floating CA derivation */
|
||||
: DownstreamPlaceholder::unknownCaOutput(drvPath, outputName).render(),
|
||||
: DownstreamPlaceholder::unknownCaOutput(drvPath, outputName, xpSettings).render(),
|
||||
NixStringContext {
|
||||
NixStringContextElem::Built {
|
||||
.drvPath = drvPath,
|
||||
|
@ -689,12 +689,15 @@ public:
|
||||
* be passed if and only if output store object is input-addressed.
|
||||
* Will be printed to form string if passed, otherwise a placeholder
|
||||
* will be used (see `DownstreamPlaceholder`).
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
void mkOutputString(
|
||||
Value & value,
|
||||
const StorePath & drvPath,
|
||||
const std::string outputName,
|
||||
std::optional<StorePath> optOutputPath);
|
||||
std::optional<StorePath> optOutputPath,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
|
@ -105,7 +105,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
};
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(Input::fromURL(parsedURL), ""),
|
||||
FlakeRef(Input::fromURL(parsedURL, isFlake), ""),
|
||||
percentDecode(match.str(6)));
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
parsedURL.query.insert_or_assign("shallow", "1");
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")),
|
||||
FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
|
||||
fragment);
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
std::string fragment;
|
||||
std::swap(fragment, parsedURL.fragment);
|
||||
|
||||
auto input = Input::fromURL(parsedURL);
|
||||
auto input = Input::fromURL(parsedURL, isFlake);
|
||||
input.parent = baseDir;
|
||||
|
||||
return std::make_pair(
|
||||
|
@ -84,23 +84,22 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
||||
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
||||
store->buildPaths(buildReqs);
|
||||
|
||||
/* Get all the output paths corresponding to the placeholders we had */
|
||||
for (auto & drv : drvs) {
|
||||
auto outputs = resolveDerivedPath(*store, drv);
|
||||
for (auto & [outputName, outputPath] : outputs) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
if (allowedPaths) {
|
||||
allowPath(outputPath);
|
||||
}
|
||||
/* Get all the output paths corresponding to the placeholders we had */
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
res.insert_or_assign(
|
||||
DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
|
||||
store->printStorePath(outputPath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
if (allowedPaths) {
|
||||
for (auto & [_placeholder, outputPath] : res) {
|
||||
allowPath(store->toRealPath(outputPath));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -37,8 +37,15 @@ RC_GTEST_FIXTURE_PROP(
|
||||
prop_built_path_placeholder_round_trip,
|
||||
(const StorePath & drvPath, const StorePathName & outputName))
|
||||
{
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
* to worry about race conditions if the tests run concurrently.
|
||||
*/
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "ca-derivations");
|
||||
|
||||
auto * v = state.allocValue();
|
||||
state.mkOutputString(*v, drvPath, outputName.name, std::nullopt);
|
||||
state.mkOutputString(*v, drvPath, outputName.name, std::nullopt, mockXpSettings);
|
||||
auto [d, _] = state.coerceToDerivedPathUnchecked(noPos, *v, "");
|
||||
DerivedPath::Built b {
|
||||
.drvPath = drvPath,
|
||||
|
@ -13,9 +13,9 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
|
||||
inputSchemes->push_back(std::move(inputScheme));
|
||||
}
|
||||
|
||||
Input Input::fromURL(const std::string & url)
|
||||
Input Input::fromURL(const std::string & url, bool requireTree)
|
||||
{
|
||||
return fromURL(parseURL(url));
|
||||
return fromURL(parseURL(url), requireTree);
|
||||
}
|
||||
|
||||
static void fixupInput(Input & input)
|
||||
@ -31,10 +31,10 @@ static void fixupInput(Input & input)
|
||||
input.locked = true;
|
||||
}
|
||||
|
||||
Input Input::fromURL(const ParsedURL & url)
|
||||
Input Input::fromURL(const ParsedURL & url, bool requireTree)
|
||||
{
|
||||
for (auto & inputScheme : *inputSchemes) {
|
||||
auto res = inputScheme->inputFromURL(url);
|
||||
auto res = inputScheme->inputFromURL(url, requireTree);
|
||||
if (res) {
|
||||
res->scheme = inputScheme;
|
||||
fixupInput(*res);
|
||||
|
@ -44,9 +44,9 @@ struct Input
|
||||
std::optional<Path> parent;
|
||||
|
||||
public:
|
||||
static Input fromURL(const std::string & url);
|
||||
static Input fromURL(const std::string & url, bool requireTree = true);
|
||||
|
||||
static Input fromURL(const ParsedURL & url);
|
||||
static Input fromURL(const ParsedURL & url, bool requireTree = true);
|
||||
|
||||
static Input fromAttrs(Attrs && attrs);
|
||||
|
||||
@ -129,7 +129,7 @@ struct InputScheme
|
||||
virtual ~InputScheme()
|
||||
{ }
|
||||
|
||||
virtual std::optional<Input> inputFromURL(const ParsedURL & url) const = 0;
|
||||
virtual std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const = 0;
|
||||
|
||||
virtual std::optional<Input> inputFromAttrs(const Attrs & attrs) const = 0;
|
||||
|
||||
|
@ -256,7 +256,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
||||
|
||||
struct GitInputScheme : InputScheme
|
||||
{
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
if (url.scheme != "git" &&
|
||||
url.scheme != "git+http" &&
|
||||
|
@ -30,7 +30,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
|
||||
virtual std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const = 0;
|
||||
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
if (url.scheme != type()) return {};
|
||||
|
||||
|
@ -7,7 +7,7 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript);
|
||||
|
||||
struct IndirectInputScheme : InputScheme
|
||||
{
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
if (url.scheme != "flake") return {};
|
||||
|
||||
|
@ -43,7 +43,7 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
|
||||
|
||||
struct MercurialInputScheme : InputScheme
|
||||
{
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
if (url.scheme != "hg+http" &&
|
||||
url.scheme != "hg+https" &&
|
||||
|
@ -6,7 +6,7 @@ namespace nix::fetchers {
|
||||
|
||||
struct PathInputScheme : InputScheme
|
||||
{
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
if (url.scheme != "path") return {};
|
||||
|
||||
|
@ -194,11 +194,11 @@ struct CurlInputScheme : InputScheme
|
||||
|| hasSuffix(path, ".tar.zst");
|
||||
}
|
||||
|
||||
virtual bool isValidURL(const ParsedURL & url) const = 0;
|
||||
virtual bool isValidURL(const ParsedURL & url, bool requireTree) const = 0;
|
||||
|
||||
std::optional<Input> inputFromURL(const ParsedURL & _url) const override
|
||||
std::optional<Input> inputFromURL(const ParsedURL & _url, bool requireTree) const override
|
||||
{
|
||||
if (!isValidURL(_url))
|
||||
if (!isValidURL(_url, requireTree))
|
||||
return std::nullopt;
|
||||
|
||||
Input input;
|
||||
@ -265,13 +265,13 @@ struct FileInputScheme : CurlInputScheme
|
||||
{
|
||||
const std::string inputType() const override { return "file"; }
|
||||
|
||||
bool isValidURL(const ParsedURL & url) const override
|
||||
bool isValidURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
auto parsedUrlScheme = parseUrlScheme(url.scheme);
|
||||
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
|
||||
&& (parsedUrlScheme.application
|
||||
? parsedUrlScheme.application.value() == inputType()
|
||||
: !hasTarballExtension(url.path));
|
||||
: (!requireTree && !hasTarballExtension(url.path)));
|
||||
}
|
||||
|
||||
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
|
||||
@ -285,14 +285,14 @@ struct TarballInputScheme : CurlInputScheme
|
||||
{
|
||||
const std::string inputType() const override { return "tarball"; }
|
||||
|
||||
bool isValidURL(const ParsedURL & url) const override
|
||||
bool isValidURL(const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
auto parsedUrlScheme = parseUrlScheme(url.scheme);
|
||||
|
||||
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
|
||||
&& (parsedUrlScheme.application
|
||||
? parsedUrlScheme.application.value() == inputType()
|
||||
: hasTarballExtension(url.path));
|
||||
: (requireTree || hasTarballExtension(url.path)));
|
||||
}
|
||||
|
||||
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||
|
@ -2308,7 +2308,6 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||
bool discardReferences = false;
|
||||
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||
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());
|
||||
|
@ -879,9 +879,11 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
||||
for (auto & [inputDrv, inputOutputs] : inputDrvs) {
|
||||
for (auto & outputName : inputOutputs) {
|
||||
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
inputRewrites.emplace(
|
||||
DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName).render(),
|
||||
store.printStorePath(*actualPath));
|
||||
}
|
||||
resolved.inputSrcs.insert(*actualPath);
|
||||
} else {
|
||||
warn("output '%s' of input '%s' missing, aborting the resolving",
|
||||
@ -993,6 +995,7 @@ DerivationOutput DerivationOutput::fromJSON(
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
std::set<std::string_view> keys;
|
||||
ensureType(_json, nlohmann::detail::value_t::object);
|
||||
auto json = (std::map<std::string, nlohmann::json>) _json;
|
||||
|
||||
for (const auto & [key, _] : json)
|
||||
@ -1097,36 +1100,51 @@ Derivation Derivation::fromJSON(
|
||||
const Store & store,
|
||||
const nlohmann::json & json)
|
||||
{
|
||||
using nlohmann::detail::value_t;
|
||||
|
||||
Derivation res;
|
||||
|
||||
res.name = json["name"];
|
||||
ensureType(json, value_t::object);
|
||||
|
||||
{
|
||||
auto & outputsObj = json["outputs"];
|
||||
res.name = ensureType(valueAt(json, "name"), value_t::string);
|
||||
|
||||
try {
|
||||
auto & outputsObj = ensureType(valueAt(json, "outputs"), value_t::object);
|
||||
for (auto & [outputName, output] : outputsObj.items()) {
|
||||
res.outputs.insert_or_assign(
|
||||
outputName,
|
||||
DerivationOutput::fromJSON(store, res.name, outputName, output));
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while reading key 'outputs'");
|
||||
throw;
|
||||
}
|
||||
|
||||
{
|
||||
auto & inputsList = json["inputSrcs"];
|
||||
try {
|
||||
auto & inputsList = ensureType(valueAt(json, "inputSrcs"), value_t::array);
|
||||
for (auto & input : inputsList)
|
||||
res.inputSrcs.insert(store.parseStorePath(static_cast<const std::string &>(input)));
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while reading key 'inputSrcs'");
|
||||
throw;
|
||||
}
|
||||
|
||||
{
|
||||
auto & inputDrvsObj = json["inputDrvs"];
|
||||
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items())
|
||||
try {
|
||||
auto & inputDrvsObj = ensureType(valueAt(json, "inputDrvs"), value_t::object);
|
||||
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items()) {
|
||||
ensureType(inputOutputs, value_t::array);
|
||||
res.inputDrvs[store.parseStorePath(inputDrvPath)] =
|
||||
static_cast<const StringSet &>(inputOutputs);
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while reading key 'inputDrvs'");
|
||||
throw;
|
||||
}
|
||||
|
||||
res.platform = json["system"];
|
||||
res.builder = json["builder"];
|
||||
res.args = json["args"];
|
||||
res.env = json["env"];
|
||||
res.platform = ensureType(valueAt(json, "system"), value_t::string);
|
||||
res.builder = ensureType(valueAt(json, "builder"), value_t::string);
|
||||
res.args = ensureType(valueAt(json, "args"), value_t::array);
|
||||
res.env = ensureType(valueAt(json, "env"), value_t::object);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ std::string DownstreamPlaceholder::render() const
|
||||
|
||||
DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
|
||||
const StorePath & drvPath,
|
||||
std::string_view outputName)
|
||||
std::string_view outputName,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
xpSettings.require(Xp::CaDerivations);
|
||||
auto drvNameWithExtension = drvPath.name();
|
||||
auto drvName = drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4);
|
||||
auto clearText = "nix-upstream-output:" + std::string { drvPath.hashPart() } + ":" + outputPathName(drvName, outputName);
|
||||
|
@ -52,10 +52,13 @@ public:
|
||||
*
|
||||
* The derivation itself is known (we have a store path for it), but
|
||||
* the output doesn't yet have a known store path.
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static DownstreamPlaceholder unknownCaOutput(
|
||||
const StorePath & drvPath,
|
||||
std::string_view outputName);
|
||||
std::string_view outputName,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Create a placehold for the output of an unknown derivation.
|
||||
|
@ -5,17 +5,24 @@
|
||||
namespace nix {
|
||||
|
||||
TEST(DownstreamPlaceholder, unknownCaOutput) {
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
* to worry about race conditions if the tests run concurrently.
|
||||
*/
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "ca-derivations");
|
||||
|
||||
ASSERT_EQ(
|
||||
DownstreamPlaceholder::unknownCaOutput(
|
||||
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
|
||||
"out").render(),
|
||||
"out",
|
||||
mockXpSettings).render(),
|
||||
"/0c6rn30q4frawknapgwq386zq358m8r6msvywcvc89n6m5p2dgbz");
|
||||
}
|
||||
|
||||
TEST(DownstreamPlaceholder, unknownDerivation) {
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
* to worry about race conditions if the tests run concurrently.
|
||||
* Same reason as above
|
||||
*/
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
|
||||
@ -24,7 +31,8 @@ TEST(DownstreamPlaceholder, unknownDerivation) {
|
||||
DownstreamPlaceholder::unknownDerivation(
|
||||
DownstreamPlaceholder::unknownCaOutput(
|
||||
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv.drv" },
|
||||
"out"),
|
||||
"out",
|
||||
mockXpSettings),
|
||||
"out",
|
||||
mockXpSettings).render(),
|
||||
"/0gn6agqxjyyalf0dpihgyf49xq5hqxgw100f0wydnj6yqrhqsb3w");
|
||||
|
@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails
|
||||
std::string_view description;
|
||||
};
|
||||
|
||||
constexpr std::array<ExperimentalFeatureDetails, 16> xpFeatureDetails = {{
|
||||
constexpr std::array<ExperimentalFeatureDetails, 15> xpFeatureDetails = {{
|
||||
{
|
||||
.tag = Xp::CaDerivations,
|
||||
.name = "ca-derivations",
|
||||
@ -182,15 +182,6 @@ constexpr std::array<ExperimentalFeatureDetails, 16> xpFeatureDetails = {{
|
||||
the [`use-cgroups`](#conf-use-cgroups) setting for details.
|
||||
)",
|
||||
},
|
||||
{
|
||||
.tag = Xp::DiscardReferences,
|
||||
.name = "discard-references",
|
||||
.description = R"(
|
||||
Allow the use of the [`unsafeDiscardReferences`](@docroot@/language/advanced-attributes.html#adv-attr-unsafeDiscardReferences) attribute in derivations
|
||||
that use [structured attributes](@docroot@/language/advanced-attributes.html#adv-attr-structuredAttrs). This disables scanning of outputs for
|
||||
runtime dependencies.
|
||||
)",
|
||||
},
|
||||
{
|
||||
.tag = Xp::DaemonTrustOverride,
|
||||
.name = "daemon-trust-override",
|
||||
|
@ -27,7 +27,6 @@ enum struct ExperimentalFeature
|
||||
ReplFlake,
|
||||
AutoAllocateUids,
|
||||
Cgroups,
|
||||
DiscardReferences,
|
||||
DaemonTrustOverride,
|
||||
DynamicDerivations,
|
||||
ParseTomlTimestamps,
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "json-utils.hh"
|
||||
#include "error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@ -16,4 +17,27 @@ nlohmann::json * get(nlohmann::json & map, const std::string & key)
|
||||
return &*i;
|
||||
}
|
||||
|
||||
const nlohmann::json & valueAt(
|
||||
const nlohmann::json & map,
|
||||
const std::string & key)
|
||||
{
|
||||
if (!map.contains(key))
|
||||
throw Error("Expected JSON object to contain key '%s' but it doesn't", key);
|
||||
|
||||
return map[key];
|
||||
}
|
||||
|
||||
const nlohmann::json & ensureType(
|
||||
const nlohmann::json & value,
|
||||
nlohmann::json::value_type expectedType
|
||||
)
|
||||
{
|
||||
if (value.type() != expectedType)
|
||||
throw Error(
|
||||
"Expected JSON value to be of type '%s' but it is of type '%s'",
|
||||
nlohmann::json(expectedType).type_name(),
|
||||
value.type_name());
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,28 @@ const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
|
||||
|
||||
nlohmann::json * get(nlohmann::json & map, const std::string & key);
|
||||
|
||||
/**
|
||||
* Get the value of a json object at a key safely, failing
|
||||
* with a Nix Error if the key does not exist.
|
||||
*
|
||||
* Use instead of nlohmann::json::at() to avoid ugly exceptions.
|
||||
*
|
||||
* _Does not check whether `map` is an object_, use `ensureType` for that.
|
||||
*/
|
||||
const nlohmann::json & valueAt(
|
||||
const nlohmann::json & map,
|
||||
const std::string & key);
|
||||
|
||||
/**
|
||||
* Ensure the type of a json object is what you expect, failing
|
||||
* with a Nix Error if it isn't.
|
||||
*
|
||||
* Use before type conversions and element access to avoid ugly exceptions.
|
||||
*/
|
||||
const nlohmann::json & ensureType(
|
||||
const nlohmann::json & value,
|
||||
nlohmann::json::value_type expectedType);
|
||||
|
||||
/**
|
||||
* For `adl_serializer<std::optional<T>>` below, we need to track what
|
||||
* types are not already using `null`. Only for them can we use `null`
|
||||
|
@ -22,6 +22,7 @@ StringPairs resolveRewrites(
|
||||
StringPairs res;
|
||||
for (auto & dep : dependencies)
|
||||
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||
for (auto & [ outputName, outputPath ] : drvDep->outputs)
|
||||
res.emplace(
|
||||
DownstreamPlaceholder::unknownCaOutput(drvDep->drvPath, outputName).render(),
|
||||
|
@ -239,7 +239,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||
if (pos != std::string::npos) {
|
||||
size_t margin = 32;
|
||||
auto pos2 = pos >= margin ? pos - margin : 0;
|
||||
hits[hash].emplace_back(fmt("%s: …%s…\n",
|
||||
hits[hash].emplace_back(fmt("%s: …%s…",
|
||||
p2,
|
||||
hilite(filterPrintable(
|
||||
std::string(contents, pos2, pos - pos2 + hash.size() + margin)),
|
||||
@ -255,7 +255,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||
for (auto & hash : hashes) {
|
||||
auto pos = target.find(hash);
|
||||
if (pos != std::string::npos)
|
||||
hits[hash].emplace_back(fmt("%s -> %s\n", p2,
|
||||
hits[hash].emplace_back(fmt("%s -> %s", p2,
|
||||
hilite(target, pos, StorePath::HashLen, getColour(hash))));
|
||||
}
|
||||
}
|
||||
@ -272,9 +272,9 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||
|
||||
for (auto & hit : hits[hash]) {
|
||||
bool first = hit == *hits[hash].begin();
|
||||
std::cout << tailPad
|
||||
<< (first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine))
|
||||
<< hit;
|
||||
logger->cout("%s%s%s", tailPad,
|
||||
(first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine)),
|
||||
hit);
|
||||
if (!all) break;
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,10 @@ nix-build -o $RESULT check-refs.nix -A test7
|
||||
nix-build -o $RESULT check-refs.nix -A test10
|
||||
|
||||
if isDaemonNewer 2.12pre20230103; then
|
||||
if ! isDaemonNewer 2.16.0; then
|
||||
enableFeatures discard-references
|
||||
restartDaemon
|
||||
fi
|
||||
|
||||
# test11 should succeed.
|
||||
test11=$(nix-build -o $RESULT check-refs.nix -A test11)
|
||||
|
@ -27,6 +27,7 @@ test_file_flake_input () {
|
||||
|
||||
mkdir inputs
|
||||
echo foo > inputs/test_input_file
|
||||
echo '{ outputs = { self }: { }; }' > inputs/flake.nix
|
||||
tar cfa test_input.tar.gz inputs
|
||||
cp test_input.tar.gz test_input_no_ext
|
||||
input_tarball_hash="$(nix hash path test_input.tar.gz)"
|
||||
@ -50,6 +51,9 @@ test_file_flake_input () {
|
||||
url = "file+file://$PWD/test_input.tar.gz";
|
||||
flake = false;
|
||||
};
|
||||
inputs.flake_no_ext = {
|
||||
url = "file://$PWD/test_input_no_ext";
|
||||
};
|
||||
outputs = { ... }: {};
|
||||
}
|
||||
EOF
|
||||
@ -58,7 +62,7 @@ EOF
|
||||
nix eval --file - <<EOF
|
||||
with (builtins.fromJSON (builtins.readFile ./flake.lock));
|
||||
|
||||
# Url inputs whose extension doesn’t match a known archive format should
|
||||
# Non-flake inputs whose extension doesn’t match a known archive format should
|
||||
# not be unpacked by default
|
||||
assert (nodes.no_ext_default_no_unpack.locked.type == "file");
|
||||
assert (nodes.no_ext_default_no_unpack.locked.unpack or false == false);
|
||||
@ -75,8 +79,16 @@ EOF
|
||||
# Explicitely passing the unpack parameter should enforce the desired behavior
|
||||
assert (nodes.no_ext_explicit_unpack.locked.narHash == nodes.tarball_default_unpack.locked.narHash);
|
||||
assert (nodes.tarball_explicit_no_unpack.locked.narHash == nodes.no_ext_default_no_unpack.locked.narHash);
|
||||
|
||||
# Flake inputs should always be tarballs
|
||||
assert (nodes.flake_no_ext.locked.type == "tarball");
|
||||
|
||||
true
|
||||
EOF
|
||||
|
||||
# Test tarball URLs on the command line.
|
||||
[[ $(nix flake metadata --json file://$PWD/test_input_no_ext | jq -r .resolved.type) = tarball ]]
|
||||
|
||||
popd
|
||||
|
||||
[[ -z "${NIX_DAEMON_PACKAGE-}" ]] && return 0
|
||||
|
@ -22,3 +22,8 @@ echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grepQuiet input-2
|
||||
# But only the “precise” one should refer to `reference-to-input-2`
|
||||
echo "$FAST_WHY_DEPENDS_OUTPUT" | grepQuietInverse reference-to-input-2
|
||||
echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grepQuiet reference-to-input-2
|
||||
|
||||
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '2p' | grepQuiet "└───reference-to-input-2 -> "
|
||||
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '3p' | grep " →" | grepQuiet "dependencies-input-2"
|
||||
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '4p' | grepQuiet " └───input0: …" # in input-2, file input0
|
||||
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '5p' | grep " →" | grepQuiet "dependencies-input-0" # is dependencies-input-0 referenced
|
||||
|
Loading…
Reference in New Issue
Block a user