diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 614dee788..efdc98d5a 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -54,7 +54,7 @@ void StoreCommand::run() run(getStore()); } -StorePathsCommand::StorePathsCommand(bool recursive) +RealisedPathsCommand::RealisedPathsCommand(bool recursive) : recursive(recursive) { if (recursive) @@ -81,30 +81,40 @@ StorePathsCommand::StorePathsCommand(bool recursive) }); } -void StorePathsCommand::run(ref store) +void RealisedPathsCommand::run(ref store) { - StorePaths storePaths; - + std::vector paths; if (all) { if (installables.size()) throw UsageError("'--all' does not expect arguments"); + // XXX: Only uses opaque paths, ignores all the realisations for (auto & p : store->queryAllValidPaths()) - storePaths.push_back(p); - } - - else { - for (auto & p : toStorePaths(store, realiseMode, operateOn, installables)) - storePaths.push_back(p); - + paths.push_back(p); + } else { + auto pathSet = toRealisedPaths(store, realiseMode, operateOn, installables); if (recursive) { - StorePathSet closure; - store->computeFSClosure(StorePathSet(storePaths.begin(), storePaths.end()), closure, false, false); - storePaths.clear(); - for (auto & p : closure) - storePaths.push_back(p); + auto roots = std::move(pathSet); + pathSet = {}; + RealisedPath::closure(*store, roots, pathSet); } + for (auto & path : pathSet) + paths.push_back(path); } + run(store, std::move(paths)); +} + +StorePathsCommand::StorePathsCommand(bool recursive) + : RealisedPathsCommand(recursive) +{ +} + +void StorePathsCommand::run(ref store, std::vector paths) +{ + StorePaths storePaths; + for (auto & p : paths) + storePaths.push_back(p.path()); + run(store, std::move(storePaths)); } diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index ed6980075..8c0b3a94a 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -141,7 +141,7 @@ private: }; /* A command that operates on zero or more store paths. */ -struct StorePathsCommand : public InstallablesCommand +struct RealisedPathsCommand : public InstallablesCommand { private: @@ -154,17 +154,28 @@ protected: public: - StorePathsCommand(bool recursive = false); + RealisedPathsCommand(bool recursive = false); using StoreCommand::run; - virtual void run(ref store, std::vector storePaths) = 0; + virtual void run(ref store, std::vector paths) = 0; void run(ref store) override; bool useDefaultInstallables() override { return !all; } }; +struct StorePathsCommand : public RealisedPathsCommand +{ + StorePathsCommand(bool recursive = false); + + using RealisedPathsCommand::run; + + virtual void run(ref store, std::vector storePaths) = 0; + + void run(ref store, std::vector paths) override; +}; + /* A command that operates on exactly one store path. */ struct StorePathCommand : public InstallablesCommand { @@ -218,6 +229,12 @@ std::set toDerivations(ref store, std::vector> installables, bool useDeriver = false); +std::set toRealisedPaths( + ref store, + Realise mode, + OperateOn operateOn, + std::vector> installables); + /* Helper function to generate args that invoke $EDITOR on filename:lineno. */ Strings editorFor(const Pos & pos); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 4e6bf4a9a..9ad02b5f0 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -704,23 +704,42 @@ Buildables build(ref store, Realise mode, return buildables; } -StorePathSet toStorePaths(ref store, - Realise mode, OperateOn operateOn, +std::set toRealisedPaths( + ref store, + Realise mode, + OperateOn operateOn, std::vector> installables) { - StorePathSet outPaths; - + std::set res; if (operateOn == OperateOn::Output) { for (auto & b : build(store, mode, installables)) std::visit(overloaded { [&](BuildableOpaque bo) { - outPaths.insert(bo.path); + res.insert(bo.path); }, [&](BuildableFromDrv bfd) { + auto drv = store->readDerivation(bfd.drvPath); + auto outputHashes = staticOutputHashes(*store, drv); for (auto & output : bfd.outputs) { - if (!output.second) - throw Error("Cannot operate on output of unbuilt CA drv"); - outPaths.insert(*output.second); + if (settings.isExperimentalFeatureEnabled("ca-derivations")) { + if (!outputHashes.count(output.first)) + throw Error( + "the derivation '%s' doesn't have an output named '%s'", + store->printStorePath(bfd.drvPath), + output.first); + auto outputId = DrvOutput{outputHashes.at(output.first), output.first}; + auto realisation = store->queryRealisation(outputId); + if (!realisation) + throw Error("cannot operate on an output of unbuilt content-addresed derivation '%s'", outputId.to_string()); + res.insert(RealisedPath{*realisation}); + } + else { + // If ca-derivations isn't enabled, behave as if + // all the paths are opaque to keep the default + // behavior + assert(output.second); + res.insert(*output.second); + } } }, }, b); @@ -731,9 +750,19 @@ StorePathSet toStorePaths(ref store, for (auto & i : installables) for (auto & b : i->toBuildables()) if (auto bfd = std::get_if(&b)) - outPaths.insert(bfd->drvPath); + res.insert(bfd->drvPath); } + return res; +} + +StorePathSet toStorePaths(ref store, + Realise mode, OperateOn operateOn, + std::vector> installables) +{ + StorePathSet outPaths; + for (auto & path : toRealisedPaths(store, mode, operateOn, installables)) + outPaths.insert(path.path()); return outPaths; } diff --git a/src/libstore/realisation.cc b/src/libstore/realisation.cc index 47ad90eee..cd74af4ee 100644 --- a/src/libstore/realisation.cc +++ b/src/libstore/realisation.cc @@ -46,4 +46,35 @@ Realisation Realisation::fromJSON( }; } +StorePath RealisedPath::path() const { + return std::visit([](auto && arg) { return arg.getPath(); }, raw); +} + +void RealisedPath::closure( + Store& store, + const RealisedPath::Set& startPaths, + RealisedPath::Set& ret) +{ + // FIXME: This only builds the store-path closure, not the real realisation + // closure + StorePathSet initialStorePaths, pathsClosure; + for (auto& path : startPaths) + initialStorePaths.insert(path.path()); + store.computeFSClosure(initialStorePaths, pathsClosure); + ret.insert(startPaths.begin(), startPaths.end()); + ret.insert(pathsClosure.begin(), pathsClosure.end()); +} + +void RealisedPath::closure(Store& store, RealisedPath::Set & ret) const +{ + RealisedPath::closure(store, {*this}, ret); +} + +RealisedPath::Set RealisedPath::closure(Store& store) const +{ + RealisedPath::Set ret; + closure(store, ret); + return ret; +} + } // namespace nix diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh index 4b8ead3c5..7c91d802a 100644 --- a/src/libstore/realisation.hh +++ b/src/libstore/realisation.hh @@ -2,6 +2,7 @@ #include "path.hh" #include +#include "comparator.hh" namespace nix { @@ -17,13 +18,7 @@ struct DrvOutput { static DrvOutput parse(const std::string &); - bool operator<(const DrvOutput& other) const { return to_pair() < other.to_pair(); } - bool operator==(const DrvOutput& other) const { return to_pair() == other.to_pair(); } - -private: - // Just to make comparison operators easier to write - std::pair to_pair() const - { return std::make_pair(drvHash, outputName); } + GENERATE_CMP(DrvOutput, me->drvHash, me->outputName); }; struct Realisation { @@ -32,8 +27,47 @@ struct Realisation { nlohmann::json toJSON() const; static Realisation fromJSON(const nlohmann::json& json, const std::string& whence); + + StorePath getPath() const { return outPath; } + + GENERATE_CMP(Realisation, me->id, me->outPath); }; -typedef std::map DrvOutputs; +struct OpaquePath { + StorePath path; + + StorePath getPath() const { return path; } + + GENERATE_CMP(OpaquePath, me->path); +}; + + +/** + * A store path with all the history of how it went into the store + */ +struct RealisedPath { + /* + * A path is either the result of the realisation of a derivation or + * an opaque blob that has been directly added to the store + */ + using Raw = std::variant; + Raw raw; + + using Set = std::set; + + RealisedPath(StorePath path) : raw(OpaquePath{path}) {} + RealisedPath(Realisation r) : raw(r) {} + + /** + * Get the raw store path associated to this + */ + StorePath path() const; + + void closure(Store& store, Set& ret) const; + static void closure(Store& store, const Set& startPaths, Set& ret); + Set closure(Store& store) const; + + GENERATE_CMP(RealisedPath, me->raw); +}; } diff --git a/src/libutil/comparator.hh b/src/libutil/comparator.hh new file mode 100644 index 000000000..0315dc506 --- /dev/null +++ b/src/libutil/comparator.hh @@ -0,0 +1,30 @@ +#pragma once + +/* Awfull hacky generation of the comparison operators by doing a lexicographic + * comparison between the choosen fields. + * + * ``` + * GENERATE_CMP(ClassName, me->field1, me->field2, ...) + * ``` + * + * will generate comparison operators semantically equivalent to: + * + * ``` + * bool operator<(const ClassName& other) { + * return field1 < other.field1 && field2 < other.field2 && ...; + * } + * ``` + */ +#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, FIELDS...) \ + 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 ); \ + return fields1 COMPARATOR fields2; \ + } +#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args) +#define GENERATE_LEQ(args...) GENERATE_ONE_CMP(<, args) +#define GENERATE_CMP(args...) \ + GENERATE_EQUAL(args) \ + GENERATE_LEQ(args) diff --git a/src/nix/copy.cc b/src/nix/copy.cc index f15031a45..c56a1def1 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -16,6 +16,8 @@ struct CmdCopy : StorePathsCommand SubstituteFlag substitute = NoSubstitute; + using StorePathsCommand::run; + CmdCopy() : StorePathsCommand(true) {