Skip to content

Commit c964271

Browse files
authored
Merge pull request #342 from DeterminateSystems/RossComputerGuy/simple-meta-provenance
Record subset of meta in provenance
2 parents 2ba1402 + b158718 commit c964271

File tree

11 files changed

+255
-38
lines changed

11 files changed

+255
-38
lines changed

src/libexpr/include/nix/expr/eval.hh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ struct StaticEvalSymbols
228228
line, column, functor, toString, right, wrong, structuredAttrs, json, allowedReferences, allowedRequisites,
229229
disallowedReferences, disallowedRequisites, maxSize, maxClosureSize, builder, args, contentAddressed, impure,
230230
outputHash, outputHashAlgo, outputHashMode, recurseForDerivations, description, self, epsilon, startSet,
231-
operator_, key, path, prefix, outputSpecified;
231+
operator_, key, path, prefix, outputSpecified, __meta, identifiers, license, licenses;
232232

233233
Expr::AstSymbols exprSymbols;
234234

@@ -281,6 +281,10 @@ struct StaticEvalSymbols
281281
.path = alloc.create("path"),
282282
.prefix = alloc.create("prefix"),
283283
.outputSpecified = alloc.create("outputSpecified"),
284+
.__meta = alloc.create("__meta"),
285+
.identifiers = alloc.create("identifier"),
286+
.license = alloc.create("license"),
287+
.licenses = alloc.create("licenses"),
284288
.exprSymbols = {
285289
.sub = alloc.create("__sub"),
286290
.lessThan = alloc.create("__lessThan"),

src/libexpr/include/nix/expr/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ headers = [ config_pub_h ] + files(
3030
'print-ambiguous.hh',
3131
'print-options.hh',
3232
'print.hh',
33+
'provenance.hh',
3334
'repl-exit-status.hh',
3435
'search-path.hh',
3536
'static-string-data.hh',
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "nix/util/provenance.hh"
4+
5+
namespace nix {
6+
7+
/**
8+
* Provenance indicating that this store path was instantiated by the `derivation` builtin function. Its main purpose is
9+
* to record `meta` fields.
10+
*/
11+
struct DerivationProvenance : Provenance
12+
{
13+
std::shared_ptr<const Provenance> next;
14+
ref<nlohmann::json> meta;
15+
16+
DerivationProvenance(std::shared_ptr<const Provenance> next, ref<nlohmann::json> meta)
17+
: next(std::move(next))
18+
, meta(std::move(meta)) {};
19+
20+
nlohmann::json to_json() const override;
21+
};
22+
23+
} // namespace nix

src/libexpr/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ sources = files(
181181
'primops.cc',
182182
'print-ambiguous.cc',
183183
'print.cc',
184+
'provenance.cc',
184185
'search-path.cc',
185186
'symbol-table.cc',
186187
'value-to-json.cc',

src/libexpr/primops.cc

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "nix/fetchers/fetch-to-store.hh"
1919
#include "nix/util/sort.hh"
2020
#include "nix/util/mounted-source-accessor.hh"
21+
#include "nix/expr/provenance.hh"
2122

2223
#include <boost/container/small_vector.hpp>
2324
#include <boost/unordered/concurrent_flat_map.hpp>
@@ -1504,6 +1505,8 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
15041505
StringSet outputs;
15051506
outputs.insert("out");
15061507

1508+
auto provenance = state.evalContext.provenance;
1509+
15071510
for (auto & i : attrs->lexicographicOrder(state.symbols)) {
15081511
if (i->name == state.s.ignoreNulls)
15091512
continue;
@@ -1571,6 +1574,29 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
15711574
experimentalFeatureSettings.require(Xp::ImpureDerivations);
15721575
}
15731576
break;
1577+
case EvalState::s.__meta.getId():
1578+
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
1579+
state.forceAttrs(*i->value, pos, "");
1580+
auto meta = i->value->attrs();
1581+
auto obj = nlohmann::json();
1582+
1583+
for (auto & i : meta->lexicographicOrder(state.symbols)) {
1584+
auto key = state.symbols[i->name];
1585+
switch (i->name.getId()) {
1586+
case EvalState::s.identifiers.getId():
1587+
case EvalState::s.license.getId():
1588+
case EvalState::s.licenses.getId():
1589+
obj.emplace(key, printValueAsJSON(state, true, *i->value, pos, context));
1590+
break;
1591+
default:
1592+
continue;
1593+
}
1594+
}
1595+
1596+
provenance =
1597+
std::make_shared<const DerivationProvenance>(provenance, make_ref<nlohmann::json>(obj));
1598+
}
1599+
break;
15741600
/* The `args' attribute is special: it supplies the
15751601
command-line arguments to the builder. */
15761602
case EvalState::s.args.getId():
@@ -1847,8 +1873,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
18471873
}
18481874

18491875
/* Write the resulting term into the Nix store directory. */
1850-
auto drvPath =
1851-
writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, state.evalContext.provenance);
1876+
auto drvPath = writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, provenance);
18521877
auto drvPathS = state.store->printStorePath(drvPath);
18531878

18541879
printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
@@ -5420,7 +5445,14 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
54205445
language feature gets added. It's not necessary to increase it
54215446
when primops get added, because you can just use `builtins ?
54225447
primOp' to check. */
5423-
v.mkInt(6);
5448+
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
5449+
/* Provenance allows for meta to be inside of derivations.
5450+
We increment the version to 7 so Nixpkgs will know when
5451+
provenance is available. */
5452+
v.mkInt(7);
5453+
} else {
5454+
v.mkInt(6);
5455+
}
54245456
addConstant(
54255457
"__langVersion",
54265458
v,

src/libexpr/provenance.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "nix/expr/provenance.hh"
2+
#include "nix/util/json-utils.hh"
3+
4+
#include <nlohmann/json.hpp>
5+
6+
namespace nix {
7+
8+
nlohmann::json DerivationProvenance::to_json() const
9+
{
10+
return nlohmann::json{
11+
{"type", "derivation"},
12+
{"meta", *meta},
13+
{"next", next ? next->to_json() : nlohmann::json(nullptr)},
14+
};
15+
}
16+
17+
Provenance::Register registerDerivationProvenance("derivation", [](nlohmann::json json) {
18+
auto & obj = getObject(json);
19+
std::shared_ptr<const Provenance> next;
20+
if (auto p = optionalValueAt(obj, "next"); p && !p->is_null())
21+
next = Provenance::from_json(*p);
22+
return make_ref<DerivationProvenance>(next, make_ref<nlohmann::json>(valueAt(obj, "meta")));
23+
});
24+
25+
} // namespace nix

src/nix/provenance-show.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The provenance chain shows the history of how the store path came to exist, incl
2222
- **Built**: The path was built from a derivation.
2323
- **Flake evaluation**: The derivation was instantiated during the evaluation of a flake output.
2424
- **Fetched**: The path was obtained by fetching a source tree.
25+
- **Meta**: Metadata associated with the derivation.
2526

2627
Note: if you want provenance in JSON format, use the `provenance` field returned by `nix path-info --json`.
2728

src/nix/provenance.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#include "nix/cmd/command.hh"
22
#include "nix/store/store-api.hh"
3+
#include "nix/expr/provenance.hh"
34
#include "nix/store/provenance.hh"
45
#include "nix/flake/provenance.hh"
56
#include "nix/fetchers/provenance.hh"
67
#include "nix/util/provenance.hh"
8+
#include "nix/util/json-utils.hh"
79

810
#include <memory>
911
#include <nlohmann/json.hpp>
@@ -92,6 +94,60 @@ struct CmdProvenanceShow : StorePathsCommand
9294
} else if (auto subpath = std::dynamic_pointer_cast<const SubpathProvenance>(provenance)) {
9395
logger->cout("← from file " ANSI_BOLD "%s" ANSI_NORMAL, subpath->subpath.abs());
9496
provenance = subpath->next;
97+
} else if (auto drv = std::dynamic_pointer_cast<const DerivationProvenance>(provenance)) {
98+
logger->cout("← with derivation metadata");
99+
#define TAB " "
100+
auto json = getObject(*(drv->meta));
101+
if (auto identifiers = optionalValueAt(json, "identifiers")) {
102+
auto ident = getObject(*identifiers);
103+
if (auto cpeParts = optionalValueAt(ident, "cpeParts")) {
104+
auto parts = getObject(*cpeParts);
105+
106+
auto vendor = parts["vendor"];
107+
auto product = parts["product"];
108+
auto version = parts["version"];
109+
auto update = parts["update"];
110+
111+
logger->cout(
112+
TAB "" ANSI_BOLD "CPE:" ANSI_NORMAL " cpe:2.3:a:%s:%s:%s:%s:*:*:*:*:*:*",
113+
vendor.is_null() ? "*" : vendor.get<std::string>(),
114+
product.is_null() ? "*" : product.get<std::string>(),
115+
version.is_null() ? "*" : version.get<std::string>(),
116+
update.is_null() ? "*" : update.get<std::string>());
117+
}
118+
}
119+
if (auto license = optionalValueAt(json, "license")) {
120+
if (license->is_array()) {
121+
logger->cout(TAB "" ANSI_BOLD "Licenses:" ANSI_NORMAL);
122+
auto licenses = getArray(*license);
123+
for (auto it = licenses.begin(); it != licenses.end(); it++) {
124+
auto license = getObject(*it);
125+
auto shortName = license["shortName"];
126+
logger->cout(TAB "" TAB "- %s", shortName.get<std::string>());
127+
}
128+
} else {
129+
auto obj = getObject(*license);
130+
auto shortName = obj["shortName"];
131+
logger->cout(TAB "" ANSI_BOLD "License:" ANSI_NORMAL " %s", shortName.get<std::string>());
132+
}
133+
}
134+
if (auto licenses = optionalValueAt(json, "licenses")) {
135+
if (licenses->is_array()) {
136+
logger->cout(TAB "" ANSI_BOLD "Licenses:" ANSI_NORMAL);
137+
auto licensesArray = getArray(*licenses);
138+
for (auto it = licensesArray.begin(); it != licensesArray.end(); it++) {
139+
auto license = getObject(*it);
140+
auto shortName = license["shortName"];
141+
logger->cout(TAB "" TAB "- %s", shortName.get<std::string>());
142+
}
143+
} else {
144+
auto license = getObject(*licenses);
145+
auto shortName = license["shortName"];
146+
logger->cout(TAB "" ANSI_BOLD "License:" ANSI_NORMAL " %s", shortName.get<std::string>());
147+
}
148+
}
149+
#undef TAB
150+
provenance = drv->next;
95151
} else {
96152
// Unknown or unhandled provenance type
97153
auto json = provenance->to_json();

tests/functional/config.nix.in

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ let
55
outputHashMode = "recursive";
66
outputHashAlgo = "sha256";
77
} else {};
8+
9+
optional = cond: elem: if cond then [ elem ] else [];
10+
optionalAttrs = cond: attrs: if cond then attrs else {};
811
in
912

1013
rec {
@@ -25,6 +28,8 @@ rec {
2528
eval "$buildCommand"
2629
'')];
2730
PATH = path;
28-
} // caArgs // removeAttrs args ["builder" "meta"])
31+
} // caArgs // optionalAttrs (builtins.langVersion >= 7) {
32+
__meta = args.meta or {};
33+
} // removeAttrs args ["builder" "meta"])
2934
// { meta = args.meta or {}; };
3035
}

0 commit comments

Comments
 (0)