Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3246bb6
Filter ANSI escape codes from "aborted" error messages
edolstra Apr 13, 2022
262a602
foreman/start-manual.sh: Use exec
edolstra Apr 13, 2022
0221608
buildRemote(): Support arbitrary stores
edolstra Apr 13, 2022
71796e7
Fix tests
edolstra Apr 14, 2022
2c3072a
Merge remote-tracking branch 'upstream/master' into use-store-api
Ericson2314 Oct 25, 2022
89c504e
Merge remote-tracking branch 'upstream/master' into use-store-api
Ericson2314 Dec 4, 2023
5c7e5b6
Merge branch 'split-buildRemote' into use-store-api
Ericson2314 Dec 4, 2023
42cc55a
Merge remote-tracking branch 'upstream/master' into use-store-api
Ericson2314 Dec 4, 2023
fdd7036
Merge branch 'master' into use-store-api
Ericson2314 Dec 9, 2023
181b527
Merge remote-tracking branch 'upstream/master' into use-store-api
Ericson2314 Jan 23, 2024
8be9f4c
Merge remote-tracking branch 'upstream/nix-2.19' into use-store-api
Ericson2314 Feb 14, 2025
9df591a
Merge remote-tracking branch 'upstream/nix-2.20' into use-store-api
Ericson2314 Feb 14, 2025
a94c1ae
Merge remote-tracking branch 'upstream/nix-2.21' into use-store-api
Ericson2314 Feb 14, 2025
9b0d74e
Merge remote-tracking branch 'upstream/nix-2.22' into use-store-api
Ericson2314 Feb 14, 2025
cde792e
Merge remote-tracking branch 'upstream/nix-2.23' into use-store-api
Ericson2314 Feb 14, 2025
19a1c5f
Merge remote-tracking branch 'upstream/nix-2.24' into use-store-api
Ericson2314 Feb 14, 2025
b767c82
Merge remote-tracking branch 'upstream/nix-2.25' into use-store-api
Ericson2314 Feb 14, 2025
1381ee8
Merge branch 'master' into use-store-api
Ericson2314 Feb 14, 2025
02a65eb
Merge branch 'master' into use-store-api
Ericson2314 Feb 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
nix-util
nix-store
nix-main
nix-cmd
nix-cli
;
nix-perl-bindings = nix.hydraJobs.perlBindings.${system};
Expand Down
2 changes: 1 addition & 1 deletion foreman/start-manual.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

mdbook serve \
exec mdbook serve \
--port 63332 \
--dest-dir ./.hydra-data/manual \
./doc/manual/
2 changes: 2 additions & 0 deletions package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
, nix-util
, nix-store
, nix-main
, nix-cmd
, nix-cli
, nix-perl-bindings
, git
Expand Down Expand Up @@ -178,6 +179,7 @@ stdenv.mkDerivation (finalAttrs: {
nix-util
nix-store
nix-main
nix-cmd
perlDeps
perl
boost
Expand Down
213 changes: 213 additions & 0 deletions src/hydra-build-step/hydra-build-step.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/* This is a helper program that performs a build step, i.e. a single
derivation. In addition to a derivation path, it takes three store
URLs as arguments:

* --store: The store that will hold the resulting store paths
(typically a binary cache).

* --eval-store: The store that holds the .drv files, as produced by
hydra-evaluator.

* --build-store: The store that performs the build (often a
SSHStore for remote builds).

The build log is written to the path indicated by --log-file.
*/

#include "util.hh"
#include "shared.hh"
#include "common-eval-args.hh"
#include "store-api.hh"
#include "build-result.hh"
#include "derivations.hh"
#include "worker-protocol.hh"

#include <chrono>

using namespace nix;

// FIXME: cut&paste
static std::string_view getS(const std::vector<Logger::Field> & fields, size_t n)
{
assert(n < fields.size());
assert(fields[n].type == Logger::Field::tString);
return fields[n].s;
}

void mainWrapped(std::list<std::string> args)
{
verbosity = lvlError;

struct MyArgs : MixEvalArgs, MixCommonArgs, RootArgs
{
Path drvPath;
std::optional<std::string> buildStoreUrl;
std::optional<Path> logPath;
std::optional<uint64_t> maxOutputSize;

MyArgs() : MixCommonArgs("hydra-build-step")
{
expectArg("drv-path", &drvPath);

addFlag({
.longName = "build-store",
.description = "The Nix store to use for building the derivation.",
//.category = category,
.labels = {"store-url"},
.handler = {&buildStoreUrl},
});

addFlag({
.longName = "log-file",
.description = "The path to the build log.",
.labels = {"path"},
.handler = {&logPath},
});

addFlag({
.longName = "max-output-size",
.description = "Maximum size of the outputs.",
.labels = {"bytes"},
.handler = {&maxOutputSize},
});
}
};

/* A logger that intercepts all build log lines and writes them to
the log file. */
MyArgs myArgs;
myArgs.parseCmdline(args);

struct MyLogger : public Logger
{
Logger & prev;
AutoCloseFD logFile;

MyLogger(Logger & prev, Path logPath) : prev(prev)
{
logFile = open(logPath.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0666);
if (!logFile)
throw SysError("creating log file '%s'", logPath);
}

void log(Verbosity lvl, std::string_view s) override
{ prev.log(lvl, s); }

void logEI(const ErrorInfo & ei) override
{ prev.logEI(ei); }

void writeToStdout(std::string_view s) override
{ prev.writeToStdout(s); }

void result(ActivityId act, ResultType type, const Fields & fields) override
{
if (type == resBuildLogLine)
writeLine(logFile.get(), std::string(getS(fields, 0)));
else
prev.result(act, type, fields);
}
};

auto destStore = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : destStore;
auto buildStore = myArgs.buildStoreUrl ? openStore(*myArgs.buildStoreUrl) : destStore;

auto drvPath = evalStore->parseStorePath(myArgs.drvPath);

auto drv = evalStore->readDerivation(drvPath);
BasicDerivation basicDrv(drv);

uint64_t overhead = 0;

/* Gather the inputs. */
StorePathSet inputs;

for (auto & p : drv.inputSrcs)
inputs.insert(p);

for (auto & [drvPath, node] : drv.inputDrvs.map) {
auto drv2 = evalStore->readDerivation(drvPath);
for (auto & name : node.value) {
if (auto i = get(drv2.outputs, name)) {
auto outPath = i->path(*evalStore, drv2.name, name);
inputs.insert(*outPath);
basicDrv.inputSrcs.insert(*outPath);
}
}
}

/* Ensure that the inputs exist in the destination store (so that
the builder can substitute them from the destination
store). This is a no-op for regular stores, but for the binary
cache store, this will copy the inputs to the binary cache from
the local store. */
{
auto now1 = std::chrono::steady_clock::now();

debug("sending closure of '%s' to '%s'",
evalStore->printStorePath(drvPath), destStore->getUri());

if (evalStore != destStore)
copyClosure(*evalStore, *destStore, drv.inputSrcs, NoRepair, NoCheckSigs);

copyClosure(*destStore, *buildStore, inputs, NoRepair, NoCheckSigs, Substitute);

auto now2 = std::chrono::steady_clock::now();

overhead += std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
}

/* Perform the build. */
if (myArgs.logPath)
logger = new MyLogger(*logger, *myArgs.logPath);

auto buildResult = buildStore->buildDerivation(drvPath, basicDrv);

/* Copy the output paths from the build store to the destination
store. */
size_t totalNarSize = 0;

if (buildResult.success()) {

std::map<StorePath, ValidPathInfo> infos;
StorePathSet outputs;
for (auto & [output, realisation] : buildResult.builtOutputs) {
auto info = buildStore->queryPathInfo(realisation.outPath);
totalNarSize += info->narSize;
infos.insert_or_assign(info->path, *info);
outputs.insert(info->path);
}

if ((!myArgs.maxOutputSize || totalNarSize <= *myArgs.maxOutputSize)
&& buildStore != destStore)
{
debug("copying outputs of '%s' from '%s' (%d bytes)",
buildStore->printStorePath(drvPath), buildStore->getUri(), totalNarSize);

auto now1 = std::chrono::steady_clock::now();

copyPaths(*buildStore, *destStore, outputs, NoRepair, NoCheckSigs);

auto now2 = std::chrono::steady_clock::now();

overhead += std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
}
}

FdSink to { STDOUT_FILENO };
WorkerProto::WriteConn wconn {
.to = to,
// Hardcode latest version because we are deploying hydra
// itself atomically
.version = PROTOCOL_VERSION,
};
WorkerProto::write(*evalStore, wconn, buildResult);
}

int main(int argc, char * * argv)
{
return handleExceptions(argv[0], [&]() {
initNix();
mainWrapped(argvToStrings(argc, argv));
});
}
14 changes: 14 additions & 0 deletions src/hydra-build-step/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
srcs = files(
'hydra-build-step.cc',
)

hydra_build_step = executable('hydra-build-step',
'hydra-build-step.cc',
srcs,
dependencies: [
libhydra_dep,
nix_dep,
dependency('nix-cmd', required: true)
],
install: true,
)
Loading
Loading