aboutsummaryrefslogtreecommitdiff
path: root/src/libcmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcmd')
-rw-r--r--src/libcmd/command.cc69
-rw-r--r--src/libcmd/command.hh64
-rw-r--r--src/libcmd/installables.cc285
-rw-r--r--src/libcmd/installables.hh14
-rw-r--r--src/libcmd/local.mk5
-rw-r--r--src/libcmd/markdown.cc8
-rw-r--r--src/libcmd/repl.cc108
7 files changed, 344 insertions, 209 deletions
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index 2c62bfa7f..4c5d985aa 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -95,8 +95,21 @@ EvalCommand::~EvalCommand()
evalState->printStats();
}
+ref<Store> EvalCommand::getEvalStore()
+{
+ if (!evalStore)
+ evalStore = evalStoreUrl ? openStore(*evalStoreUrl) : getStore();
+ return ref<Store>(evalStore);
+}
+
+ref<EvalState> EvalCommand::getEvalState()
+{
+ if (!evalState)
+ evalState = std::make_shared<EvalState>(searchPath, getEvalStore(), getStore());
+ return ref<EvalState>(evalState);
+}
-RealisedPathsCommand::RealisedPathsCommand(bool recursive)
+BuiltPathsCommand::BuiltPathsCommand(bool recursive)
: recursive(recursive)
{
if (recursive)
@@ -123,44 +136,53 @@ RealisedPathsCommand::RealisedPathsCommand(bool recursive)
});
}
-void RealisedPathsCommand::run(ref<Store> store)
+void BuiltPathsCommand::run(ref<Store> store)
{
- std::vector<RealisedPath> paths;
+ BuiltPaths 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())
- paths.push_back(p);
+ paths.push_back(BuiltPath::Opaque{p});
} else {
- auto pathSet = toRealisedPaths(store, realiseMode, operateOn, installables);
+ paths = toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
- auto roots = std::move(pathSet);
- pathSet = {};
- RealisedPath::closure(*store, roots, pathSet);
+ // XXX: This only computes the store path closure, ignoring
+ // intermediate realisations
+ StorePathSet pathsRoots, pathsClosure;
+ for (auto & root : paths) {
+ auto rootFromThis = root.outPaths();
+ pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
+ }
+ store->computeFSClosure(pathsRoots, pathsClosure);
+ for (auto & path : pathsClosure)
+ paths.push_back(BuiltPath::Opaque{path});
}
- for (auto & path : pathSet)
- paths.push_back(path);
}
run(store, std::move(paths));
}
StorePathsCommand::StorePathsCommand(bool recursive)
- : RealisedPathsCommand(recursive)
+ : BuiltPathsCommand(recursive)
{
}
-void StorePathsCommand::run(ref<Store> store, std::vector<RealisedPath> paths)
+void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths)
{
- StorePaths storePaths;
- for (auto & p : paths)
- storePaths.push_back(p.path());
+ StorePathSet storePaths;
+ for (auto & builtPath : paths)
+ for (auto & p : builtPath.outPaths())
+ storePaths.insert(p);
+
+ auto sorted = store->topoSortPaths(storePaths);
+ std::reverse(sorted.begin(), sorted.end());
- run(store, std::move(storePaths));
+ run(store, std::move(sorted));
}
-void StorePathCommand::run(ref<Store> store, std::vector<StorePath> storePaths)
+void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePaths)
{
if (storePaths.size() != 1)
throw UsageError("this command requires exactly one store path");
@@ -204,7 +226,7 @@ void MixProfile::updateProfile(const StorePath & storePath)
profile2, storePath));
}
-void MixProfile::updateProfile(const DerivedPathsWithHints & buildables)
+void MixProfile::updateProfile(const BuiltPaths & buildables)
{
if (!profile) return;
@@ -212,22 +234,19 @@ void MixProfile::updateProfile(const DerivedPathsWithHints & buildables)
for (auto & buildable : buildables) {
std::visit(overloaded {
- [&](DerivedPathWithHints::Opaque bo) {
+ [&](const BuiltPath::Opaque & bo) {
result.push_back(bo.path);
},
- [&](DerivedPathWithHints::Built bfd) {
+ [&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
- /* Output path should be known because we just tried to
- build it. */
- assert(output.second);
- result.push_back(*output.second);
+ result.push_back(output.second);
}
},
}, buildable.raw());
}
if (result.size() != 1)
- throw Error("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
+ throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
updateProfile(result[0]);
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 8566e3a6d..0d847d255 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -47,13 +47,18 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
bool startReplOnEvalErrors = false;
+ EvalCommand();
+
+ ~EvalCommand();
+
+ ref<Store> getEvalStore();
+
ref<EvalState> getEvalState();
- EvalCommand();
+private:
+ std::shared_ptr<Store> evalStore;
std::shared_ptr<EvalState> evalState;
-
- ~EvalCommand();
};
struct MixFlakeOptions : virtual Args, EvalCommand
@@ -105,6 +110,8 @@ enum class Realise {
exists. */
Derivation,
/* Evaluate in dry-run mode. Postcondition: nothing. */
+ // FIXME: currently unused, but could be revived if we can
+ // evaluate derivations in-memory.
Nothing
};
@@ -147,7 +154,7 @@ private:
};
/* A command that operates on zero or more store paths. */
-struct RealisedPathsCommand : public InstallablesCommand
+struct BuiltPathsCommand : public InstallablesCommand
{
private:
@@ -160,26 +167,26 @@ protected:
public:
- RealisedPathsCommand(bool recursive = false);
+ BuiltPathsCommand(bool recursive = false);
using StoreCommand::run;
- virtual void run(ref<Store> store, std::vector<RealisedPath> paths) = 0;
+ virtual void run(ref<Store> store, BuiltPaths && paths) = 0;
void run(ref<Store> store) override;
bool useDefaultInstallables() override { return !all; }
};
-struct StorePathsCommand : public RealisedPathsCommand
+struct StorePathsCommand : public BuiltPathsCommand
{
StorePathsCommand(bool recursive = false);
- using RealisedPathsCommand::run;
+ using BuiltPathsCommand::run;
- virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0;
+ virtual void run(ref<Store> store, std::vector<StorePath> && storePaths) = 0;
- void run(ref<Store> store, std::vector<RealisedPath> paths) override;
+ void run(ref<Store> store, BuiltPaths && paths) override;
};
/* A command that operates on exactly one store path. */
@@ -189,7 +196,7 @@ struct StorePathCommand : public StorePathsCommand
virtual void run(ref<Store> store, const StorePath & storePath) = 0;
- void run(ref<Store> store, std::vector<StorePath> storePaths) override;
+ void run(ref<Store> store, std::vector<StorePath> && storePaths) override;
};
/* A helper class for registering commands globally. */
@@ -220,26 +227,37 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
}
-DerivedPathsWithHints build(ref<Store> store, Realise mode,
- std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode = bmNormal);
+BuiltPaths build(
+ ref<Store> evalStore,
+ ref<Store> store, Realise mode,
+ const std::vector<std::shared_ptr<Installable>> & installables,
+ BuildMode bMode = bmNormal);
-std::set<StorePath> toStorePaths(ref<Store> store,
- Realise mode, OperateOn operateOn,
- std::vector<std::shared_ptr<Installable>> installables);
+std::set<StorePath> toStorePaths(
+ ref<Store> evalStore,
+ ref<Store> store,
+ Realise mode,
+ OperateOn operateOn,
+ const std::vector<std::shared_ptr<Installable>> & installables);
-StorePath toStorePath(ref<Store> store,
- Realise mode, OperateOn operateOn,
+StorePath toStorePath(
+ ref<Store> evalStore,
+ ref<Store> store,
+ Realise mode,
+ OperateOn operateOn,
std::shared_ptr<Installable> installable);
-std::set<StorePath> toDerivations(ref<Store> store,
- std::vector<std::shared_ptr<Installable>> installables,
+std::set<StorePath> toDerivations(
+ ref<Store> store,
+ const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver = false);
-std::set<RealisedPath> toRealisedPaths(
+BuiltPaths toBuiltPaths(
+ ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
- std::vector<std::shared_ptr<Installable>> installables);
+ const std::vector<std::shared_ptr<Installable>> & installables);
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
@@ -256,7 +274,7 @@ struct MixProfile : virtual StoreCommand
/* If 'profile' is set, make it point at the store path produced
by 'buildables'. */
- void updateProfile(const DerivedPathsWithHints & buildables);
+ void updateProfile(const BuiltPaths & buildables);
};
struct MixDefaultProfile : MixProfile
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 7f7a89b37..5758b52ad 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -58,9 +58,13 @@ MixFlakeOptions::MixFlakeOptions()
addFlag({
.longName = "no-registries",
- .description = "Don't allow lookups in the flake registries.",
+ .description =
+ "Don't allow lookups in the flake registries. This option is deprecated; use `--no-use-registries`.",
.category = category,
- .handler = {&lockFlags.useRegistries, false}
+ .handler = {[&]() {
+ lockFlags.useRegistries = false;
+ warn("'--no-registries' is deprecated; use '--no-use-registries'");
+ }}
});
addFlag({
@@ -171,14 +175,50 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
void SourceExprCommand::completeInstallable(std::string_view prefix)
{
- if (file) return; // FIXME
+ if (file) {
+ evalSettings.pureEval = false;
+ auto state = getEvalState();
+ Expr *e = state->parseExprFromFile(
+ resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
+ );
+
+ Value root;
+ state->eval(e, root);
+
+ auto autoArgs = getAutoArgs(*state);
- completeFlakeRefWithFragment(
- getEvalState(),
- lockFlags,
- getDefaultFlakeAttrPathPrefixes(),
- getDefaultFlakeAttrPaths(),
- prefix);
+ std::string prefix_ = std::string(prefix);
+ auto sep = prefix_.rfind('.');
+ std::string searchWord;
+ if (sep != std::string::npos) {
+ searchWord = prefix_.substr(sep, std::string::npos);
+ prefix_ = prefix_.substr(0, sep);
+ } else {
+ searchWord = prefix_;
+ prefix_ = "";
+ }
+
+ Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
+ state->forceValue(v1);
+ Value v2;
+ state->autoCallFunction(*autoArgs, v1, v2);
+
+ if (v2.type() == nAttrs) {
+ for (auto & i : *v2.attrs) {
+ std::string name = i.name;
+ if (name.find(searchWord) == 0) {
+ completions->add(i.name);
+ }
+ }
+ }
+ } else {
+ completeFlakeRefWithFragment(
+ getEvalState(),
+ lockFlags,
+ getDefaultFlakeAttrPathPrefixes(),
+ getDefaultFlakeAttrPaths(),
+ prefix);
+ }
}
void completeFlakeRefWithFragment(
@@ -249,7 +289,6 @@ void completeFlakeRefWithFragment(
completeFlakeRef(evalState->store, prefix);
}
-
void completeFlakeRef(ref<Store> store, std::string_view prefix)
{
if (prefix == "")
@@ -273,9 +312,9 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
}
}
-DerivedPathWithHints Installable::toDerivedPathWithHints()
+DerivedPath Installable::toDerivedPath()
{
- auto buildables = toDerivedPathsWithHints();
+ auto buildables = toDerivedPaths();
if (buildables.size() != 1)
throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size());
return std::move(buildables[0]);
@@ -309,22 +348,19 @@ struct InstallableStorePath : Installable
std::string what() override { return store->printStorePath(storePath); }
- DerivedPathsWithHints toDerivedPathsWithHints() override
+ DerivedPaths toDerivedPaths() override
{
if (storePath.isDerivation()) {
- std::map<std::string, std::optional<StorePath>> outputs;
auto drv = store->readDerivation(storePath);
- for (auto & [name, output] : drv.outputsAndOptPaths(*store))
- outputs.emplace(name, output.second);
return {
- DerivedPathWithHints::Built {
+ DerivedPath::Built {
.drvPath = storePath,
- .outputs = std::move(outputs)
+ .outputs = drv.outputNames(),
}
};
} else {
return {
- DerivedPathWithHints::Opaque {
+ DerivedPath::Opaque {
.path = storePath,
}
};
@@ -337,22 +373,24 @@ struct InstallableStorePath : Installable
}
};
-DerivedPathsWithHints InstallableValue::toDerivedPathsWithHints()
+DerivedPaths InstallableValue::toDerivedPaths()
{
- DerivedPathsWithHints res;
+ DerivedPaths res;
- std::map<StorePath, std::map<std::string, std::optional<StorePath>>> drvsToOutputs;
+ std::map<StorePath, std::set<std::string>> drvsToOutputs;
+ RealisedPath::Set drvsToCopy;
// Group by derivation, helps with .all in particular
for (auto & drv : toDerivations()) {
auto outputName = drv.outputName;
if (outputName == "")
throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath));
- drvsToOutputs[drv.drvPath].insert_or_assign(outputName, drv.outPath);
+ drvsToOutputs[drv.drvPath].insert(outputName);
+ drvsToCopy.insert(drv.drvPath);
}
for (auto & i : drvsToOutputs)
- res.push_back(DerivedPathWithHints::Built { i.first, i.second });
+ res.push_back(DerivedPath::Built { i.first, i.second });
return res;
}
@@ -564,10 +602,10 @@ InstallableFlake::getCursors(EvalState & state)
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
+ flake::LockFlags lockFlagsApplyConfig = lockFlags;
+ lockFlagsApplyConfig.applyNixConfig = true;
if (!_lockedFlake) {
- _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
- _lockedFlake->flake.config.apply();
- // FIXME: send new config to the daemon.
+ _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
@@ -616,6 +654,17 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
for (auto & s : ss) {
std::exception_ptr ex;
+ if (s.find('/') != std::string::npos) {
+ try {
+ result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
+ continue;
+ } catch (BadStorePath &) {
+ } catch (...) {
+ if (!ex)
+ ex = std::current_exception();
+ }
+ }
+
try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath("."));
result.push_back(std::make_shared<InstallableFlake>(
@@ -630,25 +679,7 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
ex = std::current_exception();
}
- if (s.find('/') != std::string::npos) {
- try {
- result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
- continue;
- } catch (BadStorePath &) {
- } catch (...) {
- if (!ex)
- ex = std::current_exception();
- }
- }
-
std::rethrow_exception(ex);
-
- /*
- throw Error(
- pathExists(s)
- ? "path '%s' is not a flake or a store path"
- : "don't know how to handle argument '%s'", s);
- */
}
}
@@ -663,107 +694,121 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
return installables.front();
}
-DerivedPathsWithHints build(ref<Store> store, Realise mode,
- std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode)
+BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPaths & hopefullyBuiltPaths)
+{
+ BuiltPaths res;
+ for (const auto & b : hopefullyBuiltPaths)
+ std::visit(
+ overloaded{
+ [&](const DerivedPath::Opaque & bo) {
+ res.push_back(BuiltPath::Opaque{bo.path});
+ },
+ [&](const DerivedPath::Built & bfd) {
+ OutputPathMap outputs;
+ auto drv = evalStore->readDerivation(bfd.drvPath);
+ auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
+ auto drvOutputs = drv.outputsAndOptPaths(*store);
+ for (auto & output : bfd.outputs) {
+ if (!outputHashes.count(output))
+ throw Error(
+ "the derivation '%s' doesn't have an output named '%s'",
+ store->printStorePath(bfd.drvPath), output);
+ if (settings.isExperimentalFeatureEnabled(
+ Xp::CaDerivations)) {
+ auto outputId =
+ DrvOutput{outputHashes.at(output), output};
+ auto realisation =
+ store->queryRealisation(outputId);
+ if (!realisation)
+ throw Error(
+ "cannot operate on an output of unbuilt "
+ "content-addressed derivation '%s'",
+ outputId.to_string());
+ outputs.insert_or_assign(
+ output, realisation->outPath);
+ } else {
+ // If ca-derivations isn't enabled, assume that
+ // the output path is statically known.
+ assert(drvOutputs.count(output));
+ assert(drvOutputs.at(output).second);
+ outputs.insert_or_assign(
+ output, *drvOutputs.at(output).second);
+ }
+ }
+ res.push_back(BuiltPath::Built{bfd.drvPath, outputs});
+ },
+ },
+ b.raw());
+
+ return res;
+}
+
+BuiltPaths build(
+ ref<Store> evalStore,
+ ref<Store> store,
+ Realise mode,
+ const std::vector<std::shared_ptr<Installable>> & installables,
+ BuildMode bMode)
{
if (mode == Realise::Nothing)
settings.readOnlyMode = true;
- DerivedPathsWithHints buildables;
-
std::vector<DerivedPath> pathsToBuild;
for (auto & i : installables) {
- for (auto & b : i->toDerivedPathsWithHints()) {
- std::visit(overloaded {
- [&](DerivedPathWithHints::Opaque bo) {
- pathsToBuild.push_back(bo);
- },
- [&](DerivedPathWithHints::Built bfd) {
- StringSet outputNames;
- for (auto & output : bfd.outputs)
- outputNames.insert(output.first);
- pathsToBuild.push_back(
- DerivedPath::Built{bfd.drvPath, outputNames});
- },
- }, b.raw());
- buildables.push_back(std::move(b));
- }
+ auto b = i->toDerivedPaths();
+ pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
}
- if (mode == Realise::Nothing)
+ if (mode == Realise::Nothing || mode == Realise::Derivation)
printMissing(store, pathsToBuild, lvlError);
else if (mode == Realise::Outputs)
- store->buildPaths(pathsToBuild, bMode);
+ store->buildPaths(pathsToBuild, bMode, evalStore);
- return buildables;
+ return getBuiltPaths(evalStore, store, pathsToBuild);
}
-std::set<RealisedPath> toRealisedPaths(
+BuiltPaths toBuiltPaths(
+ ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
- std::vector<std::shared_ptr<Installable>> installables)
+ const std::vector<std::shared_ptr<Installable>> & installables)
{
- std::set<RealisedPath> res;
- if (operateOn == OperateOn::Output) {
- for (auto & b : build(store, mode, installables))
- std::visit(overloaded {
- [&](DerivedPathWithHints::Opaque bo) {
- res.insert(bo.path);
- },
- [&](DerivedPathWithHints::Built bfd) {
- auto drv = store->readDerivation(bfd.drvPath);
- auto outputHashes = staticOutputHashes(*store, drv);
- for (auto & output : bfd.outputs) {
- 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.raw());
- } else {
+ if (operateOn == OperateOn::Output)
+ return build(evalStore, store, mode, installables);
+ else {
if (mode == Realise::Nothing)
settings.readOnlyMode = true;
- auto drvPaths = toDerivations(store, installables, true);
- res.insert(drvPaths.begin(), drvPaths.end());
+ BuiltPaths res;
+ for (auto & drvPath : toDerivations(store, installables, true))
+ res.push_back(BuiltPath::Opaque{drvPath});
+ return res;
}
-
- return res;
}
-StorePathSet toStorePaths(ref<Store> store,
+StorePathSet toStorePaths(
+ ref<Store> evalStore,
+ ref<Store> store,
Realise mode, OperateOn operateOn,
- std::vector<std::shared_ptr<Installable>> installables)
+ const std::vector<std::shared_ptr<Installable>> & installables)
{
StorePathSet outPaths;
- for (auto & path : toRealisedPaths(store, mode, operateOn, installables))
- outPaths.insert(path.path());
+ for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
+ auto thisOutPaths = path.outPaths();
+ outPaths.insert(thisOutPaths.begin(), thisOutPaths.end());
+ }
return outPaths;
}
-StorePath toStorePath(ref<Store> store,
+StorePath toStorePath(
+ ref<Store> evalStore,
+ ref<Store> store,
Realise mode, OperateOn operateOn,
std::shared_ptr<Installable> installable)
{
- auto paths = toStorePaths(store, mode, operateOn, {installable});
+ auto paths = toStorePaths(evalStore, store, mode, operateOn, {installable});
if (paths.size() != 1)
throw Error("argument '%s' should evaluate to one store path", installable->what());
@@ -771,15 +816,17 @@ StorePath toStorePath(ref<Store> store,
return *paths.begin();
}
-StorePathSet toDerivations(ref<Store> store,
- std::vector<std::shared_ptr<Installable>> installables, bool useDeriver)
+StorePathSet toDerivations(
+ ref<Store> store,
+ const std::vector<std::shared_ptr<Installable>> & installables,
+ bool useDeriver)
{
StorePathSet drvPaths;
- for (auto & i : installables)
- for (auto & b : i->toDerivedPathsWithHints())
+ for (const auto & i : installables)
+ for (const auto & b : i->toDerivedPaths())
std::visit(overloaded {
- [&](DerivedPathWithHints::Opaque bo) {
+ [&](const DerivedPath::Opaque & bo) {
if (!useDeriver)
throw Error("argument '%s' did not evaluate to a derivation", i->what());
auto derivers = store->queryValidDerivers(bo.path);
@@ -788,7 +835,7 @@ StorePathSet toDerivations(ref<Store> store,
// FIXME: use all derivers?
drvPaths.insert(*derivers.begin());
},
- [&](DerivedPathWithHints::Built bfd) {
+ [&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);
},
}, b.raw());
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 403403c07..79931ad3e 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -23,17 +23,23 @@ struct App
// FIXME: add args, sandbox settings, metadata, ...
};
+struct UnresolvedApp
+{
+ App unresolved;
+ App resolve(ref<Store> evalStore, ref<Store> store);
+};
+
struct Installable
{
virtual ~Installable() { }
virtual std::string what() = 0;
- virtual DerivedPathsWithHints toDerivedPathsWithHints() = 0;
+ virtual DerivedPaths toDerivedPaths() = 0;
- DerivedPathWithHints toDerivedPathWithHints();
+ DerivedPath toDerivedPath();
- App toApp(EvalState & state);
+ UnresolvedApp toApp(EvalState & state);
virtual std::pair<Value *, Pos> toValue(EvalState & state)
{
@@ -74,7 +80,7 @@ struct InstallableValue : Installable
virtual std::vector<DerivationInfo> toDerivations() = 0;
- DerivedPathsWithHints toDerivedPathsWithHints() override;
+ DerivedPaths toDerivedPaths() override;
};
struct InstallableFlake : InstallableValue
diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk
index df904612b..1ec258a54 100644
--- a/src/libcmd/local.mk
+++ b/src/libcmd/local.mk
@@ -8,8 +8,9 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers -I src/nix
-libcmd_LDFLAGS = $(EDITLINE_LIBS) -llowdown
+# libcmd_LDFLAGS = $(EDITLINE_LIBS) -llowdown
+libcmd_LDFLAGS += -llowdown -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers libnix
-$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(prefix)/lib/pkgconfig, 0644))
+$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644))
diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc
index d25113d93..29bb4d31e 100644
--- a/src/libcmd/markdown.cc
+++ b/src/libcmd/markdown.cc
@@ -12,7 +12,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
struct lowdown_opts opts {
.type = LOWDOWN_TERM,
.maxdepth = 20,
- .cols = std::min(getWindowSize().second, (unsigned short) 80),
+ .cols = std::max(getWindowSize().second, (unsigned short) 80),
.hmargin = 0,
.vmargin = 0,
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
@@ -25,7 +25,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
Finally freeDoc([&]() { lowdown_doc_free(doc); });
size_t maxn = 0;
- auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size());
+ auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size(), nullptr);
if (!node)
throw Error("cannot parse Markdown document");
Finally freeNode([&]() { lowdown_node_free(node); });
@@ -40,11 +40,11 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
throw Error("cannot allocate Markdown output buffer");
Finally freeBuffer([&]() { lowdown_buf_free(buf); });
- int rndr_res = lowdown_term_rndr(buf, nullptr, renderer, node);
+ int rndr_res = lowdown_term_rndr(buf, renderer, node);
if (!rndr_res)
throw Error("allocation error while rendering Markdown");
- return std::string(buf->data, buf->size);
+ return filterANSIEscapes(std::string(buf->data, buf->size), !shouldANSI());
}
}
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index bfc131d27..6faa9f9fa 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -68,6 +68,7 @@ struct NixRepl
StorePath getDerivationPath(Value & v);
bool processLine(string line);
void loadFile(const Path & path);
+ void loadFlake(const std::string & flakeRef);
void initEnv();
void reloadFiles();
void addAttrsToScope(Value & attrs);
@@ -104,6 +105,25 @@ NixRepl::~NixRepl()
write_history(historyFile.c_str());
}
+string runNix(Path program, const Strings & args,
+ const std::optional<std::string> & input = {})
+{
+ auto subprocessEnv = getEnv();
+ subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
+
+ auto res = runProgram(RunOptions {
+ .program = settings.nixBinDir+ "/" + program,
+ .args = args,
+ .environment = subprocessEnv,
+ .input = input,
+ });
+
+ if (!statusOk(res.first))
+ throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
+
+ return res.second;
+}
+
static NixRepl * curRepl; // ugly
static char * completionCallback(char * s, int *match) {
@@ -180,15 +200,14 @@ namespace {
void NixRepl::mainLoop(const std::vector<std::string> & files)
{
string error = ANSI_RED "error:" ANSI_NORMAL " ";
- std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl;
+ notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
if (!files.empty()) {
for (auto & i : files)
loadedFiles.push_back(i);
- reloadFiles();
- if (!loadedFiles.empty()) std::cout << std::endl;
- }
+ reloadFiles();
+ if (!loadedFiles.empty()) notice("");
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
@@ -378,6 +397,8 @@ bool NixRepl::processLine(string line)
{
if (line == "") return true;
+ _isInterrupted = false;
+
string command, arg;
if (line[0] == ':') {
@@ -397,9 +418,10 @@ bool NixRepl::processLine(string line)
<< " <x> = <expr> Bind expression to variable\n"
<< " :a <expr> Add attributes from resulting set to scope\n"
<< " :b <expr> Build derivation\n"
- << " :e <expr> Open the derivation in $EDITOR\n"
+ << " :e <expr> Open package or function in $EDITOR\n"
<< " :i <expr> Build derivation, then install result into current profile\n"
<< " :l <path> Load Nix expression and add it to scope\n"
+ << " :lf <ref> Load Nix flake and add it to scope\n"
<< " :p <expr> Evaluate and print expression recursively\n"
<< " :q Exit nix-repl\n"
<< " :r Reload all files\n"
@@ -420,6 +442,10 @@ bool NixRepl::processLine(string line)
loadFile(arg);
}
+ else if (command == ":lf" || command == ":load-flake") {
+ loadFlake(arg);
+ }
+
else if (command == ":r" || command == ":reload") {
state->resetFileCache();
reloadFiles();
@@ -439,14 +465,17 @@ bool NixRepl::processLine(string line)
pos = v.lambda.fun->pos;
} else {
// assume it's a derivation
- pos = findDerivationFilename(*state, v, arg);
+ pos = findPackageFilename(*state, v, arg);
}
// Open in EDITOR
auto args = editorFor(pos);
auto editor = args.front();
args.pop_front();
- runProgram(editor, true, args);
+
+ // runProgram redirects stdout to a StringSink,
+ // using runProgram2 to allow editors to display their UI
+ runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
// Reload right after exiting the editor
state->resetFileCache();
@@ -456,16 +485,17 @@ bool NixRepl::processLine(string line)
else if (command == ":t") {
Value v;
evalString(arg, v);
- std::cout << showType(v) << std::endl;
+ logger->cout(showType(v));
+ }
- } else if (command == ":u") {
+ else if (command == ":u") {
Value v, f, result;
evalString(arg, v);
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state->callFunction(f, v, result, Pos());
StorePath drvPath = getDerivationPath(result);
- runProgram(settings.nixBinDir + "/nix-shell", true, {state->store->printStorePath(drvPath)});
+ runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
else if (command == ":b" || command == ":i" || command == ":s") {
@@ -475,21 +505,15 @@ bool NixRepl::processLine(string line)
Path drvPathRaw = state->store->printStorePath(drvPath);
if (command == ":b") {
- /* We could do the build in this process using buildPaths(),
- but doing it in a child makes it easier to recover from
- problems / SIGINT. */
- try {
- runProgram(settings.nixBinDir + "/nix", true, {"build", "--no-link", drvPathRaw});
- auto drv = state->store->readDerivation(drvPath);
- std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
- for (auto & i : drv.outputsAndOptPaths(*state->store))
- std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(*i.second.second));
- } catch (ExecError &) {
- }
+ state->store->buildPaths({DerivedPath::Built{drvPath}});
+ auto drv = state->store->readDerivation(drvPath);
+ logger->cout("\nThis derivation produced the following outputs:");
+ for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath))
+ logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
} else if (command == ":i") {
- runProgram(settings.nixBinDir + "/nix-env", true, {"-i", drvPathRaw});
+ runNix("nix-env", {"-i", drvPathRaw});
} else {
- runProgram(settings.nixBinDir + "/nix-shell", true, {drvPathRaw});
+ runNix("nix-shell", {drvPathRaw});
}
}
@@ -518,9 +542,9 @@ bool NixRepl::processLine(string line)
+ concatStringsSep(" ", args) + "\n\n";
}
- markdown += trim(stripIndentation(doc->doc));
+ markdown += stripIndentation(doc->doc);
- std::cout << renderMarkdownToTerminal(markdown);
+ logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else
throw Error("value does not have documentation");
}
@@ -561,6 +585,25 @@ void NixRepl::loadFile(const Path & path)
addAttrsToScope(v2);
}
+void NixRepl::loadFlake(const std::string & flakeRefS)
+{
+ auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
+ if (evalSettings.pureEval && !flakeRef.input.isImmutable())
+ throw Error("cannot use ':load-flake' on mutable flake reference '%s' (use --impure to override)", flakeRefS);
+
+ Value v;
+
+ flake::callFlake(*state,
+ flake::lockFlake(*state, flakeRef,
+ flake::LockFlags {
+ .updateLockFile = false,
+ .useRegistries = !evalSettings.pureEval,
+ .allowMutable = !evalSettings.pureEval,
+ }),
+ v);
+ addAttrsToScope(v);
+}
+
void NixRepl::initEnv()
{
@@ -584,9 +627,9 @@ void NixRepl::reloadFiles()
bool first = true;
for (auto & i : old) {
- if (!first) std::cout << std::endl;
+ if (!first) notice("");
first = false;
- std::cout << format("Loading '%1%'...") % i << std::endl;
+ notice("Loading '%1%'...", i);
loadFile(i);
}
}
@@ -596,8 +639,8 @@ void NixRepl::addAttrsToScope(Value & attrs)
{
state->forceAttrs(attrs);
for (auto & i : *attrs.attrs)
- addVarToScope(i.name, i.value);
- std::cout << format("Added %1% variables.") % attrs.attrs->size() << std::endl;
+ addVarToScope(i.name, *i.value);
+ notice("Added %1% variables.", attrs.attrs->size());
}
@@ -605,8 +648,9 @@ void NixRepl::addVarToScope(const Symbol & name, Value * v)
{
if (displ >= envSize)
throw Error("environment full; cannot add more variables");
- staticEnv->vars[name] = displ;
- env->values[displ++] = v;
+ staticEnv->vars.emplace_back(name, displ);
+ staticEnv->sort();
+ env->values[displ++] = &v;
varNames.insert((string) name);
}
@@ -665,7 +709,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break;
case nString:
- str << ANSI_YELLOW;
+ str << ANSI_WARNING;
printStringValue(str, v.string.s);
str << ANSI_NORMAL;
break;