Merge branch 'master' into error-format

This commit is contained in:
Ben Burdette 2020-04-06 10:00:00 -06:00
commit 216263c36f
73 changed files with 1007 additions and 432 deletions

2
.gitignore vendored
View file

@ -47,7 +47,7 @@ perl/Makefile.config
/src/libexpr/nix.tbl /src/libexpr/nix.tbl
# /src/libstore/ # /src/libstore/
/src/libstore/*.gen.hh *.gen.*
/src/nix/nix /src/nix/nix

View file

@ -37,6 +37,7 @@ prefix = @prefix@
sandbox_shell = @sandbox_shell@ sandbox_shell = @sandbox_shell@
storedir = @storedir@ storedir = @storedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
system = @system@
doc_generate = @doc_generate@ doc_generate = @doc_generate@
xmllint = @xmllint@ xmllint = @xmllint@
xsltproc = @xsltproc@ xsltproc = @xsltproc@

View file

@ -360,7 +360,6 @@ EOF
<arg choice='plain'><option>--print-roots</option></arg> <arg choice='plain'><option>--print-roots</option></arg>
<arg choice='plain'><option>--print-live</option></arg> <arg choice='plain'><option>--print-live</option></arg>
<arg choice='plain'><option>--print-dead</option></arg> <arg choice='plain'><option>--print-dead</option></arg>
<arg choice='plain'><option>--delete</option></arg>
</group> </group>
<arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg> <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
@ -407,14 +406,6 @@ the Nix store not reachable via file system references from a set of
</varlistentry> </varlistentry>
<varlistentry><term><option>--delete</option></term>
<listitem><para>This operation performs an actual garbage
collection. All dead paths are removed from the
store. This is the default.</para></listitem>
</varlistentry>
</variablelist> </variablelist>
<para>By default, all unreachable paths are deleted. The following <para>By default, all unreachable paths are deleted. The following
@ -444,10 +435,10 @@ and <link
linkend="conf-keep-derivations"><literal>keep-derivations</literal></link> linkend="conf-keep-derivations"><literal>keep-derivations</literal></link>
variables in the Nix configuration file.</para> variables in the Nix configuration file.</para>
<para>With <option>--delete</option>, the collector prints the total <para>By default, the collector prints the total number of freed bytes
number of freed bytes when it finishes (or when it is interrupted). when it finishes (or when it is interrupted). With
With <option>--print-dead</option>, it prints the number of bytes that <option>--print-dead</option>, it prints the number of bytes that would
would be freed.</para> be freed.</para>
</refsection> </refsection>
@ -1148,7 +1139,7 @@ the information that Nix considers important. For instance,
timestamps are elided because all files in the Nix store have their timestamps are elided because all files in the Nix store have their
timestamp set to 0 anyway. Likewise, all permissions are left out timestamp set to 0 anyway. Likewise, all permissions are left out
except for the execute bit, because all files in the Nix store have except for the execute bit, because all files in the Nix store have
644 or 755 permission.</para> 444 or 555 permission.</para>
<para>Also, a NAR archive is <emphasis>canonical</emphasis>, meaning <para>Also, a NAR archive is <emphasis>canonical</emphasis>, meaning
that “equal” paths always produce the same NAR archive. For instance, that “equal” paths always produce the same NAR archive. For instance,

View file

@ -6,9 +6,11 @@ dist-files += configure config.h.in perl/configure
clean-files += Makefile.config clean-files += Makefile.config
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr -I src/nix -Wno-deprecated-declarations GLOBAL_CXXFLAGS += -Wno-deprecated-declarations
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh), \ $(foreach i, config.h $(call rwildcard, src/lib*, *.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
$(GCH) $(PCH): src/libutil/util.hh config.h $(GCH) $(PCH): src/libutil/util.hh config.h
GCH_CXXFLAGS = -I src/libutil

View file

@ -8,14 +8,14 @@ GCH = $(buildprefix)precompiled-headers.h.gch
$(GCH): precompiled-headers.h $(GCH): precompiled-headers.h
@rm -f $@ @rm -f $@
@mkdir -p "$(dir $@)" @mkdir -p "$(dir $@)"
$(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS)
PCH = $(buildprefix)precompiled-headers.h.pch PCH = $(buildprefix)precompiled-headers.h.pch
$(PCH): precompiled-headers.h $(PCH): precompiled-headers.h
@rm -f $@ @rm -f $@
@mkdir -p "$(dir $@)" @mkdir -p "$(dir $@)"
$(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS)
clean-files += $(GCH) $(PCH) clean-files += $(GCH) $(PCH)

View file

@ -12,11 +12,9 @@ let
builtins.readFile ./.version builtins.readFile ./.version
+ (if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}"); + (if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}");
jobs = rec {
# Create a "vendor" directory that contains the crates listed in # Create a "vendor" directory that contains the crates listed in
# Cargo.lock. This allows Nix to be built without network access. # Cargo.lock. This allows Nix to be built without network access.
vendoredCrates = vendoredCrates' =
let let
lockFile = builtins.fromTOML (builtins.readFile nix-rust/Cargo.lock); lockFile = builtins.fromTOML (builtins.readFile nix-rust/Cargo.lock);
@ -57,6 +55,20 @@ let
'') files)} '') files)}
''; '';
jobs = rec {
vendoredCrates =
with pkgs;
runCommand "vendored-crates" {}
''
mkdir -p $out/nix-support
name=nix-vendored-crates-${version}
fn=$out/$name.tar.xz
tar cvfJ $fn -C ${vendoredCrates'} vendor \
--owner=0 --group=0 --mode=u+rw,uga+r \
--transform "s,vendor,$name,"
echo "file crates-tarball $fn" >> $out/nix-support/hydra-build-products
'';
build = pkgs.lib.genAttrs systems (system: build = pkgs.lib.genAttrs systems (system:
@ -89,7 +101,7 @@ let
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
''} ''}
ln -sfn ${vendoredCrates}/vendor/ nix-rust/vendor ln -sfn ${vendoredCrates'}/vendor/ nix-rust/vendor
(cd perl; autoreconf --install --force --verbose) (cd perl; autoreconf --install --force --verbose)
''; '';

View file

@ -13,12 +13,12 @@ set -o pipefail
# however tracking which bits came from which would be impossible. # however tracking which bits came from which would be impossible.
readonly ESC='\033[0m' readonly ESC='\033[0m'
readonly BOLD='\033[38;1m' readonly BOLD='\033[1m'
readonly BLUE='\033[38;34m' readonly BLUE='\033[34m'
readonly BLUE_UL='\033[38;4;34m' readonly BLUE_UL='\033[4;34m'
readonly GREEN='\033[38;32m' readonly GREEN='\033[32m'
readonly GREEN_UL='\033[38;4;32m' readonly GREEN_UL='\033[4;32m'
readonly RED='\033[38;31m' readonly RED='\033[31m'
readonly NIX_USER_COUNT="32" readonly NIX_USER_COUNT="32"
readonly NIX_BUILD_GROUP_ID="30000" readonly NIX_BUILD_GROUP_ID="30000"
@ -567,7 +567,7 @@ install_from_extracted_nix() {
cd "$EXTRACTED_NIX_PATH" cd "$EXTRACTED_NIX_PATH"
_sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \ _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
rsync -rlpt ./store/* "$NIX_ROOT/store/" rsync -rlpt --chmod=-w ./store/* "$NIX_ROOT/store/"
if [ -d "$NIX_INSTALLED_NIX" ]; then if [ -d "$NIX_INSTALLED_NIX" ]; then
echo " Alright! We have our first nix at $NIX_INSTALLED_NIX" echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"

View file

@ -36,6 +36,7 @@ tarball="$tmpDir/$(basename "$tmpDir/nix-@nixVersion@-$system.tar.xz")"
require_util curl "download the binary tarball" require_util curl "download the binary tarball"
require_util tar "unpack the binary tarball" require_util tar "unpack the binary tarball"
require_util xz "unpack the binary tarball"
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..." echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
curl -L "$url" -o "$tarball" || oops "failed to download '$url'" curl -L "$url" -o "$tarball" || oops "failed to download '$url'"

View file

@ -17,7 +17,7 @@
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "local-store.hh" #include "local-store.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
using namespace nix; using namespace nix;
using std::cin; using std::cin;

View file

@ -32,15 +32,13 @@ static Strings parseAttrPath(const string & s)
} }
Value * findAlongAttrPath(EvalState & state, const string & attrPath, std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Value & vIn) Bindings & autoArgs, Value & vIn)
{ {
Strings tokens = parseAttrPath(attrPath); Strings tokens = parseAttrPath(attrPath);
Error attrError =
Error(format("attribute selection path '%1%' does not match expression") % attrPath);
Value * v = &vIn; Value * v = &vIn;
Pos pos = noPos;
for (auto & attr : tokens) { for (auto & attr : tokens) {
@ -70,8 +68,9 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
if (a == v->attrs->end()) if (a == v->attrs->end())
throw Error(format("attribute '%1%' in selection path '%2%' not found") % attr % attrPath); throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath);
v = &*a->value; v = &*a->value;
pos = *a->pos;
} }
else if (apType == apIndex) { else if (apType == apIndex) {
@ -82,14 +81,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
% attrPath % showType(*v)); % attrPath % showType(*v));
if (attrIndex >= v->listSize()) if (attrIndex >= v->listSize())
throw Error(format("list index %1% in selection path '%2%' is out of range") % attrIndex % attrPath); throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
v = v->listElems()[attrIndex]; v = v->listElems()[attrIndex];
pos = noPos;
} }
} }
return v; return {v, pos};
} }
@ -98,9 +98,9 @@ Pos findDerivationFilename(EvalState & state, Value & v, std::string what)
Value * v2; Value * v2;
try { try {
auto dummyArgs = state.allocBindings(0); auto dummyArgs = state.allocBindings(0);
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v); v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
} catch (Error &) { } catch (Error &) {
throw Error("package '%s' has no source location information", what); throw NoPositionInfo("package '%s' has no source location information", what);
} }
// FIXME: is it possible to extract the Pos object instead of doing this // FIXME: is it possible to extract the Pos object instead of doing this

View file

@ -7,7 +7,10 @@
namespace nix { namespace nix {
Value * findAlongAttrPath(EvalState & state, const string & attrPath, MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Value & vIn); Bindings & autoArgs, Value & vIn);
/* Heuristic to find the filename and lineno or a nix value. */ /* Heuristic to find the filename and lineno or a nix value. */

View file

@ -43,6 +43,12 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
} }
Value * EvalState::allocAttr(Value & vAttrs, const std::string & name)
{
return allocAttr(vAttrs, symbols.create(name));
}
void Bindings::sort() void Bindings::sort()
{ {
std::sort(begin(), end()); std::sort(begin(), end());

View file

@ -272,6 +272,7 @@ public:
Env & allocEnv(size_t size); Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, const Symbol & name); Value * allocAttr(Value & vAttrs, const Symbol & name);
Value * allocAttr(Value & vAttrs, const std::string & name);
Bindings * allocBindings(size_t capacity); Bindings * allocBindings(size_t capacity);
@ -367,7 +368,7 @@ struct EvalSettings : Config
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."}; "Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls", Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
"Emit log messages for each function entry and exit at the 'vomit' log level (-vvvv)"}; "Emit log messages for each function entry and exit at the 'vomit' log level (-vvvv)."};
}; };
extern EvalSettings evalSettings; extern EvalSettings evalSettings;

View file

@ -1,4 +1,5 @@
#include "function-trace.hh" #include "function-trace.hh"
#include "logging.hh"
namespace nix { namespace nix {

View file

@ -6,6 +6,8 @@ libexpr_DIR := $(d)
libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libmain -I src/libexpr
libexpr_LIBS = libutil libstore libnixrust libexpr_LIBS = libutil libstore libnixrust
libexpr_LDFLAGS = libexpr_LDFLAGS =

View file

@ -1,7 +1,7 @@
#include "primops.hh" #include "primops.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
#include "cpptoml/cpptoml.h" #include "../../cpptoml/cpptoml.h"
namespace nix { namespace nix {

View file

@ -6,6 +6,8 @@ libmain_DIR := $(d)
libmain_SOURCES := $(wildcard $(d)/*.cc) libmain_SOURCES := $(wildcard $(d)/*.cc)
libmain_CXXFLAGS += -I src/libutil -I src/libstore
libmain_LDFLAGS = $(OPENSSL_LIBS) libmain_LDFLAGS = $(OPENSSL_LIBS)
libmain_LIBS = libstore libutil libmain_LIBS = libstore libutil

View file

@ -6,6 +6,7 @@
#include "archive.hh" #include "archive.hh"
#include "affinity.hh" #include "affinity.hh"
#include "builtins.hh" #include "builtins.hh"
#include "builtins/buildenv.hh"
#include "download.hh" #include "download.hh"
#include "finally.hh" #include "finally.hh"
#include "compression.hh" #include "compression.hh"
@ -1397,7 +1398,7 @@ void DerivationGoal::tryToBuild()
few seconds and then retry this goal. */ few seconds and then retry this goal. */
PathSet lockFiles; PathSet lockFiles;
for (auto & outPath : drv->outputPaths()) for (auto & outPath : drv->outputPaths())
lockFiles.insert(worker.store.toRealPath(worker.store.printStorePath(outPath))); lockFiles.insert(worker.store.Store::toRealPath(outPath));
if (!outputLocks.lockPaths(lockFiles, "", false)) { if (!outputLocks.lockPaths(lockFiles, "", false)) {
worker.waitForAWhile(shared_from_this()); worker.waitForAWhile(shared_from_this());
@ -1428,7 +1429,7 @@ void DerivationGoal::tryToBuild()
for (auto & i : drv->outputs) { for (auto & i : drv->outputs) {
if (worker.store.isValidPath(i.second.path)) continue; if (worker.store.isValidPath(i.second.path)) continue;
debug("removing invalid path '%s'", worker.store.printStorePath(i.second.path)); debug("removing invalid path '%s'", worker.store.printStorePath(i.second.path));
deletePath(worker.store.toRealPath(worker.store.printStorePath(i.second.path))); deletePath(worker.store.Store::toRealPath(i.second.path));
} }
/* Don't do a remote build if the derivation has the attribute /* Don't do a remote build if the derivation has the attribute
@ -1685,7 +1686,7 @@ void DerivationGoal::buildDone()
/* Delete unused redirected outputs (when doing hash rewriting). */ /* Delete unused redirected outputs (when doing hash rewriting). */
for (auto & i : redirectedOutputs) for (auto & i : redirectedOutputs)
deletePath(worker.store.toRealPath(worker.store.printStorePath(i.second))); deletePath(worker.store.Store::toRealPath(i.second));
/* Delete the chroot (if we were using one). */ /* Delete the chroot (if we were using one). */
autoDelChroot.reset(); /* this runs the destructor */ autoDelChroot.reset(); /* this runs the destructor */
@ -1904,7 +1905,7 @@ void DerivationGoal::startBuilder()
concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()),
worker.store.printStorePath(drvPath), worker.store.printStorePath(drvPath),
settings.thisSystem, settings.thisSystem,
concatStringsSep(", ", settings.systemFeatures)); concatStringsSep<StringSet>(", ", settings.systemFeatures));
if (drv->isBuiltin()) if (drv->isBuiltin())
preloadNSS(); preloadNSS();
@ -2071,7 +2072,7 @@ void DerivationGoal::startBuilder()
environment using bind-mounts. We put it in the Nix store environment using bind-mounts. We put it in the Nix store
to ensure that we can create hard-links to non-directory to ensure that we can create hard-links to non-directory
inputs in the fake Nix store in the chroot (see below). */ inputs in the fake Nix store in the chroot (see below). */
chrootRootDir = worker.store.toRealPath(worker.store.printStorePath(drvPath)) + ".chroot"; chrootRootDir = worker.store.Store::toRealPath(drvPath) + ".chroot";
deletePath(chrootRootDir); deletePath(chrootRootDir);
/* Clean up the chroot directory automatically. */ /* Clean up the chroot directory automatically. */
@ -2464,7 +2465,7 @@ void DerivationGoal::initTmpDir() {
auto hash = hashString(htSHA256, i.first); auto hash = hashString(htSHA256, i.first);
string fn = ".attr-" + hash.to_string(Base32, false); string fn = ".attr-" + hash.to_string(Base32, false);
Path p = tmpDir + "/" + fn; Path p = tmpDir + "/" + fn;
writeFile(p, i.second); writeFile(p, rewriteStrings(i.second, inputRewrites));
chownToBuilder(p); chownToBuilder(p);
env[i.first + "Path"] = tmpDirInSandbox + "/" + fn; env[i.first + "Path"] = tmpDirInSandbox + "/" + fn;
} }
@ -2550,7 +2551,7 @@ static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
void DerivationGoal::writeStructuredAttrs() void DerivationGoal::writeStructuredAttrs()
{ {
auto & structuredAttrs = parsedDrv->getStructuredAttrs(); auto structuredAttrs = parsedDrv->getStructuredAttrs();
if (!structuredAttrs) return; if (!structuredAttrs) return;
auto json = *structuredAttrs; auto json = *structuredAttrs;
@ -2916,7 +2917,7 @@ void DerivationGoal::addDependency(const StorePath & path)
#if __linux__ #if __linux__
Path source = worker.store.toRealPath(worker.store.printStorePath(path)); Path source = worker.store.Store::toRealPath(path);
Path target = chrootRootDir + worker.store.printStorePath(path); Path target = chrootRootDir + worker.store.printStorePath(path);
debug("bind-mounting %s -> %s", target, source); debug("bind-mounting %s -> %s", target, source);
@ -3578,7 +3579,7 @@ void DerivationGoal::registerOutputs()
if (needsHashRewrite()) { if (needsHashRewrite()) {
auto r = redirectedOutputs.find(i.second.path); auto r = redirectedOutputs.find(i.second.path);
if (r != redirectedOutputs.end()) { if (r != redirectedOutputs.end()) {
auto redirected = worker.store.toRealPath(worker.store.printStorePath(r->second)); auto redirected = worker.store.Store::toRealPath(r->second);
if (buildMode == bmRepair if (buildMode == bmRepair
&& redirectedBadOutputs.count(i.second.path) && redirectedBadOutputs.count(i.second.path)
&& pathExists(redirected)) && pathExists(redirected))
@ -3671,7 +3672,7 @@ void DerivationGoal::registerOutputs()
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s", BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
worker.store.printStorePath(dest), h.to_string(SRI), h2.to_string(SRI))); worker.store.printStorePath(dest), h.to_string(SRI), h2.to_string(SRI)));
Path actualDest = worker.store.toRealPath(worker.store.printStorePath(dest)); Path actualDest = worker.store.Store::toRealPath(dest);
if (worker.store.isValidPath(dest)) if (worker.store.isValidPath(dest))
std::rethrow_exception(delayedException); std::rethrow_exception(delayedException);

View file

@ -6,7 +6,6 @@ namespace nix {
// TODO: make pluggable. // TODO: make pluggable.
void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData); void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData);
void builtinBuildenv(const BasicDerivation & drv);
void builtinUnpackChannel(const BasicDerivation & drv); void builtinUnpackChannel(const BasicDerivation & drv);
} }

View file

@ -1,4 +1,4 @@
#include "builtins.hh" #include "buildenv.hh"
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -7,16 +7,14 @@
namespace nix { namespace nix {
typedef std::map<Path,int> Priorities; struct State
{
// FIXME: change into local variables. std::map<Path, int> priorities;
unsigned long symlinks = 0;
static Priorities priorities; };
static unsigned long symlinks;
/* For each activated package, create symlinks */ /* For each activated package, create symlinks */
static void createLinks(const Path & srcDir, const Path & dstDir, int priority) static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority)
{ {
DirEntries srcFiles; DirEntries srcFiles;
@ -67,7 +65,7 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
auto res = lstat(dstFile.c_str(), &dstSt); auto res = lstat(dstFile.c_str(), &dstSt);
if (res == 0) { if (res == 0) {
if (S_ISDIR(dstSt.st_mode)) { if (S_ISDIR(dstSt.st_mode)) {
createLinks(srcFile, dstFile, priority); createLinks(state, srcFile, dstFile, priority);
continue; continue;
} else if (S_ISLNK(dstSt.st_mode)) { } else if (S_ISLNK(dstSt.st_mode)) {
auto target = canonPath(dstFile, true); auto target = canonPath(dstFile, true);
@ -77,8 +75,8 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
throw SysError(format("unlinking '%1%'") % dstFile); throw SysError(format("unlinking '%1%'") % dstFile);
if (mkdir(dstFile.c_str(), 0755) == -1) if (mkdir(dstFile.c_str(), 0755) == -1)
throw SysError(format("creating directory '%1%'")); throw SysError(format("creating directory '%1%'"));
createLinks(target, dstFile, priorities[dstFile]); createLinks(state, target, dstFile, state.priorities[dstFile]);
createLinks(srcFile, dstFile, priority); createLinks(state, srcFile, dstFile, priority);
continue; continue;
} }
} else if (errno != ENOENT) } else if (errno != ENOENT)
@ -90,7 +88,7 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
auto res = lstat(dstFile.c_str(), &dstSt); auto res = lstat(dstFile.c_str(), &dstSt);
if (res == 0) { if (res == 0) {
if (S_ISLNK(dstSt.st_mode)) { if (S_ISLNK(dstSt.st_mode)) {
auto prevPriority = priorities[dstFile]; auto prevPriority = state.priorities[dstFile];
if (prevPriority == priority) if (prevPriority == priority)
throw Error( throw Error(
"packages '%1%' and '%2%' have the same priority %3%; " "packages '%1%' and '%2%' have the same priority %3%; "
@ -109,22 +107,20 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
} }
createSymlink(srcFile, dstFile); createSymlink(srcFile, dstFile);
priorities[dstFile] = priority; state.priorities[dstFile] = priority;
symlinks++; state.symlinks++;
} }
} }
typedef std::set<Path> FileProp; void buildProfile(const Path & out, Packages && pkgs)
static FileProp done;
static FileProp postponed = FileProp{};
static Path out;
static void addPkg(const Path & pkgDir, int priority)
{ {
State state;
std::set<Path> done, postponed;
auto addPkg = [&](const Path & pkgDir, int priority) {
if (!done.insert(pkgDir).second) return; if (!done.insert(pkgDir).second) return;
createLinks(pkgDir, out, priority); createLinks(state, pkgDir, out, priority);
try { try {
for (const auto & p : tokenizeString<std::vector<string>>( for (const auto & p : tokenizeString<std::vector<string>>(
@ -134,43 +130,8 @@ static void addPkg(const Path & pkgDir, int priority)
} catch (SysError & e) { } catch (SysError & e) {
if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw; if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw;
} }
}
struct Package {
Path path;
bool active;
int priority;
Package(Path path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
}; };
typedef std::vector<Package> Packages;
void builtinBuildenv(const BasicDerivation & drv)
{
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
};
out = getAttr("out");
createDirs(out);
/* Convert the stuff we get from the environment back into a
* coherent data type. */
Packages pkgs;
auto derivations = tokenizeString<Strings>(getAttr("derivations"));
while (!derivations.empty()) {
/* !!! We're trusting the caller to structure derivations env var correctly */
auto active = derivations.front(); derivations.pop_front();
auto priority = stoi(derivations.front()); derivations.pop_front();
auto outputs = stoi(derivations.front()); derivations.pop_front();
for (auto n = 0; n < outputs; n++) {
auto path = derivations.front(); derivations.pop_front();
pkgs.emplace_back(path, active != "false", priority);
}
}
/* Symlink to the packages that have been installed explicitly by the /* Symlink to the packages that have been installed explicitly by the
* user. Process in priority order to reduce unnecessary * user. Process in priority order to reduce unnecessary
* symlink/unlink steps. * symlink/unlink steps.
@ -189,13 +150,42 @@ void builtinBuildenv(const BasicDerivation & drv)
*/ */
auto priorityCounter = 1000; auto priorityCounter = 1000;
while (!postponed.empty()) { while (!postponed.empty()) {
auto pkgDirs = postponed; std::set<Path> pkgDirs;
postponed = FileProp{}; postponed.swap(pkgDirs);
for (const auto & pkgDir : pkgDirs) for (const auto & pkgDir : pkgDirs)
addPkg(pkgDir, priorityCounter++); addPkg(pkgDir, priorityCounter++);
} }
printError("created %d symlinks in user environment", symlinks); debug("created %d symlinks in user environment", state.symlinks);
}
void builtinBuildenv(const BasicDerivation & drv)
{
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
};
Path out = getAttr("out");
createDirs(out);
/* Convert the stuff we get from the environment back into a
* coherent data type. */
Packages pkgs;
auto derivations = tokenizeString<Strings>(getAttr("derivations"));
while (!derivations.empty()) {
/* !!! We're trusting the caller to structure derivations env var correctly */
auto active = derivations.front(); derivations.pop_front();
auto priority = stoi(derivations.front()); derivations.pop_front();
auto outputs = stoi(derivations.front()); derivations.pop_front();
for (auto n = 0; n < outputs; n++) {
auto path = derivations.front(); derivations.pop_front();
pkgs.emplace_back(path, active != "false", priority);
}
}
buildProfile(out, std::move(pkgs));
createSymlink(getAttr("manifest"), out + "/manifest.nix"); createSymlink(getAttr("manifest"), out + "/manifest.nix");
} }

View file

@ -0,0 +1,21 @@
#pragma once
#include "derivations.hh"
#include "store-api.hh"
namespace nix {
struct Package {
Path path;
bool active;
int priority;
Package(Path path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
};
typedef std::vector<Package> Packages;
void buildProfile(const Path & out, Packages && pkgs);
void builtinBuildenv(const BasicDerivation & drv);
}

View file

@ -65,7 +65,7 @@ bool BasicDerivation::isBuiltin() const
StorePath writeDerivation(ref<Store> store, StorePath writeDerivation(ref<Store> store,
const Derivation & drv, const string & name, RepairFlag repair) const Derivation & drv, std::string_view name, RepairFlag repair)
{ {
auto references = cloneStorePathSet(drv.inputSrcs); auto references = cloneStorePathSet(drv.inputSrcs);
for (auto & i : drv.inputDrvs) for (auto & i : drv.inputDrvs)
@ -73,8 +73,8 @@ StorePath writeDerivation(ref<Store> store,
/* Note that the outputs of a derivation are *not* references /* Note that the outputs of a derivation are *not* references
(that can be missing (of course) and should not necessarily be (that can be missing (of course) and should not necessarily be
held during a garbage collection). */ held during a garbage collection). */
string suffix = name + drvExtension; auto suffix = std::string(name) + drvExtension;
string contents = drv.unparse(*store, false); auto contents = drv.unparse(*store, false);
return settings.readOnlyMode return settings.readOnlyMode
? store->computeStorePathForText(suffix, contents, references) ? store->computeStorePathForText(suffix, contents, references)
: store->addTextToStore(suffix, contents, references, repair); : store->addTextToStore(suffix, contents, references, repair);

View file

@ -79,7 +79,7 @@ class Store;
/* Write a derivation to the Nix store, and return its path. */ /* Write a derivation to the Nix store, and return its path. */
StorePath writeDerivation(ref<Store> store, StorePath writeDerivation(ref<Store> store,
const Derivation & drv, const string & name, RepairFlag repair = NoRepair); const Derivation & drv, std::string_view name, RepairFlag repair = NoRepair);
/* Read a derivation from a file. */ /* Read a derivation from a file. */
Derivation readDerivation(const Store & store, const Path & drvPath); Derivation readDerivation(const Store & store, const Path & drvPath);

View file

@ -390,6 +390,7 @@ struct CurlDownloader : public Downloader
case CURLE_SSL_CACERT_BADFILE: case CURLE_SSL_CACERT_BADFILE:
case CURLE_TOO_MANY_REDIRECTS: case CURLE_TOO_MANY_REDIRECTS:
case CURLE_WRITE_ERROR: case CURLE_WRITE_ERROR:
case CURLE_UNSUPPORTED_PROTOCOL:
err = Misc; err = Misc;
break; break;
default: // Shut up warnings default: // Shut up warnings

View file

@ -20,13 +20,6 @@ namespace nix {
must be deleted and recreated on startup.) */ must be deleted and recreated on startup.) */
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket" #define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
/* chroot-like behavior from Apple's sandbox */
#if __APPLE__
#define DEFAULT_ALLOWED_IMPURE_PREFIXES "/System/Library /usr/lib /dev /bin/sh"
#else
#define DEFAULT_ALLOWED_IMPURE_PREFIXES ""
#endif
Settings settings; Settings settings;
static GlobalConfig::Register r1(&settings); static GlobalConfig::Register r1(&settings);
@ -68,7 +61,12 @@ Settings::Settings()
sandboxPaths = tokenizeString<StringSet>("/bin/sh=" SANDBOX_SHELL); sandboxPaths = tokenizeString<StringSet>("/bin/sh=" SANDBOX_SHELL);
#endif #endif
allowedImpureHostPrefixes = tokenizeString<StringSet>(DEFAULT_ALLOWED_IMPURE_PREFIXES);
/* chroot-like behavior from Apple's sandbox */
#if __APPLE__
sandboxPaths = tokenizeString<StringSet>("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib");
allowedImpureHostPrefixes = tokenizeString<StringSet>("/System/Library /usr/lib /dev /bin/sh");
#endif
} }
void loadConfFile() void loadConfFile()

View file

@ -311,12 +311,7 @@ public:
Setting<bool> printMissing{this, true, "print-missing", Setting<bool> printMissing{this, true, "print-missing",
"Whether to print what paths need to be built or downloaded."}; "Whether to print what paths need to be built or downloaded."};
Setting<std::string> preBuildHook{this, Setting<std::string> preBuildHook{this, "",
#if __APPLE__
nixLibexecDir + "/nix/resolve-system-dependencies",
#else
"",
#endif
"pre-build-hook", "pre-build-hook",
"A program to run just before a build to set derivation-specific build settings."}; "A program to run just before a build to set derivation-specific build settings."};

View file

@ -163,10 +163,11 @@ static RegisterStoreImplementation regStore([](
const std::string & uri, const Store::Params & params) const std::string & uri, const Store::Params & params)
-> std::shared_ptr<Store> -> std::shared_ptr<Store>
{ {
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1";
if (std::string(uri, 0, 7) != "http://" && if (std::string(uri, 0, 7) != "http://" &&
std::string(uri, 0, 8) != "https://" && std::string(uri, 0, 8) != "https://" &&
(getEnv("_NIX_FORCE_HTTP_BINARY_CACHE_STORE") != "1" || std::string(uri, 0, 7) != "file://") (!forceHttp || std::string(uri, 0, 7) != "file://"))
) return 0; return 0;
auto store = std::make_shared<HttpBinaryCacheStore>(params, uri); auto store = std::make_shared<HttpBinaryCacheStore>(params, uri);
store->init(); store->init();
return store; return store;

View file

@ -298,9 +298,7 @@ void LocalStore::openDB(State & state, bool create)
/* Open the Nix database. */ /* Open the Nix database. */
string dbPath = dbDir + "/db.sqlite"; string dbPath = dbDir + "/db.sqlite";
auto & db(state.db); auto & db(state.db);
if (sqlite3_open_v2(dbPath.c_str(), &db.db, state.db = SQLite(dbPath, create);
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
throw Error(format("cannot open Nix database '%1%'") % dbPath);
#ifdef __CYGWIN__ #ifdef __CYGWIN__
/* The cygwin version of sqlite3 has a patch which calls /* The cygwin version of sqlite3 has a patch which calls
@ -312,11 +310,6 @@ void LocalStore::openDB(State & state, bool create)
SetDllDirectoryW(L""); SetDllDirectoryW(L"");
#endif #endif
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
throwSQLiteError(db, "setting timeout");
db.exec("pragma foreign_keys = 1");
/* !!! check whether sqlite has been built with foreign key /* !!! check whether sqlite has been built with foreign key
support */ support */
@ -350,7 +343,7 @@ void LocalStore::openDB(State & state, bool create)
/* Initialise the database schema, if necessary. */ /* Initialise the database schema, if necessary. */
if (create) { if (create) {
const char * schema = static const char schema[] =
#include "schema.sql.gen.hh" #include "schema.sql.gen.hh"
; ;
db.exec(schema); db.exec(schema);
@ -1275,7 +1268,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
else else
hashSink = std::make_unique<HashModuloSink>(info->narHash.type, storePathToHash(printStorePath(info->path))); hashSink = std::make_unique<HashModuloSink>(info->narHash.type, storePathToHash(printStorePath(info->path)));
dumpPath(toRealPath(printStorePath(i)), *hashSink); dumpPath(Store::toRealPath(i), *hashSink);
auto current = hashSink->finish(); auto current = hashSink->finish();
if (info->narHash != nullHash && info->narHash != current.first) { if (info->narHash != nullHash && info->narHash != current.first) {

View file

@ -31,7 +31,8 @@ ifeq ($(HAVE_SECCOMP), 1)
libstore_LDFLAGS += -lseccomp libstore_LDFLAGS += -lseccomp
endif endif
libstore_CXXFLAGS = \ libstore_CXXFLAGS += \
-I src/libutil -I src/libstore \
-DNIX_PREFIX=\"$(prefix)\" \ -DNIX_PREFIX=\"$(prefix)\" \
-DNIX_STORE_DIR=\"$(storedir)\" \ -DNIX_STORE_DIR=\"$(storedir)\" \
-DNIX_DATA_DIR=\"$(datadir)\" \ -DNIX_DATA_DIR=\"$(datadir)\" \

View file

@ -78,12 +78,7 @@ public:
state->db = SQLite(dbPath); state->db = SQLite(dbPath);
if (sqlite3_busy_timeout(state->db, 60 * 60 * 1000) != SQLITE_OK) state->db.isCache();
throwSQLiteError(state->db, "setting timeout");
// We can always reproduce the cache.
state->db.exec("pragma synchronous = off");
state->db.exec("pragma main.journal_mode = truncate");
state->db.exec(schema); state->db.exec(schema);

View file

@ -10,7 +10,7 @@ class NarInfoDiskCache
public: public:
typedef enum { oValid, oInvalid, oUnknown } Outcome; typedef enum { oValid, oInvalid, oUnknown } Outcome;
virtual ~NarInfoDiskCache() { }; virtual ~NarInfoDiskCache() { }
virtual void createCache(const std::string & uri, const Path & storeDir, virtual void createCache(const std::string & uri, const Path & storeDir,
bool wantMassQuery, int priority) = 0; bool wantMassQuery, int priority) = 0;

View file

@ -1,5 +1,7 @@
#include "parsed-derivations.hh" #include "parsed-derivations.hh"
#include <nlohmann/json.hpp>
namespace nix { namespace nix {
ParsedDerivation::ParsedDerivation(StorePath && drvPath, BasicDerivation & drv) ParsedDerivation::ParsedDerivation(StorePath && drvPath, BasicDerivation & drv)
@ -9,13 +11,15 @@ ParsedDerivation::ParsedDerivation(StorePath && drvPath, BasicDerivation & drv)
auto jsonAttr = drv.env.find("__json"); auto jsonAttr = drv.env.find("__json");
if (jsonAttr != drv.env.end()) { if (jsonAttr != drv.env.end()) {
try { try {
structuredAttrs = nlohmann::json::parse(jsonAttr->second); structuredAttrs = std::make_unique<nlohmann::json>(nlohmann::json::parse(jsonAttr->second));
} catch (std::exception & e) { } catch (std::exception & e) {
throw Error("cannot process __json attribute of '%s': %s", drvPath.to_string(), e.what()); throw Error("cannot process __json attribute of '%s': %s", drvPath.to_string(), e.what());
} }
} }
} }
ParsedDerivation::~ParsedDerivation() { }
std::optional<std::string> ParsedDerivation::getStringAttr(const std::string & name) const std::optional<std::string> ParsedDerivation::getStringAttr(const std::string & name) const
{ {
if (structuredAttrs) { if (structuredAttrs) {

View file

@ -1,6 +1,6 @@
#include "derivations.hh" #include "derivations.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json_fwd.hpp>
namespace nix { namespace nix {
@ -8,15 +8,17 @@ class ParsedDerivation
{ {
StorePath drvPath; StorePath drvPath;
BasicDerivation & drv; BasicDerivation & drv;
std::optional<nlohmann::json> structuredAttrs; std::unique_ptr<nlohmann::json> structuredAttrs;
public: public:
ParsedDerivation(StorePath && drvPath, BasicDerivation & drv); ParsedDerivation(StorePath && drvPath, BasicDerivation & drv);
const std::optional<nlohmann::json> & getStructuredAttrs() const ~ParsedDerivation();
const nlohmann::json * getStructuredAttrs() const
{ {
return structuredAttrs; return structuredAttrs.get();
} }
std::optional<std::string> getStringAttr(const std::string & name) const; std::optional<std::string> getStringAttr(const std::string & name) const;

View file

@ -256,4 +256,22 @@ string optimisticLockProfile(const Path & profile)
} }
Path getDefaultProfile()
{
Path profileLink = getHome() + "/.nix-profile";
try {
if (!pathExists(profileLink)) {
replaceSymlink(
getuid() == 0
? settings.nixStateDir + "/profiles/default"
: fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName()),
profileLink);
}
return absPath(readLink(profileLink), dirOf(profileLink));
} catch (Error &) {
return profileLink;
}
}
} }

View file

@ -64,4 +64,8 @@ void lockProfile(PathLocks & lock, const Path & profile);
rebuilt. */ rebuilt. */
string optimisticLockProfile(const Path & profile); string optimisticLockProfile(const Path & profile);
/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
it. */
Path getDefaultProfile();
} }

View file

@ -71,6 +71,12 @@
(literal "/dev/zero") (literal "/dev/zero")
(subpath "/dev/fd")) (subpath "/dev/fd"))
; Allow pseudo-terminals.
(allow file*
(literal "/dev/ptmx")
(regex #"^/dev/pty[a-z]+")
(regex #"^/dev/ttys[0-9]+"))
; Does nothing, but reduces build noise. ; Does nothing, but reduces build noise.
(allow file* (literal "/dev/dtracehelper")) (allow file* (literal "/dev/dtracehelper"))
@ -85,3 +91,7 @@
(literal "/etc") (literal "/etc")
(literal "/var") (literal "/var")
(literal "/private/var/tmp")) (literal "/private/var/tmp"))
; This is used by /bin/sh on macOS 10.15 and later.
(allow file*
(literal "/private/var/select/sh"))

View file

@ -25,11 +25,16 @@ namespace nix {
throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path); throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path);
} }
SQLite::SQLite(const Path & path) SQLite::SQLite(const Path & path, bool create)
{ {
if (sqlite3_open_v2(path.c_str(), &db, if (sqlite3_open_v2(path.c_str(), &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK) SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
throw Error(format("cannot open SQLite database '%s'") % path); throw Error(format("cannot open SQLite database '%s'") % path);
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
throwSQLiteError(db, "setting timeout");
exec("pragma foreign_keys = 1");
} }
SQLite::~SQLite() SQLite::~SQLite()
@ -42,6 +47,12 @@ SQLite::~SQLite()
} }
} }
void SQLite::isCache()
{
exec("pragma synchronous = off");
exec("pragma main.journal_mode = truncate");
}
void SQLite::exec(const std::string & stmt) void SQLite::exec(const std::string & stmt)
{ {
retrySQLite<void>([&]() { retrySQLite<void>([&]() {
@ -94,6 +105,16 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool
return *this; return *this;
} }
SQLiteStmt::Use & SQLiteStmt::Use::operator () (const unsigned char * data, size_t len, bool notNull)
{
if (notNull) {
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
throwSQLiteError(stmt.db, "binding argument");
} else
bind();
return *this;
}
SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull) SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
{ {
if (notNull) { if (notNull) {

View file

@ -5,8 +5,8 @@
#include "types.hh" #include "types.hh"
class sqlite3; struct sqlite3;
class sqlite3_stmt; struct sqlite3_stmt;
namespace nix { namespace nix {
@ -15,13 +15,16 @@ struct SQLite
{ {
sqlite3 * db = 0; sqlite3 * db = 0;
SQLite() { } SQLite() { }
SQLite(const Path & path); SQLite(const Path & path, bool create = true);
SQLite(const SQLite & from) = delete; SQLite(const SQLite & from) = delete;
SQLite& operator = (const SQLite & from) = delete; SQLite& operator = (const SQLite & from) = delete;
SQLite& operator = (SQLite && from) { db = from.db; from.db = 0; return *this; } SQLite& operator = (SQLite && from) { db = from.db; from.db = 0; return *this; }
~SQLite(); ~SQLite();
operator sqlite3 * () { return db; } operator sqlite3 * () { return db; }
/* Disable synchronous mode, set truncate journal mode. */
void isCache();
void exec(const std::string & stmt); void exec(const std::string & stmt);
}; };
@ -52,6 +55,7 @@ struct SQLiteStmt
/* Bind the next parameter. */ /* Bind the next parameter. */
Use & operator () (const std::string & value, bool notNull = true); Use & operator () (const std::string & value, bool notNull = true);
Use & operator () (const unsigned char * data, size_t len, bool notNull = true);
Use & operator () (int64_t value, bool notNull = true); Use & operator () (int64_t value, bool notNull = true);
Use & bind(); // null Use & bind(); // null

View file

@ -441,7 +441,9 @@ string Store::makeValidityRegistration(const StorePathSet & paths,
void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths, void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths,
bool includeImpureInfo, bool showClosureSize, AllowInvalidFlag allowInvalid) bool includeImpureInfo, bool showClosureSize,
Base hashBase,
AllowInvalidFlag allowInvalid)
{ {
auto jsonList = jsonOut.list(); auto jsonList = jsonOut.list();
@ -453,7 +455,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
auto info = queryPathInfo(storePath); auto info = queryPathInfo(storePath);
jsonPath jsonPath
.attr("narHash", info->narHash.to_string()) .attr("narHash", info->narHash.to_string(hashBase))
.attr("narSize", info->narSize); .attr("narSize", info->narSize);
{ {
@ -741,12 +743,7 @@ std::string Store::showPaths(const StorePathSet & paths)
string showPaths(const PathSet & paths) string showPaths(const PathSet & paths)
{ {
string s; return concatStringsSep(", ", quoteStrings(paths));
for (auto & i : paths) {
if (s.size() != 0) s += ", ";
s += "'" + i + "'";
}
return s;
} }

View file

@ -561,6 +561,7 @@ public:
each path is included. */ each path is included. */
void pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths, void pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths,
bool includeImpureInfo, bool showClosureSize, bool includeImpureInfo, bool showClosureSize,
Base hashBase = Base32,
AllowInvalidFlag allowInvalid = DisallowInvalid); AllowInvalidFlag allowInvalid = DisallowInvalid);
/* Return the size of the closure of the specified path, that is, /* Return the size of the closure of the specified path, that is,
@ -676,6 +677,11 @@ public:
return storePath; return storePath;
} }
Path toRealPath(const StorePath & storePath)
{
return toRealPath(printStorePath(storePath));
}
virtual void createUser(const std::string & userName, uid_t userId) virtual void createUser(const std::string & userName, uid_t userId)
{ } { }

View file

@ -80,6 +80,18 @@ struct Hash
or base-64. By default, this is prefixed by the hash type or base-64. By default, this is prefixed by the hash type
(e.g. "sha256:"). */ (e.g. "sha256:"). */
std::string to_string(Base base = Base32, bool includeType = true) const; std::string to_string(Base base = Base32, bool includeType = true) const;
std::string gitRev() const
{
assert(type == htSHA1);
return to_string(Base16, false);
}
std::string gitShortRev() const
{
assert(type == htSHA1);
return std::string(to_string(Base16, false), 0, 7);
}
}; };

View file

@ -97,10 +97,10 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
} }
Path absPath(Path path, Path dir) Path absPath(Path path, std::optional<Path> dir)
{ {
if (path[0] != '/') { if (path[0] != '/') {
if (dir == "") { if (!dir) {
#ifdef __GNU__ #ifdef __GNU__
/* GNU (aka. GNU/Hurd) doesn't have any limitation on path /* GNU (aka. GNU/Hurd) doesn't have any limitation on path
lengths and doesn't define `PATH_MAX'. */ lengths and doesn't define `PATH_MAX'. */
@ -116,7 +116,7 @@ Path absPath(Path path, Path dir)
free(buf); free(buf);
#endif #endif
} }
path = dir + "/" + path; path = *dir + "/" + path;
} }
return canonPath(path); return canonPath(path);
} }
@ -478,6 +478,17 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
} }
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
{
Path tmpl(getEnv("TMPDIR").value_or("/tmp") + "/" + prefix + ".XXXXXX");
// Strictly speaking, this is UB, but who cares...
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
if (!fd)
throw SysError("creating temporary file '%s'", tmpl);
return {std::move(fd), tmpl};
}
std::string getUserName() std::string getUserName()
{ {
auto pw = getpwuid(geteuid()); auto pw = getpwuid(geteuid());
@ -1205,28 +1216,6 @@ template StringSet tokenizeString(std::string_view s, const string & separators)
template vector<string> tokenizeString(std::string_view s, const string & separators); template vector<string> tokenizeString(std::string_view s, const string & separators);
string concatStringsSep(const string & sep, const Strings & ss)
{
string s;
for (auto & i : ss) {
if (s.size() != 0) s += sep;
s += i;
}
return s;
}
string concatStringsSep(const string & sep, const StringSet & ss)
{
string s;
for (auto & i : ss) {
if (s.size() != 0) s += sep;
s += i;
}
return s;
}
string chomp(const string & s) string chomp(const string & s)
{ {
size_t i = s.find_last_not_of(" \n\r\t"); size_t i = s.find_last_not_of(" \n\r\t");

View file

@ -48,7 +48,7 @@ void clearEnv();
/* Return an absolutized path, resolving paths relative to the /* Return an absolutized path, resolving paths relative to the
specified directory, or the current directory otherwise. The path specified directory, or the current directory otherwise. The path
is also canonicalised. */ is also canonicalised. */
Path absPath(Path path, Path dir = ""); Path absPath(Path path, std::optional<Path> dir = {});
/* Canonicalise a path by removing all `.' or `..' components and /* Canonicalise a path by removing all `.' or `..' components and
double or trailing slashes. Optionally resolves all symlink double or trailing slashes. Optionally resolves all symlink
@ -124,10 +124,6 @@ void deletePath(const Path & path);
void deletePath(const Path & path, unsigned long long & bytesFreed); void deletePath(const Path & path, unsigned long long & bytesFreed);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
std::string getUserName(); std::string getUserName();
/* Return $HOME or the user's home directory from /etc/passwd. */ /* Return $HOME or the user's home directory from /etc/passwd. */
@ -207,6 +203,14 @@ public:
}; };
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/* Create a temporary file, returning a file handle and its path. */
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");
class Pipe class Pipe
{ {
public: public:
@ -347,8 +351,26 @@ template<class C> C tokenizeString(std::string_view s, const string & separators
/* Concatenate the given strings with a separator between the /* Concatenate the given strings with a separator between the
elements. */ elements. */
string concatStringsSep(const string & sep, const Strings & ss); template<class C>
string concatStringsSep(const string & sep, const StringSet & ss); string concatStringsSep(const string & sep, const C & ss)
{
string s;
for (auto & i : ss) {
if (s.size() != 0) s += sep;
s += i;
}
return s;
}
/* Add quotes around a collection of strings. */
template<class C> Strings quoteStrings(const C & c)
{
Strings res;
for (auto & s : c)
res.push_back("'" + s + "'");
return res;
}
/* Remove trailing whitespace from a string. */ /* Remove trailing whitespace from a string. */
@ -426,6 +448,13 @@ void ignoreException();
/* Tree formatting. */
constexpr char treeConn[] = "├───";
constexpr char treeLast[] = "└───";
constexpr char treeLine[] = "";
constexpr char treeNull[] = " ";
/* Truncate a string to 'width' printable characters. If 'filterAll' /* Truncate a string to 'width' printable characters. If 'filterAll'
is true, all ANSI escape sequences are filtered out. Otherwise, is true, all ANSI escape sequences are filtered out. Otherwise,
some escape sequences (such as colour setting) are copied but not some escape sequences (such as colour setting) are copied but not
@ -444,10 +473,11 @@ string base64Decode(const string & s);
/* Get a value for the specified key from an associate container, or a /* Get a value for the specified key from an associate container, or a
default value if the key doesn't exist. */ default value if the key doesn't exist. */
template <class T> template <class T>
std::optional<std::string> get(const T & map, const std::string & key) std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
{ {
auto i = map.find(key); auto i = map.find(key);
return i == map.end() ? std::optional<std::string>() : i->second; if (i == map.end()) return {};
return std::optional<typename T::mapped_type>(i->second);
} }
@ -551,4 +581,31 @@ extern PathFilter defaultPathFilter;
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
// A Rust/Python-like enumerate() iterator adapter.
// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
template <typename T,
typename TIter = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto enumerate(T && iterable)
{
struct iterator
{
size_t i;
TIter iter;
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ () { ++i; ++iter; }
auto operator * () const { return std::tie(i, *iter); }
};
struct iterable_wrapper
{
T iterable;
auto begin() { return iterator{ 0, std::begin(iterable) }; }
auto end() { return iterator{ 0, std::end(iterable) }; }
};
return iterable_wrapper{ std::forward<T>(iterable) };
}
} }

View file

@ -16,7 +16,7 @@
#include "get-drvs.hh" #include "get-drvs.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "attr-path.hh" #include "attr-path.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
using namespace nix; using namespace nix;
using namespace std::string_literals; using namespace std::string_literals;
@ -314,7 +314,7 @@ static void _main(int argc, char * * argv)
state->eval(e, vRoot); state->eval(e, vRoot);
for (auto & i : attrPaths) { for (auto & i : attrPaths) {
Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot)); Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot).first);
state->forceValue(v); state->forceValue(v);
getDerivations(*state, v, "", *autoArgs, drvs, false); getDerivations(*state, v, "", *autoArgs, drvs, false);
} }

View file

@ -2,7 +2,7 @@
#include "globals.hh" #include "globals.hh"
#include "download.hh" #include "download.hh"
#include "store-api.hh" #include "store-api.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include <fcntl.h> #include <fcntl.h>
#include <regex> #include <regex>

View file

@ -2,7 +2,7 @@
#include "profiles.hh" #include "profiles.hh"
#include "shared.hh" #include "shared.hh"
#include "globals.hh" #include "globals.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include <iostream> #include <iostream>
#include <cerrno> #include <cerrno>

View file

@ -1,6 +1,6 @@
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
using namespace nix; using namespace nix;

View file

@ -6,7 +6,7 @@
#include "globals.hh" #include "globals.hh"
#include "derivations.hh" #include "derivations.hh"
#include "finally.hh" #include "finally.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include "daemon.hh" #include "daemon.hh"
#include <algorithm> #include <algorithm>

View file

@ -13,7 +13,7 @@
#include "json.hh" #include "json.hh"
#include "value-to-json.hh" #include "value-to-json.hh"
#include "xml-writer.hh" #include "xml-writer.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include <cerrno> #include <cerrno>
#include <ctime> #include <ctime>
@ -178,7 +178,7 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
Value vRoot; Value vRoot;
loadSourceExpr(state, nixExprPath, vRoot); loadSourceExpr(state, nixExprPath, vRoot);
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot)); Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot).first);
getDerivations(state, v, pathPrefix, autoArgs, elems, true); getDerivations(state, v, pathPrefix, autoArgs, elems, true);
@ -408,7 +408,7 @@ static void queryInstSources(EvalState & state,
Value vRoot; Value vRoot;
loadSourceExpr(state, instSource.nixExprPath, vRoot); loadSourceExpr(state, instSource.nixExprPath, vRoot);
for (auto & i : args) { for (auto & i : args) {
Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot)); Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot).first);
getDerivations(state, v, "", *instSource.autoArgs, elems, true); getDerivations(state, v, "", *instSource.autoArgs, elems, true);
} }
break; break;
@ -1428,21 +1428,8 @@ static int _main(int argc, char * * argv)
if (globals.profile == "") if (globals.profile == "")
globals.profile = getEnv("NIX_PROFILE").value_or(""); globals.profile = getEnv("NIX_PROFILE").value_or("");
if (globals.profile == "") { if (globals.profile == "")
Path profileLink = getHome() + "/.nix-profile"; globals.profile = getDefaultProfile();
try {
if (!pathExists(profileLink)) {
replaceSymlink(
getuid() == 0
? settings.nixStateDir + "/profiles/default"
: fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName()),
profileLink);
}
globals.profile = absPath(readLink(profileLink), dirOf(profileLink));
} catch (Error &) {
globals.profile = profileLink;
}
}
op(globals, opFlags, opArgs); op(globals, opFlags, opArgs);

View file

@ -15,6 +15,8 @@ namespace nix {
DrvInfos queryInstalled(EvalState & state, const Path & userEnv) DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
{ {
DrvInfos elems; DrvInfos elems;
if (pathExists(userEnv + "/manifest.json"))
throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv);
Path manifestFile = userEnv + "/manifest.nix"; Path manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) { if (pathExists(manifestFile)) {
Value v; Value v;

View file

@ -9,7 +9,7 @@
#include "util.hh" #include "util.hh"
#include "store-api.hh" #include "store-api.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include <map> #include <map>
#include <iostream> #include <iostream>
@ -39,7 +39,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
state.eval(e, vRoot); state.eval(e, vRoot);
for (auto & i : attrPaths) { for (auto & i : attrPaths) {
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot)); Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
state.forceValue(v); state.forceValue(v);
PathSet context; PathSet context;

View file

@ -6,9 +6,9 @@
#include "eval-inline.hh" #include "eval-inline.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "attr-path.hh" #include "attr-path.hh"
#include "legacy.hh"
#include "finally.hh" #include "finally.hh"
#include "progress-bar.hh" #include "../nix/legacy.hh"
#include "../nix/progress-bar.hh"
#include "tarfile.hh" #include "tarfile.hh"
#include <iostream> #include <iostream>
@ -120,7 +120,7 @@ static int _main(int argc, char * * argv)
Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0])); Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
Value vRoot; Value vRoot;
state->evalFile(path, vRoot); state->evalFile(path, vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot)); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
state->forceAttrs(v); state->forceAttrs(v);
/* Extract the URI. */ /* Extract the URI. */

View file

@ -9,7 +9,7 @@
#include "util.hh" #include "util.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "graphml.hh" #include "graphml.hh"
#include "legacy.hh" #include "../nix/legacy.hh"
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
@ -229,12 +229,6 @@ static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput,
/* Some code to print a tree representation of a derivation dependency /* Some code to print a tree representation of a derivation dependency
graph. Topological sorting is used to keep the tree relatively graph. Topological sorting is used to keep the tree relatively
flat. */ flat. */
const string treeConn = "+---";
const string treeLine = "| ";
const string treeNull = " ";
static void printTree(const StorePath & path, static void printTree(const StorePath & path,
const string & firstPad, const string & tailPad, StorePathSet & done) const string & firstPad, const string & tailPad, StorePathSet & done)
{ {
@ -254,10 +248,11 @@ static void printTree(const StorePath & path,
auto sorted = store->topoSortPaths(info->references); auto sorted = store->topoSortPaths(info->references);
reverse(sorted.begin(), sorted.end()); reverse(sorted.begin(), sorted.end());
for (auto i = sorted.begin(); i != sorted.end(); ++i) { for (const auto &[n, i] : enumerate(sorted)) {
auto j = i; ++j; bool last = n + 1 == sorted.size();
printTree(*i, tailPad + treeConn, printTree(i,
j == sorted.end() ? tailPad + treeNull : tailPad + treeLine, tailPad + (last ? treeLast : treeConn),
tailPad + (last ? treeNull : treeLine),
done); done);
} }
} }
@ -577,7 +572,6 @@ static void opGC(Strings opFlags, Strings opArgs)
if (*i == "--print-roots") printRoots = true; if (*i == "--print-roots") printRoots = true;
else if (*i == "--print-live") options.action = GCOptions::gcReturnLive; else if (*i == "--print-live") options.action = GCOptions::gcReturnLive;
else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead; else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead;
else if (*i == "--delete") options.action = GCOptions::gcDeleteDead;
else if (*i == "--max-freed") { else if (*i == "--max-freed") {
long long maxFreed = getIntArg<long long>(*i, i, opFlags.end(), true); long long maxFreed = getIntArg<long long>(*i, i, opFlags.end(), true);
options.maxFreed = maxFreed >= 0 ? maxFreed : 0; options.maxFreed = maxFreed >= 0 ? maxFreed : 0;

View file

@ -5,7 +5,7 @@
using namespace nix; using namespace nix;
struct CmdBuild : MixDryRun, InstallablesCommand struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile
{ {
Path outLink = "result"; Path outLink = "result";
@ -40,6 +40,10 @@ struct CmdBuild : MixDryRun, InstallablesCommand
"To build the build.x86_64-linux attribute from release.nix:", "To build the build.x86_64-linux attribute from release.nix:",
"nix build -f release.nix build.x86_64-linux" "nix build -f release.nix build.x86_64-linux"
}, },
Example{
"To make a profile point at GNU Hello:",
"nix build --profile /tmp/profile nixpkgs.hello"
},
}; };
} }
@ -49,11 +53,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand
if (dryRun) return; if (dryRun) return;
if (outLink != "") {
for (size_t i = 0; i < buildables.size(); ++i) { for (size_t i = 0; i < buildables.size(); ++i) {
auto & b(buildables[i]); for (auto & output : buildables[i].outputs)
if (outLink != "")
for (auto & output : b.outputs)
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) { if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
std::string symlink = outLink; std::string symlink = outLink;
if (i) symlink += fmt("-%d", i); if (i) symlink += fmt("-%d", i);
@ -62,6 +64,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand
} }
} }
} }
updateProfile(buildables);
}
}; };
static auto r1 = registerCommand<CmdBuild>("build"); static auto r1 = registerCommand<CmdBuild>("build");

View file

@ -2,6 +2,9 @@
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
#include "profiles.hh"
extern char * * environ;
namespace nix { namespace nix {
@ -96,4 +99,95 @@ Strings editorFor(const Pos & pos)
return args; return args;
} }
MixProfile::MixProfile()
{
mkFlag()
.longName("profile")
.description("profile to update")
.labels({"path"})
.dest(&profile);
}
void MixProfile::updateProfile(const StorePath & storePath)
{
if (!profile) return;
auto store = getStore().dynamic_pointer_cast<LocalFSStore>();
if (!store) throw Error("'--profile' is not supported for this Nix store");
auto profile2 = absPath(*profile);
switchLink(profile2,
createGeneration(
ref<LocalFSStore>(store),
profile2, store->printStorePath(storePath)));
}
void MixProfile::updateProfile(const Buildables & buildables)
{
if (!profile) return;
std::optional<StorePath> result;
for (auto & buildable : buildables) {
for (auto & output : buildable.outputs) {
if (result)
throw Error("'--profile' requires that the arguments produce a single store path, but there are multiple");
result = output.second.clone();
}
}
if (!result)
throw Error("'--profile' requires that the arguments produce a single store path, but there are none");
updateProfile(*result);
}
MixDefaultProfile::MixDefaultProfile()
{
profile = getDefaultProfile();
}
MixEnvironment::MixEnvironment() : ignoreEnvironment(false) {
mkFlag()
.longName("ignore-environment")
.shortName('i')
.description("clear the entire environment (except those specified with --keep)")
.set(&ignoreEnvironment, true);
mkFlag()
.longName("keep")
.shortName('k')
.description("keep specified environment variable")
.arity(1)
.labels({"name"})
.handler([&](std::vector<std::string> ss) { keep.insert(ss.front()); });
mkFlag()
.longName("unset")
.shortName('u')
.description("unset specified environment variable")
.arity(1)
.labels({"name"})
.handler([&](std::vector<std::string> ss) { unset.insert(ss.front()); });
}
void MixEnvironment::setEnviron() {
if (ignoreEnvironment) {
if (!unset.empty())
throw UsageError("--unset does not make sense with --ignore-environment");
for (const auto & var : keep) {
auto val = getenv(var.c_str());
if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val));
}
vectorEnv = stringsToCharPtrs(stringsEnv);
environ = vectorEnv.data();
} else {
if (!keep.empty())
throw UsageError("--keep does not make sense without --ignore-environment");
for (const auto & var : unset)
unsetenv(var.c_str());
}
}
} }

View file

@ -1,19 +1,15 @@
#pragma once #pragma once
#include "installables.hh"
#include "args.hh" #include "args.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "path.hh" #include "path.hh"
#include "eval.hh"
namespace nix { namespace nix {
extern std::string programPath; extern std::string programPath;
struct Value;
class Bindings;
class EvalState;
struct Pos;
class Store;
/* A command that requires a Nix store. */ /* A command that requires a Nix store. */
struct StoreCommand : virtual Command struct StoreCommand : virtual Command
{ {
@ -27,34 +23,7 @@ private:
std::shared_ptr<Store> _store; std::shared_ptr<Store> _store;
}; };
struct Buildable struct SourceExprCommand : virtual StoreCommand, MixEvalArgs
{
std::optional<StorePath> drvPath;
std::map<std::string, StorePath> outputs;
};
typedef std::vector<Buildable> Buildables;
struct Installable
{
virtual ~Installable() { }
virtual std::string what() = 0;
virtual Buildables toBuildables()
{
throw Error("argument '%s' cannot be built", what());
}
Buildable toBuildable();
virtual Value * toValue(EvalState & state)
{
throw Error("argument '%s' cannot be evaluated", what());
}
};
struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs
{ {
Path file; Path file;
@ -189,4 +158,36 @@ std::set<StorePath> toDerivations(ref<Store> store,
filename:lineno. */ filename:lineno. */
Strings editorFor(const Pos & pos); Strings editorFor(const Pos & pos);
struct MixProfile : virtual StoreCommand
{
std::optional<Path> profile;
MixProfile();
/* If 'profile' is set, make it point at 'storePath'. */
void updateProfile(const StorePath & storePath);
/* If 'profile' is set, make it point at the store path produced
by 'buildables'. */
void updateProfile(const Buildables & buildables);
};
struct MixDefaultProfile : MixProfile
{
MixDefaultProfile();
};
struct MixEnvironment : virtual Args {
StringSet keep, unset;
Strings stringsEnv;
std::vector<char*> vectorEnv;
bool ignoreEnvironment;
MixEnvironment();
/* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */
void setEnviron();
};
} }

View file

@ -29,9 +29,15 @@ struct CmdEdit : InstallableCommand
{ {
auto state = getEvalState(); auto state = getEvalState();
auto v = installable->toValue(*state); auto [v, pos] = installable->toValue(*state);
Pos pos = findDerivationFilename(*state, *v, installable->what()); try {
pos = findDerivationFilename(*state, *v, installable->what());
} catch (NoPositionInfo &) {
}
if (pos == noPos)
throw Error("cannot find position information for '%s", installable->what());
stopProgressBar(); stopProgressBar();

View file

@ -52,7 +52,7 @@ struct CmdEval : MixJSON, InstallableCommand
auto state = getEvalState(); auto state = getEvalState();
auto v = installable->toValue(*state); auto v = installable->toValue(*state).first;
PathSet context; PathSet context;
stopProgressBar(); stopProgressBar();

View file

@ -109,6 +109,11 @@ struct InstallableStorePath : Installable
bs.push_back(std::move(b)); bs.push_back(std::move(b));
return bs; return bs;
} }
std::optional<StorePath> getStorePath() override
{
return storePath.clone();
}
}; };
struct InstallableValue : Installable struct InstallableValue : Installable
@ -121,7 +126,7 @@ struct InstallableValue : Installable
{ {
auto state = cmd.getEvalState(); auto state = cmd.getEvalState();
auto v = toValue(*state); auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state); Bindings & autoArgs = *cmd.getAutoArgs(*state);
@ -169,11 +174,11 @@ struct InstallableExpr : InstallableValue
std::string what() override { return text; } std::string what() override { return text; }
Value * toValue(EvalState & state) override std::pair<Value *, Pos> toValue(EvalState & state) override
{ {
auto v = state.allocValue(); auto v = state.allocValue();
state.eval(state.parseExprFromString(text, absPath(".")), *v); state.eval(state.parseExprFromString(text, absPath(".")), *v);
return v; return {v, noPos};
} }
}; };
@ -187,16 +192,16 @@ struct InstallableAttrPath : InstallableValue
std::string what() override { return attrPath; } std::string what() override { return attrPath; }
Value * toValue(EvalState & state) override std::pair<Value *, Pos> toValue(EvalState & state) override
{ {
auto source = cmd.getSourceExpr(state); auto source = cmd.getSourceExpr(state);
Bindings & autoArgs = *cmd.getAutoArgs(state); Bindings & autoArgs = *cmd.getAutoArgs(state);
Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); auto v = findAlongAttrPath(state, attrPath, autoArgs, *source).first;
state.forceValue(*v); state.forceValue(*v);
return v; return {v, noPos};
} }
}; };

45
src/nix/installables.hh Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include "util.hh"
#include "path.hh"
#include "eval.hh"
#include <optional>
namespace nix {
struct Buildable
{
std::optional<StorePath> drvPath;
std::map<std::string, StorePath> outputs;
};
typedef std::vector<Buildable> Buildables;
struct Installable
{
virtual ~Installable() { }
virtual std::string what() = 0;
virtual Buildables toBuildables()
{
throw Error("argument '%s' cannot be built", what());
}
Buildable toBuildable();
virtual std::pair<Value *, Pos> toValue(EvalState & state)
{
throw Error("argument '%s' cannot be evaluated", what());
}
/* Return a value only if this installable is a store path or a
symlink to it. */
virtual std::optional<StorePath> getStorePath()
{
return {};
}
};
}

View file

@ -15,6 +15,8 @@ nix_SOURCES := \
$(wildcard src/nix-prefetch-url/*.cc) \ $(wildcard src/nix-prefetch-url/*.cc) \
$(wildcard src/nix-store/*.cc) \ $(wildcard src/nix-store/*.cc) \
nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain
nix_LIBS = libexpr libmain libstore libutil libnixrust nix_LIBS = libexpr libmain libstore libutil libnixrust
nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system

View file

@ -55,6 +55,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{ {
bool printBuildLogs = false; bool printBuildLogs = false;
bool useNet = true; bool useNet = true;
bool refresh = false;
NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix")
{ {
@ -92,6 +93,11 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
.longName("no-net") .longName("no-net")
.description("disable substituters and consider all previously downloaded files up-to-date") .description("disable substituters and consider all previously downloaded files up-to-date")
.handler([&]() { useNet = false; }); .handler([&]() { useNet = false; });
mkFlag()
.longName("refresh")
.description("consider all previously downloaded files out-of-date")
.handler([&]() { refresh = true; });
} }
void printFlags(std::ostream & out) override void printFlags(std::ostream & out) override
@ -176,6 +182,9 @@ void mainWrapped(int argc, char * * argv)
downloadSettings.connectTimeout = 1; downloadSettings.connectTimeout = 1;
} }
if (args.refresh)
settings.tarballTtl = 0;
args.command->prepare(); args.command->prepare();
args.command->run(); args.command->run();
} }

View file

@ -89,7 +89,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
store->pathInfoToJSON(jsonRoot, store->pathInfoToJSON(jsonRoot,
// FIXME: preserve order? // FIXME: preserve order?
storePathsToSet(storePaths), storePathsToSet(storePaths),
true, showClosureSize, AllowInvalid); true, showClosureSize, SRI, AllowInvalid);
} }
else { else {

View file

@ -8,6 +8,7 @@
#include "fs-accessor.hh" #include "fs-accessor.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
#include "affinity.hh" #include "affinity.hh"
#include "eval.hh"
#if __linux__ #if __linux__
#include <sys/mount.h> #include <sys/mount.h>
@ -19,11 +20,46 @@ using namespace nix;
std::string chrootHelperName = "__run_in_chroot"; std::string chrootHelperName = "__run_in_chroot";
struct CmdRun : InstallablesCommand struct RunCommon : virtual Command
{
void runProgram(ref<Store> store,
const std::string & program,
const Strings & args)
{
stopProgressBar();
restoreSignals();
restoreAffinity();
/* If this is a diverted store (i.e. its "logical" location
(typically /nix/store) differs from its "physical" location
(e.g. /home/eelco/nix/store), then run the command in a
chroot. For non-root users, this requires running it in new
mount and user namespaces. Unfortunately,
unshare(CLONE_NEWUSER) doesn't work in a multithreaded
program (which "nix" is), so we exec() a single-threaded
helper program (chrootHelper() below) to do the work. */
auto store2 = store.dynamic_pointer_cast<LocalStore>();
if (store2 && store->storeDir != store2->realStoreDir) {
Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program };
for (auto & arg : args) helperArgs.push_back(arg);
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
throw SysError("could not execute chroot helper");
}
execvp(program.c_str(), stringsToCharPtrs(args).data());
throw SysError("unable to execute '%s'", program);
}
};
struct CmdRun : InstallablesCommand, RunCommon, MixEnvironment
{ {
std::vector<std::string> command = { "bash" }; std::vector<std::string> command = { "bash" };
StringSet keep, unset;
bool ignoreEnvironment = false;
CmdRun() CmdRun()
{ {
@ -37,28 +73,6 @@ struct CmdRun : InstallablesCommand
if (ss.empty()) throw UsageError("--command requires at least one argument"); if (ss.empty()) throw UsageError("--command requires at least one argument");
command = ss; command = ss;
}); });
mkFlag()
.longName("ignore-environment")
.shortName('i')
.description("clear the entire environment (except those specified with --keep)")
.set(&ignoreEnvironment, true);
mkFlag()
.longName("keep")
.shortName('k')
.description("keep specified environment variable")
.arity(1)
.labels({"name"})
.handler([&](std::vector<std::string> ss) { keep.insert(ss.front()); });
mkFlag()
.longName("unset")
.shortName('u')
.description("unset specified environment variable")
.arity(1)
.labels({"name"})
.handler([&](std::vector<std::string> ss) { unset.insert(ss.front()); });
} }
std::string description() override std::string description() override
@ -94,35 +108,13 @@ struct CmdRun : InstallablesCommand
auto accessor = store->getFSAccessor(); auto accessor = store->getFSAccessor();
if (ignoreEnvironment) {
if (!unset.empty())
throw UsageError("--unset does not make sense with --ignore-environment");
std::map<std::string, std::string> kept;
for (auto & var : keep) {
auto s = getenv(var.c_str());
if (s) kept[var] = s;
}
clearEnv();
for (auto & var : kept)
setenv(var.first.c_str(), var.second.c_str(), 1);
} else {
if (!keep.empty())
throw UsageError("--keep does not make sense without --ignore-environment");
for (auto & var : unset)
unsetenv(var.c_str());
}
std::unordered_set<StorePath> done; std::unordered_set<StorePath> done;
std::queue<StorePath> todo; std::queue<StorePath> todo;
for (auto & path : outPaths) todo.push(path.clone()); for (auto & path : outPaths) todo.push(path.clone());
setEnviron();
auto unixPath = tokenizeString<Strings>(getEnv("PATH").value_or(""), ":"); auto unixPath = tokenizeString<Strings>(getEnv("PATH").value_or(""), ":");
while (!todo.empty()) { while (!todo.empty()) {
@ -142,38 +134,10 @@ struct CmdRun : InstallablesCommand
setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1);
std::string cmd = *command.begin();
Strings args; Strings args;
for (auto & arg : command) args.push_back(arg); for (auto & arg : command) args.push_back(arg);
stopProgressBar(); runProgram(store, *command.begin(), args);
restoreSignals();
restoreAffinity();
/* If this is a diverted store (i.e. its "logical" location
(typically /nix/store) differs from its "physical" location
(e.g. /home/eelco/nix/store), then run the command in a
chroot. For non-root users, this requires running it in new
mount and user namespaces. Unfortunately,
unshare(CLONE_NEWUSER) doesn't work in a multithreaded
program (which "nix" is), so we exec() a single-threaded
helper program (chrootHelper() below) to do the work. */
auto store2 = store.dynamic_pointer_cast<LocalStore>();
if (store2 && store->storeDir != store2->realStoreDir) {
Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, cmd };
for (auto & arg : args) helperArgs.push_back(arg);
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
throw SysError("could not execute chroot helper");
}
execvp(cmd.c_str(), stringsToCharPtrs(args).data());
throw SysError("unable to exec '%s'", cmd);
} }
}; };

319
src/nix/shell.cc Normal file
View file

@ -0,0 +1,319 @@
#include "eval.hh"
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "affinity.hh"
#include "progress-bar.hh"
#include <regex>
using namespace nix;
struct Var
{
bool exported;
std::string value; // quoted string or array
};
struct BuildEnvironment
{
std::map<std::string, Var> env;
std::string bashFunctions;
};
BuildEnvironment readEnvironment(const Path & path)
{
BuildEnvironment res;
std::set<std::string> exported;
debug("reading environment file '%s'", path);
auto file = readFile(path);
auto pos = file.cbegin();
static std::string varNameRegex =
R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re";
static std::regex declareRegex(
"^declare -x (" + varNameRegex + ")" +
R"re((?:="((?:[^"\\]|\\.)*)")?\n)re");
static std::string simpleStringRegex =
R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re";
static std::string quotedStringRegex =
R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re";
static std::string arrayRegex =
R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re";
static std::regex varRegex(
"^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n");
static std::regex functionRegex(
"^" + varNameRegex + " \\(\\) *\n");
while (pos != file.end()) {
std::smatch match;
if (std::regex_search(pos, file.cend(), match, declareRegex)) {
pos = match[0].second;
exported.insert(match[1]);
}
else if (std::regex_search(pos, file.cend(), match, varRegex)) {
pos = match[0].second;
res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }});
}
else if (std::regex_search(pos, file.cend(), match, functionRegex)) {
res.bashFunctions = std::string(pos, file.cend());
break;
}
else throw Error("shell environment '%s' has unexpected line '%s'",
path, file.substr(pos - file.cbegin(), 60));
}
return res;
}
/* Given an existing derivation, return the shell environment as
initialised by stdenv's setup script. We do this by building a
modified derivation with the same dependencies and nearly the same
initial environment variables, that just writes the resulting
environment to a file and exits. */
StorePath getDerivationEnvironment(ref<Store> store, Derivation drv)
{
auto builder = baseNameOf(drv.builder);
if (builder != "bash")
throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder");
drv.args = {
"-c",
"set -e; "
"export IN_NIX_SHELL=impure; "
"export dontAddDisableDepTrack=1; "
"if [[ -n $stdenv ]]; then "
" source $stdenv/setup; "
"fi; "
"export > $out; "
"set >> $out "};
/* Remove derivation checks. */
drv.env.erase("allowedReferences");
drv.env.erase("allowedRequisites");
drv.env.erase("disallowedReferences");
drv.env.erase("disallowedRequisites");
// FIXME: handle structured attrs
/* Rehash and write the derivation. FIXME: would be nice to use
'buildDerivation', but that's privileged. */
auto drvName = drv.env["name"] + "-env";
for (auto & output : drv.outputs)
drv.env.erase(output.first);
drv.env["out"] = "";
drv.env["outputs"] = "out";
Hash h = hashDerivationModulo(*store, drv, true);
auto shellOutPath = store->makeOutputPath("out", h, drvName);
drv.outputs.insert_or_assign("out", DerivationOutput(shellOutPath.clone(), "", ""));
drv.env["out"] = store->printStorePath(shellOutPath);
auto shellDrvPath2 = writeDerivation(store, drv, drvName);
/* Build the derivation. */
store->buildPaths({shellDrvPath2});
assert(store->isValidPath(shellOutPath));
return shellOutPath;
}
struct Common : InstallableCommand, MixProfile
{
std::set<string> ignoreVars{
"BASHOPTS",
"EUID",
"HOME", // FIXME: don't ignore in pure mode?
"NIX_BUILD_TOP",
"NIX_ENFORCE_PURITY",
"NIX_LOG_FD",
"PPID",
"PWD",
"SHELLOPTS",
"SHLVL",
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
"TEMP",
"TEMPDIR",
"TERM",
"TMP",
"TMPDIR",
"TZ",
"UID",
};
void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out)
{
out << "nix_saved_PATH=\"$PATH\"\n";
for (auto & i : buildEnvironment.env) {
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
out << fmt("%s=%s\n", i.first, i.second.value);
if (i.second.exported)
out << fmt("export %s\n", i.first);
}
}
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
out << buildEnvironment.bashFunctions << "\n";
// FIXME: set outputs
out << "export NIX_BUILD_TOP=\"$(mktemp -d --tmpdir nix-shell.XXXXXX)\"\n";
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i);
out << "eval \"$shellHook\"\n";
}
StorePath getShellOutPath(ref<Store> store)
{
auto path = installable->getStorePath();
if (path && hasSuffix(path->to_string(), "-env"))
return path->clone();
else {
auto drvs = toDerivations(store, {installable});
if (drvs.size() != 1)
throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations",
installable->what(), drvs.size());
auto & drvPath = *drvs.begin();
return getDerivationEnvironment(store, store->derivationFromPath(drvPath));
}
}
BuildEnvironment getBuildEnvironment(ref<Store> store)
{
auto shellOutPath = getShellOutPath(store);
updateProfile(shellOutPath);
return readEnvironment(store->printStorePath(shellOutPath));
}
};
struct CmdDevShell : Common, MixEnvironment
{
std::vector<std::string> command;
CmdDevShell()
{
mkFlag()
.longName("command")
.shortName('c')
.description("command and arguments to be executed insted of an interactive shell")
.labels({"command", "args"})
.arity(ArityAny)
.handler([&](std::vector<std::string> ss) {
if (ss.empty()) throw UsageError("--command requires at least one argument");
command = ss;
});
}
std::string description() override
{
return "run a bash shell that provides the build environment of a derivation";
}
Examples examples() override
{
return {
Example{
"To get the build environment of GNU hello:",
"nix dev-shell nixpkgs.hello"
},
Example{
"To store the build environment in a profile:",
"nix dev-shell --profile /tmp/my-shell nixpkgs.hello"
},
Example{
"To use a build environment previously recorded in a profile:",
"nix dev-shell /tmp/my-shell"
},
};
}
void run(ref<Store> store) override
{
auto buildEnvironment = getBuildEnvironment(store);
auto [rcFileFd, rcFilePath] = createTempFile("nix-shell");
std::ostringstream ss;
makeRcScript(buildEnvironment, ss);
ss << fmt("rm -f '%s'\n", rcFilePath);
if (!command.empty()) {
std::vector<std::string> args;
for (auto s : command)
args.push_back(shellEscape(s));
ss << fmt("exec %s\n", concatStringsSep(" ", args));
}
writeFull(rcFileFd.get(), ss.str());
stopProgressBar();
auto shell = getEnv("SHELL").value_or("bash");
setEnviron();
auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath};
restoreAffinity();
restoreSignals();
execvp(shell.c_str(), stringsToCharPtrs(args).data());
throw SysError("executing shell '%s'", shell);
}
};
struct CmdPrintDevEnv : Common
{
std::string description() override
{
return "print shell code that can be sourced by bash to reproduce the build environment of a derivation";
}
Examples examples() override
{
return {
Example{
"To apply the build environment of GNU hello to the current shell:",
". <(nix print-dev-env nixpkgs.hello)"
},
};
}
void run(ref<Store> store) override
{
auto buildEnvironment = getBuildEnvironment(store);
stopProgressBar();
makeRcScript(buildEnvironment, std::cout);
}
};
static auto r1 = registerCommand<CmdPrintDevEnv>("print-dev-env");
static auto r2 = registerCommand<CmdDevShell>("dev-shell");

View file

@ -145,7 +145,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto v = state->allocValue(); auto v = state->allocValue();
state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v); state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
Bindings & bindings(*state->allocBindings(0)); Bindings & bindings(*state->allocBindings(0));
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
return store->parseStorePath(state->forceString(*v2)); return store->parseStorePath(state->forceString(*v2));
} }

View file

@ -143,11 +143,6 @@ struct CmdWhyDepends : SourceExprCommand
and `dependency`. */ and `dependency`. */
std::function<void(Node &, const string &, const string &)> printNode; std::function<void(Node &, const string &, const string &)> printNode;
const string treeConn = "╠═══";
const string treeLast = "╚═══";
const string treeLine = "";
const string treeNull = " ";
struct BailOut { }; struct BailOut { };
printNode = [&](Node & node, const string & firstPad, const string & tailPad) { printNode = [&](Node & node, const string & firstPad, const string & tailPad) {
@ -157,7 +152,7 @@ struct CmdWhyDepends : SourceExprCommand
std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n",
firstPad, firstPad,
node.visited ? "\e[38;5;244m" : "", node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? "=> " : "", firstPad != "" ? " " : "",
pathS); pathS);
if (node.path == dependencyPath && !all if (node.path == dependencyPath && !all

View file

@ -6,6 +6,8 @@ resolve-system-dependencies_DIR := $(d)
resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix
resolve-system-dependencies_CXXFLAGS += -I src/libutil -I src/libstore -I src/libmain
resolve-system-dependencies_LIBS := libstore libmain libutil libnixrust resolve-system-dependencies_LIBS := libstore libmain libutil libnixrust
resolve-system-dependencies_SOURCES := $(d)/resolve-system-dependencies.cc resolve-system-dependencies_SOURCES := $(d)/resolve-system-dependencies.cc

View file

@ -48,7 +48,7 @@ basicTests
# Test HttpBinaryCacheStore. # Test HttpBinaryCacheStore.
export _NIX_FORCE_HTTP_BINARY_CACHE_STORE=1 export _NIX_FORCE_HTTP=1
basicTests basicTests
@ -126,7 +126,7 @@ badKey="$(cat $TEST_ROOT/pk2)"
res=($(nix-store --generate-binary-cache-key foo.nixos.org-1 $TEST_ROOT/sk3 $TEST_ROOT/pk3)) res=($(nix-store --generate-binary-cache-key foo.nixos.org-1 $TEST_ROOT/sk3 $TEST_ROOT/pk3))
otherKey="$(cat $TEST_ROOT/pk3)" otherKey="$(cat $TEST_ROOT/pk3)"
_NIX_FORCE_HTTP_BINARY_CACHE_STORE= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath _NIX_FORCE_HTTP= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath
# Downloading should fail if we don't provide a key. # Downloading should fail if we don't provide a key.

View file

@ -3,7 +3,7 @@ rec {
path = "@coreutils@"; path = "@coreutils@";
system = builtins.currentSystem; system = "@system@";
shared = builtins.getEnv "_NIX_TEST_SHARED"; shared = builtins.getEnv "_NIX_TEST_SHARED";

View file

@ -6,7 +6,7 @@ drvPath=$(nix-instantiate dependencies.nix)
echo "derivation is $drvPath" echo "derivation is $drvPath"
nix-store -q --tree "$drvPath" | grep ' +---.*builder1.sh' nix-store -q --tree "$drvPath" | grep '───.*builder1.sh'
# Test Graphviz graph generation. # Test Graphviz graph generation.
nix-store -q --graph "$drvPath" > $TEST_ROOT/graph nix-store -q --graph "$drvPath" > $TEST_ROOT/graph
@ -24,7 +24,7 @@ if test -n "$dot"; then
$dot < $TEST_ROOT/graph $dot < $TEST_ROOT/graph
fi fi
nix-store -q --tree "$outPath" | grep '+---.*dependencies-input-2' nix-store -q --tree "$outPath" | grep '───.*dependencies-input-2'
echo "output path is $outPath" echo "output path is $outPath"

View file

@ -9,6 +9,8 @@ clearStore
repo=$TEST_ROOT/git repo=$TEST_ROOT/git
export _NIX_FORCE_HTTP=1
rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/gitv2 rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/gitv2
git init $repo git init $repo

View file

@ -7,3 +7,5 @@ libplugintest_SOURCES := $(d)/plugintest.cc
libplugintest_ALLOW_UNDEFINED := 1 libplugintest_ALLOW_UNDEFINED := 1
libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1 libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
libplugintest_CXXFLAGS := -I src/libutil -I src/libexpr