2020-10-12 20:08:52 +03:00
# include "derivation-goal.hh"
# include "hook-instance.hh"
# include "worker.hh"
2015-07-20 05:30:16 +03:00
# include "builtins.hh"
2019-10-21 18:17:15 +03:00
# include "builtins/buildenv.hh"
2020-10-11 19:19:10 +03:00
# include "references.hh"
2016-04-29 14:57:08 +03:00
# include "finally.hh"
2020-10-11 19:19:10 +03:00
# include "util.hh"
# include "archive.hh"
# include "compression.hh"
2019-12-05 20:11:09 +02:00
# include "worker-protocol.hh"
2020-08-07 22:09:26 +03:00
# include "topo-sort.hh"
2020-09-21 19:40:11 +03:00
# include "callback.hh"
2021-02-26 17:20:33 +02:00
# include "local-store.hh" // TODO remove, along with remaining downcasts
2006-09-05 00:06:23 +03:00
2020-10-12 20:08:52 +03:00
# include <regex>
# include <queue>
2021-12-13 17:58:43 +02:00
# include <fstream>
2004-05-11 21:05:44 +03:00
# include <sys/types.h>
2018-06-12 14:05:14 +03:00
# include <sys/socket.h>
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
# include <sys/un.h>
2021-04-19 21:31:58 +03:00
# include <sys/wait.h>
2018-06-12 14:05:14 +03:00
# include <netdb.h>
2020-10-11 19:19:10 +03:00
# include <fcntl.h>
2019-05-17 23:29:15 +03:00
# include <termios.h>
2020-10-11 19:19:10 +03:00
# include <unistd.h>
# include <sys/mman.h>
# include <sys/utsname.h>
# include <sys/resource.h>
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
# if HAVE_STATVFS
# include <sys/statvfs.h>
# endif
2005-10-17 18:33:24 +03:00
2015-12-03 17:30:19 +02:00
/* Includes required for chroot support. */
# if __linux__
2012-06-23 07:28:35 +03:00
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <net/if.h>
# include <netinet/ip.h>
2015-10-21 15:45:56 +03:00
# include <sys/mman.h>
2015-12-03 17:30:19 +02:00
# include <sched.h>
# include <sys/param.h>
# include <sys/mount.h>
# include <sys/syscall.h>
2018-02-18 09:35:01 +02:00
# if HAVE_SECCOMP
2017-05-29 12:34:24 +03:00
# include <seccomp.h>
2018-02-18 09:35:01 +02:00
# endif
2015-12-03 17:30:19 +02:00
# define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
2009-01-12 18:30:32 +02:00
# endif
2020-12-03 23:41:59 +02:00
# if __APPLE__
# include <spawn.h>
2021-01-12 06:05:32 +02:00
# include <sys/sysctl.h>
2020-12-03 23:41:59 +02:00
# endif
2020-10-11 19:19:10 +03:00
# include <pwd.h>
# include <grp.h>
2014-02-17 15:15:56 +02:00
2017-10-25 14:01:50 +03:00
# include <nlohmann/json.hpp>
2006-09-05 00:06:23 +03:00
namespace nix {
2020-10-11 19:19:10 +03:00
DerivationGoal : : DerivationGoal ( const StorePath & drvPath ,
2023-01-12 01:57:18 +02:00
const OutputsSpec & wantedOutputs , Worker & worker , BuildMode buildMode )
2022-03-09 13:25:35 +02:00
: Goal ( worker , DerivedPath : : Built { . drvPath = drvPath , . outputs = wantedOutputs } )
2020-10-11 19:19:10 +03:00
, useDerivation ( true )
, drvPath ( drvPath )
, wantedOutputs ( wantedOutputs )
, buildMode ( buildMode )
{
state = & DerivationGoal : : getDerivation ;
name = fmt (
" building of '%s' from .drv file " ,
2021-04-05 16:48:18 +03:00
DerivedPath : : Built { drvPath , wantedOutputs } . to_string ( worker . store ) ) ;
2020-10-11 19:19:10 +03:00
trace ( " created " ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
mcExpectedBuilds = std : : make_unique < MaintainCount < uint64_t > > ( worker . expectedBuilds ) ;
worker . updateProgress ( ) ;
}
2004-06-18 21:09:32 +03:00
2014-11-24 17:48:04 +02:00
2020-10-11 19:19:10 +03:00
DerivationGoal : : DerivationGoal ( const StorePath & drvPath , const BasicDerivation & drv ,
2023-01-12 01:57:18 +02:00
const OutputsSpec & wantedOutputs , Worker & worker , BuildMode buildMode )
2022-03-09 13:25:35 +02:00
: Goal ( worker , DerivedPath : : Built { . drvPath = drvPath , . outputs = wantedOutputs } )
2020-10-11 19:19:10 +03:00
, useDerivation ( false )
, drvPath ( drvPath )
, wantedOutputs ( wantedOutputs )
, buildMode ( buildMode )
{
2021-02-23 15:12:11 +02:00
this - > drv = std : : make_unique < Derivation > ( drv ) ;
2021-02-04 15:41:49 +02:00
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : haveDerivation ;
name = fmt (
" building of '%s' from in-memory derivation " ,
2021-04-05 16:48:18 +03:00
DerivedPath : : Built { drvPath , drv . outputNames ( ) } . to_string ( worker . store ) ) ;
2020-10-11 19:19:10 +03:00
trace ( " created " ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
mcExpectedBuilds = std : : make_unique < MaintainCount < uint64_t > > ( worker . expectedBuilds ) ;
worker . updateProgress ( ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* Prevent the .chroot directory from being
garbage - collected . ( See isActiveTempFile ( ) in gc . cc . ) */
worker . store . addTempRoot ( this - > drvPath ) ;
}
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
DerivationGoal : : ~ DerivationGoal ( )
2004-06-18 21:09:32 +03:00
{
2020-10-11 19:19:10 +03:00
/* Careful: we should never ever throw an exception from a
destructor . */
try { closeLogFile ( ) ; } catch ( . . . ) { ignoreException ( ) ; }
}
2004-06-18 21:09:32 +03:00
2004-06-25 18:36:09 +03:00
2022-02-25 17:00:00 +02:00
std : : string DerivationGoal : : key ( )
2020-10-12 20:08:52 +03:00
{
/* Ensure that derivations get built in order of their name,
i . e . a derivation named " aardvark " always comes before
" baboon " . And substitution goals always happen before
derivation goals ( due to " b$ " ) . */
return " b$ " + std : : string ( drvPath . name ( ) ) + " $ " + worker . store . printStorePath ( drvPath ) ;
}
2020-10-11 19:19:10 +03:00
void DerivationGoal : : killChild ( )
{
hook . reset ( ) ;
}
2005-02-23 13:19:27 +02:00
2020-06-15 20:25:35 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : timedOut ( Error & & ex )
{
killChild ( ) ;
2022-12-07 13:58:58 +02:00
done ( BuildResult : : TimedOut , { } , std : : move ( ex ) ) ;
2020-10-11 19:19:10 +03:00
}
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : work ( )
{
( this - > * state ) ( ) ;
}
2004-06-18 21:09:32 +03:00
2023-01-12 01:57:18 +02:00
void DerivationGoal : : addWantedOutputs ( const OutputsSpec & outputs )
2020-10-11 19:19:10 +03:00
{
2023-01-13 03:20:27 +02:00
auto newWanted = wantedOutputs . union_ ( outputs ) ;
2023-02-14 20:25:55 +02:00
switch ( needRestart ) {
case NeedRestartForMoreOutputs : : OutputsUnmodifedDontNeed :
if ( ! newWanted . isSubsetOf ( wantedOutputs ) )
needRestart = NeedRestartForMoreOutputs : : OutputsAddedDoNeed ;
break ;
case NeedRestartForMoreOutputs : : OutputsAddedDoNeed :
/* No need to check whether we added more outputs, because a
restart is already queued up . */
break ;
case NeedRestartForMoreOutputs : : BuildInProgressWillNotNeed :
/* We are already building all outputs, so it doesn't matter if
we now want more . */
break ;
} ;
2023-01-13 03:20:27 +02:00
wantedOutputs = newWanted ;
2020-10-11 19:19:10 +03:00
}
2005-10-17 18:33:24 +03:00
2004-06-29 12:41:50 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : getDerivation ( )
{
trace ( " init " ) ;
2005-02-18 11:50:20 +02:00
2020-10-11 19:19:10 +03:00
/* The first thing to do is to make sure that the derivation
exists . If it doesn ' t , it may be created through a
substitute . */
2021-07-19 16:43:08 +03:00
if ( buildMode = = bmNormal & & worker . evalStore . isValidPath ( drvPath ) ) {
2020-10-11 19:19:10 +03:00
loadDerivation ( ) ;
return ;
2005-02-18 11:50:20 +02:00
}
2012-07-27 16:59:18 +03:00
2020-11-09 14:47:06 +02:00
addWaitee ( upcast_goal ( worker . makePathSubstitutionGoal ( drvPath ) ) ) ;
2006-12-08 19:26:21 +02:00
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : loadDerivation ;
}
2014-11-24 17:48:04 +02:00
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : loadDerivation ( )
{
trace ( " loading derivation " ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
if ( nrFailed ! = 0 ) {
2022-03-08 20:50:46 +02:00
done ( BuildResult : : MiscFailure , { } , Error ( " cannot build missing derivation '%s' " , worker . store . printStorePath ( drvPath ) ) ) ;
2020-10-11 19:19:10 +03:00
return ;
}
2014-11-24 17:48:04 +02:00
2020-10-11 19:19:10 +03:00
/* `drvPath' should already be a root, but let's be on the safe
side : if the user forgot to make it a root , we wouldn ' t want
things being garbage collected while we ' re busy . */
2021-07-19 16:43:08 +03:00
worker . evalStore . addTempRoot ( drvPath ) ;
2014-11-24 17:48:04 +02:00
2021-07-19 16:43:08 +03:00
assert ( worker . evalStore . isValidPath ( drvPath ) ) ;
2016-12-06 22:58:04 +02:00
2020-10-11 19:19:10 +03:00
/* Get the derivation. */
2021-12-08 15:03:44 +02:00
drv = std : : make_unique < Derivation > ( worker . evalStore . readDerivation ( drvPath ) ) ;
2016-12-06 22:58:04 +02:00
2020-10-11 19:19:10 +03:00
haveDerivation ( ) ;
}
2004-06-20 00:45:04 +03:00
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : haveDerivation ( )
2004-06-18 21:09:32 +03:00
{
2020-10-11 19:19:10 +03:00
trace ( " have derivation " ) ;
2004-06-18 21:09:32 +03:00
2022-03-30 17:31:01 +03:00
parsedDrv = std : : make_unique < ParsedDerivation > ( drvPath , * drv ) ;
2022-03-18 02:36:52 +02:00
if ( ! drv - > type ( ) . hasKnownOutputPaths ( ) )
2023-03-17 16:33:48 +02:00
experimentalFeatureSettings . require ( Xp : : CaDerivations ) ;
2004-06-25 18:36:09 +03:00
2022-03-30 17:31:01 +03:00
if ( ! drv - > type ( ) . isPure ( ) ) {
2023-03-17 16:33:48 +02:00
experimentalFeatureSettings . require ( Xp : : ImpureDerivations ) ;
2022-03-30 17:31:01 +03:00
for ( auto & [ outputName , output ] : drv - > outputs ) {
auto randomPath = StorePath : : random ( outputPathName ( drv - > name , outputName ) ) ;
assert ( ! worker . store . isValidPath ( randomPath ) ) ;
initialOutputs . insert ( {
outputName ,
InitialOutput {
. wanted = true ,
. outputHash = impureOutputHash ,
. known = InitialOutputStatus {
. path = randomPath ,
. status = PathStatus : : Absent
}
}
} ) ;
}
gaveUpOnSubstitution ( ) ;
return ;
}
2020-10-11 19:19:10 +03:00
for ( auto & i : drv - > outputsAndOptPaths ( worker . store ) )
if ( i . second . second )
worker . store . addTempRoot ( * i . second . second ) ;
2004-06-18 21:09:32 +03:00
2021-07-19 16:43:08 +03:00
auto outputHashes = staticOutputHashes ( worker . evalStore , * drv ) ;
for ( auto & [ outputName , outputHash ] : outputHashes )
2022-03-08 20:50:46 +02:00
initialOutputs . insert ( {
2021-02-23 15:12:11 +02:00
outputName ,
2022-03-08 20:50:46 +02:00
InitialOutput {
2021-02-23 15:12:11 +02:00
. wanted = true , // Will be refined later
. outputHash = outputHash
}
2022-03-08 20:50:46 +02:00
} ) ;
2021-02-23 15:12:11 +02:00
2020-10-11 19:19:10 +03:00
/* Check what outputs paths are not already valid. */
2022-03-08 20:50:46 +02:00
auto [ allValid , validOutputs ] = checkPathValidity ( ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* If they are all valid, then we're done. */
if ( allValid & & buildMode = = bmNormal ) {
2022-03-08 20:50:46 +02:00
done ( BuildResult : : AlreadyValid , std : : move ( validOutputs ) ) ;
2020-10-11 19:19:10 +03:00
return ;
}
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* We are first going to try to create the invalid output paths
through substitutes . If that doesn ' t work , we ' ll build
them . */
if ( settings . useSubstitutes & & parsedDrv - > substitutesAllowed ( ) )
2020-11-09 16:40:10 +02:00
for ( auto & [ outputName , status ] : initialOutputs ) {
2020-10-11 19:19:10 +03:00
if ( ! status . wanted ) continue ;
2020-11-09 16:40:10 +02:00
if ( ! status . known )
addWaitee (
upcast_goal (
worker . makeDrvOutputSubstitutionGoal (
DrvOutput { status . outputHash , outputName } ,
buildMode = = bmRepair ? Repair : NoRepair
)
)
) ;
else
addWaitee ( upcast_goal ( worker . makePathSubstitutionGoal (
status . known - > path ,
buildMode = = bmRepair ? Repair : NoRepair ,
getDerivationCA ( * drv ) ) ) ) ;
2020-10-11 19:19:10 +03:00
}
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
if ( waitees . empty ( ) ) /* to prevent hang (no wake-up event) */
outputsSubstitutionTried ( ) ;
else
state = & DerivationGoal : : outputsSubstitutionTried ;
}
2009-03-23 03:05:54 +02:00
2011-06-30 18:19:13 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : outputsSubstitutionTried ( )
{
trace ( " all outputs substituted (maybe) " ) ;
2016-04-08 19:07:13 +03:00
2022-03-30 17:31:01 +03:00
assert ( drv - > type ( ) . isPure ( ) ) ;
2020-10-11 19:19:10 +03:00
if ( nrFailed > 0 & & nrFailed > nrNoSubstituters + nrIncompleteClosure & & ! settings . tryFallback ) {
2022-03-08 20:50:46 +02:00
done ( BuildResult : : TransientFailure , { } ,
2022-01-17 20:28:42 +02:00
Error ( " some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source " ,
2020-10-11 19:19:10 +03:00
worker . store . printStorePath ( drvPath ) ) ) ;
return ;
}
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* If the substitutes form an incomplete closure, then we should
build the dependencies of this derivation , but after that , we
2020-10-18 15:21:53 +03:00
can still use the substitutes for this derivation itself .
If the nrIncompleteClosure ! = nrFailed , we have another issue as well .
In particular , it may be the case that the hole in the closure is
an output of the current derivation , which causes a loop if retried .
*/
2023-02-14 20:25:55 +02:00
{
bool substitutionFailed =
nrIncompleteClosure > 0 & &
nrIncompleteClosure = = nrFailed ;
switch ( retrySubstitution ) {
case RetrySubstitution : : NoNeed :
if ( substitutionFailed )
retrySubstitution = RetrySubstitution : : YesNeed ;
break ;
case RetrySubstitution : : YesNeed :
// Should not be able to reach this state from here.
assert ( false ) ;
break ;
case RetrySubstitution : : AlreadyRetried :
debug ( " substitution failed again, but we already retried once. Not retrying again. " ) ;
break ;
}
}
2017-08-14 21:14:55 +03:00
2020-10-11 19:19:10 +03:00
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0 ;
2010-12-13 18:53:23 +02:00
2023-02-14 20:25:55 +02:00
if ( needRestart = = NeedRestartForMoreOutputs : : OutputsAddedDoNeed ) {
needRestart = NeedRestartForMoreOutputs : : OutputsUnmodifedDontNeed ;
2020-10-11 19:19:10 +03:00
haveDerivation ( ) ;
return ;
2017-08-14 21:14:55 +03:00
}
2004-06-18 21:09:32 +03:00
2022-03-08 20:50:46 +02:00
auto [ allValid , validOutputs ] = checkPathValidity ( ) ;
2004-06-18 21:09:32 +03:00
2022-03-08 20:50:46 +02:00
if ( buildMode = = bmNormal & & allValid ) {
done ( BuildResult : : Substituted , std : : move ( validOutputs ) ) ;
2020-10-11 19:19:10 +03:00
return ;
}
2022-03-08 20:50:46 +02:00
if ( buildMode = = bmRepair & & allValid ) {
2020-10-11 19:19:10 +03:00
repairClosure ( ) ;
return ;
}
2022-03-08 20:50:46 +02:00
if ( buildMode = = bmCheck & & ! allValid )
2020-10-11 19:19:10 +03:00
throw Error ( " some outputs of '%s' are not valid, so checking is not possible " ,
worker . store . printStorePath ( drvPath ) ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* Nothing to wait for; tail call */
gaveUpOnSubstitution ( ) ;
2014-03-30 01:49:23 +02:00
}
2022-03-25 00:24:48 +02:00
2020-10-11 19:19:10 +03:00
/* At least one of the output paths could not be
produced using a substitute . So we have to build instead . */
void DerivationGoal : : gaveUpOnSubstitution ( )
2004-06-18 21:09:32 +03:00
{
2023-02-14 20:25:55 +02:00
/* At this point we are building all outputs, so if more are wanted there
is no need to restart . */
needRestart = NeedRestartForMoreOutputs : : BuildInProgressWillNotNeed ;
Make `KeyedBuildResult`, `BuildResult` like before, and fix bug another way
In https://github.com/NixOS/nix/pull/6311#discussion_r834863823, I
realized since derivation goals' wanted outputs can "grow" due to
overlapping dependencies (See `DerivationGoal::addWantedOutputs`, called
by `Worker::makeDerivationGoalCommon`), the previous bug fix had an
unfortunate side effect of causing more pointless rebuilds.
In paticular, we have this situation:
1. Goal made from `DerivedPath::Built { foo, {a} }`.
2. Goal gives on on substituting, starts building.
3. Goal made from `DerivedPath::Built { foo, {b} }`, in fact is just
modified original goal.
4. Though the goal had gotten as far as building, so all outputs were
going to be produced, `addWantedOutputs` no longer knows that and so
the goal is flagged to be restarted.
This might sound far-fetched with input-addressed drvs, where we usually
basically have all our goals "planned out" before we start doing
anything, but with CA derivation goals and especially RFC 92, where *drv
resolution* means goals are created after some building is completed, it
is more likely to happen.
So the first thing to do was restore the clearing of `wantedOutputs` we
used to do, and then filter the outputs in `buildPathsWithResults` to
only get the ones we care about.
But fix also has its own side effect in that the `DerivedPath` in the
`BuildResult` in `DerivationGoal` cannot be trusted; it is merely the
*first* `DerivedPath` for which this goal was originally created.
To remedy this, I made `BuildResult` be like it was before, and instead
made `KeyedBuildResult` be a subclass wit the path. Only
`buildPathsWithResults` returns `KeyedBuildResult`s, everything else
just becomes like it was before, where the "key" is unambiguous from
context.
I think separating the "primary key" field(s) from the other fields is
good practical in general anyways. (I would like to do the same thing
for `ValidPathInfo`.) Among other things, it allows constructions like
`std::map<Key, ThingWithKey>` where doesn't contain duplicate keys and
just precludes the possibility of those duplicate keys being out of
sync.
We might leverage the above someday to overload `buildPathsWithResults`
to take a *set* of return a *map* per the above.
-----
Unfortunately, we need to avoid C++20 strictness on designated
initializers.
(BTW
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2287r1.html
this offers some new syntax for this use-case. Hopefully this will be
adopted and we can eventually use it.)
No having that yet, maybe it would be better to not make
`KeyedBuildResult` a subclass to just avoid this.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2022-03-25 03:26:07 +02:00
2020-10-11 19:19:10 +03:00
/* The inputs must be built before we can build this goal. */
2022-03-30 17:31:01 +03:00
inputDrvOutputs . clear ( ) ;
2020-10-11 19:19:10 +03:00
if ( useDerivation )
2022-03-30 17:31:01 +03:00
for ( auto & i : dynamic_cast < Derivation * > ( drv . get ( ) ) - > inputDrvs ) {
2022-03-11 14:23:23 +02:00
/* Ensure that pure, non-fixed-output derivations don't
depend on impure derivations . */
2023-03-17 16:33:48 +02:00
if ( experimentalFeatureSettings . isEnabled ( Xp : : ImpureDerivations ) & & drv - > type ( ) . isPure ( ) & & ! drv - > type ( ) . isFixed ( ) ) {
2022-03-30 17:31:01 +03:00
auto inputDrv = worker . evalStore . readDerivation ( i . first ) ;
if ( ! inputDrv . type ( ) . isPure ( ) )
throw Error ( " pure derivation '%s' depends on impure derivation '%s' " ,
worker . store . printStorePath ( drvPath ) ,
worker . store . printStorePath ( i . first ) ) ;
}
2020-10-11 19:19:10 +03:00
addWaitee ( worker . makeDerivationGoal ( i . first , i . second , buildMode = = bmRepair ? bmRepair : bmNormal ) ) ;
2022-03-30 17:31:01 +03:00
}
2004-06-18 21:09:32 +03:00
2021-07-19 16:43:08 +03:00
/* Copy the input sources from the eval store to the build
store . */
if ( & worker . evalStore ! = & worker . store ) {
2021-07-22 23:43:08 +03:00
RealisedPath : : Set inputSrcs ;
2021-07-19 16:43:08 +03:00
for ( auto & i : drv - > inputSrcs )
inputSrcs . insert ( i ) ;
2021-07-22 23:43:08 +03:00
copyClosure ( worker . evalStore , worker . store , inputSrcs ) ;
2021-07-19 16:43:08 +03:00
}
2020-10-11 19:19:10 +03:00
for ( auto & i : drv - > inputSrcs ) {
if ( worker . store . isValidPath ( i ) ) continue ;
if ( ! settings . useSubstitutes )
throw Error ( " dependency '%s' of '%s' does not exist, and substitution is disabled " ,
worker . store . printStorePath ( i ) , worker . store . printStorePath ( drvPath ) ) ;
2020-11-09 14:47:06 +02:00
addWaitee ( upcast_goal ( worker . makePathSubstitutionGoal ( i ) ) ) ;
2020-10-11 19:19:10 +03:00
}
2005-02-18 11:50:20 +02:00
2020-10-11 19:19:10 +03:00
if ( waitees . empty ( ) ) /* to prevent hang (no wake-up event) */
inputsRealised ( ) ;
else
state = & DerivationGoal : : inputsRealised ;
}
2012-07-27 16:59:18 +03:00
2012-07-09 01:39:24 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : repairClosure ( )
{
2022-03-30 17:31:01 +03:00
assert ( drv - > type ( ) . isPure ( ) ) ;
2020-10-11 19:19:10 +03:00
/* If we're repairing, we now know that our own outputs are valid.
Now check whether the other paths in the outputs closure are
good . If not , then start derivation goals for the derivations
that produced those outputs . */
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
/* Get the output closure. */
auto outputs = queryDerivationOutputMap ( ) ;
StorePathSet outputClosure ;
for ( auto & i : outputs ) {
2023-01-12 01:57:18 +02:00
if ( ! wantedOutputs . contains ( i . first ) ) continue ;
2020-10-11 19:19:10 +03:00
worker . store . computeFSClosure ( i . second , outputClosure ) ;
}
2013-01-02 13:38:28 +02:00
2020-10-11 19:19:10 +03:00
/* Filter out our own outputs (which we have already checked). */
for ( auto & i : outputs )
outputClosure . erase ( i . second ) ;
2004-06-28 13:42:57 +03:00
2020-10-11 19:19:10 +03:00
/* Get all dependencies of this derivation so that we know which
derivation is responsible for which path in the output
closure . */
StorePathSet inputClosure ;
if ( useDerivation ) worker . store . computeFSClosure ( drvPath , inputClosure ) ;
std : : map < StorePath , StorePath > outputsToDrv ;
for ( auto & i : inputClosure )
if ( i . isDerivation ( ) ) {
auto depOutputs = worker . store . queryPartialDerivationOutputMap ( i ) ;
for ( auto & j : depOutputs )
if ( j . second )
outputsToDrv . insert_or_assign ( * j . second , i ) ;
2004-06-28 13:42:57 +03:00
}
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* Check each path (slow!). */
for ( auto & i : outputClosure ) {
if ( worker . pathContentsGood ( i ) ) continue ;
2021-01-21 01:27:36 +02:00
printError (
" found corrupted or missing path '%s' in the output closure of '%s' " ,
worker . store . printStorePath ( i ) , worker . store . printStorePath ( drvPath ) ) ;
2020-10-11 19:19:10 +03:00
auto drvPath2 = outputsToDrv . find ( i ) ;
if ( drvPath2 = = outputsToDrv . end ( ) )
2020-11-09 14:47:06 +02:00
addWaitee ( upcast_goal ( worker . makePathSubstitutionGoal ( i , Repair ) ) ) ;
2020-06-15 20:25:35 +03:00
else
2023-01-12 01:57:18 +02:00
addWaitee ( worker . makeDerivationGoal ( drvPath2 - > second , OutputsSpec : : All ( ) , bmRepair ) ) ;
2020-06-15 20:25:35 +03:00
}
2020-10-11 19:19:10 +03:00
if ( waitees . empty ( ) ) {
2022-03-08 20:50:46 +02:00
done ( BuildResult : : AlreadyValid , assertPathValidity ( ) ) ;
2020-10-11 19:19:10 +03:00
return ;
2004-06-25 18:36:09 +03:00
}
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : closureRepaired ;
2004-06-18 21:09:32 +03:00
}
2020-10-11 19:19:10 +03:00
void DerivationGoal : : closureRepaired ( )
2004-06-25 18:36:09 +03:00
{
2020-10-11 19:19:10 +03:00
trace ( " closure repaired " ) ;
if ( nrFailed > 0 )
throw Error ( " some paths in the output closure of derivation '%s' could not be repaired " ,
worker . store . printStorePath ( drvPath ) ) ;
2022-03-08 20:50:46 +02:00
done ( BuildResult : : AlreadyValid , assertPathValidity ( ) ) ;
2004-06-25 18:36:09 +03:00
}
2020-10-11 19:19:10 +03:00
void DerivationGoal : : inputsRealised ( )
2019-05-10 23:39:31 +03:00
{
2020-10-11 19:19:10 +03:00
trace ( " all inputs realised " ) ;
2019-05-11 23:35:53 +03:00
2020-10-11 19:19:10 +03:00
if ( nrFailed ! = 0 ) {
if ( ! useDerivation )
throw Error ( " some dependencies of '%s' are missing " , worker . store . printStorePath ( drvPath ) ) ;
2022-03-08 20:50:46 +02:00
done ( BuildResult : : DependencyFailed , { } , Error (
2020-10-11 19:19:10 +03:00
" %s dependencies of derivation '%s' failed to build " ,
nrFailed , worker . store . printStorePath ( drvPath ) ) ) ;
return ;
2019-05-10 23:39:31 +03:00
}
2005-10-20 19:58:34 +03:00
2023-02-14 20:25:55 +02:00
if ( retrySubstitution = = RetrySubstitution : : YesNeed ) {
retrySubstitution = RetrySubstitution : : AlreadyRetried ;
2020-10-11 19:19:10 +03:00
haveDerivation ( ) ;
return ;
}
2005-10-20 19:58:34 +03:00
2020-10-11 19:19:10 +03:00
/* Gather information necessary for computing the closure and/or
running the build hook . */
2005-10-20 19:58:34 +03:00
2020-10-11 19:19:10 +03:00
/* Determine the full set of input paths. */
2006-12-07 18:33:31 +02:00
2020-10-11 19:19:10 +03:00
/* First, the input derivations. */
if ( useDerivation ) {
auto & fullDrv = * dynamic_cast < Derivation * > ( drv . get ( ) ) ;
2006-12-07 02:16:07 +02:00
2022-03-18 02:36:52 +02:00
auto drvType = fullDrv . type ( ) ;
bool resolveDrv = std : : visit ( overloaded {
[ & ] ( const DerivationType : : InputAddressed & ia ) {
/* must resolve if deferred. */
return ia . deferred ;
} ,
[ & ] ( const DerivationType : : ContentAddressed & ca ) {
return ! fullDrv . inputDrvs . empty ( ) & & (
ca . fixed
/* Can optionally resolve if fixed, which is good
for avoiding unnecessary rebuilds . */
2023-03-17 16:33:48 +02:00
? experimentalFeatureSettings . isEnabled ( Xp : : CaDerivations )
2022-03-18 02:36:52 +02:00
/* Must resolve if floating and there are any inputs
drvs . */
: true ) ;
} ,
2022-03-30 17:31:01 +03:00
[ & ] ( const DerivationType : : Impure & ) {
return true ;
}
2022-03-18 02:36:52 +02:00
} , drvType . raw ( ) ) ;
2022-03-30 17:31:01 +03:00
if ( resolveDrv & & ! fullDrv . inputDrvs . empty ( ) ) {
2023-03-17 16:33:48 +02:00
experimentalFeatureSettings . require ( Xp : : CaDerivations ) ;
2022-03-18 02:36:52 +02:00
2020-10-11 19:19:10 +03:00
/* We are be able to resolve this derivation based on the
2022-03-30 17:31:01 +03:00
now - known results of dependencies . If so , we become a
stub goal aliasing that resolved derivation goal . */
std : : optional attempt = fullDrv . tryResolve ( worker . store , inputDrvOutputs ) ;
2022-12-05 18:27:47 +02:00
if ( ! attempt ) {
/* TODO (impure derivations-induced tech debt) (see below):
The above attempt should have found it , but because we manage
inputDrvOutputs statefully , sometimes it gets out of sync with
the real source of truth ( store ) . So we query the store
directly if there ' s a problem . */
attempt = fullDrv . tryResolve ( worker . store ) ;
}
2020-10-11 19:19:10 +03:00
assert ( attempt ) ;
Derivation drvResolved { * std : : move ( attempt ) } ;
2020-05-08 12:22:39 +03:00
2020-10-11 19:19:10 +03:00
auto pathResolved = writeDerivation ( worker . store , drvResolved ) ;
2012-07-27 16:59:18 +03:00
2022-03-30 17:31:01 +03:00
auto msg = fmt ( " resolved derivation: '%s' -> '%s' " ,
2020-10-11 19:19:10 +03:00
worker . store . printStorePath ( drvPath ) ,
worker . store . printStorePath ( pathResolved ) ) ;
act = std : : make_unique < Activity > ( * logger , lvlInfo , actBuildWaiting , msg ,
Logger : : Fields {
worker . store . printStorePath ( drvPath ) ,
worker . store . printStorePath ( pathResolved ) ,
} ) ;
2005-10-20 19:58:34 +03:00
2021-12-13 17:56:44 +02:00
resolvedDrvGoal = worker . makeDerivationGoal (
2020-10-11 19:19:10 +03:00
pathResolved , wantedOutputs , buildMode ) ;
2021-12-13 17:56:44 +02:00
addWaitee ( resolvedDrvGoal ) ;
2005-10-20 19:58:34 +03:00
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : resolvedFinished ;
return ;
}
2020-05-05 13:04:36 +03:00
2020-10-11 19:19:10 +03:00
for ( auto & [ depDrvPath , wantedDepOutputs ] : fullDrv . inputDrvs ) {
/* Add the relevant output closures of the input derivation
` i ' as input paths . Only add the closures of output paths
that are specified as inputs . */
2022-11-14 17:26:20 +02:00
for ( auto & j : wantedDepOutputs ) {
/* TODO (impure derivations-induced tech debt):
Tracking input derivation outputs statefully through the
goals is error prone and has led to bugs .
For a robust nix , we need to move towards the ` else ` branch ,
which does not rely on goal state to match up with the
reality of the store , which is our real source of truth .
However , the impure derivations feature still relies on this
fragile way of doing things , because its builds do not have
a representation in the store , which is a usability problem
2022-11-15 18:57:40 +02:00
in itself . When implementing this logic entirely with lookups
make sure that they ' re cached . */
2022-11-14 17:26:20 +02:00
if ( auto outPath = get ( inputDrvOutputs , { depDrvPath , j } ) ) {
2022-03-30 17:31:01 +03:00
worker . store . computeFSClosure ( * outPath , inputPaths ) ;
2022-11-14 17:26:20 +02:00
}
else {
auto outMap = worker . evalStore . queryDerivationOutputMap ( depDrvPath ) ;
auto outMapPath = outMap . find ( j ) ;
if ( outMapPath = = outMap . end ( ) ) {
throw Error (
" derivation '%s' requires non-existent output '%s' from input derivation '%s' " ,
worker . store . printStorePath ( drvPath ) , j , worker . store . printStorePath ( depDrvPath ) ) ;
}
worker . store . computeFSClosure ( outMapPath - > second , inputPaths ) ;
}
}
2020-10-11 19:19:10 +03:00
}
2006-12-06 22:00:15 +02:00
}
2020-10-11 19:19:10 +03:00
/* Second, the input sources. */
worker . store . computeFSClosure ( drv - > inputSrcs , inputPaths ) ;
2009-09-23 20:05:51 +03:00
2020-10-11 19:19:10 +03:00
debug ( " added input paths %s " , worker . store . showPaths ( inputPaths ) ) ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
/* What type of derivation are we building? */
derivationType = drv - > type ( ) ;
2005-10-20 19:58:34 +03:00
2020-10-11 19:19:10 +03:00
/* Okay, try to build. Note that here we don't wait for a build
slot to become available , since we don ' t need one if there is a
build hook . */
state = & DerivationGoal : : tryToBuild ;
worker . wakeUp ( shared_from_this ( ) ) ;
}
2012-07-27 16:59:18 +03:00
2022-03-08 20:50:46 +02:00
void DerivationGoal : : started ( )
{
2020-10-11 19:19:10 +03:00
auto msg = fmt (
buildMode = = bmRepair ? " repairing outputs of '%s' " :
buildMode = = bmCheck ? " checking outputs of '%s' " :
2022-09-26 21:55:56 +03:00
" building '%s' " , worker . store . printStorePath ( drvPath ) ) ;
2020-10-11 19:19:10 +03:00
fmt ( " building '%s' " , worker . store . printStorePath ( drvPath ) ) ;
if ( hook ) msg + = fmt ( " on '%s' " , machineName ) ;
act = std : : make_unique < Activity > ( * logger , lvlInfo , actBuild , msg ,
2022-09-26 21:55:56 +03:00
Logger : : Fields { worker . store . printStorePath ( drvPath ) , hook ? machineName : " " , 1 , 1 } ) ;
2020-10-11 19:19:10 +03:00
mcRunningBuilds = std : : make_unique < MaintainCount < uint64_t > > ( worker . runningBuilds ) ;
worker . updateProgress ( ) ;
2006-12-07 16:14:35 +02:00
}
2020-10-11 19:19:10 +03:00
void DerivationGoal : : tryToBuild ( )
2010-08-25 23:44:28 +03:00
{
2020-10-11 19:19:10 +03:00
trace ( " trying to build " ) ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
/* Obtain locks on all output paths, if the paths are known a priori.
2010-08-25 23:44:28 +03:00
2020-10-11 19:19:10 +03:00
The locks are automatically released when we exit this function or Nix
crashes . If we can ' t acquire the lock , then continue ; hopefully some
other goal can start a build , and if not , the main loop will sleep a few
seconds and then retry this goal . */
PathSet lockFiles ;
/* FIXME: Should lock something like the drv itself so we don't build same
CA drv concurrently */
2021-07-20 07:57:56 +03:00
if ( dynamic_cast < LocalStore * > ( & worker . store ) ) {
2020-12-20 21:55:21 +02:00
/* If we aren't a local store, we might need to use the local store as
a build remote , but that would cause a deadlock . */
/* FIXME: Make it so we can use ourselves as a build remote even if we
are the local store ( separate locking for building vs scheduling ? */
/* FIXME: find some way to lock for scheduling for the other stores so
a forking daemon with - - store still won ' t farm out redundant builds .
*/
2021-07-20 07:57:56 +03:00
for ( auto & i : drv - > outputsAndOptPaths ( worker . store ) ) {
2020-12-20 21:55:21 +02:00
if ( i . second . second )
lockFiles . insert ( worker . store . Store : : toRealPath ( * i . second . second ) ) ;
2021-07-20 07:57:56 +03:00
else
lockFiles . insert (
2021-09-02 10:57:41 +03:00
worker . store . Store : : toRealPath ( drvPath ) + " . " + i . first
2021-07-20 07:57:56 +03:00
) ;
}
}
2017-10-23 21:43:04 +03:00
2020-10-11 19:19:10 +03:00
if ( ! outputLocks . lockPaths ( lockFiles , " " , false ) ) {
if ( ! actLock )
actLock = std : : make_unique < Activity > ( * logger , lvlWarn , actBuildWaiting ,
fmt ( " waiting for lock on %s " , yellowtxt ( showPaths ( lockFiles ) ) ) ) ;
worker . waitForAWhile ( shared_from_this ( ) ) ;
return ;
}
2017-10-24 14:41:52 +03:00
2020-10-11 19:19:10 +03:00
actLock . reset ( ) ;
2010-08-25 23:44:28 +03:00
2020-10-11 19:19:10 +03:00
/* Now check again whether the outputs are valid. This is because
another process may have started building in parallel . After
it has finished and released the locks , we can ( and should )
reuse its results . ( Strictly speaking the first check can be
omitted , but that would be less efficient . ) Note that since we
now hold the locks on the output paths , no other process can
build this derivation , so no further checks are necessary . */
2022-03-08 20:50:46 +02:00
auto [ allValid , validOutputs ] = checkPathValidity ( ) ;
2020-10-11 19:19:10 +03:00
if ( buildMode ! = bmCheck & & allValid ) {
debug ( " skipping build of derivation '%s', someone beat us to it " , worker . store . printStorePath ( drvPath ) ) ;
outputLocks . setDeletion ( true ) ;
2022-03-08 20:50:46 +02:00
done ( BuildResult : : AlreadyValid , std : : move ( validOutputs ) ) ;
2020-10-11 19:19:10 +03:00
return ;
}
2010-08-25 23:44:28 +03:00
2020-10-11 19:19:10 +03:00
/* If any of the outputs already exist but are not valid, delete
them . */
for ( auto & [ _ , status ] : initialOutputs ) {
if ( ! status . known | | status . known - > isValid ( ) ) continue ;
auto storePath = status . known - > path ;
debug ( " removing invalid path '%s' " , worker . store . printStorePath ( status . known - > path ) ) ;
deletePath ( worker . store . Store : : toRealPath ( storePath ) ) ;
}
2010-08-25 23:44:28 +03:00
2020-10-11 19:19:10 +03:00
/* Don't do a remote build if the derivation has the attribute
` preferLocalBuild ' set . Also , check and repair modes are only
supported for local builds . */
2021-10-27 15:21:31 +03:00
bool buildLocally =
( buildMode ! = bmNormal | | parsedDrv - > willBuildLocally ( worker . store ) )
& & settings . maxBuildJobs . get ( ) ! = 0 ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
if ( ! buildLocally ) {
switch ( tryBuildHook ( ) ) {
case rpAccept :
/* Yes, it has started doing so. Wait until we get
EOF from the hook . */
actLock . reset ( ) ;
2022-03-08 20:50:46 +02:00
buildResult . startTime = time ( 0 ) ; // inexact
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : buildDone ;
started ( ) ;
return ;
case rpPostpone :
/* Not now; wait until at least one child finishes or
the wake - up timeout expires . */
if ( ! actLock )
actLock = std : : make_unique < Activity > ( * logger , lvlWarn , actBuildWaiting ,
fmt ( " waiting for a machine to build '%s' " , yellowtxt ( worker . store . printStorePath ( drvPath ) ) ) ) ;
worker . waitForAWhile ( shared_from_this ( ) ) ;
outputLocks . unlock ( ) ;
return ;
case rpDecline :
/* We should do it ourselves. */
break ;
}
}
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
actLock . reset ( ) ;
2010-08-25 23:44:28 +03:00
2020-10-11 19:19:10 +03:00
state = & DerivationGoal : : tryLocalBuild ;
worker . wakeUp ( shared_from_this ( ) ) ;
}
2010-08-30 17:53:03 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : tryLocalBuild ( ) {
2021-02-26 17:20:33 +02:00
throw Error (
" unable to build with a primary store that isn't a local store; "
" either pass a different '--store' or enable remote builds. "
2021-12-06 17:42:57 +02:00
" \n https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html " ) ;
2010-08-25 23:44:28 +03:00
}
2020-10-11 19:19:10 +03:00
static void chmod_ ( const Path & path , mode_t mode )
2010-08-25 23:44:28 +03:00
{
2020-10-11 19:19:10 +03:00
if ( chmod ( path . c_str ( ) , mode ) = = - 1 )
throw SysError ( " setting permissions on '%s' " , path ) ;
2010-08-25 23:44:28 +03:00
}
2020-10-11 19:19:10 +03:00
/* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if
it ' s a directory and we ' re not root ( to be able to update the
directory ' s parent link " .. " ) . */
static void movePath ( const Path & src , const Path & dst )
2004-05-11 21:05:44 +03:00
{
2020-10-11 19:19:10 +03:00
auto st = lstat ( src ) ;
2012-11-26 18:15:09 +02:00
2020-10-11 19:19:10 +03:00
bool changePerm = ( geteuid ( ) & & S_ISDIR ( st . st_mode ) & & ! ( st . st_mode & S_IWUSR ) ) ;
2012-11-26 18:15:09 +02:00
2020-10-11 19:19:10 +03:00
if ( changePerm )
chmod_ ( src , st . st_mode | S_IWUSR ) ;
2013-01-02 13:38:28 +02:00
2022-04-13 15:10:36 +03:00
renameFile ( src , dst ) ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
if ( changePerm )
chmod_ ( dst , st . st_mode ) ;
}
2018-09-28 13:43:01 +03:00
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
void replaceValidPath ( const Path & storePath , const Path & tmpPath )
{
/* We can't atomically replace storePath (the original) with
tmpPath ( the replacement ) , so we have to move it out of the
way first . We ' d better not be interrupted here , because if
we ' re repairing ( say ) Glibc , we end up with a broken system . */
2023-03-02 16:44:19 +02:00
Path oldPath = fmt ( " %1%.old-%2%-%3% " , storePath , getpid ( ) , random ( ) ) ;
2020-10-11 19:19:10 +03:00
if ( pathExists ( storePath ) )
movePath ( storePath , oldPath ) ;
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
try {
movePath ( tmpPath , storePath ) ;
} catch ( . . . ) {
try {
// attempt to recover
movePath ( oldPath , storePath ) ;
} catch ( . . . ) {
ignoreException ( ) ;
}
throw ;
}
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
deletePath ( oldPath ) ;
}
2013-06-13 17:43:20 +03:00
2005-10-17 18:33:24 +03:00
2021-02-26 17:20:33 +02:00
int DerivationGoal : : getChildStatus ( )
{
return hook - > pid . kill ( ) ;
}
void DerivationGoal : : closeReadPipes ( )
{
hook - > builderOut . readSide = - 1 ;
hook - > fromHook . readSide = - 1 ;
}
void DerivationGoal : : cleanupHookFinally ( )
{
}
void DerivationGoal : : cleanupPreChildKill ( )
{
}
void DerivationGoal : : cleanupPostChildKill ( )
{
}
bool DerivationGoal : : cleanupDecideWhetherDiskFull ( )
{
return false ;
}
void DerivationGoal : : cleanupPostOutputsRegisteredModeCheck ( )
{
}
void DerivationGoal : : cleanupPostOutputsRegisteredModeNonCheck ( )
{
}
2004-05-11 21:05:44 +03:00
2021-06-24 12:41:51 +03:00
void runPostBuildHook (
Store & store ,
Logger & logger ,
const StorePath & drvPath ,
2022-04-28 14:36:01 +03:00
const StorePathSet & outputPaths )
2021-06-24 12:41:51 +03:00
{
auto hook = settings . postBuildHook ;
if ( hook = = " " )
return ;
Activity act ( logger , lvlInfo , actPostBuildHook ,
fmt ( " running post-build-hook '%s' " , settings . postBuildHook ) ,
Logger : : Fields { store . printStorePath ( drvPath ) } ) ;
PushActivity pact ( act . id ) ;
std : : map < std : : string , std : : string > hookEnvironment = getEnv ( ) ;
hookEnvironment . emplace ( " DRV_PATH " , store . printStorePath ( drvPath ) ) ;
hookEnvironment . emplace ( " OUT_PATHS " , chomp ( concatStringsSep ( " " , store . printStorePathSet ( outputPaths ) ) ) ) ;
2021-07-15 19:17:18 +03:00
hookEnvironment . emplace ( " NIX_CONFIG " , globalConfig . toKeyValue ( ) ) ;
2021-06-24 12:41:51 +03:00
struct LogSink : Sink {
Activity & act ;
std : : string currentLine ;
LogSink ( Activity & act ) : act ( act ) { }
void operator ( ) ( std : : string_view data ) override {
for ( auto c : data ) {
if ( c = = ' \n ' ) {
flushLine ( ) ;
} else {
currentLine + = c ;
}
}
}
void flushLine ( ) {
act . result ( resPostBuildLogLine , currentLine ) ;
currentLine . clear ( ) ;
}
~ LogSink ( ) {
if ( currentLine ! = " " ) {
currentLine + = ' \n ' ;
flushLine ( ) ;
}
}
} ;
LogSink sink ( act ) ;
2021-09-14 00:22:09 +03:00
runProgram2 ( {
. program = settings . postBuildHook ,
. environment = hookEnvironment ,
. standardOut = & sink ,
. mergeStderrToStdout = true ,
} ) ;
2021-06-24 12:41:51 +03:00
}
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
void DerivationGoal : : buildDone ( )
{
trace ( " build done " ) ;
2015-12-02 15:59:07 +02:00
2021-02-26 17:20:33 +02:00
Finally releaseBuildUser ( [ & ] ( ) { this - > cleanupHookFinally ( ) ; } ) ;
2004-05-11 21:05:44 +03:00
2021-02-26 17:20:33 +02:00
cleanupPreChildKill ( ) ;
2013-09-02 12:58:18 +03:00
2020-10-11 19:19:10 +03:00
/* Since we got an EOF on the logger pipe, the builder is presumed
to have terminated . In fact , the builder could also have
simply have closed its end of the pipe , so just to be sure ,
kill it . */
2021-02-26 17:20:33 +02:00
int status = getChildStatus ( ) ;
2016-04-25 17:47:46 +03:00
2020-10-11 19:19:10 +03:00
debug ( " builder process for '%s' finished " , worker . store . printStorePath ( drvPath ) ) ;
2016-04-25 17:47:46 +03:00
2022-03-08 20:50:46 +02:00
buildResult . timesBuilt + + ;
buildResult . stopTime = time ( 0 ) ;
2017-10-24 14:41:52 +03:00
2020-10-11 19:19:10 +03:00
/* So the child is gone now. */
worker . childTerminated ( this ) ;
2004-05-13 22:14:49 +03:00
2020-10-11 19:19:10 +03:00
/* Close the read side of the logger pipe. */
2021-02-26 17:20:33 +02:00
closeReadPipes ( ) ;
2016-06-09 19:27:39 +03:00
2020-10-11 19:19:10 +03:00
/* Close the log file. */
closeLogFile ( ) ;
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2021-02-26 17:20:33 +02:00
cleanupPostChildKill ( ) ;
2012-07-27 16:59:18 +03:00
2022-11-18 14:40:59 +02:00
if ( buildResult . cpuUser & & buildResult . cpuSystem ) {
debug ( " builder for '%s' terminated with status %d, user CPU %.3fs, system CPU %.3fs " ,
worker . store . printStorePath ( drvPath ) ,
status ,
( ( double ) buildResult . cpuUser - > count ( ) ) / 1000000 ,
( ( double ) buildResult . cpuSystem - > count ( ) ) / 1000000 ) ;
}
2020-10-11 19:19:10 +03:00
bool diskFull = false ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
try {
2007-10-27 03:46:59 +03:00
2020-10-11 19:19:10 +03:00
/* Check the exit status. */
if ( ! statusOk ( status ) ) {
2009-03-25 23:05:42 +02:00
2021-02-26 17:20:33 +02:00
diskFull | = cleanupDecideWhetherDiskFull ( ) ;
2020-08-07 22:09:26 +03:00
2020-10-11 19:19:10 +03:00
auto msg = fmt ( " builder for '%s' %s " ,
yellowtxt ( worker . store . printStorePath ( drvPath ) ) ,
statusToString ( status ) ) ;
2020-08-07 22:09:26 +03:00
2020-10-11 19:19:10 +03:00
if ( ! logger - > isVerbose ( ) & & ! logTail . empty ( ) ) {
2021-01-21 01:27:36 +02:00
msg + = fmt ( " ; \n last %d log lines: \n " , logTail . size ( ) ) ;
for ( auto & line : logTail ) {
msg + = " > " ;
msg + = line ;
msg + = " \n " ;
}
2023-03-28 09:22:26 +03:00
auto nixLogCommand = experimentalFeatureSettings . isEnabled ( Xp : : NixCommand )
? " nix log "
: " nix-store -l " ;
msg + = fmt ( " For full logs, run ' " ANSI_BOLD " %s %s " ANSI_NORMAL " '. " ,
nixLogCommand ,
2021-01-25 18:15:38 +02:00
worker . store . printStorePath ( drvPath ) ) ;
2020-10-11 19:19:10 +03:00
}
2020-08-07 22:09:26 +03:00
2020-10-11 19:19:10 +03:00
if ( diskFull )
msg + = " \n note: build failure may have been caused by lack of free disk space " ;
2014-02-18 00:04:52 +02:00
2020-10-11 19:19:10 +03:00
throw BuildError ( msg ) ;
}
2012-10-03 00:13:46 +03:00
2020-10-11 19:19:10 +03:00
/* Compute the FS closure of the outputs and register them as
being valid . */
2022-03-08 20:50:46 +02:00
auto builtOutputs = registerOutputs ( ) ;
2015-07-20 04:15:45 +03:00
2021-06-24 12:41:51 +03:00
StorePathSet outputPaths ;
2022-04-28 14:36:01 +03:00
for ( auto & [ _ , output ] : builtOutputs )
2022-03-08 20:50:46 +02:00
outputPaths . insert ( output . outPath ) ;
2021-06-24 12:41:51 +03:00
runPostBuildHook (
worker . store ,
* logger ,
drvPath ,
outputPaths
) ;
2017-10-24 15:24:57 +03:00
2021-02-26 17:20:33 +02:00
cleanupPostOutputsRegisteredModeNonCheck ( ) ;
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2020-10-11 19:19:10 +03:00
/* It is now safe to delete the lock files, since all future
lockers will see that the output paths are valid ; they will
not create new lock files with the same names as the old
( unlinked ) lock files . */
outputLocks . setDeletion ( true ) ;
outputLocks . unlock ( ) ;
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2022-03-08 20:50:46 +02:00
done ( BuildResult : : Built , std : : move ( builtOutputs ) ) ;
2020-10-11 19:19:10 +03:00
} catch ( BuildError & e ) {
outputLocks . unlock ( ) ;
2004-05-11 21:05:44 +03:00
2020-10-11 19:19:10 +03:00
BuildResult : : Status st = BuildResult : : MiscFailure ;
2019-05-12 23:47:41 +03:00
2020-10-11 19:19:10 +03:00
if ( hook & & WIFEXITED ( status ) & & WEXITSTATUS ( status ) = = 101 )
st = BuildResult : : TimedOut ;
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
else if ( hook & & ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = 100 ) ) {
}
2014-11-24 17:48:04 +02:00
2020-10-11 19:19:10 +03:00
else {
st =
dynamic_cast < NotDeterministic * > ( & e ) ? BuildResult : : NotDeterministic :
statusOk ( status ) ? BuildResult : : OutputRejected :
2022-03-31 17:06:40 +03:00
! derivationType . isSandboxed ( ) | | diskFull ? BuildResult : : TransientFailure :
2020-10-11 19:19:10 +03:00
BuildResult : : PermanentFailure ;
}
2004-06-20 00:45:04 +03:00
2022-12-07 13:58:58 +02:00
done ( st , { } , std : : move ( e ) ) ;
2020-10-11 19:19:10 +03:00
return ;
2005-02-23 13:19:27 +02:00
}
2020-10-11 19:19:10 +03:00
}
2019-10-13 02:02:57 +03:00
2022-03-08 20:50:46 +02:00
void DerivationGoal : : resolvedFinished ( )
{
2022-03-30 17:31:01 +03:00
trace ( " resolved derivation finished " ) ;
2021-12-13 17:56:44 +02:00
assert ( resolvedDrvGoal ) ;
auto resolvedDrv = * resolvedDrvGoal - > drv ;
2022-03-30 17:31:01 +03:00
auto & resolvedResult = resolvedDrvGoal - > buildResult ;
2021-01-27 11:03:05 +02:00
2022-03-30 17:31:01 +03:00
DrvOutputs builtOutputs ;
2021-01-27 11:03:05 +02:00
2022-03-30 17:31:01 +03:00
if ( resolvedResult . success ( ) ) {
auto resolvedHashes = staticOutputHashes ( worker . store , resolvedDrv ) ;
2021-06-24 12:41:51 +03:00
2022-03-30 17:31:01 +03:00
StorePathSet outputPaths ;
2021-01-27 11:03:05 +02:00
2023-01-12 01:57:18 +02:00
// `wantedOutputs` might merely indicate “all the outputs”
auto realWantedOutputs = std : : visit ( overloaded {
[ & ] ( const OutputsSpec : : All & ) {
return resolvedDrv . outputNames ( ) ;
} ,
[ & ] ( const OutputsSpec : : Names & names ) {
2023-01-11 18:54:43 +02:00
return static_cast < std : : set < std : : string > > ( names ) ;
2023-01-12 01:57:18 +02:00
} ,
} , wantedOutputs . raw ( ) ) ;
2022-03-30 17:31:01 +03:00
for ( auto & wantedOutput : realWantedOutputs ) {
2022-05-04 08:44:32 +03:00
auto initialOutput = get ( initialOutputs , wantedOutput ) ;
auto resolvedHash = get ( resolvedHashes , wantedOutput ) ;
if ( ( ! initialOutput ) | | ( ! resolvedHash ) )
throw Error (
" derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,resolve) " ,
worker . store . printStorePath ( drvPath ) , wantedOutput ) ;
2022-12-06 06:21:58 +02:00
2022-12-06 18:25:38 +02:00
auto realisation = [ & ] {
auto take1 = get ( resolvedResult . builtOutputs , DrvOutput { * resolvedHash , wantedOutput } ) ;
if ( take1 ) return * take1 ;
2022-12-06 06:21:58 +02:00
/* The above `get` should work. But sateful tracking of
outputs in resolvedResult , this can get out of sync with the
store , which is our actual source of truth . For now we just
check the store directly if it fails . */
2022-12-06 18:25:38 +02:00
auto take2 = worker . evalStore . queryRealisation ( DrvOutput { * resolvedHash , wantedOutput } ) ;
if ( take2 ) return * take2 ;
throw Error (
" derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,realisation) " ,
worker . store . printStorePath ( resolvedDrvGoal - > drvPath ) , wantedOutput ) ;
} ( ) ;
2022-03-30 17:31:01 +03:00
if ( drv - > type ( ) . isPure ( ) ) {
2022-12-06 18:25:38 +02:00
auto newRealisation = realisation ;
2022-05-04 08:44:32 +03:00
newRealisation . id = DrvOutput { initialOutput - > outputHash , wantedOutput } ;
2022-03-30 17:31:01 +03:00
newRealisation . signatures . clear ( ) ;
2022-03-11 14:23:23 +02:00
if ( ! drv - > type ( ) . isFixed ( ) )
2022-12-06 18:25:38 +02:00
newRealisation . dependentRealisations = drvOutputReferences ( worker . store , * drv , realisation . outPath ) ;
2022-03-30 17:31:01 +03:00
signRealisation ( newRealisation ) ;
worker . store . registerDrvOutput ( newRealisation ) ;
}
2022-12-06 18:25:38 +02:00
outputPaths . insert ( realisation . outPath ) ;
builtOutputs . emplace ( realisation . id , realisation ) ;
2022-03-30 17:31:01 +03:00
}
2022-03-08 20:50:46 +02:00
2022-03-30 17:31:01 +03:00
runPostBuildHook (
worker . store ,
* logger ,
drvPath ,
outputPaths
2021-01-27 11:03:05 +02:00
) ;
}
2022-03-30 17:31:01 +03:00
auto status = resolvedResult . status ;
if ( status = = BuildResult : : AlreadyValid )
status = BuildResult : : ResolvesToAlreadyValid ;
2021-12-13 17:56:44 +02:00
2022-03-08 20:50:46 +02:00
done ( status , std : : move ( builtOutputs ) ) ;
2020-10-11 19:19:10 +03:00
}
Add support for passing structured data to builders
Previously, all derivation attributes had to be coerced into strings
so that they could be passed via the environment. This is lossy
(e.g. lists get flattened, necessitating configureFlags
vs. configureFlagsArray, of which the latter cannot be specified as an
attribute), doesn't support attribute sets at all, and has size
limitations (necessitating hacks like passAsFile).
This patch adds a new mode for passing attributes to builders, namely
encoded as a JSON file ".attrs.json" in the current directory of the
builder. This mode is activated via the special attribute
__structuredAttrs = true;
(The idea is that one day we can set this in stdenv.mkDerivation.)
For example,
stdenv.mkDerivation {
__structuredAttrs = true;
name = "foo";
buildInputs = [ pkgs.hello pkgs.cowsay ];
doCheck = true;
hardening.format = false;
}
results in a ".attrs.json" file containing (sans the indentation):
{
"buildInputs": [],
"builder": "/nix/store/ygl61ycpr2vjqrx775l1r2mw1g2rb754-bash-4.3-p48/bin/bash",
"configureFlags": [
"--with-foo",
"--with-bar=1 2"
],
"doCheck": true,
"hardening": {
"format": false
},
"name": "foo",
"nativeBuildInputs": [
"/nix/store/10h6li26i7g6z3mdpvra09yyf10mmzdr-hello-2.10",
"/nix/store/4jnvjin0r6wp6cv1hdm5jbkx3vinlcvk-cowsay-3.03"
],
"propagatedBuildInputs": [],
"propagatedNativeBuildInputs": [],
"stdenv": "/nix/store/f3hw3p8armnzy6xhd4h8s7anfjrs15n2-stdenv",
"system": "x86_64-linux"
}
"passAsFile" is ignored in this mode because it's not needed - large
strings are included directly in the JSON representation.
It is up to the builder to do something with the JSON
representation. For example, in bash-based builders, lists/attrsets of
string values could be mapped to bash (associative) arrays.
2017-01-25 17:42:07 +02:00
2020-10-11 19:19:10 +03:00
HookReply DerivationGoal : : tryBuildHook ( )
{
if ( ! worker . tryBuildHook | | ! useDerivation ) return rpDecline ;
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2020-10-11 19:19:10 +03:00
if ( ! worker . hook )
worker . hook = std : : make_unique < HookInstance > ( ) ;
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2020-10-11 19:19:10 +03:00
try {
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
2020-10-11 19:19:10 +03:00
/* Send the request to the hook. */
worker . hook - > sink
< < " try "
< < ( worker . getNrLocalBuilds ( ) < settings . maxBuildJobs ? 1 : 0 )
< < drv - > platform
< < worker . store . printStorePath ( drvPath )
< < parsedDrv - > getRequiredSystemFeatures ( ) ;
worker . hook - > sink . flush ( ) ;
2017-01-25 13:00:28 +02:00
2020-10-11 19:19:10 +03:00
/* Read the first line of input, which should be a word indicating
whether the hook wishes to perform the build . */
2022-02-25 17:00:00 +02:00
std : : string reply ;
2020-10-11 19:19:10 +03:00
while ( true ) {
2021-02-05 13:11:50 +02:00
auto s = [ & ] ( ) {
try {
return readLine ( worker . hook - > fromHook . readSide . get ( ) ) ;
} catch ( Error & e ) {
e . addTrace ( { } , " while reading the response from the build hook " ) ;
2021-09-27 15:35:55 +03:00
throw ;
2021-02-05 13:11:50 +02:00
}
} ( ) ;
2020-10-11 19:19:10 +03:00
if ( handleJSONLogMessage ( s , worker . act , worker . hook - > activities , true ) )
;
2022-02-25 17:00:00 +02:00
else if ( s . substr ( 0 , 2 ) = = " # " ) {
reply = s . substr ( 2 ) ;
2020-10-11 19:19:10 +03:00
break ;
}
else {
s + = " \n " ;
writeToStderr ( s ) ;
}
}
2012-06-25 22:45:16 +03:00
2020-10-11 19:19:10 +03:00
debug ( " hook reply is '%1%' " , reply ) ;
2012-06-25 22:45:16 +03:00
2020-10-11 19:19:10 +03:00
if ( reply = = " decline " )
return rpDecline ;
else if ( reply = = " decline-permanently " ) {
worker . tryBuildHook = false ;
worker . hook = 0 ;
return rpDecline ;
}
else if ( reply = = " postpone " )
return rpPostpone ;
else if ( reply ! = " accept " )
throw Error ( " bad hook reply '%s' " , reply ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
} catch ( SysError & e ) {
if ( e . errNo = = EPIPE ) {
2021-01-21 01:27:36 +02:00
printError (
" build hook died unexpectedly: %s " ,
chomp ( drainFD ( worker . hook - > fromHook . readSide . get ( ) ) ) ) ;
2020-10-11 19:19:10 +03:00
worker . hook = 0 ;
return rpDecline ;
} else
throw ;
}
2018-10-22 22:49:56 +03:00
2020-10-11 19:19:10 +03:00
hook = std : : move ( worker . hook ) ;
2004-06-18 21:09:32 +03:00
2021-02-05 13:11:50 +02:00
try {
machineName = readLine ( hook - > fromHook . readSide . get ( ) ) ;
} catch ( Error & e ) {
e . addTrace ( { } , " while reading the machine name from the build hook " ) ;
2021-09-27 15:35:55 +03:00
throw ;
2021-02-05 13:11:50 +02:00
}
2012-05-30 17:12:29 +03:00
2020-10-11 19:19:10 +03:00
/* Tell the hook all the inputs that have to be copied to the
remote system . */
worker_proto : : write ( worker . store , hook - > sink , inputPaths ) ;
2004-06-18 21:09:32 +03:00
2020-10-11 19:19:10 +03:00
/* Tell the hooks the missing outputs that have to be copied back
from the remote system . */
{
2021-01-26 11:48:41 +02:00
StringSet missingOutputs ;
for ( auto & [ outputName , status ] : initialOutputs ) {
2021-01-26 10:35:10 +02:00
// XXX: Does this include known CA outputs?
if ( buildMode ! = bmCheck & & status . known & & status . known - > isValid ( ) ) continue ;
2021-01-26 11:48:41 +02:00
missingOutputs . insert ( outputName ) ;
2020-10-11 19:19:10 +03:00
}
2021-01-26 11:48:41 +02:00
worker_proto : : write ( worker . store , hook - > sink , missingOutputs ) ;
2020-10-11 19:19:10 +03:00
}
2004-06-29 12:41:50 +03:00
2020-10-11 19:19:10 +03:00
hook - > sink = FdSink ( ) ;
hook - > toHook . writeSide = - 1 ;
2020-08-07 22:09:26 +03:00
2020-10-11 19:19:10 +03:00
/* Create the log file and pipe. */
Path logFile = openLogFile ( ) ;
2009-03-25 23:05:42 +02:00
2022-02-21 17:28:23 +02:00
std : : set < int > fds ;
2020-10-11 19:19:10 +03:00
fds . insert ( hook - > fromHook . readSide . get ( ) ) ;
fds . insert ( hook - > builderOut . readSide . get ( ) ) ;
worker . childStarted ( shared_from_this ( ) , fds , false , false ) ;
2012-10-03 00:13:46 +03:00
2020-10-11 19:19:10 +03:00
return rpAccept ;
}
2012-10-03 17:38:09 +03:00
2015-07-20 04:15:45 +03:00
2022-03-08 20:50:46 +02:00
DrvOutputs DerivationGoal : : registerOutputs ( )
2021-02-26 17:20:33 +02:00
{
/* When using a build hook, the build hook can register the output
as valid ( by doing ` nix - store - - import ' ) . If so we don ' t have
to do anything here .
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 18:57:40 +03:00
2021-02-26 17:20:33 +02:00
We can only early return when the outputs are known a priori . For
floating content - addressed derivations this isn ' t the case .
*/
2022-03-08 20:50:46 +02:00
return assertPathValidity ( ) ;
2004-05-11 21:05:44 +03:00
}
2021-02-26 17:20:33 +02:00
Path DerivationGoal : : openLogFile ( )
2019-05-12 23:47:41 +03:00
{
2021-02-26 17:20:33 +02:00
logSize = 0 ;
2006-12-08 20:41:48 +02:00
2021-02-26 17:20:33 +02:00
if ( ! settings . keepLog ) return " " ;
2012-07-27 16:59:18 +03:00
2021-02-26 17:20:33 +02:00
auto baseName = std : : string ( baseNameOf ( worker . store . printStorePath ( drvPath ) ) ) ;
2010-08-25 23:44:28 +03:00
2021-02-26 17:20:33 +02:00
/* Create a log file. */
Path logDir ;
if ( auto localStore = dynamic_cast < LocalStore * > ( & worker . store ) )
logDir = localStore - > logDir ;
else
logDir = settings . nixLogDir ;
2022-02-25 17:00:00 +02:00
Path dir = fmt ( " %s/%s/%s/ " , logDir , LocalFSStore : : drvsLogDir , baseName . substr ( 0 , 2 ) ) ;
2021-02-26 17:20:33 +02:00
createDirs ( dir ) ;
2004-05-11 21:05:44 +03:00
2022-02-25 17:00:00 +02:00
Path logFileName = fmt ( " %s/%s%s " , dir , baseName . substr ( 2 ) ,
2021-02-26 17:20:33 +02:00
settings . compressLog ? " .bz2 " : " " ) ;
2014-11-24 17:44:35 +02:00
2021-02-26 17:20:33 +02:00
fdLogFile = open ( logFileName . c_str ( ) , O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC , 0666 ) ;
if ( ! fdLogFile ) throw SysError ( " creating log file '%1%' " , logFileName ) ;
2004-06-25 13:21:44 +03:00
2021-02-26 17:20:33 +02:00
logFileSink = std : : make_shared < FdSink > ( fdLogFile . get ( ) ) ;
2003-07-20 22:29:38 +03:00
2021-02-26 17:20:33 +02:00
if ( settings . compressLog )
logSink = std : : shared_ptr < CompressionSink > ( makeCompressionSink ( " bzip2 " , * logFileSink ) ) ;
else
logSink = logFileSink ;
2004-05-11 21:05:44 +03:00
2021-02-26 17:20:33 +02:00
return logFileName ;
}
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 18:57:40 +03:00
2005-01-31 12:27:25 +02:00
2021-02-26 17:20:33 +02:00
void DerivationGoal : : closeLogFile ( )
{
auto logSink2 = std : : dynamic_pointer_cast < CompressionSink > ( logSink ) ;
if ( logSink2 ) logSink2 - > finish ( ) ;
if ( logFileSink ) logFileSink - > flush ( ) ;
logSink = logFileSink = 0 ;
fdLogFile = - 1 ;
}
2005-01-25 12:55:33 +02:00
2021-02-26 17:20:33 +02:00
bool DerivationGoal : : isReadDesc ( int fd )
{
return fd = = hook - > builderOut . readSide . get ( ) ;
}
2012-11-26 18:15:09 +02:00
2022-02-25 17:00:00 +02:00
void DerivationGoal : : handleChildOutput ( int fd , std : : string_view data )
2021-02-26 17:20:33 +02:00
{
2022-02-20 17:02:44 +02:00
// local & `ssh://`-builds are dealt with here.
2022-01-31 23:05:28 +02:00
auto isWrittenToLog = isReadDesc ( fd ) ;
if ( isWrittenToLog )
2021-02-26 17:20:33 +02:00
{
logSize + = data . size ( ) ;
if ( settings . maxLogSize & & logSize > settings . maxLogSize ) {
killChild ( ) ;
done (
2022-03-08 20:50:46 +02:00
BuildResult : : LogLimitExceeded , { } ,
2021-02-26 17:20:33 +02:00
Error ( " %s killed after writing more than %d bytes of log output " ,
getName ( ) , settings . maxLogSize ) ) ;
return ;
2020-10-11 19:19:10 +03:00
}
2012-10-03 17:38:09 +03:00
2021-02-26 17:20:33 +02:00
for ( auto c : data )
if ( c = = ' \r ' )
currentLogLinePos = 0 ;
else if ( c = = ' \n ' )
flushLine ( ) ;
else {
if ( currentLogLinePos > = currentLogLine . size ( ) )
currentLogLine . resize ( currentLogLinePos + 1 ) ;
currentLogLine [ currentLogLinePos + + ] = c ;
2020-10-11 19:19:10 +03:00
}
2020-10-14 13:20:58 +03:00
2021-02-26 17:20:33 +02:00
if ( logSink ) ( * logSink ) ( data ) ;
2009-03-23 01:53:05 +02:00
}
2012-07-27 16:59:18 +03:00
2021-02-26 17:20:33 +02:00
if ( hook & & fd = = hook - > fromHook . readSide . get ( ) ) {
for ( auto c : data )
if ( c = = ' \n ' ) {
2022-02-19 23:34:50 +02:00
auto json = parseJSONMessage ( currentHookLine ) ;
if ( json ) {
auto s = handleJSONLogMessage ( * json , worker . act , hook - > activities , true ) ;
2022-02-20 17:02:44 +02:00
// ensure that logs from a builder using `ssh-ng://` as protocol
// are also available to `nix log`.
if ( s & & ! isWrittenToLog & & logSink & & ( * json ) [ " type " ] = = resBuildLogLine ) {
auto f = ( * json ) [ " fields " ] ;
( * logSink ) ( ( f . size ( ) > 0 ? f . at ( 0 ) . get < std : : string > ( ) : " " ) + " \n " ) ;
2022-01-31 23:05:28 +02:00
}
}
2021-02-26 17:20:33 +02:00
currentHookLine . clear ( ) ;
} else
currentHookLine + = c ;
2020-05-14 17:00:54 +03:00
}
2020-10-11 19:19:10 +03:00
}
2020-05-14 17:00:54 +03:00
2020-06-15 17:03:29 +03:00
2021-02-26 17:20:33 +02:00
void DerivationGoal : : handleEOF ( int fd )
{
if ( ! currentLogLine . empty ( ) ) flushLine ( ) ;
worker . wakeUp ( shared_from_this ( ) ) ;
2020-09-23 22:29:10 +03:00
}
2021-02-26 17:20:33 +02:00
void DerivationGoal : : flushLine ( )
2020-09-23 22:29:10 +03:00
{
2021-02-26 17:20:33 +02:00
if ( handleJSONLogMessage ( currentLogLine , * act , builderActivities , false ) )
;
2020-10-11 19:19:10 +03:00
2021-02-26 17:20:33 +02:00
else {
logTail . push_back ( currentLogLine ) ;
if ( logTail . size ( ) > settings . logLines ) logTail . pop_front ( ) ;
2012-10-03 00:13:46 +03:00
2021-02-26 17:20:33 +02:00
act - > result ( resBuildLogLine , currentLogLine ) ;
}
2012-10-03 00:13:46 +03:00
2021-02-26 17:20:33 +02:00
currentLogLine = " " ;
currentLogLinePos = 0 ;
}
2015-11-10 00:16:24 +02:00
2021-02-26 17:20:33 +02:00
std : : map < std : : string , std : : optional < StorePath > > DerivationGoal : : queryPartialDerivationOutputMap ( )
2017-08-31 17:02:36 +03:00
{
2022-03-30 17:31:01 +03:00
assert ( drv - > type ( ) . isPure ( ) ) ;
2022-03-18 02:36:52 +02:00
if ( ! useDerivation | | drv - > type ( ) . hasKnownOutputPaths ( ) ) {
2020-10-11 19:19:10 +03:00
std : : map < std : : string , std : : optional < StorePath > > res ;
for ( auto & [ name , output ] : drv - > outputs )
res . insert_or_assign ( name , output . path ( worker . store , drv - > name , name ) ) ;
return res ;
} else {
return worker . store . queryPartialDerivationOutputMap ( drvPath ) ;
}
2017-08-31 17:02:36 +03:00
}
2020-10-11 19:19:10 +03:00
OutputPathMap DerivationGoal : : queryDerivationOutputMap ( )
2003-07-29 13:43:12 +03:00
{
2022-03-30 17:31:01 +03:00
assert ( drv - > type ( ) . isPure ( ) ) ;
2022-03-18 02:36:52 +02:00
if ( ! useDerivation | | drv - > type ( ) . hasKnownOutputPaths ( ) ) {
2020-10-11 19:19:10 +03:00
OutputPathMap res ;
for ( auto & [ name , output ] : drv - > outputsAndOptPaths ( worker . store ) )
res . insert_or_assign ( name , * output . second ) ;
return res ;
} else {
return worker . store . queryDerivationOutputMap ( drvPath ) ;
2012-11-26 16:39:10 +02:00
}
2020-10-11 19:19:10 +03:00
}
2005-02-23 13:19:27 +02:00
2022-03-08 20:50:46 +02:00
std : : pair < bool , DrvOutputs > DerivationGoal : : checkPathValidity ( )
2020-10-11 19:19:10 +03:00
{
2022-03-30 17:31:01 +03:00
if ( ! drv - > type ( ) . isPure ( ) ) return { false , { } } ;
2020-10-11 19:19:10 +03:00
bool checkHash = buildMode = = bmRepair ;
2023-01-12 01:57:18 +02:00
auto wantedOutputsLeft = std : : visit ( overloaded {
[ & ] ( const OutputsSpec : : All & ) {
return StringSet { } ;
} ,
[ & ] ( const OutputsSpec : : Names & names ) {
2023-01-11 18:54:43 +02:00
return static_cast < StringSet > ( names ) ;
2023-01-12 01:57:18 +02:00
} ,
} , wantedOutputs . raw ( ) ) ;
2022-03-08 20:50:46 +02:00
DrvOutputs validOutputs ;
2020-10-11 19:19:10 +03:00
for ( auto & i : queryPartialDerivationOutputMap ( ) ) {
2022-05-04 08:44:32 +03:00
auto initialOutput = get ( initialOutputs , i . first ) ;
if ( ! initialOutput )
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
continue ;
auto & info = * initialOutput ;
2023-01-12 01:57:18 +02:00
info . wanted = wantedOutputs . contains ( i . first ) ;
2021-02-12 23:50:50 +02:00
if ( info . wanted )
wantedOutputsLeft . erase ( i . first ) ;
2020-10-11 19:19:10 +03:00
if ( i . second ) {
auto outputPath = * i . second ;
info . known = {
. path = outputPath ,
. status = ! worker . store . isValidPath ( outputPath )
? PathStatus : : Absent
: ! checkHash | | worker . pathContentsGood ( outputPath )
? PathStatus : : Valid
: PathStatus : : Corrupt ,
} ;
2005-02-23 13:19:27 +02:00
}
2022-05-04 08:44:32 +03:00
auto drvOutput = DrvOutput { info . outputHash , i . first } ;
2023-03-17 16:33:48 +02:00
if ( experimentalFeatureSettings . isEnabled ( Xp : : CaDerivations ) ) {
2021-04-22 18:55:36 +03:00
if ( auto real = worker . store . queryRealisation ( drvOutput ) ) {
2021-01-27 11:03:05 +02:00
info . known = {
. path = real - > outPath ,
. status = PathStatus : : Valid ,
} ;
2022-03-08 20:50:46 +02:00
} else if ( info . known & & info . known - > isValid ( ) ) {
// We know the output because it's a static output of the
2021-04-22 18:55:36 +03:00
// derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built
2022-03-08 20:50:46 +02:00
// without the `ca-derivations` experimental flag).
2021-04-22 18:55:36 +03:00
worker . store . registerDrvOutput (
2022-03-08 20:50:46 +02:00
Realisation {
2021-04-22 18:55:36 +03:00
drvOutput ,
info . known - > path ,
}
) ;
2021-01-27 11:03:05 +02:00
}
}
2022-03-08 20:50:46 +02:00
if ( info . wanted & & info . known & & info . known - > isValid ( ) )
validOutputs . emplace ( drvOutput , Realisation { drvOutput , info . known - > path } ) ;
2020-06-15 20:25:35 +03:00
}
2022-03-30 17:31:01 +03:00
2023-01-12 01:57:18 +02:00
// If we requested all the outputs, we are always fine.
2021-02-12 23:50:50 +02:00
// If we requested specific elements, the loop above removes all the valid
// ones, so any that are left must be invalid.
if ( ! wantedOutputsLeft . empty ( ) )
2021-03-01 17:07:09 +02:00
throw Error ( " derivation '%s' does not have wanted outputs %s " ,
worker . store . printStorePath ( drvPath ) ,
concatStringsSep ( " , " , quoteStrings ( wantedOutputsLeft ) ) ) ;
2022-03-08 20:50:46 +02:00
bool allValid = true ;
for ( auto & [ _ , status ] : initialOutputs ) {
if ( ! status . wanted ) continue ;
if ( ! status . known | | ! status . known - > isValid ( ) ) {
allValid = false ;
break ;
}
}
return { allValid , validOutputs } ;
}
DrvOutputs DerivationGoal : : assertPathValidity ( )
{
auto [ allValid , validOutputs ] = checkPathValidity ( ) ;
if ( ! allValid )
throw Error ( " some outputs are unexpectedly invalid " ) ;
return validOutputs ;
2003-07-29 13:43:12 +03:00
}
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 18:57:40 +03:00
2022-03-08 20:50:46 +02:00
void DerivationGoal : : done (
BuildResult : : Status status ,
DrvOutputs builtOutputs ,
std : : optional < Error > ex )
2020-10-11 19:19:10 +03:00
{
2022-03-08 20:50:46 +02:00
buildResult . status = status ;
2020-10-11 19:19:10 +03:00
if ( ex )
2022-04-08 12:48:30 +03:00
buildResult . errorMsg = fmt ( " %s " , normaltxt ( ex - > info ( ) . msg ) ) ;
2022-03-08 20:50:46 +02:00
if ( buildResult . status = = BuildResult : : TimedOut )
2020-10-11 19:19:10 +03:00
worker . timedOut = true ;
2022-03-08 20:50:46 +02:00
if ( buildResult . status = = BuildResult : : PermanentFailure )
2020-10-11 19:19:10 +03:00
worker . permanentFailure = true ;
2005-02-23 13:19:27 +02:00
2020-10-11 19:19:10 +03:00
mcExpectedBuilds . reset ( ) ;
mcRunningBuilds . reset ( ) ;
2005-02-23 13:19:27 +02:00
2022-03-08 20:50:46 +02:00
if ( buildResult . success ( ) ) {
assert ( ! builtOutputs . empty ( ) ) ;
buildResult . builtOutputs = std : : move ( builtOutputs ) ;
2020-10-11 19:19:10 +03:00
if ( status = = BuildResult : : Built )
worker . doneBuilds + + ;
} else {
if ( status ! = BuildResult : : DependencyFailed )
worker . failedBuilds + + ;
2020-06-15 20:25:35 +03:00
}
2012-07-27 16:59:18 +03:00
2020-10-11 19:19:10 +03:00
worker . updateProgress ( ) ;
2021-12-13 17:58:43 +02:00
auto traceBuiltOutputsFile = getEnv ( " _NIX_TRACE_BUILT_OUTPUTS " ) . value_or ( " " ) ;
if ( traceBuiltOutputsFile ! = " " ) {
std : : fstream fs ;
fs . open ( traceBuiltOutputsFile , std : : fstream : : out ) ;
2022-03-08 20:50:46 +02:00
fs < < worker . store . printStorePath ( drvPath ) < < " \t " < < buildResult . toString ( ) < < std : : endl ;
2021-12-13 17:58:43 +02:00
}
2022-03-30 17:31:01 +03:00
2022-12-07 13:58:58 +02:00
amDone ( buildResult . success ( ) ? ecSuccess : ecFailed , std : : move ( ex ) ) ;
2012-10-02 21:08:59 +03:00
}
2022-03-30 17:31:01 +03:00
void DerivationGoal : : waiteeDone ( GoalPtr waitee , ExitCode result )
{
Goal : : waiteeDone ( waitee , result ) ;
Make `KeyedBuildResult`, `BuildResult` like before, and fix bug another way
In https://github.com/NixOS/nix/pull/6311#discussion_r834863823, I
realized since derivation goals' wanted outputs can "grow" due to
overlapping dependencies (See `DerivationGoal::addWantedOutputs`, called
by `Worker::makeDerivationGoalCommon`), the previous bug fix had an
unfortunate side effect of causing more pointless rebuilds.
In paticular, we have this situation:
1. Goal made from `DerivedPath::Built { foo, {a} }`.
2. Goal gives on on substituting, starts building.
3. Goal made from `DerivedPath::Built { foo, {b} }`, in fact is just
modified original goal.
4. Though the goal had gotten as far as building, so all outputs were
going to be produced, `addWantedOutputs` no longer knows that and so
the goal is flagged to be restarted.
This might sound far-fetched with input-addressed drvs, where we usually
basically have all our goals "planned out" before we start doing
anything, but with CA derivation goals and especially RFC 92, where *drv
resolution* means goals are created after some building is completed, it
is more likely to happen.
So the first thing to do was restore the clearing of `wantedOutputs` we
used to do, and then filter the outputs in `buildPathsWithResults` to
only get the ones we care about.
But fix also has its own side effect in that the `DerivedPath` in the
`BuildResult` in `DerivationGoal` cannot be trusted; it is merely the
*first* `DerivedPath` for which this goal was originally created.
To remedy this, I made `BuildResult` be like it was before, and instead
made `KeyedBuildResult` be a subclass wit the path. Only
`buildPathsWithResults` returns `KeyedBuildResult`s, everything else
just becomes like it was before, where the "key" is unambiguous from
context.
I think separating the "primary key" field(s) from the other fields is
good practical in general anyways. (I would like to do the same thing
for `ValidPathInfo`.) Among other things, it allows constructions like
`std::map<Key, ThingWithKey>` where doesn't contain duplicate keys and
just precludes the possibility of those duplicate keys being out of
sync.
We might leverage the above someday to overload `buildPathsWithResults`
to take a *set* of return a *map* per the above.
-----
Unfortunately, we need to avoid C++20 strictness on designated
initializers.
(BTW
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2287r1.html
this offers some new syntax for this use-case. Hopefully this will be
adopted and we can eventually use it.)
No having that yet, maybe it would be better to not make
`KeyedBuildResult` a subclass to just avoid this.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2022-03-25 03:26:07 +02:00
if ( ! useDerivation ) return ;
auto & fullDrv = * dynamic_cast < Derivation * > ( drv . get ( ) ) ;
auto * dg = dynamic_cast < DerivationGoal * > ( & * waitee ) ;
if ( ! dg ) return ;
auto outputs = fullDrv . inputDrvs . find ( dg - > drvPath ) ;
if ( outputs = = fullDrv . inputDrvs . end ( ) ) return ;
for ( auto & outputName : outputs - > second ) {
auto buildResult = dg - > getBuildResult ( DerivedPath : : Built {
. drvPath = dg - > drvPath ,
. outputs = OutputsSpec : : Names { outputName } ,
} ) ;
if ( buildResult . success ( ) ) {
for ( auto & [ output , realisation ] : buildResult . builtOutputs ) {
2022-03-30 17:31:01 +03:00
inputDrvOutputs . insert_or_assign (
Make `KeyedBuildResult`, `BuildResult` like before, and fix bug another way
In https://github.com/NixOS/nix/pull/6311#discussion_r834863823, I
realized since derivation goals' wanted outputs can "grow" due to
overlapping dependencies (See `DerivationGoal::addWantedOutputs`, called
by `Worker::makeDerivationGoalCommon`), the previous bug fix had an
unfortunate side effect of causing more pointless rebuilds.
In paticular, we have this situation:
1. Goal made from `DerivedPath::Built { foo, {a} }`.
2. Goal gives on on substituting, starts building.
3. Goal made from `DerivedPath::Built { foo, {b} }`, in fact is just
modified original goal.
4. Though the goal had gotten as far as building, so all outputs were
going to be produced, `addWantedOutputs` no longer knows that and so
the goal is flagged to be restarted.
This might sound far-fetched with input-addressed drvs, where we usually
basically have all our goals "planned out" before we start doing
anything, but with CA derivation goals and especially RFC 92, where *drv
resolution* means goals are created after some building is completed, it
is more likely to happen.
So the first thing to do was restore the clearing of `wantedOutputs` we
used to do, and then filter the outputs in `buildPathsWithResults` to
only get the ones we care about.
But fix also has its own side effect in that the `DerivedPath` in the
`BuildResult` in `DerivationGoal` cannot be trusted; it is merely the
*first* `DerivedPath` for which this goal was originally created.
To remedy this, I made `BuildResult` be like it was before, and instead
made `KeyedBuildResult` be a subclass wit the path. Only
`buildPathsWithResults` returns `KeyedBuildResult`s, everything else
just becomes like it was before, where the "key" is unambiguous from
context.
I think separating the "primary key" field(s) from the other fields is
good practical in general anyways. (I would like to do the same thing
for `ValidPathInfo`.) Among other things, it allows constructions like
`std::map<Key, ThingWithKey>` where doesn't contain duplicate keys and
just precludes the possibility of those duplicate keys being out of
sync.
We might leverage the above someday to overload `buildPathsWithResults`
to take a *set* of return a *map* per the above.
-----
Unfortunately, we need to avoid C++20 strictness on designated
initializers.
(BTW
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2287r1.html
this offers some new syntax for this use-case. Hopefully this will be
adopted and we can eventually use it.)
No having that yet, maybe it would be better to not make
`KeyedBuildResult` a subclass to just avoid this.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2022-03-25 03:26:07 +02:00
{ dg - > drvPath , output . outputName } ,
2022-03-30 17:31:01 +03:00
realisation . outPath ) ;
Make `KeyedBuildResult`, `BuildResult` like before, and fix bug another way
In https://github.com/NixOS/nix/pull/6311#discussion_r834863823, I
realized since derivation goals' wanted outputs can "grow" due to
overlapping dependencies (See `DerivationGoal::addWantedOutputs`, called
by `Worker::makeDerivationGoalCommon`), the previous bug fix had an
unfortunate side effect of causing more pointless rebuilds.
In paticular, we have this situation:
1. Goal made from `DerivedPath::Built { foo, {a} }`.
2. Goal gives on on substituting, starts building.
3. Goal made from `DerivedPath::Built { foo, {b} }`, in fact is just
modified original goal.
4. Though the goal had gotten as far as building, so all outputs were
going to be produced, `addWantedOutputs` no longer knows that and so
the goal is flagged to be restarted.
This might sound far-fetched with input-addressed drvs, where we usually
basically have all our goals "planned out" before we start doing
anything, but with CA derivation goals and especially RFC 92, where *drv
resolution* means goals are created after some building is completed, it
is more likely to happen.
So the first thing to do was restore the clearing of `wantedOutputs` we
used to do, and then filter the outputs in `buildPathsWithResults` to
only get the ones we care about.
But fix also has its own side effect in that the `DerivedPath` in the
`BuildResult` in `DerivationGoal` cannot be trusted; it is merely the
*first* `DerivedPath` for which this goal was originally created.
To remedy this, I made `BuildResult` be like it was before, and instead
made `KeyedBuildResult` be a subclass wit the path. Only
`buildPathsWithResults` returns `KeyedBuildResult`s, everything else
just becomes like it was before, where the "key" is unambiguous from
context.
I think separating the "primary key" field(s) from the other fields is
good practical in general anyways. (I would like to do the same thing
for `ValidPathInfo`.) Among other things, it allows constructions like
`std::map<Key, ThingWithKey>` where doesn't contain duplicate keys and
just precludes the possibility of those duplicate keys being out of
sync.
We might leverage the above someday to overload `buildPathsWithResults`
to take a *set* of return a *map* per the above.
-----
Unfortunately, we need to avoid C++20 strictness on designated
initializers.
(BTW
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2287r1.html
this offers some new syntax for this use-case. Hopefully this will be
adopted and we can eventually use it.)
No having that yet, maybe it would be better to not make
`KeyedBuildResult` a subclass to just avoid this.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2022-03-25 03:26:07 +02:00
}
}
}
2022-03-30 17:31:01 +03:00
}
2006-09-05 00:06:23 +03:00
}