2015-05-28 17:39:29 +02:00
|
|
|
|
#include "build-result.hh"
|
|
|
|
|
#include "store-api.hh"
|
|
|
|
|
#include "util.hh"
|
2015-06-19 17:20:20 +02:00
|
|
|
|
#include "regex.hh"
|
2016-02-26 14:45:03 +01:00
|
|
|
|
#include "fs-accessor.hh"
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
BuildOutput getBuildOutput(nix::ref<Store> store,
|
|
|
|
|
nix::ref<nix::FSAccessor> accessor, const Derivation & drv)
|
2015-05-28 17:39:29 +02:00
|
|
|
|
{
|
2015-07-21 01:45:00 +02:00
|
|
|
|
BuildOutput res;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
|
|
|
|
/* Compute the closure size. */
|
|
|
|
|
PathSet outputs;
|
|
|
|
|
for (auto & output : drv.outputs)
|
|
|
|
|
outputs.insert(output.second.path);
|
|
|
|
|
PathSet closure;
|
|
|
|
|
for (auto & output : outputs)
|
2016-02-11 15:59:47 +01:00
|
|
|
|
store->computeFSClosure(output, closure);
|
2015-05-28 17:39:29 +02:00
|
|
|
|
for (auto & path : closure) {
|
|
|
|
|
auto info = store->queryPathInfo(path);
|
2016-04-20 15:29:40 +02:00
|
|
|
|
res.closureSize += info->narSize;
|
|
|
|
|
if (outputs.find(path) != outputs.end()) res.size += info->narSize;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get build products. */
|
|
|
|
|
bool explicitProducts = false;
|
|
|
|
|
|
2015-06-19 17:20:20 +02:00
|
|
|
|
Regex regex(
|
|
|
|
|
"(([a-zA-Z0-9_-]+)" // type (e.g. "doc")
|
|
|
|
|
"[[:space:]]+"
|
|
|
|
|
"([a-zA-Z0-9_-]+)" // subtype (e.g. "readme")
|
|
|
|
|
"[[:space:]]+"
|
|
|
|
|
"(\"[^\"]+\"|[^[:space:]\"]+))" // path (may be quoted)
|
|
|
|
|
"([[:space:]]+([^[:space:]]+))?" // entry point
|
|
|
|
|
, true);
|
|
|
|
|
|
2015-05-28 17:39:29 +02:00
|
|
|
|
for (auto & output : outputs) {
|
2015-06-17 17:11:42 +02:00
|
|
|
|
Path failedFile = output + "/nix-support/failed";
|
2016-02-26 14:45:03 +01:00
|
|
|
|
if (accessor->stat(failedFile).type == FSAccessor::Type::tRegular)
|
|
|
|
|
res.failed = true;
|
2015-06-17 17:11:42 +02:00
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
Path productsFile = output + "/nix-support/hydra-build-products";
|
|
|
|
|
if (accessor->stat(productsFile).type != FSAccessor::Type::tRegular)
|
|
|
|
|
continue;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
2015-07-31 00:57:30 +02:00
|
|
|
|
explicitProducts = true;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
for (auto & line : tokenizeString<Strings>(accessor->readFile(productsFile), "\n")) {
|
2015-05-28 17:39:29 +02:00
|
|
|
|
BuildProduct product;
|
|
|
|
|
|
2015-06-19 17:20:20 +02:00
|
|
|
|
Regex::Subs subs;
|
|
|
|
|
if (!regex.matches(line, subs)) continue;
|
|
|
|
|
|
|
|
|
|
product.type = subs[1];
|
|
|
|
|
product.subtype = subs[2];
|
|
|
|
|
product.path = subs[3][0] == '"' ? string(subs[3], 1, subs[3].size() - 2) : subs[3];
|
|
|
|
|
product.defaultPath = subs[5];
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
2015-06-19 14:51:59 +02:00
|
|
|
|
/* Ensure that the path exists and points into the Nix
|
|
|
|
|
store. */
|
2016-02-26 14:45:03 +01:00
|
|
|
|
// FIXME: should we disallow products referring to other
|
|
|
|
|
// store paths, or that are outside the input closure?
|
2015-05-28 17:39:29 +02:00
|
|
|
|
if (product.path == "" || product.path[0] != '/') continue;
|
2016-02-26 14:45:03 +01:00
|
|
|
|
product.path = canonPath(product.path);
|
|
|
|
|
if (!isInStore(product.path)) continue;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
auto st = accessor->stat(product.path);
|
|
|
|
|
if (st.type == FSAccessor::Type::tMissing) continue;
|
2015-05-28 17:39:29 +02:00
|
|
|
|
|
|
|
|
|
product.name = product.path == output ? "" : baseNameOf(product.path);
|
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
if (st.type == FSAccessor::Type::tRegular) {
|
2015-05-28 17:39:29 +02:00
|
|
|
|
product.isRegular = true;
|
2016-02-26 14:45:03 +01:00
|
|
|
|
product.fileSize = st.fileSize;
|
|
|
|
|
auto contents = accessor->readFile(product.path);
|
|
|
|
|
product.sha1hash = hashString(htSHA1, contents);
|
|
|
|
|
product.sha256hash = hashString(htSHA256, contents);
|
2015-05-28 17:39:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.products.push_back(product);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If no build products were explicitly declared, then add all
|
|
|
|
|
outputs as a product of type "nix-build". */
|
|
|
|
|
if (!explicitProducts) {
|
|
|
|
|
for (auto & output : drv.outputs) {
|
|
|
|
|
BuildProduct product;
|
|
|
|
|
product.path = output.second.path;
|
|
|
|
|
product.type = "nix-build";
|
|
|
|
|
product.subtype = output.first == "out" ? "" : output.first;
|
|
|
|
|
product.name = storePathToName(product.path);
|
|
|
|
|
|
2016-02-26 14:45:03 +01:00
|
|
|
|
auto st = accessor->stat(product.path);
|
|
|
|
|
if (st.type == FSAccessor::Type::tMissing)
|
|
|
|
|
throw Error(format("getting status of ‘%1%’") % product.path);
|
|
|
|
|
if (st.type == FSAccessor::Type::tDirectory)
|
2015-05-28 17:39:29 +02:00
|
|
|
|
res.products.push_back(product);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the release name from $output/nix-support/hydra-release-name. */
|
|
|
|
|
for (auto & output : outputs) {
|
|
|
|
|
Path p = output + "/nix-support/hydra-release-name";
|
2016-02-26 14:45:03 +01:00
|
|
|
|
if (accessor->stat(p).type != FSAccessor::Type::tRegular) continue;
|
2015-06-19 17:20:20 +02:00
|
|
|
|
try {
|
2016-02-26 14:45:03 +01:00
|
|
|
|
res.releaseName = trim(accessor->readFile(p));
|
2015-06-19 17:20:20 +02:00
|
|
|
|
} catch (Error & e) { continue; }
|
2015-05-28 17:39:29 +02:00
|
|
|
|
// FIXME: validate release name
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-31 00:57:30 +02:00
|
|
|
|
/* Get metrics. */
|
|
|
|
|
for (auto & output : outputs) {
|
2016-02-26 14:45:03 +01:00
|
|
|
|
Path metricsFile = output + "/nix-support/hydra-metrics";
|
|
|
|
|
if (accessor->stat(metricsFile).type != FSAccessor::Type::tRegular) continue;
|
|
|
|
|
for (auto & line : tokenizeString<Strings>(accessor->readFile(metricsFile), "\n")) {
|
2015-07-31 00:57:30 +02:00
|
|
|
|
auto fields = tokenizeString<std::vector<std::string>>(line);
|
|
|
|
|
if (fields.size() < 2) continue;
|
|
|
|
|
BuildMetric metric;
|
|
|
|
|
metric.name = fields[0]; // FIXME: validate
|
|
|
|
|
metric.value = atof(fields[1].c_str()); // FIXME
|
|
|
|
|
metric.unit = fields.size() >= 3 ? fields[2] : "";
|
|
|
|
|
res.metrics[metric.name] = metric;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 17:39:29 +02:00
|
|
|
|
return res;
|
|
|
|
|
}
|