nix build --json: Include build statistics

Example:

  # nix build -L --extra-experimental-features cgroups --impure --expr 'with import <nixpkgs> {}; runCommand "foo" {} "dd if=/dev/urandom bs=1M count=1024 | md5sum; mkdir $out"' --json
  [
    {
      "cpuSystem": 1.911431,
      "cpuUser": 1.214249,
      "drvPath": "/nix/store/xzdqz67xba18hljhycp0hwfigzrs2z69-foo.drv",
      "outputs": {
        "out": "/nix/store/rh9mc9l2gkpq8kn2sgzndr6ll7ffjh6l-foo"
      },
      "startTime": 1669024076,
      "stopTime": 1669024079
    }
  ]
This commit is contained in:
Eelco Dolstra 2022-11-21 10:49:01 +01:00
parent f538ee4342
commit 300753d594
7 changed files with 77 additions and 46 deletions

View file

@ -840,20 +840,20 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
return installables.front(); return installables.front();
} }
BuiltPaths Installable::build( std::vector<BuiltPathWithResult> Installable::build(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables, const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode) BuildMode bMode)
{ {
BuiltPaths res; std::vector<BuiltPathWithResult> res;
for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode)) for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode))
res.push_back(builtPath); res.push_back(builtPathWithResult);
return res; return res;
} }
std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::build2( std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Installable::build2(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
@ -873,7 +873,7 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
} }
} }
std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> res; std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> res;
switch (mode) { switch (mode) {
@ -914,10 +914,10 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
output, *drvOutput->second); output, *drvOutput->second);
} }
} }
res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); res.push_back({installable, {.path = BuiltPath::Built { bfd.drvPath, outputs }}});
}, },
[&](const DerivedPath::Opaque & bo) { [&](const DerivedPath::Opaque & bo) {
res.push_back({installable, BuiltPath::Opaque { bo.path }}); res.push_back({installable, {.path = BuiltPath::Opaque { bo.path }}});
}, },
}, path.raw()); }, path.raw());
} }
@ -939,10 +939,10 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
std::map<std::string, StorePath> outputs; std::map<std::string, StorePath> outputs;
for (auto & path : buildResult.builtOutputs) for (auto & path : buildResult.builtOutputs)
outputs.emplace(path.first.outputName, path.second.outPath); outputs.emplace(path.first.outputName, path.second.outPath);
res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); res.push_back({installable, {.path = BuiltPath::Built { bfd.drvPath, outputs }, .result = buildResult}});
}, },
[&](const DerivedPath::Opaque & bo) { [&](const DerivedPath::Opaque & bo) {
res.push_back({installable, BuiltPath::Opaque { bo.path }}); res.push_back({installable, {.path = BuiltPath::Opaque { bo.path }, .result = buildResult}});
}, },
}, buildResult.path.raw()); }, buildResult.path.raw());
} }
@ -965,9 +965,12 @@ BuiltPaths Installable::toBuiltPaths(
OperateOn operateOn, OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables) const std::vector<std::shared_ptr<Installable>> & installables)
{ {
if (operateOn == OperateOn::Output) if (operateOn == OperateOn::Output) {
return Installable::build(evalStore, store, mode, installables); BuiltPaths res;
else { for (auto & p : Installable::build(evalStore, store, mode, installables))
res.push_back(p.path);
return res;
} else {
if (mode == Realise::Nothing) if (mode == Realise::Nothing)
settings.readOnlyMode = true; settings.readOnlyMode = true;

View file

@ -7,6 +7,7 @@
#include "eval.hh" #include "eval.hh"
#include "store-api.hh" #include "store-api.hh"
#include "flake/flake.hh" #include "flake/flake.hh"
#include "build-result.hh"
#include <optional> #include <optional>
@ -51,6 +52,12 @@ enum class OperateOn {
Derivation Derivation
}; };
struct BuiltPathWithResult
{
BuiltPath path;
std::optional<BuildResult> result;
};
struct Installable struct Installable
{ {
virtual ~Installable() { } virtual ~Installable() { }
@ -91,14 +98,14 @@ struct Installable
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
} }
static BuiltPaths build( static std::vector<BuiltPathWithResult> build(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables, const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal); BuildMode bMode = bmNormal);
static std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> build2( static std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> build2(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,

View file

@ -53,28 +53,13 @@ StorePathSet BuiltPath::outPaths() const
); );
} }
template<typename T> std::string DerivedPath::Opaque::to_string(const Store & store) const
nlohmann::json stuffToJSON(const std::vector<T> & ts, ref<Store> store) { {
auto res = nlohmann::json::array();
for (const T & t : ts) {
std::visit([&res, store](const auto & t) {
res.push_back(t.toJSON(store));
}, t.raw());
}
return res;
}
nlohmann::json builtPathsToJSON(const BuiltPaths & buildables, ref<Store> store)
{ return stuffToJSON<BuiltPath>(buildables, store); }
nlohmann::json derivedPathsToJSON(const DerivedPaths & paths, ref<Store> store)
{ return stuffToJSON<DerivedPath>(paths, store); }
std::string DerivedPath::Opaque::to_string(const Store & store) const {
return store.printStorePath(path); return store.printStorePath(path);
} }
std::string DerivedPath::Built::to_string(const Store & store) const { std::string DerivedPath::Built::to_string(const Store & store) const
{
return store.printStorePath(drvPath) return store.printStorePath(drvPath)
+ "!" + "!"
+ (outputs.empty() ? std::string { "*" } : concatStringsSep(",", outputs)); + (outputs.empty() ? std::string { "*" } : concatStringsSep(",", outputs));

View file

@ -125,7 +125,4 @@ struct BuiltPath : _BuiltPathRaw {
typedef std::vector<DerivedPath> DerivedPaths; typedef std::vector<DerivedPath> DerivedPaths;
typedef std::vector<BuiltPath> BuiltPaths; typedef std::vector<BuiltPath> BuiltPaths;
nlohmann::json builtPathsToJSON(const BuiltPaths & buildables, ref<Store> store);
nlohmann::json derivedPathsToJSON(const DerivedPaths & , ref<Store> store);
} }

View file

@ -37,11 +37,13 @@ struct InstallableDerivedPath : Installable
* Return the rewrites that are needed to resolve a string whose context is * Return the rewrites that are needed to resolve a string whose context is
* included in `dependencies`. * included in `dependencies`.
*/ */
StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies) StringPairs resolveRewrites(
Store & store,
const std::vector<BuiltPathWithResult> & dependencies)
{ {
StringPairs res; StringPairs res;
for (auto & dep : dependencies) for (auto & dep : dependencies)
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep)) if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
for (auto & [ outputName, outputPath ] : drvDep->outputs) for (auto & [ outputName, outputPath ] : drvDep->outputs)
res.emplace( res.emplace(
downstreamPlaceholder(store, drvDep->drvPath, outputName), downstreamPlaceholder(store, drvDep->drvPath, outputName),
@ -53,7 +55,10 @@ StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies)
/** /**
* Resolve the given string assuming the given context. * Resolve the given string assuming the given context.
*/ */
std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies) std::string resolveString(
Store & store,
const std::string & toResolve,
const std::vector<BuiltPathWithResult> & dependencies)
{ {
auto rewrites = resolveRewrites(store, dependencies); auto rewrites = resolveRewrites(store, dependencies);
return rewriteStrings(toResolve, rewrites); return rewriteStrings(toResolve, rewrites);

View file

@ -10,6 +10,37 @@
using namespace nix; using namespace nix;
nlohmann::json derivedPathsToJSON(const DerivedPaths & paths, ref<Store> store)
{
auto res = nlohmann::json::array();
for (auto & t : paths) {
std::visit([&res, store](const auto & t) {
res.push_back(t.toJSON(store));
}, t.raw());
}
return res;
}
nlohmann::json builtPathsWithResultToJSON(const std::vector<BuiltPathWithResult> & buildables, ref<Store> store)
{
auto res = nlohmann::json::array();
for (auto & b : buildables) {
std::visit([&](const auto & t) {
auto j = t.toJSON(store);
if (b.result) {
j["startTime"] = b.result->startTime;
j["stopTime"] = b.result->stopTime;
if (b.result->cpuUser)
j["cpuUser"] = ((double) b.result->cpuUser->count()) / 1000000;
if (b.result->cpuSystem)
j["cpuSystem"] = ((double) b.result->cpuSystem->count()) / 1000000;
}
res.push_back(j);
}, b.path.raw());
}
return res;
}
struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
{ {
Path outLink = "result"; Path outLink = "result";
@ -78,7 +109,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
Realise::Outputs, Realise::Outputs,
installables, buildMode); installables, buildMode);
if (json) logger->cout("%s", builtPathsToJSON(buildables, store).dump()); if (json) logger->cout("%s", builtPathsWithResultToJSON(buildables, store).dump());
if (outLink != "") if (outLink != "")
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
@ -98,7 +129,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
store2->addPermRoot(output.second, absPath(symlink)); store2->addPermRoot(output.second, absPath(symlink));
} }
}, },
}, buildable.raw()); }, buildable.path.raw());
} }
if (printOutputPaths) { if (printOutputPaths) {
@ -113,11 +144,14 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
std::cout << store->printStorePath(output.second) << std::endl; std::cout << store->printStorePath(output.second) << std::endl;
} }
}, },
}, buildable.raw()); }, buildable.path.raw());
} }
} }
updateProfile(buildables); BuiltPaths buildables2;
for (auto & b : buildables)
buildables2.push_back(b.path);
updateProfile(buildables2);
} }
}; };

View file

@ -253,11 +253,11 @@ struct ProfileManifest
static std::map<Installable *, BuiltPaths> static std::map<Installable *, BuiltPaths>
builtPathsPerInstallable( builtPathsPerInstallable(
const std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> & builtPaths) const std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> & builtPaths)
{ {
std::map<Installable *, BuiltPaths> res; std::map<Installable *, BuiltPaths> res;
for (auto & [installable, builtPath] : builtPaths) for (auto & [installable, builtPath] : builtPaths)
res[installable.get()].push_back(builtPath); res[installable.get()].push_back(builtPath.path);
return res; return res;
} }