Merge pull request #7887 from obsidiansystems/add-derivation
`nix derivation add`, `show-derivation` -> `derivation show`
This commit is contained in:
commit
54b3b6ebc6
|
@ -15,7 +15,7 @@
|
|||
|
||||
Example: `/nix/store/g946hcz4c8mdvq2g8vxx42z51qb71rvp-git-2.38.1.drv`
|
||||
|
||||
See [`nix show-derivation`](./command-ref/new-cli/nix3-show-derivation.md) (experimental) for displaying the contents of store derivations.
|
||||
See [`nix derivation show`](./command-ref/new-cli/nix3-derivation-show.md) (experimental) for displaying the contents of store derivations.
|
||||
|
||||
[store derivation]: #gloss-store-derivation
|
||||
|
||||
|
|
|
@ -45,3 +45,14 @@
|
|||
This is useful for scripting interactions with (non-legacy-ssh) remote Nix stores.
|
||||
|
||||
`nix store ping` and `nix doctor` now display this information.
|
||||
|
||||
* A new command `nix derivation add` is created, to allow adding derivations to the store without involving the Nix language.
|
||||
It exists to round out our collection of basic utility/plumbing commands, and allow for a low barrier-to-entry way of experimenting with alternative front-ends to the Nix Store.
|
||||
It uses the same JSON layout as `nix show-derivation`, and is its inverse.
|
||||
|
||||
* `nix show-derivation` has been renamed to `nix derivation show`.
|
||||
This matches `nix derivation add`, and avoids bloating the top-level namespace.
|
||||
The old name is still kept as an alias for compatibility, however.
|
||||
|
||||
* The `nix derivation {add,show}` JSON format now includes the derivation name as a top-level field.
|
||||
This is useful in general, but especially necessary for the `add` direction, as otherwise we would need to pass in the name out of band for certain cases.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <variant>
|
||||
#include "hash.hh"
|
||||
#include "comparator.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -30,6 +31,8 @@ struct TextHash {
|
|||
* Hash of the contents of the text/file.
|
||||
*/
|
||||
Hash hash;
|
||||
|
||||
GENERATE_CMP(TextHash, me->hash);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -46,6 +49,8 @@ struct FixedOutputHash {
|
|||
Hash hash;
|
||||
|
||||
std::string printMethodAlgo() const;
|
||||
|
||||
GENERATE_CMP(FixedOutputHash, me->method, me->hash);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -889,6 +889,67 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
|||
return resolved;
|
||||
}
|
||||
|
||||
|
||||
void Derivation::checkInvariants(Store & store, const StorePath & drvPath) const
|
||||
{
|
||||
assert(drvPath.isDerivation());
|
||||
std::string drvName(drvPath.name());
|
||||
drvName = drvName.substr(0, drvName.size() - drvExtension.size());
|
||||
|
||||
if (drvName != name) {
|
||||
throw Error("Derivation '%s' has name '%s' which does not match its path", store.printStorePath(drvPath), name);
|
||||
}
|
||||
|
||||
auto envHasRightPath = [&](const StorePath & actual, const std::string & varName)
|
||||
{
|
||||
auto j = env.find(varName);
|
||||
if (j == env.end() || store.parseStorePath(j->second) != actual)
|
||||
throw Error("derivation '%s' has incorrect environment variable '%s', should be '%s'",
|
||||
store.printStorePath(drvPath), varName, store.printStorePath(actual));
|
||||
};
|
||||
|
||||
|
||||
// Don't need the answer, but do this anyways to assert is proper
|
||||
// combination. The code below is more general and naturally allows
|
||||
// combinations that are currently prohibited.
|
||||
type();
|
||||
|
||||
std::optional<DrvHash> hashesModulo;
|
||||
for (auto & i : outputs) {
|
||||
std::visit(overloaded {
|
||||
[&](const DerivationOutput::InputAddressed & doia) {
|
||||
if (!hashesModulo) {
|
||||
// somewhat expensive so we do lazily
|
||||
hashesModulo = hashDerivationModulo(store, *this, true);
|
||||
}
|
||||
auto currentOutputHash = get(hashesModulo->hashes, i.first);
|
||||
if (!currentOutputHash)
|
||||
throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'",
|
||||
store.printStorePath(drvPath), store.printStorePath(doia.path), i.first);
|
||||
StorePath recomputed = store.makeOutputPath(i.first, *currentOutputHash, drvName);
|
||||
if (doia.path != recomputed)
|
||||
throw Error("derivation '%s' has incorrect output '%s', should be '%s'",
|
||||
store.printStorePath(drvPath), store.printStorePath(doia.path), store.printStorePath(recomputed));
|
||||
envHasRightPath(doia.path, i.first);
|
||||
},
|
||||
[&](const DerivationOutput::CAFixed & dof) {
|
||||
StorePath path = store.makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName);
|
||||
envHasRightPath(path, i.first);
|
||||
},
|
||||
[&](const DerivationOutput::CAFloating &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
[&](const DerivationOutput::Deferred &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
[&](const DerivationOutput::Impure &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
}, i.second.raw());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Hash impureOutputHash = hashString(htSHA256, "impure");
|
||||
|
||||
nlohmann::json DerivationOutput::toJSON(
|
||||
|
@ -916,10 +977,79 @@ nlohmann::json DerivationOutput::toJSON(
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
DerivationOutput DerivationOutput::fromJSON(
|
||||
const Store & store, std::string_view drvName, std::string_view outputName,
|
||||
const nlohmann::json & _json)
|
||||
{
|
||||
std::set<std::string_view> keys;
|
||||
auto json = (std::map<std::string, nlohmann::json>) _json;
|
||||
|
||||
for (const auto & [key, _] : json)
|
||||
keys.insert(key);
|
||||
|
||||
auto methodAlgo = [&]() -> std::pair<FileIngestionMethod, HashType> {
|
||||
std::string hashAlgo = json["hashAlgo"];
|
||||
auto method = FileIngestionMethod::Flat;
|
||||
if (hashAlgo.substr(0, 2) == "r:") {
|
||||
method = FileIngestionMethod::Recursive;
|
||||
hashAlgo = hashAlgo.substr(2);
|
||||
}
|
||||
auto hashType = parseHashType(hashAlgo);
|
||||
return { method, hashType };
|
||||
};
|
||||
|
||||
if (keys == (std::set<std::string_view> { "path" })) {
|
||||
return DerivationOutput::InputAddressed {
|
||||
.path = store.parseStorePath((std::string) json["path"]),
|
||||
};
|
||||
}
|
||||
|
||||
else if (keys == (std::set<std::string_view> { "path", "hashAlgo", "hash" })) {
|
||||
auto [method, hashType] = methodAlgo();
|
||||
auto dof = DerivationOutput::CAFixed {
|
||||
.hash = {
|
||||
.method = method,
|
||||
.hash = Hash::parseNonSRIUnprefixed((std::string) json["hash"], hashType),
|
||||
},
|
||||
};
|
||||
if (dof.path(store, drvName, outputName) != store.parseStorePath((std::string) json["path"]))
|
||||
throw Error("Path doesn't match derivation output");
|
||||
return dof;
|
||||
}
|
||||
|
||||
else if (keys == (std::set<std::string_view> { "hashAlgo" })) {
|
||||
auto [method, hashType] = methodAlgo();
|
||||
return DerivationOutput::CAFloating {
|
||||
.method = method,
|
||||
.hashType = hashType,
|
||||
};
|
||||
}
|
||||
|
||||
else if (keys == (std::set<std::string_view> { })) {
|
||||
return DerivationOutput::Deferred {};
|
||||
}
|
||||
|
||||
else if (keys == (std::set<std::string_view> { "hashAlgo", "impure" })) {
|
||||
auto [method, hashType] = methodAlgo();
|
||||
return DerivationOutput::Impure {
|
||||
.method = method,
|
||||
.hashType = hashType,
|
||||
};
|
||||
}
|
||||
|
||||
else {
|
||||
throw Error("invalid JSON for derivation output");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json Derivation::toJSON(const Store & store) const
|
||||
{
|
||||
nlohmann::json res = nlohmann::json::object();
|
||||
|
||||
res["name"] = name;
|
||||
|
||||
{
|
||||
nlohmann::json & outputsObj = res["outputs"];
|
||||
outputsObj = nlohmann::json::object();
|
||||
|
@ -950,4 +1080,43 @@ nlohmann::json Derivation::toJSON(const Store & store) const
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
Derivation Derivation::fromJSON(
|
||||
const Store & store,
|
||||
const nlohmann::json & json)
|
||||
{
|
||||
Derivation res;
|
||||
|
||||
res.name = json["name"];
|
||||
|
||||
{
|
||||
auto & outputsObj = json["outputs"];
|
||||
for (auto & [outputName, output] : outputsObj.items()) {
|
||||
res.outputs.insert_or_assign(
|
||||
outputName,
|
||||
DerivationOutput::fromJSON(store, res.name, outputName, output));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto & inputsList = json["inputSrcs"];
|
||||
for (auto & input : inputsList)
|
||||
res.inputSrcs.insert(store.parseStorePath(static_cast<const std::string &>(input)));
|
||||
}
|
||||
|
||||
{
|
||||
auto & inputDrvsObj = json["inputDrvs"];
|
||||
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items())
|
||||
res.inputDrvs[store.parseStorePath(inputDrvPath)] =
|
||||
static_cast<const StringSet &>(inputOutputs);
|
||||
}
|
||||
|
||||
res.platform = json["system"];
|
||||
res.builder = json["builder"];
|
||||
res.args = json["args"];
|
||||
res.env = json["env"];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "content-address.hh"
|
||||
#include "repair-flag.hh"
|
||||
#include "sync.hh"
|
||||
#include "comparator.hh"
|
||||
|
||||
#include <map>
|
||||
#include <variant>
|
||||
|
@ -24,6 +25,8 @@ class Store;
|
|||
struct DerivationOutputInputAddressed
|
||||
{
|
||||
StorePath path;
|
||||
|
||||
GENERATE_CMP(DerivationOutputInputAddressed, me->path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -44,6 +47,8 @@ struct DerivationOutputCAFixed
|
|||
* @param outputName The name of this output.
|
||||
*/
|
||||
StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;
|
||||
|
||||
GENERATE_CMP(DerivationOutputCAFixed, me->hash);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -62,13 +67,17 @@ struct DerivationOutputCAFloating
|
|||
* How the serialization will be hashed
|
||||
*/
|
||||
HashType hashType;
|
||||
|
||||
GENERATE_CMP(DerivationOutputCAFloating, me->method, me->hashType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Input-addressed output which depends on a (CA) derivation whose hash
|
||||
* isn't known yet.
|
||||
*/
|
||||
struct DerivationOutputDeferred {};
|
||||
struct DerivationOutputDeferred {
|
||||
GENERATE_CMP(DerivationOutputDeferred);
|
||||
};
|
||||
|
||||
/**
|
||||
* Impure output which is moved to a content-addressed location (like
|
||||
|
@ -85,6 +94,8 @@ struct DerivationOutputImpure
|
|||
* How the serialization will be hashed
|
||||
*/
|
||||
HashType hashType;
|
||||
|
||||
GENERATE_CMP(DerivationOutputImpure, me->method, me->hashType);
|
||||
};
|
||||
|
||||
typedef std::variant<
|
||||
|
@ -125,6 +136,11 @@ struct DerivationOutput : _DerivationOutputRaw
|
|||
const Store & store,
|
||||
std::string_view drvName,
|
||||
std::string_view outputName) const;
|
||||
static DerivationOutput fromJSON(
|
||||
const Store & store,
|
||||
std::string_view drvName,
|
||||
std::string_view outputName,
|
||||
const nlohmann::json & json);
|
||||
};
|
||||
|
||||
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
|
||||
|
@ -273,6 +289,15 @@ struct BasicDerivation
|
|||
DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const;
|
||||
|
||||
static std::string_view nameFromPath(const StorePath & storePath);
|
||||
|
||||
GENERATE_CMP(BasicDerivation,
|
||||
me->outputs,
|
||||
me->inputSrcs,
|
||||
me->platform,
|
||||
me->builder,
|
||||
me->args,
|
||||
me->env,
|
||||
me->name);
|
||||
};
|
||||
|
||||
struct Derivation : BasicDerivation
|
||||
|
@ -308,11 +333,26 @@ struct Derivation : BasicDerivation
|
|||
Store & store,
|
||||
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const;
|
||||
|
||||
/* Check that the derivation is valid and does not present any
|
||||
illegal states.
|
||||
|
||||
This is mainly a matter of checking the outputs, where our C++
|
||||
representation supports all sorts of combinations we do not yet
|
||||
allow. */
|
||||
void checkInvariants(Store & store, const StorePath & drvPath) const;
|
||||
|
||||
Derivation() = default;
|
||||
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
|
||||
Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
|
||||
|
||||
nlohmann::json toJSON(const Store & store) const;
|
||||
static Derivation fromJSON(
|
||||
const Store & store,
|
||||
const nlohmann::json & json);
|
||||
|
||||
GENERATE_CMP(Derivation,
|
||||
static_cast<const BasicDerivation &>(*me),
|
||||
me->inputDrvs);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -710,62 +710,6 @@ void canonicalisePathMetaData(const Path & path,
|
|||
canonicalisePathMetaData(path, uidRange, inodesSeen);
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv)
|
||||
{
|
||||
assert(drvPath.isDerivation());
|
||||
std::string drvName(drvPath.name());
|
||||
drvName = drvName.substr(0, drvName.size() - drvExtension.size());
|
||||
|
||||
auto envHasRightPath = [&](const StorePath & actual, const std::string & varName)
|
||||
{
|
||||
auto j = drv.env.find(varName);
|
||||
if (j == drv.env.end() || parseStorePath(j->second) != actual)
|
||||
throw Error("derivation '%s' has incorrect environment variable '%s', should be '%s'",
|
||||
printStorePath(drvPath), varName, printStorePath(actual));
|
||||
};
|
||||
|
||||
|
||||
// Don't need the answer, but do this anyways to assert is proper
|
||||
// combination. The code below is more general and naturally allows
|
||||
// combinations that are currently prohibited.
|
||||
drv.type();
|
||||
|
||||
std::optional<DrvHash> hashesModulo;
|
||||
for (auto & i : drv.outputs) {
|
||||
std::visit(overloaded {
|
||||
[&](const DerivationOutput::InputAddressed & doia) {
|
||||
if (!hashesModulo) {
|
||||
// somewhat expensive so we do lazily
|
||||
hashesModulo = hashDerivationModulo(*this, drv, true);
|
||||
}
|
||||
auto currentOutputHash = get(hashesModulo->hashes, i.first);
|
||||
if (!currentOutputHash)
|
||||
throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'",
|
||||
printStorePath(drvPath), printStorePath(doia.path), i.first);
|
||||
StorePath recomputed = makeOutputPath(i.first, *currentOutputHash, drvName);
|
||||
if (doia.path != recomputed)
|
||||
throw Error("derivation '%s' has incorrect output '%s', should be '%s'",
|
||||
printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed));
|
||||
envHasRightPath(doia.path, i.first);
|
||||
},
|
||||
[&](const DerivationOutput::CAFixed & dof) {
|
||||
StorePath path = makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName);
|
||||
envHasRightPath(path, i.first);
|
||||
},
|
||||
[&](const DerivationOutput::CAFloating &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
[&](const DerivationOutput::Deferred &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
[&](const DerivationOutput::Impure &) {
|
||||
/* Nothing to check */
|
||||
},
|
||||
}, i.second.raw());
|
||||
}
|
||||
}
|
||||
|
||||
void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs)
|
||||
{
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
@ -876,7 +820,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
|||
derivations). Note that if this throws an error, then the
|
||||
DB transaction is rolled back, so the path validity
|
||||
registration above is undone. */
|
||||
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
||||
if (checkOutputs) drv.checkInvariants(*this, info.path);
|
||||
|
||||
for (auto & i : drv.outputsAndOptPaths(*this)) {
|
||||
/* Floating CA derivations have indeterminate output paths until
|
||||
|
@ -1175,8 +1119,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
|||
for (auto & [_, i] : infos)
|
||||
if (i.path.isDerivation()) {
|
||||
// FIXME: inefficient; we already loaded the derivation in addValidPath().
|
||||
checkDerivationOutputs(i.path,
|
||||
readInvalidDerivation(i.path));
|
||||
readInvalidDerivation(i.path).checkInvariants(*this, i.path);
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
|
|
|
@ -270,8 +270,6 @@ private:
|
|||
|
||||
std::pair<Path, AutoCloseFD> createTempDirInStore();
|
||||
|
||||
void checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv);
|
||||
|
||||
typedef std::unordered_set<ino_t> InodeHash;
|
||||
|
||||
InodeHash loadInodeHash();
|
||||
|
|
|
@ -11,15 +11,29 @@ class DerivationTest : public LibStoreTest
|
|||
{
|
||||
};
|
||||
|
||||
#define TEST_JSON(TYPE, NAME, STR, VAL, ...) \
|
||||
TEST_F(DerivationTest, TYPE ## _ ## NAME ## _to_json) { \
|
||||
using nlohmann::literals::operator "" _json; \
|
||||
ASSERT_EQ( \
|
||||
STR ## _json, \
|
||||
(TYPE { VAL }).toJSON(*store __VA_OPT__(,) __VA_ARGS__)); \
|
||||
#define TEST_JSON(NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
|
||||
TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _to_json) { \
|
||||
using nlohmann::literals::operator "" _json; \
|
||||
ASSERT_EQ( \
|
||||
STR ## _json, \
|
||||
(DerivationOutput { VAL }).toJSON( \
|
||||
*store, \
|
||||
DRV_NAME, \
|
||||
OUTPUT_NAME)); \
|
||||
} \
|
||||
\
|
||||
TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _from_json) { \
|
||||
using nlohmann::literals::operator "" _json; \
|
||||
ASSERT_EQ( \
|
||||
DerivationOutput { VAL }, \
|
||||
DerivationOutput::fromJSON( \
|
||||
*store, \
|
||||
DRV_NAME, \
|
||||
OUTPUT_NAME, \
|
||||
STR ## _json)); \
|
||||
}
|
||||
|
||||
TEST_JSON(DerivationOutput, inputAddressed,
|
||||
TEST_JSON(inputAddressed,
|
||||
R"({
|
||||
"path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
|
||||
})",
|
||||
|
@ -28,7 +42,7 @@ TEST_JSON(DerivationOutput, inputAddressed,
|
|||
}),
|
||||
"drv-name", "output-name")
|
||||
|
||||
TEST_JSON(DerivationOutput, caFixed,
|
||||
TEST_JSON(caFixed,
|
||||
R"({
|
||||
"hashAlgo": "r:sha256",
|
||||
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
|
||||
|
@ -42,7 +56,7 @@ TEST_JSON(DerivationOutput, caFixed,
|
|||
}),
|
||||
"drv-name", "output-name")
|
||||
|
||||
TEST_JSON(DerivationOutput, caFloating,
|
||||
TEST_JSON(caFloating,
|
||||
R"({
|
||||
"hashAlgo": "r:sha256"
|
||||
})",
|
||||
|
@ -52,12 +66,12 @@ TEST_JSON(DerivationOutput, caFloating,
|
|||
}),
|
||||
"drv-name", "output-name")
|
||||
|
||||
TEST_JSON(DerivationOutput, deferred,
|
||||
TEST_JSON(deferred,
|
||||
R"({ })",
|
||||
DerivationOutput::Deferred { },
|
||||
"drv-name", "output-name")
|
||||
|
||||
TEST_JSON(DerivationOutput, impure,
|
||||
TEST_JSON(impure,
|
||||
R"({
|
||||
"hashAlgo": "r:sha256",
|
||||
"impure": true
|
||||
|
@ -68,8 +82,28 @@ TEST_JSON(DerivationOutput, impure,
|
|||
}),
|
||||
"drv-name", "output-name")
|
||||
|
||||
TEST_JSON(Derivation, impure,
|
||||
#undef TEST_JSON
|
||||
|
||||
#define TEST_JSON(NAME, STR, VAL, DRV_NAME) \
|
||||
TEST_F(DerivationTest, Derivation_ ## NAME ## _to_json) { \
|
||||
using nlohmann::literals::operator "" _json; \
|
||||
ASSERT_EQ( \
|
||||
STR ## _json, \
|
||||
(Derivation { VAL }).toJSON(*store)); \
|
||||
} \
|
||||
\
|
||||
TEST_F(DerivationTest, Derivation_ ## NAME ## _from_json) { \
|
||||
using nlohmann::literals::operator "" _json; \
|
||||
ASSERT_EQ( \
|
||||
Derivation { VAL }, \
|
||||
Derivation::fromJSON( \
|
||||
*store, \
|
||||
STR ## _json)); \
|
||||
}
|
||||
|
||||
TEST_JSON(simple,
|
||||
R"({
|
||||
"name": "my-derivation",
|
||||
"inputSrcs": [
|
||||
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
|
||||
],
|
||||
|
@ -92,6 +126,7 @@ TEST_JSON(Derivation, impure,
|
|||
})",
|
||||
({
|
||||
Derivation drv;
|
||||
drv.name = "my-derivation";
|
||||
drv.inputSrcs = {
|
||||
store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
||||
};
|
||||
|
@ -117,7 +152,8 @@ TEST_JSON(Derivation, impure,
|
|||
},
|
||||
};
|
||||
drv;
|
||||
}))
|
||||
}),
|
||||
"drv-name")
|
||||
|
||||
#undef TEST_JSON
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, FIELDS...) \
|
||||
#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, ...) \
|
||||
bool operator COMPARATOR(const MY_TYPE& other) const { \
|
||||
const MY_TYPE* me = this; \
|
||||
auto fields1 = std::make_tuple( FIELDS ); \
|
||||
me = &other; \
|
||||
auto fields2 = std::make_tuple( FIELDS ); \
|
||||
__VA_OPT__(const MY_TYPE* me = this;) \
|
||||
auto fields1 = std::make_tuple( __VA_ARGS__ ); \
|
||||
__VA_OPT__(me = &other;) \
|
||||
auto fields2 = std::make_tuple( __VA_ARGS__ ); \
|
||||
return fields1 COMPARATOR fields2; \
|
||||
}
|
||||
#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args)
|
||||
|
|
45
src/nix/derivation-add.cc
Normal file
45
src/nix/derivation-add.cc
Normal file
|
@ -0,0 +1,45 @@
|
|||
// FIXME: rename to 'nix plan add' or 'nix derivation add'?
|
||||
|
||||
#include "command.hh"
|
||||
#include "common-args.hh"
|
||||
#include "store-api.hh"
|
||||
#include "archive.hh"
|
||||
#include "derivations.hh"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace nix;
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct CmdAddDerivation : MixDryRun, StoreCommand
|
||||
{
|
||||
std::string description() override
|
||||
{
|
||||
return "Add a store derivation";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "derivation-add.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
auto json = nlohmann::json::parse(drainFD(STDIN_FILENO));
|
||||
|
||||
auto drv = Derivation::fromJSON(*store, json);
|
||||
|
||||
auto drvPath = writeDerivation(*store, drv, NoRepair, /* read only */ dryRun);
|
||||
|
||||
drv.checkInvariants(*store, drvPath);
|
||||
|
||||
writeDerivation(*store, drv, NoRepair, dryRun);
|
||||
|
||||
logger->cout("%s", store->printStorePath(drvPath));
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdAddDerivation = registerCommand2<CmdAddDerivation>({"derivation", "add"});
|
18
src/nix/derivation-add.md
Normal file
18
src/nix/derivation-add.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
R""(
|
||||
|
||||
# Description
|
||||
|
||||
This command reads from standard input a JSON representation of a
|
||||
[store derivation] to which an [*installable*](./nix.md#installables) evaluates.
|
||||
|
||||
Store derivations are used internally by Nix. They are store paths with
|
||||
extension `.drv` that represent the build-time dependency graph to which
|
||||
a Nix expression evaluates.
|
||||
|
||||
[store derivation]: ../../glossary.md#gloss-store-derivation
|
||||
|
||||
The JSON format is documented under the [`derivation show`] command.
|
||||
|
||||
[`derivation show`]: ./nix3-derivation-show.md
|
||||
|
||||
)""
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME: integrate this with nix path-info?
|
||||
// FIXME: rename to 'nix store show-derivation' or 'nix debug show-derivation'?
|
||||
// FIXME: rename to 'nix store derivation show' or 'nix debug derivation show'?
|
||||
|
||||
#include "command.hh"
|
||||
#include "common-args.hh"
|
||||
|
@ -33,7 +33,7 @@ struct CmdShowDerivation : InstallablesCommand
|
|||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "show-derivation.md"
|
||||
#include "derivation-show.md"
|
||||
;
|
||||
}
|
||||
|
||||
|
@ -61,4 +61,4 @@ struct CmdShowDerivation : InstallablesCommand
|
|||
}
|
||||
};
|
||||
|
||||
static auto rCmdShowDerivation = registerCommand<CmdShowDerivation>("show-derivation");
|
||||
static auto rCmdShowDerivation = registerCommand2<CmdShowDerivation>({"derivation", "show"});
|
|
@ -8,7 +8,7 @@ R""(
|
|||
[store derivation]: ../../glossary.md#gloss-store-derivation
|
||||
|
||||
```console
|
||||
# nix show-derivation nixpkgs#hello
|
||||
# nix derivation show nixpkgs#hello
|
||||
{
|
||||
"/nix/store/s6rn4jz1sin56rf4qj5b5v8jxjm32hlk-hello-2.10.drv": {
|
||||
…
|
||||
|
@ -20,14 +20,14 @@ R""(
|
|||
NixOS system:
|
||||
|
||||
```console
|
||||
# nix show-derivation -r /run/current-system
|
||||
# nix derivation show -r /run/current-system
|
||||
```
|
||||
|
||||
* Print all files fetched using `fetchurl` by Firefox's dependency
|
||||
graph:
|
||||
|
||||
```console
|
||||
# nix show-derivation -r nixpkgs#firefox \
|
||||
# nix derivation show -r nixpkgs#firefox \
|
||||
| jq -r '.[] | select(.outputs.out.hash and .env.urls) | .env.urls' \
|
||||
| uniq | sort
|
||||
```
|
||||
|
@ -39,10 +39,11 @@ R""(
|
|||
# Description
|
||||
|
||||
This command prints on standard output a JSON representation of the
|
||||
[store derivation]s to which [*installables*](./nix.md#installables) evaluate. Store derivations
|
||||
are used internally by Nix. They are store paths with extension `.drv`
|
||||
that represent the build-time dependency graph to which a Nix
|
||||
expression evaluates.
|
||||
[store derivation]s to which [*installables*](./nix.md#installables) evaluate.
|
||||
|
||||
Store derivations are used internally by Nix. They are store paths with
|
||||
extension `.drv` that represent the build-time dependency graph to which
|
||||
a Nix expression evaluates.
|
||||
|
||||
By default, this command only shows top-level derivations, but with
|
||||
`--recursive`, it also shows their dependencies.
|
||||
|
@ -51,6 +52,9 @@ The JSON output is a JSON object whose keys are the store paths of the
|
|||
derivations, and whose values are a JSON object with the following
|
||||
fields:
|
||||
|
||||
* `name`: The name of the derivation. This is used when calculating the
|
||||
store paths of the derivation's outputs.
|
||||
|
||||
* `outputs`: Information about the output paths of the
|
||||
derivation. This is a JSON object with one member per output, where
|
||||
the key is the output name and the value is a JSON object with these
|
25
src/nix/derivation.cc
Normal file
25
src/nix/derivation.cc
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "command.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdDerivation : virtual NixMultiCommand
|
||||
{
|
||||
CmdDerivation() : MultiCommand(RegisterCommand::getCommandsFor({"derivation"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "Work with derivations, Nix's notion of a build plan.";
|
||||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix derivation' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdDerivation = registerCommand<CmdDerivation>("derivation");
|
|
@ -127,6 +127,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||
{"optimise-store", {"store", "optimise"}},
|
||||
{"ping-store", {"store", "ping"}},
|
||||
{"sign-paths", {"store", "sign"}},
|
||||
{"show-derivation", {"derivation", "show"}},
|
||||
{"to-base16", {"hash", "to-base16"}},
|
||||
{"to-base32", {"hash", "to-base32"}},
|
||||
{"to-base64", {"hash", "to-base64"}},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
source common.sh
|
||||
|
||||
drv=$(nix-instantiate ./content-addressed.nix -A rootCA --arg seed 1)
|
||||
nix show-derivation "$drv" --arg seed 1
|
||||
nix derivation show "$drv" --arg seed 1
|
||||
|
||||
buildAttr () {
|
||||
local derivationPath=$1
|
||||
|
|
26
tests/ca/derivation-json.sh
Normal file
26
tests/ca/derivation-json.sh
Normal file
|
@ -0,0 +1,26 @@
|
|||
source common.sh
|
||||
|
||||
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||
|
||||
drvPath=$(nix-instantiate ../simple.nix)
|
||||
|
||||
nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json
|
||||
|
||||
drvPath2=$(nix derivation add < $TEST_HOME/simple.json)
|
||||
|
||||
[[ "$drvPath" = "$drvPath2" ]]
|
||||
|
||||
# Content-addressed derivations can be renamed.
|
||||
jq '.name = "foo"' < $TEST_HOME/simple.json > $TEST_HOME/foo.json
|
||||
drvPath3=$(nix derivation add --dry-run < $TEST_HOME/foo.json)
|
||||
# With --dry-run nothing is actually written
|
||||
[[ ! -e "$drvPath3" ]]
|
||||
|
||||
# Without --dry-run it is actually written
|
||||
drvPath4=$(nix derivation add < $TEST_HOME/foo.json)
|
||||
[[ "$drvPath4" = "$drvPath3" ]]
|
||||
[[ -e "$drvPath3" ]]
|
||||
|
||||
# The modified derivation read back as JSON matches
|
||||
nix derivation show $drvPath3 | jq .[] > $TEST_HOME/foo-read.json
|
||||
diff $TEST_HOME/foo.json $TEST_HOME/foo-read.json
|
12
tests/derivation-json.sh
Normal file
12
tests/derivation-json.sh
Normal file
|
@ -0,0 +1,12 @@
|
|||
source common.sh
|
||||
|
||||
drvPath=$(nix-instantiate simple.nix)
|
||||
|
||||
nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json
|
||||
|
||||
drvPath2=$(nix derivation add < $TEST_HOME/simple.json)
|
||||
|
||||
[[ "$drvPath" = "$drvPath2" ]]
|
||||
|
||||
# Input addressed derivations cannot be renamed.
|
||||
jq '.name = "foo"' < $TEST_HOME/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output"
|
|
@ -37,8 +37,8 @@ path4=$(nix build -L --no-link --json --file ./impure-derivations.nix impureOnIm
|
|||
(! nix build -L --no-link --json --file ./impure-derivations.nix inputAddressed 2>&1) | grep 'depends on impure derivation'
|
||||
|
||||
drvPath=$(nix eval --json --file ./impure-derivations.nix impure.drvPath | jq -r .)
|
||||
[[ $(nix show-derivation $drvPath | jq ".[\"$drvPath\"].outputs.out.impure") = true ]]
|
||||
[[ $(nix show-derivation $drvPath | jq ".[\"$drvPath\"].outputs.stuff.impure") = true ]]
|
||||
[[ $(nix derivation show $drvPath | jq ".[\"$drvPath\"].outputs.out.impure") = true ]]
|
||||
[[ $(nix derivation show $drvPath | jq ".[\"$drvPath\"].outputs.stuff.impure") = true ]]
|
||||
|
||||
# Fixed-output derivations *can* depend on impure derivations.
|
||||
path5=$(nix build -L --no-link --json --file ./impure-derivations.nix contentAddressed | jq -r .[].outputs.out)
|
||||
|
|
|
@ -102,6 +102,8 @@ nix_tests = \
|
|||
eval-store.sh \
|
||||
why-depends.sh \
|
||||
ca/why-depends.sh \
|
||||
derivation-json.sh \
|
||||
ca/derivation-json.sh \
|
||||
import-derivation.sh \
|
||||
ca/import-derivation.sh \
|
||||
nix_path.sh \
|
||||
|
|
Loading…
Reference in a new issue