forked from lix-project/lix
Merge remote-tracking branch 'origin/master' into push-docker-image-to-docker-hub
This commit is contained in:
commit
a078a645da
|
@ -284,6 +284,10 @@ The points of interest are:
|
||||||
function is called with the `localServer` argument set to `true` but
|
function is called with the `localServer` argument set to `true` but
|
||||||
the `db4` argument set to `null`, then the evaluation fails.
|
the `db4` argument set to `null`, then the evaluation fails.
|
||||||
|
|
||||||
|
Note that `->` is the [logical
|
||||||
|
implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication)
|
||||||
|
Boolean operation.
|
||||||
|
|
||||||
2. This is a more subtle condition: if Subversion is built with Apache
|
2. This is a more subtle condition: if Subversion is built with Apache
|
||||||
(`httpServer`) support, then the Expat library (an XML library) used
|
(`httpServer`) support, then the Expat library (an XML library) used
|
||||||
by Subversion should be same as the one used by Apache. This is
|
by Subversion should be same as the one used by Apache. This is
|
||||||
|
|
|
@ -276,6 +276,9 @@ more than 2800 commits from 195 contributors since release 2.3.
|
||||||
|
|
||||||
* Plugins can now register `nix` subcommands.
|
* Plugins can now register `nix` subcommands.
|
||||||
|
|
||||||
|
* The `--indirect` flag to `nix-store --add-root` has become a no-op.
|
||||||
|
`--add-root` will always generate indirect GC roots from now on.
|
||||||
|
|
||||||
## Incompatible changes
|
## Incompatible changes
|
||||||
|
|
||||||
* The `nix` command is now marked as an experimental feature. This
|
* The `nix` command is now marked as an experimental feature. This
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
|
* The Nix cli now searches for a flake.nix up until the root of the current git repository or a filesystem boundary rather than just in the current directory
|
||||||
* The TOML parser used by `builtins.fromTOML` has been replaced by [a
|
* The TOML parser used by `builtins.fromTOML` has been replaced by [a
|
||||||
more compliant one](https://github.com/ToruNiina/toml11).
|
more compliant one](https://github.com/ToruNiina/toml11).
|
||||||
* Added `:st`/`:show-trace` commands to nix repl, which are used to
|
* Added `:st`/`:show-trace` commands to nix repl, which are used to
|
||||||
set or toggle display of error traces.
|
set or toggle display of error traces.
|
||||||
* New builtin function `builtins.zipAttrsWith` with same functionality
|
* New builtin function `builtins.zipAttrsWith` with same functionality
|
||||||
as `lib.zipAttrsWith` from nixpkgs, but much more efficient.
|
as `lib.zipAttrsWith` from nixpkgs, but much more efficient.
|
||||||
|
* New command `nix store copy-log` to copy build logs from one store
|
||||||
|
to another.
|
||||||
|
* The `commit-lockfile-summary` option can be set to a non-empty string
|
||||||
|
to override the commit summary used when commiting an updated lockfile.
|
||||||
|
This may be used in conjunction with the nixConfig attribute in
|
||||||
|
`flake.nix` to better conform to repository conventions.
|
||||||
|
|
|
@ -21,6 +21,7 @@ let
|
||||||
cacert.out
|
cacert.out
|
||||||
findutils
|
findutils
|
||||||
iana-etc
|
iana-etc
|
||||||
|
git
|
||||||
];
|
];
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
|
@ -200,6 +201,8 @@ let
|
||||||
|
|
||||||
mkdir $out/tmp
|
mkdir $out/tmp
|
||||||
|
|
||||||
|
mkdir -p $out/var/tmp
|
||||||
|
|
||||||
mkdir -p $out/etc/nix
|
mkdir -p $out/etc/nix
|
||||||
cat $nixConfContentsPath > $out/etc/nix/nix.conf
|
cat $nixConfContentsPath > $out/etc/nix/nix.conf
|
||||||
|
|
||||||
|
@ -235,6 +238,7 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
|
||||||
'';
|
'';
|
||||||
fakeRootCommands = ''
|
fakeRootCommands = ''
|
||||||
chmod 1777 tmp
|
chmod 1777 tmp
|
||||||
|
chmod 1777 var/tmp
|
||||||
'';
|
'';
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
15
flake.nix
15
flake.nix
|
@ -299,6 +299,8 @@
|
||||||
|
|
||||||
propagatedBuildInputs = propagatedDeps;
|
propagatedBuildInputs = propagatedDeps;
|
||||||
|
|
||||||
|
disallowedReferences = [ boost ];
|
||||||
|
|
||||||
preConfigure =
|
preConfigure =
|
||||||
''
|
''
|
||||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||||
|
@ -310,6 +312,13 @@
|
||||||
chmod u+w $out/lib/*.so.*
|
chmod u+w $out/lib/*.so.*
|
||||||
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||||
''}
|
''}
|
||||||
|
${lib.optionalString currentStdenv.isDarwin ''
|
||||||
|
for LIB in $out/lib/*.dylib; do
|
||||||
|
chmod u+w $LIB
|
||||||
|
install_name_tool -id $LIB $LIB
|
||||||
|
done
|
||||||
|
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
|
||||||
|
''}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
configureFlags = configureFlags ++
|
configureFlags = configureFlags ++
|
||||||
|
@ -326,6 +335,12 @@
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
mkdir -p $doc/nix-support
|
mkdir -p $doc/nix-support
|
||||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||||
|
${lib.optionalString currentStdenv.isDarwin ''
|
||||||
|
install_name_tool \
|
||||||
|
-change ${boost}/lib/libboost_context.dylib \
|
||||||
|
$out/lib/libboost_context.dylib \
|
||||||
|
$out/lib/libnixutil.dylib
|
||||||
|
''}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
doInstallCheck = true;
|
doInstallCheck = true;
|
||||||
|
|
|
@ -54,6 +54,36 @@ void StoreCommand::run()
|
||||||
run(getStore());
|
run(getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CopyCommand::CopyCommand()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "from",
|
||||||
|
.description = "URL of the source Nix store.",
|
||||||
|
.labels = {"store-uri"},
|
||||||
|
.handler = {&srcUri},
|
||||||
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "to",
|
||||||
|
.description = "URL of the destination Nix store.",
|
||||||
|
.labels = {"store-uri"},
|
||||||
|
.handler = {&dstUri},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<Store> CopyCommand::createStore()
|
||||||
|
{
|
||||||
|
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<Store> CopyCommand::getDstStore()
|
||||||
|
{
|
||||||
|
if (srcUri.empty() && dstUri.empty())
|
||||||
|
throw UsageError("you must pass '--from' and/or '--to'");
|
||||||
|
|
||||||
|
return dstUri.empty() ? openStore() : openStore(dstUri);
|
||||||
|
}
|
||||||
|
|
||||||
EvalCommand::EvalCommand()
|
EvalCommand::EvalCommand()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -73,13 +103,16 @@ ref<Store> EvalCommand::getEvalStore()
|
||||||
|
|
||||||
ref<EvalState> EvalCommand::getEvalState()
|
ref<EvalState> EvalCommand::getEvalState()
|
||||||
{
|
{
|
||||||
if (!evalState) evalState =
|
if (!evalState)
|
||||||
|
evalState =
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
|
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
|
||||||
|
searchPath, getEvalStore(), getStore())
|
||||||
#else
|
#else
|
||||||
std::make_shared<EvalState>(
|
std::make_shared<EvalState>(
|
||||||
|
searchPath, getEvalStore(), getStore())
|
||||||
#endif
|
#endif
|
||||||
searchPath, getEvalStore(), getStore());
|
;
|
||||||
return ref<EvalState>(evalState);
|
return ref<EvalState>(evalState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,19 @@ private:
|
||||||
std::shared_ptr<Store> _store;
|
std::shared_ptr<Store> _store;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* A command that copies something between `--from` and `--to`
|
||||||
|
stores. */
|
||||||
|
struct CopyCommand : virtual StoreCommand
|
||||||
|
{
|
||||||
|
std::string srcUri, dstUri;
|
||||||
|
|
||||||
|
CopyCommand();
|
||||||
|
|
||||||
|
ref<Store> createStore() override;
|
||||||
|
|
||||||
|
ref<Store> getDstStore();
|
||||||
|
};
|
||||||
|
|
||||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||||
{
|
{
|
||||||
EvalCommand();
|
EvalCommand();
|
||||||
|
|
|
@ -345,6 +345,18 @@ Installable::getCursor(EvalState & state)
|
||||||
return cursors[0];
|
return cursors[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static StorePath getDeriver(
|
||||||
|
ref<Store> store,
|
||||||
|
const Installable & i,
|
||||||
|
const StorePath & drvPath)
|
||||||
|
{
|
||||||
|
auto derivers = store->queryValidDerivers(drvPath);
|
||||||
|
if (derivers.empty())
|
||||||
|
throw Error("'%s' does not have a known deriver", i.what());
|
||||||
|
// FIXME: use all derivers?
|
||||||
|
return *derivers.begin();
|
||||||
|
}
|
||||||
|
|
||||||
struct InstallableStorePath : Installable
|
struct InstallableStorePath : Installable
|
||||||
{
|
{
|
||||||
ref<Store> store;
|
ref<Store> store;
|
||||||
|
@ -353,7 +365,7 @@ struct InstallableStorePath : Installable
|
||||||
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
||||||
: store(store), storePath(std::move(storePath)) { }
|
: store(store), storePath(std::move(storePath)) { }
|
||||||
|
|
||||||
std::string what() override { return store->printStorePath(storePath); }
|
std::string what() const override { return store->printStorePath(storePath); }
|
||||||
|
|
||||||
DerivedPaths toDerivedPaths() override
|
DerivedPaths toDerivedPaths() override
|
||||||
{
|
{
|
||||||
|
@ -374,6 +386,15 @@ struct InstallableStorePath : Installable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StorePathSet toDrvPaths(ref<Store> store) override
|
||||||
|
{
|
||||||
|
if (storePath.isDerivation()) {
|
||||||
|
return {storePath};
|
||||||
|
} else {
|
||||||
|
return {getDeriver(store, *this, storePath)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<StorePath> getStorePath() override
|
std::optional<StorePath> getStorePath() override
|
||||||
{
|
{
|
||||||
return storePath;
|
return storePath;
|
||||||
|
@ -402,6 +423,14 @@ DerivedPaths InstallableValue::toDerivedPaths()
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StorePathSet InstallableValue::toDrvPaths(ref<Store> store)
|
||||||
|
{
|
||||||
|
StorePathSet res;
|
||||||
|
for (auto & drv : toDerivations())
|
||||||
|
res.insert(drv.drvPath);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
struct InstallableAttrPath : InstallableValue
|
struct InstallableAttrPath : InstallableValue
|
||||||
{
|
{
|
||||||
SourceExprCommand & cmd;
|
SourceExprCommand & cmd;
|
||||||
|
@ -412,7 +441,7 @@ struct InstallableAttrPath : InstallableValue
|
||||||
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
|
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
std::string what() override { return attrPath; }
|
std::string what() const override { return attrPath; }
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
std::pair<Value *, Pos> toValue(EvalState & state) override
|
||||||
{
|
{
|
||||||
|
@ -836,11 +865,7 @@ StorePathSet toDerivations(
|
||||||
[&](const DerivedPath::Opaque & bo) {
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
if (!useDeriver)
|
if (!useDeriver)
|
||||||
throw Error("argument '%s' did not evaluate to a derivation", i->what());
|
throw Error("argument '%s' did not evaluate to a derivation", i->what());
|
||||||
auto derivers = store->queryValidDerivers(bo.path);
|
drvPaths.insert(getDeriver(store, *i, bo.path));
|
||||||
if (derivers.empty())
|
|
||||||
throw Error("'%s' does not have a known deriver", i->what());
|
|
||||||
// FIXME: use all derivers?
|
|
||||||
drvPaths.insert(*derivers.begin());
|
|
||||||
},
|
},
|
||||||
[&](const DerivedPath::Built & bfd) {
|
[&](const DerivedPath::Built & bfd) {
|
||||||
drvPaths.insert(bfd.drvPath);
|
drvPaths.insert(bfd.drvPath);
|
||||||
|
|
|
@ -33,10 +33,15 @@ struct Installable
|
||||||
{
|
{
|
||||||
virtual ~Installable() { }
|
virtual ~Installable() { }
|
||||||
|
|
||||||
virtual std::string what() = 0;
|
virtual std::string what() const = 0;
|
||||||
|
|
||||||
virtual DerivedPaths toDerivedPaths() = 0;
|
virtual DerivedPaths toDerivedPaths() = 0;
|
||||||
|
|
||||||
|
virtual StorePathSet toDrvPaths(ref<Store> store)
|
||||||
|
{
|
||||||
|
throw Error("'%s' cannot be converted to a derivation path", what());
|
||||||
|
}
|
||||||
|
|
||||||
DerivedPath toDerivedPath();
|
DerivedPath toDerivedPath();
|
||||||
|
|
||||||
UnresolvedApp toApp(EvalState & state);
|
UnresolvedApp toApp(EvalState & state);
|
||||||
|
@ -81,6 +86,8 @@ struct InstallableValue : Installable
|
||||||
virtual std::vector<DerivationInfo> toDerivations() = 0;
|
virtual std::vector<DerivationInfo> toDerivations() = 0;
|
||||||
|
|
||||||
DerivedPaths toDerivedPaths() override;
|
DerivedPaths toDerivedPaths() override;
|
||||||
|
|
||||||
|
StorePathSet toDrvPaths(ref<Store> store) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InstallableFlake : InstallableValue
|
struct InstallableFlake : InstallableValue
|
||||||
|
@ -99,7 +106,7 @@ struct InstallableFlake : InstallableValue
|
||||||
Strings && prefixes,
|
Strings && prefixes,
|
||||||
const flake::LockFlags & lockFlags);
|
const flake::LockFlags & lockFlags);
|
||||||
|
|
||||||
std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||||
|
|
||||||
std::vector<std::string> getActualAttrPaths();
|
std::vector<std::string> getActualAttrPaths();
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,8 @@ class BindingsBuilder
|
||||||
Bindings * bindings;
|
Bindings * bindings;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// needed by std::back_inserter
|
||||||
|
using value_type = Attr;
|
||||||
|
|
||||||
EvalState & state;
|
EvalState & state;
|
||||||
|
|
||||||
|
@ -134,6 +136,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(const Attr & attr)
|
void insert(const Attr & attr)
|
||||||
|
{
|
||||||
|
push_back(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const Attr & attr)
|
||||||
{
|
{
|
||||||
bindings->push_back(attr);
|
bindings->push_back(attr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,19 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
static char * allocString(size_t size)
|
||||||
|
{
|
||||||
|
char * t;
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
t = (char *) GC_MALLOC_ATOMIC(size);
|
||||||
|
#else
|
||||||
|
t = malloc(size);
|
||||||
|
#endif
|
||||||
|
if (!t) throw std::bad_alloc();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char * dupString(const char * s)
|
static char * dupString(const char * s)
|
||||||
{
|
{
|
||||||
char * t;
|
char * t;
|
||||||
|
@ -412,6 +425,11 @@ EvalState::EvalState(
|
||||||
, sDescription(symbols.create("description"))
|
, sDescription(symbols.create("description"))
|
||||||
, sSelf(symbols.create("self"))
|
, sSelf(symbols.create("self"))
|
||||||
, sEpsilon(symbols.create(""))
|
, sEpsilon(symbols.create(""))
|
||||||
|
, sStartSet(symbols.create("startSet"))
|
||||||
|
, sOperator(symbols.create("operator"))
|
||||||
|
, sKey(symbols.create("key"))
|
||||||
|
, sPath(symbols.create("path"))
|
||||||
|
, sPrefix(symbols.create("prefix"))
|
||||||
, repair(NoRepair)
|
, repair(NoRepair)
|
||||||
, emptyBindings(0)
|
, emptyBindings(0)
|
||||||
, store(store)
|
, store(store)
|
||||||
|
@ -771,17 +789,28 @@ void Value::mkString(std::string_view s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void copyContextToValue(Value & v, const PathSet & context)
|
||||||
|
{
|
||||||
|
if (!context.empty()) {
|
||||||
|
size_t n = 0;
|
||||||
|
v.string.context = (const char * *)
|
||||||
|
allocBytes((context.size() + 1) * sizeof(char *));
|
||||||
|
for (auto & i : context)
|
||||||
|
v.string.context[n++] = dupString(i.c_str());
|
||||||
|
v.string.context[n] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Value::mkString(std::string_view s, const PathSet & context)
|
void Value::mkString(std::string_view s, const PathSet & context)
|
||||||
{
|
{
|
||||||
mkString(s);
|
mkString(s);
|
||||||
if (!context.empty()) {
|
copyContextToValue(*this, context);
|
||||||
size_t n = 0;
|
|
||||||
string.context = (const char * *)
|
|
||||||
allocBytes((context.size() + 1) * sizeof(char *));
|
|
||||||
for (auto & i : context)
|
|
||||||
string.context[n++] = dupString(i.c_str());
|
|
||||||
string.context[n] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Value::mkStringMove(const char * s, const PathSet & context)
|
||||||
|
{
|
||||||
|
mkString(s);
|
||||||
|
copyContextToValue(*this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1660,13 +1689,34 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
|
||||||
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
std::ostringstream s;
|
std::vector<std::string> s;
|
||||||
|
size_t sSize = 0;
|
||||||
NixInt n = 0;
|
NixInt n = 0;
|
||||||
NixFloat nf = 0;
|
NixFloat nf = 0;
|
||||||
|
|
||||||
bool first = !forceString;
|
bool first = !forceString;
|
||||||
ValueType firstType = nString;
|
ValueType firstType = nString;
|
||||||
|
|
||||||
|
const auto str = [&] {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(sSize);
|
||||||
|
for (const auto & part : s) result += part;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
/* c_str() is not str().c_str() because we want to create a string
|
||||||
|
Value. allocating a GC'd string directly and moving it into a
|
||||||
|
Value lets us avoid an allocation and copy. */
|
||||||
|
const auto c_str = [&] {
|
||||||
|
char * result = allocString(sSize + 1);
|
||||||
|
char * tmp = result;
|
||||||
|
for (const auto & part : s) {
|
||||||
|
memcpy(tmp, part.c_str(), part.size());
|
||||||
|
tmp += part.size();
|
||||||
|
}
|
||||||
|
*tmp = 0;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
for (auto & [i_pos, i] : *es) {
|
for (auto & [i_pos, i] : *es) {
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
i->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
@ -1696,11 +1746,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
||||||
} else
|
} else {
|
||||||
|
if (s.empty()) s.reserve(es->size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
s.emplace_back(
|
||||||
|
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
|
||||||
|
sSize += s.back().size();
|
||||||
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -1712,9 +1766,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||||
v.mkPath(canonPath(s.str()));
|
v.mkPath(canonPath(str()));
|
||||||
} else
|
} else
|
||||||
v.mkString(s.str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,8 @@ public:
|
||||||
sContentAddressed,
|
sContentAddressed,
|
||||||
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||||
sRecurseForDerivations,
|
sRecurseForDerivations,
|
||||||
sDescription, sSelf, sEpsilon;
|
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||||
|
sPrefix;
|
||||||
Symbol sDerivationNix;
|
Symbol sDerivationNix;
|
||||||
|
|
||||||
/* If set, force copying files to the Nix store even if they
|
/* If set, force copying files to the Nix store even if they
|
||||||
|
@ -179,8 +180,8 @@ public:
|
||||||
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified string. */
|
/* Parse a Nix expression from the specified string. */
|
||||||
Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv);
|
Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
|
||||||
Expr * parseExprFromString(std::string_view s, const Path & basePath);
|
Expr * parseExprFromString(std::string s, const Path & basePath);
|
||||||
|
|
||||||
Expr * parseStdin();
|
Expr * parseStdin();
|
||||||
|
|
||||||
|
@ -308,7 +309,7 @@ private:
|
||||||
friend struct ExprAttrs;
|
friend struct ExprAttrs;
|
||||||
friend struct ExprLet;
|
friend struct ExprLet;
|
||||||
|
|
||||||
Expr * parse(const char * text, FileOrigin origin, const Path & path,
|
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
|
||||||
const Path & basePath, StaticEnv & staticEnv);
|
const Path & basePath, StaticEnv & staticEnv);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -399,6 +400,7 @@ private:
|
||||||
friend struct ExprSelect;
|
friend struct ExprSelect;
|
||||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||||
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||||
|
friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||||
|
|
||||||
friend struct Value;
|
friend struct Value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -633,12 +633,24 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
newLockFile.write(path);
|
newLockFile.write(path);
|
||||||
|
|
||||||
|
std::optional<std::string> commitMessage = std::nullopt;
|
||||||
|
if (lockFlags.commitLockFile) {
|
||||||
|
std::string cm;
|
||||||
|
|
||||||
|
cm = settings.commitLockFileSummary.get();
|
||||||
|
|
||||||
|
if (cm == "") {
|
||||||
|
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||||
|
}
|
||||||
|
|
||||||
|
cm += "\n\nFlake lock file updates:\n\n";
|
||||||
|
cm += filterANSIEscapes(diff, true);
|
||||||
|
commitMessage = cm;
|
||||||
|
}
|
||||||
|
|
||||||
topRef.input.markChangedFile(
|
topRef.input.markChangedFile(
|
||||||
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
||||||
lockFlags.commitLockFile
|
commitMessage);
|
||||||
? std::optional<std::string>(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
|
|
||||||
relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
|
|
||||||
: std::nullopt);
|
|
||||||
|
|
||||||
/* Rewriting the lockfile changed the top-level
|
/* Rewriting the lockfile changed the top-level
|
||||||
repo, so we should re-read it. FIXME: we could
|
repo, so we should re-read it. FIXME: we could
|
||||||
|
|
|
@ -122,6 +122,28 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
|
|
||||||
if (isFlake) {
|
if (isFlake) {
|
||||||
|
|
||||||
|
if (!allowMissing && !pathExists(path + "/flake.nix")){
|
||||||
|
notice("path '%s' does not contain a 'flake.nix', searching up",path);
|
||||||
|
|
||||||
|
// Save device to detect filesystem boundary
|
||||||
|
dev_t device = lstat(path).st_dev;
|
||||||
|
bool found = false;
|
||||||
|
while (path != "/") {
|
||||||
|
if (pathExists(path + "/flake.nix")) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else if (pathExists(path + "/.git"))
|
||||||
|
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
|
||||||
|
else {
|
||||||
|
if (lstat(path).st_dev != device)
|
||||||
|
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
|
||||||
|
}
|
||||||
|
path = dirOf(path);
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
throw BadURL("could not find a flake.nix file");
|
||||||
|
}
|
||||||
|
|
||||||
if (!S_ISDIR(lstat(path).st_mode))
|
if (!S_ISDIR(lstat(path).st_mode))
|
||||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||||
|
|
||||||
|
|
|
@ -64,29 +64,32 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: optimize
|
// we make use of the fact that the parser receives a private copy of the input
|
||||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
|
// string and can munge around in it.
|
||||||
|
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
||||||
{
|
{
|
||||||
string t;
|
char * result = s;
|
||||||
t.reserve(length);
|
char * t = s;
|
||||||
char c;
|
char c;
|
||||||
|
// the input string is terminated with *two* NULs, so we can safely take
|
||||||
|
// *one* character after the one being checked against.
|
||||||
while ((c = *s++)) {
|
while ((c = *s++)) {
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
assert(*s);
|
|
||||||
c = *s++;
|
c = *s++;
|
||||||
if (c == 'n') t += '\n';
|
if (c == 'n') *t = '\n';
|
||||||
else if (c == 'r') t += '\r';
|
else if (c == 'r') *t = '\r';
|
||||||
else if (c == 't') t += '\t';
|
else if (c == 't') *t = '\t';
|
||||||
else t += c;
|
else *t = c;
|
||||||
}
|
}
|
||||||
else if (c == '\r') {
|
else if (c == '\r') {
|
||||||
/* Normalise CR and CR/LF into LF. */
|
/* Normalise CR and CR/LF into LF. */
|
||||||
t += '\n';
|
*t = '\n';
|
||||||
if (*s == '\n') s++; /* cr/lf */
|
if (*s == '\n') s++; /* cr/lf */
|
||||||
}
|
}
|
||||||
else t += c;
|
else *t = c;
|
||||||
|
t++;
|
||||||
}
|
}
|
||||||
return new ExprString(symbols.create(t));
|
return new ExprString(symbols.create({result, size_t(t - result)}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ or { return OR_KW; }
|
||||||
\/\/ { return UPDATE; }
|
\/\/ { return UPDATE; }
|
||||||
\+\+ { return CONCAT; }
|
\+\+ { return CONCAT; }
|
||||||
|
|
||||||
{ID} { yylval->id = strdup(yytext); return ID; }
|
{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
|
||||||
{INT} { errno = 0;
|
{INT} { errno = 0;
|
||||||
try {
|
try {
|
||||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||||
|
@ -221,14 +224,14 @@ or { return OR_KW; }
|
||||||
<PATH_START>{PATH_SEG} {
|
<PATH_START>{PATH_SEG} {
|
||||||
POP_STATE();
|
POP_STATE();
|
||||||
PUSH_STATE(INPATH_SLASH);
|
PUSH_STATE(INPATH_SLASH);
|
||||||
yylval->path = strdup(yytext);
|
yylval->path = {yytext, (size_t) yyleng};
|
||||||
return PATH;
|
return PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
<PATH_START>{HPATH_START} {
|
<PATH_START>{HPATH_START} {
|
||||||
POP_STATE();
|
POP_STATE();
|
||||||
PUSH_STATE(INPATH_SLASH);
|
PUSH_STATE(INPATH_SLASH);
|
||||||
yylval->path = strdup(yytext);
|
yylval->path = {yytext, (size_t) yyleng};
|
||||||
return HPATH;
|
return HPATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +240,7 @@ or { return OR_KW; }
|
||||||
PUSH_STATE(INPATH_SLASH);
|
PUSH_STATE(INPATH_SLASH);
|
||||||
else
|
else
|
||||||
PUSH_STATE(INPATH);
|
PUSH_STATE(INPATH);
|
||||||
yylval->path = strdup(yytext);
|
yylval->path = {yytext, (size_t) yyleng};
|
||||||
return PATH;
|
return PATH;
|
||||||
}
|
}
|
||||||
{HPATH} {
|
{HPATH} {
|
||||||
|
@ -245,7 +248,7 @@ or { return OR_KW; }
|
||||||
PUSH_STATE(INPATH_SLASH);
|
PUSH_STATE(INPATH_SLASH);
|
||||||
else
|
else
|
||||||
PUSH_STATE(INPATH);
|
PUSH_STATE(INPATH);
|
||||||
yylval->path = strdup(yytext);
|
yylval->path = {yytext, (size_t) yyleng};
|
||||||
return HPATH;
|
return HPATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,8 +283,8 @@ or { return OR_KW; }
|
||||||
throw ParseError("path has a trailing slash");
|
throw ParseError("path has a trailing slash");
|
||||||
}
|
}
|
||||||
|
|
||||||
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
|
||||||
{URI} { yylval->uri = strdup(yytext); return URI; }
|
{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
|
||||||
|
|
||||||
[ \t\r\n]+ /* eat up whitespace */
|
[ \t\r\n]+ /* eat up whitespace */
|
||||||
\#[^\r\n]* /* single-line comments */
|
\#[^\r\n]* /* single-line comments */
|
||||||
|
|
|
@ -473,7 +473,7 @@ string ExprLambda::showNamePos() const
|
||||||
size_t SymbolTable::totalSize() const
|
size_t SymbolTable::totalSize() const
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (auto & i : symbols)
|
for (auto & i : store)
|
||||||
n += i.size();
|
n += i.size();
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,9 +273,16 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
nix::Formal * formal;
|
nix::Formal * formal;
|
||||||
nix::NixInt n;
|
nix::NixInt n;
|
||||||
nix::NixFloat nf;
|
nix::NixFloat nf;
|
||||||
const char * id; // !!! -> Symbol
|
// using C a struct allows us to avoid having to define the special
|
||||||
char * path;
|
// members that using string_view here would implicitly delete.
|
||||||
char * uri;
|
struct StringToken {
|
||||||
|
const char * p;
|
||||||
|
size_t l;
|
||||||
|
operator std::string_view() const { return {p, l}; }
|
||||||
|
};
|
||||||
|
StringToken id; // !!! -> Symbol
|
||||||
|
StringToken path;
|
||||||
|
StringToken uri;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
||||||
}
|
}
|
||||||
|
@ -397,7 +404,7 @@ expr_select
|
||||||
|
|
||||||
expr_simple
|
expr_simple
|
||||||
: ID {
|
: ID {
|
||||||
if (strcmp($1, "__curPos") == 0)
|
if (strncmp($1.p, "__curPos", $1.l) == 0)
|
||||||
$$ = new ExprPos(CUR_POS);
|
$$ = new ExprPos(CUR_POS);
|
||||||
else
|
else
|
||||||
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
||||||
|
@ -414,7 +421,7 @@ expr_simple
|
||||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||||
}
|
}
|
||||||
| SPATH {
|
| SPATH {
|
||||||
string path($1 + 1, strlen($1) - 2);
|
string path($1.p + 1, $1.l - 2);
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(data->symbols.create("__findFile")),
|
new ExprVar(data->symbols.create("__findFile")),
|
||||||
{new ExprVar(data->symbols.create("__nixPath")),
|
{new ExprVar(data->symbols.create("__nixPath")),
|
||||||
|
@ -460,14 +467,14 @@ string_parts_interpolated
|
||||||
|
|
||||||
path_start
|
path_start
|
||||||
: PATH {
|
: PATH {
|
||||||
Path path(absPath($1, data->basePath));
|
Path path(absPath({$1.p, $1.l}, data->basePath));
|
||||||
/* add back in the trailing '/' to the first segment */
|
/* add back in the trailing '/' to the first segment */
|
||||||
if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
|
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
||||||
path += "/";
|
path += "/";
|
||||||
$$ = new ExprPath(path);
|
$$ = new ExprPath(path);
|
||||||
}
|
}
|
||||||
| HPATH {
|
| HPATH {
|
||||||
Path path(getHome() + string($1 + 1));
|
Path path(getHome() + string($1.p + 1, $1.l - 1));
|
||||||
$$ = new ExprPath(path);
|
$$ = new ExprPath(path);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -543,7 +550,7 @@ attrpath
|
||||||
|
|
||||||
attr
|
attr
|
||||||
: ID { $$ = $1; }
|
: ID { $$ = $1; }
|
||||||
| OR_KW { $$ = "or"; }
|
| OR_KW { $$ = {"or", 2}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
string_attr
|
string_attr
|
||||||
|
@ -589,7 +596,7 @@ formal
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parse(const char * text, FileOrigin origin,
|
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
|
@ -609,7 +616,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
|
||||||
data.basePath = basePath;
|
data.basePath = basePath;
|
||||||
|
|
||||||
yylex_init(&scanner);
|
yylex_init(&scanner);
|
||||||
yy_scan_string(text, scanner);
|
yy_scan_buffer(text, length, scanner);
|
||||||
int res = yyparse(scanner, &data);
|
int res = yyparse(scanner, &data);
|
||||||
yylex_destroy(scanner);
|
yylex_destroy(scanner);
|
||||||
|
|
||||||
|
@ -655,26 +662,33 @@ Expr * EvalState::parseExprFromFile(const Path & path)
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
|
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
|
auto buffer = readFile(path);
|
||||||
|
// readFile should have left some extra space for terminators
|
||||||
|
buffer.append("\0\0", 2);
|
||||||
|
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
|
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
return parse(s.data(), foString, "", basePath, staticEnv);
|
s.append("\0\0", 2);
|
||||||
|
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
|
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath)
|
||||||
{
|
{
|
||||||
return parseExprFromString(s, basePath, staticBaseEnv);
|
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseStdin()
|
Expr * EvalState::parseStdin()
|
||||||
{
|
{
|
||||||
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
||||||
return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
|
auto buffer = drainFD(0);
|
||||||
|
// drainFD should have left some extra space for terminators
|
||||||
|
buffer.append("\0\0", 2);
|
||||||
|
return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "value-to-xml.hh"
|
#include "value-to-xml.hh"
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
|
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -378,7 +380,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
auto output = runProgram(program, true, commandArgs);
|
auto output = runProgram(program, true, commandArgs);
|
||||||
Expr * parsed;
|
Expr * parsed;
|
||||||
try {
|
try {
|
||||||
parsed = state.parseExprFromString(output, pos.file);
|
parsed = state.parseExprFromString(std::move(output), pos.file);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, "While parsing the output from '%1%'", program);
|
e.addTrace(pos, "While parsing the output from '%1%'", program);
|
||||||
throw;
|
throw;
|
||||||
|
@ -592,16 +594,16 @@ typedef list<Value *> ValueList;
|
||||||
|
|
||||||
static Bindings::iterator getAttr(
|
static Bindings::iterator getAttr(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
string funcName,
|
std::string_view funcName,
|
||||||
string attrName,
|
Symbol attrSym,
|
||||||
Bindings * attrSet,
|
Bindings * attrSet,
|
||||||
const Pos & pos)
|
const Pos & pos)
|
||||||
{
|
{
|
||||||
Bindings::iterator value = attrSet->find(state.symbols.create(attrName));
|
Bindings::iterator value = attrSet->find(attrSym);
|
||||||
if (value == attrSet->end()) {
|
if (value == attrSet->end()) {
|
||||||
hintformat errorMsg = hintfmt(
|
hintformat errorMsg = hintfmt(
|
||||||
"attribute '%s' missing for call to '%s'",
|
"attribute '%s' missing for call to '%s'",
|
||||||
attrName,
|
attrSym,
|
||||||
funcName
|
funcName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -635,7 +637,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
Bindings::iterator startSet = getAttr(
|
Bindings::iterator startSet = getAttr(
|
||||||
state,
|
state,
|
||||||
"genericClosure",
|
"genericClosure",
|
||||||
"startSet",
|
state.sStartSet,
|
||||||
args[0]->attrs,
|
args[0]->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
@ -650,7 +652,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
Bindings::iterator op = getAttr(
|
Bindings::iterator op = getAttr(
|
||||||
state,
|
state,
|
||||||
"genericClosure",
|
"genericClosure",
|
||||||
"operator",
|
state.sOperator,
|
||||||
args[0]->attrs,
|
args[0]->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
@ -672,7 +674,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceAttrs(*e, pos);
|
state.forceAttrs(*e, pos);
|
||||||
|
|
||||||
Bindings::iterator key =
|
Bindings::iterator key =
|
||||||
e->attrs->find(state.symbols.create("key"));
|
e->attrs->find(state.sKey);
|
||||||
if (key == e->attrs->end())
|
if (key == e->attrs->end())
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("attribute 'key' required"),
|
.msg = hintfmt("attribute 'key' required"),
|
||||||
|
@ -1079,10 +1081,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
} else {
|
} else {
|
||||||
auto s = state.coerceToString(*i->pos, *i->value, context, true);
|
auto s = state.coerceToString(*i->pos, *i->value, context, true);
|
||||||
drv.env.emplace(key, s);
|
drv.env.emplace(key, s);
|
||||||
if (i->name == state.sBuilder) drv.builder = s;
|
if (i->name == state.sBuilder) drv.builder = std::move(s);
|
||||||
else if (i->name == state.sSystem) drv.platform = s;
|
else if (i->name == state.sSystem) drv.platform = std::move(s);
|
||||||
else if (i->name == state.sOutputHash) outputHash = s;
|
else if (i->name == state.sOutputHash) outputHash = std::move(s);
|
||||||
else if (i->name == state.sOutputHashAlgo) outputHashAlgo = s;
|
else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s);
|
||||||
else if (i->name == state.sOutputHashMode) handleHashMode(s);
|
else if (i->name == state.sOutputHashMode) handleHashMode(s);
|
||||||
else if (i->name == state.sOutputs)
|
else if (i->name == state.sOutputs)
|
||||||
handleOutputs(tokenizeString<Strings>(s));
|
handleOutputs(tokenizeString<Strings>(s));
|
||||||
|
@ -1498,14 +1500,14 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
state.forceAttrs(*v2, pos);
|
state.forceAttrs(*v2, pos);
|
||||||
|
|
||||||
string prefix;
|
string prefix;
|
||||||
Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix"));
|
Bindings::iterator i = v2->attrs->find(state.sPrefix);
|
||||||
if (i != v2->attrs->end())
|
if (i != v2->attrs->end())
|
||||||
prefix = state.forceStringNoCtx(*i->value, pos);
|
prefix = state.forceStringNoCtx(*i->value, pos);
|
||||||
|
|
||||||
i = getAttr(
|
i = getAttr(
|
||||||
state,
|
state,
|
||||||
"findFile",
|
"findFile",
|
||||||
"path",
|
state.sPath,
|
||||||
v2->attrs,
|
v2->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
@ -2163,7 +2165,10 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
|
||||||
v.listElems()[n++] = (Value *) &i;
|
v.listElems()[n++] = (Value *) &i;
|
||||||
|
|
||||||
std::sort(v.listElems(), v.listElems() + n,
|
std::sort(v.listElems(), v.listElems() + n,
|
||||||
[](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
|
[](Value * v1, Value * v2) {
|
||||||
|
std::string_view s1 = ((Attr *) v1)->name, s2 = ((Attr *) v2)->name;
|
||||||
|
return s1 < s2;
|
||||||
|
});
|
||||||
|
|
||||||
for (unsigned int i = 0; i < n; ++i)
|
for (unsigned int i = 0; i < n; ++i)
|
||||||
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
|
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
|
||||||
|
@ -2184,11 +2189,10 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string attr = state.forceStringNoCtx(*args[0], pos);
|
string attr = state.forceStringNoCtx(*args[0], pos);
|
||||||
state.forceAttrs(*args[1], pos);
|
state.forceAttrs(*args[1], pos);
|
||||||
// !!! Should we create a symbol here or just do a lookup?
|
|
||||||
Bindings::iterator i = getAttr(
|
Bindings::iterator i = getAttr(
|
||||||
state,
|
state,
|
||||||
"getAttr",
|
"getAttr",
|
||||||
attr,
|
state.symbols.create(attr),
|
||||||
args[1]->attrs,
|
args[1]->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
@ -2268,21 +2272,25 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
|
|
||||||
/* Get the attribute names to be removed. */
|
/* Get the attribute names to be removed.
|
||||||
std::set<Symbol> names;
|
We keep them as Attrs instead of Symbols so std::set_difference
|
||||||
|
can be used to remove them from attrs[0]. */
|
||||||
|
boost::container::small_vector<Attr, 64> names;
|
||||||
|
names.reserve(args[1]->listSize());
|
||||||
for (auto elem : args[1]->listItems()) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
state.forceStringNoCtx(*elem, pos);
|
state.forceStringNoCtx(*elem, pos);
|
||||||
names.insert(state.symbols.create(elem->string.s));
|
names.emplace_back(state.symbols.create(elem->string.s), nullptr);
|
||||||
}
|
}
|
||||||
|
std::sort(names.begin(), names.end());
|
||||||
|
|
||||||
/* Copy all attributes not in that set. Note that we don't need
|
/* Copy all attributes not in that set. Note that we don't need
|
||||||
to sort v.attrs because it's a subset of an already sorted
|
to sort v.attrs because it's a subset of an already sorted
|
||||||
vector. */
|
vector. */
|
||||||
auto attrs = state.buildBindings(args[0]->attrs->size());
|
auto attrs = state.buildBindings(args[0]->attrs->size());
|
||||||
for (auto & i : *args[0]->attrs) {
|
std::set_difference(
|
||||||
if (!names.count(i.name))
|
args[0]->attrs->begin(), args[0]->attrs->end(),
|
||||||
attrs.insert(i);
|
names.begin(), names.end(),
|
||||||
}
|
std::back_inserter(attrs));
|
||||||
v.mkAttrs(attrs.alreadySorted());
|
v.mkAttrs(attrs.alreadySorted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3520,18 +3528,20 @@ static RegisterPrimOp primop_match({
|
||||||
|
|
||||||
/* Split a string with a regular expression, and return a list of the
|
/* Split a string with a regular expression, and return a list of the
|
||||||
non-matching parts interleaved by the lists of the matching groups. */
|
non-matching parts interleaved by the lists of the matching groups. */
|
||||||
static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
auto re = state.forceStringNoCtx(*args[0], pos);
|
auto re = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
std::regex regex(re, std::regex::extended);
|
auto regex = state.regexCache->cache.find(re);
|
||||||
|
if (regex == state.regexCache->cache.end())
|
||||||
|
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
const std::string str = state.forceString(*args[1], context, pos);
|
const std::string str = state.forceString(*args[1], context, pos);
|
||||||
|
|
||||||
auto begin = std::sregex_iterator(str.begin(), str.end(), regex);
|
auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second);
|
||||||
auto end = std::sregex_iterator();
|
auto end = std::sregex_iterator();
|
||||||
|
|
||||||
// Any matches results are surrounded by non-matching results.
|
// Any matches results are surrounded by non-matching results.
|
||||||
|
@ -3915,9 +3925,12 @@ void EvalState::createBaseEnv()
|
||||||
|
|
||||||
/* Note: we have to initialize the 'derivation' constant *after*
|
/* Note: we have to initialize the 'derivation' constant *after*
|
||||||
building baseEnv/staticBaseEnv because it uses 'builtins'. */
|
building baseEnv/staticBaseEnv because it uses 'builtins'. */
|
||||||
eval(parse(
|
char code[] =
|
||||||
#include "primops/derivation.nix.gen.hh"
|
#include "primops/derivation.nix.gen.hh"
|
||||||
, foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
|
// the parser needs two NUL bytes as terminators; one of them
|
||||||
|
// is implied by being a C string.
|
||||||
|
"\0";
|
||||||
|
eval(parse(code, sizeof(code), foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_set>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
|
@ -70,15 +71,21 @@ public:
|
||||||
class SymbolTable
|
class SymbolTable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
typedef std::unordered_set<string> Symbols;
|
std::unordered_map<std::string_view, Symbol> symbols;
|
||||||
Symbols symbols;
|
std::list<string> store;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Symbol create(std::string_view s)
|
Symbol create(std::string_view s)
|
||||||
{
|
{
|
||||||
// FIXME: avoid allocation if 's' already exists in the symbol table.
|
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||||
std::pair<Symbols::iterator, bool> res = symbols.emplace(std::string(s));
|
// for lookup performance.
|
||||||
return Symbol(&*res.first);
|
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
||||||
|
// on the original implementation using unordered_set
|
||||||
|
auto it = symbols.find(s);
|
||||||
|
if (it != symbols.end()) return it->second;
|
||||||
|
|
||||||
|
const string & rawSym = store.emplace_back(s);
|
||||||
|
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const
|
size_t size() const
|
||||||
|
@ -91,7 +98,7 @@ public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void dump(T callback)
|
void dump(T callback)
|
||||||
{
|
{
|
||||||
for (auto & s : symbols)
|
for (auto & s : store)
|
||||||
callback(s);
|
callback(s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -241,6 +241,8 @@ public:
|
||||||
|
|
||||||
void mkString(std::string_view s, const PathSet & context);
|
void mkString(std::string_view s, const PathSet & context);
|
||||||
|
|
||||||
|
void mkStringMove(const char * s, const PathSet & context);
|
||||||
|
|
||||||
inline void mkString(const Symbol & s)
|
inline void mkString(const Symbol & s)
|
||||||
{
|
{
|
||||||
mkString(((const std::string &) s).c_str());
|
mkString(((const std::string &) s).c_str());
|
||||||
|
|
|
@ -67,18 +67,18 @@ DownloadFileResult downloadFile(
|
||||||
storePath = std::move(cached->storePath);
|
storePath = std::move(cached->storePath);
|
||||||
} else {
|
} else {
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpString(*res.data, sink);
|
dumpString(res.data, sink);
|
||||||
auto hash = hashString(htSHA256, *res.data);
|
auto hash = hashString(htSHA256, res.data);
|
||||||
ValidPathInfo info {
|
ValidPathInfo info {
|
||||||
store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name),
|
store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name),
|
||||||
hashString(htSHA256, *sink.s),
|
hashString(htSHA256, sink.s),
|
||||||
};
|
};
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s.size();
|
||||||
info.ca = FixedOutputHash {
|
info.ca = FixedOutputHash {
|
||||||
.method = FileIngestionMethod::Flat,
|
.method = FileIngestionMethod::Flat,
|
||||||
.hash = hash,
|
.hash = hash,
|
||||||
};
|
};
|
||||||
auto source = StringSource { *sink.s };
|
auto source = StringSource(sink.s);
|
||||||
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
||||||
storePath = std::move(info.path);
|
storePath = std::move(info.path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
|
||||||
|
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
sink << narVersionMagic1;
|
sink << narVersionMagic1;
|
||||||
narMagic = *sink.s;
|
narMagic = sink.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryCacheStore::init()
|
void BinaryCacheStore::init()
|
||||||
|
@ -68,7 +68,7 @@ void BinaryCacheStore::upsertFile(const std::string & path,
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryCacheStore::getFile(const std::string & path,
|
void BinaryCacheStore::getFile(const std::string & path,
|
||||||
Callback<std::shared_ptr<std::string>> callback) noexcept
|
Callback<std::optional<std::string>> callback) noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
callback(getFile(path));
|
callback(getFile(path));
|
||||||
|
@ -77,9 +77,9 @@ void BinaryCacheStore::getFile(const std::string & path,
|
||||||
|
|
||||||
void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
|
void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
|
||||||
{
|
{
|
||||||
std::promise<std::shared_ptr<std::string>> promise;
|
std::promise<std::optional<std::string>> promise;
|
||||||
getFile(path,
|
getFile(path,
|
||||||
{[&](std::future<std::shared_ptr<std::string>> result) {
|
{[&](std::future<std::optional<std::string>> result) {
|
||||||
try {
|
try {
|
||||||
promise.set_value(result.get());
|
promise.set_value(result.get());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -89,15 +89,15 @@ void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
|
||||||
sink(*promise.get_future().get());
|
sink(*promise.get_future().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
|
std::optional<std::string> BinaryCacheStore::getFile(const std::string & path)
|
||||||
{
|
{
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
try {
|
try {
|
||||||
getFile(path, sink);
|
getFile(path, sink);
|
||||||
} catch (NoSuchBinaryCacheFile &) {
|
} catch (NoSuchBinaryCacheFile &) {
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return sink.s;
|
return std::move(sink.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
||||||
|
@ -367,11 +367,11 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
getFile(narInfoFile,
|
getFile(narInfoFile,
|
||||||
{[=](std::future<std::shared_ptr<std::string>> fut) {
|
{[=](std::future<std::optional<std::string>> fut) {
|
||||||
try {
|
try {
|
||||||
auto data = fut.get();
|
auto data = fut.get();
|
||||||
|
|
||||||
if (!data) return (*callbackPtr)(nullptr);
|
if (!data) return (*callbackPtr)({});
|
||||||
|
|
||||||
stats.narInfoRead++;
|
stats.narInfoRead++;
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
||||||
|
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpString(s, sink);
|
dumpString(s, sink);
|
||||||
auto source = StringSource { *sink.s };
|
StringSource source(sink.s);
|
||||||
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
|
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
|
||||||
ValidPathInfo info { path, nar.first };
|
ValidPathInfo info { path, nar.first };
|
||||||
info.narSize = nar.second;
|
info.narSize = nar.second;
|
||||||
|
@ -446,11 +446,11 @@ void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
|
||||||
|
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
Callback<std::shared_ptr<std::string>> newCallback = {
|
Callback<std::optional<std::string>> newCallback = {
|
||||||
[=](std::future<std::shared_ptr<std::string>> fut) {
|
[=](std::future<std::optional<std::string>> fut) {
|
||||||
try {
|
try {
|
||||||
auto data = fut.get();
|
auto data = fut.get();
|
||||||
if (!data) return (*callbackPtr)(nullptr);
|
if (!data) return (*callbackPtr)({});
|
||||||
|
|
||||||
auto realisation = Realisation::fromJSON(
|
auto realisation = Realisation::fromJSON(
|
||||||
nlohmann::json::parse(*data), outputInfoFilePath);
|
nlohmann::json::parse(*data), outputInfoFilePath);
|
||||||
|
@ -490,7 +490,7 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
|
||||||
writeNarInfo(narInfo);
|
writeNarInfo(narInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
||||||
{
|
{
|
||||||
auto drvPath = path;
|
auto drvPath = path;
|
||||||
|
|
||||||
|
@ -498,10 +498,10 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
|
||||||
try {
|
try {
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
// FIXME: add a "Log" field to .narinfo
|
// FIXME: add a "Log" field to .narinfo
|
||||||
if (!info->deriver) return nullptr;
|
if (!info->deriver) return std::nullopt;
|
||||||
drvPath = *info->deriver;
|
drvPath = *info->deriver;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,4 +512,14 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
|
||||||
return getFile(logPath);
|
return getFile(logPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BinaryCacheStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
|
{
|
||||||
|
assert(drvPath.isDerivation());
|
||||||
|
|
||||||
|
upsertFile(
|
||||||
|
"log/" + std::string(drvPath.to_string()),
|
||||||
|
(std::string) log, // FIXME: don't copy
|
||||||
|
"text/plain; charset=utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ public:
|
||||||
const std::string & mimeType) = 0;
|
const std::string & mimeType) = 0;
|
||||||
|
|
||||||
void upsertFile(const std::string & path,
|
void upsertFile(const std::string & path,
|
||||||
|
// FIXME: use std::string_view
|
||||||
std::string && data,
|
std::string && data,
|
||||||
const std::string & mimeType);
|
const std::string & mimeType);
|
||||||
|
|
||||||
|
@ -62,10 +63,11 @@ public:
|
||||||
|
|
||||||
/* Fetch the specified file and call the specified callback with
|
/* Fetch the specified file and call the specified callback with
|
||||||
the result. A subclass may implement this asynchronously. */
|
the result. A subclass may implement this asynchronously. */
|
||||||
virtual void getFile(const std::string & path,
|
virtual void getFile(
|
||||||
Callback<std::shared_ptr<std::string>> callback) noexcept;
|
const std::string & path,
|
||||||
|
Callback<std::optional<std::string>> callback) noexcept;
|
||||||
|
|
||||||
std::shared_ptr<std::string> getFile(const std::string & path);
|
std::optional<std::string> getFile(const std::string & path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -117,7 +119,9 @@ public:
|
||||||
|
|
||||||
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
|
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
|
||||||
|
|
||||||
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
|
std::optional<std::string> getBuildLog(const StorePath & path) override;
|
||||||
|
|
||||||
|
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,7 @@ void DerivationGoal::outputsSubstitutionTried()
|
||||||
|
|
||||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
|
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
|
||||||
done(BuildResult::TransientFailure,
|
done(BuildResult::TransientFailure,
|
||||||
fmt("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
||||||
worker.store.printStorePath(drvPath)));
|
worker.store.printStorePath(drvPath)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2226,8 +2226,8 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpPath(actualPath, sink);
|
dumpPath(actualPath, sink);
|
||||||
deletePath(actualPath);
|
deletePath(actualPath);
|
||||||
sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites));
|
sink.s = rewriteStrings(sink.s, outputRewrites);
|
||||||
StringSource source(*sink.s);
|
StringSource source(sink.s);
|
||||||
restorePath(actualPath, source);
|
restorePath(actualPath, source);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2295,7 +2295,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpPath(actualPath, sink);
|
dumpPath(actualPath, sink);
|
||||||
RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
|
RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
|
||||||
rsink2(*sink.s);
|
rsink2(sink.s);
|
||||||
rsink2.flush();
|
rsink2.flush();
|
||||||
});
|
});
|
||||||
Path tmpPath = actualPath + ".tmp";
|
Path tmpPath = actualPath + ".tmp";
|
||||||
|
|
|
@ -69,7 +69,7 @@ struct TunnelLogger : public Logger
|
||||||
|
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_NEXT << (fs.s + "\n");
|
buf << STDERR_NEXT << (fs.s + "\n");
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logEI(const ErrorInfo & ei) override
|
void logEI(const ErrorInfo & ei) override
|
||||||
|
@ -81,7 +81,7 @@ struct TunnelLogger : public Logger
|
||||||
|
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_NEXT << oss.str();
|
buf << STDERR_NEXT << oss.str();
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* startWork() means that we're starting an operation for which we
|
/* startWork() means that we're starting an operation for which we
|
||||||
|
@ -129,7 +129,7 @@ struct TunnelLogger : public Logger
|
||||||
|
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
|
buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopActivity(ActivityId act) override
|
void stopActivity(ActivityId act) override
|
||||||
|
@ -137,7 +137,7 @@ struct TunnelLogger : public Logger
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_STOP_ACTIVITY << act;
|
buf << STDERR_STOP_ACTIVITY << act;
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void result(ActivityId act, ResultType type, const Fields & fields) override
|
void result(ActivityId act, ResultType type, const Fields & fields) override
|
||||||
|
@ -145,7 +145,7 @@ struct TunnelLogger : public Logger
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_RESULT << act << type << fields;
|
buf << STDERR_RESULT << act << type << fields;
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(buf.s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -468,10 +468,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
dontCheckSigs = false;
|
dontCheckSigs = false;
|
||||||
|
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
|
{
|
||||||
FramedSource source(from);
|
FramedSource source(from);
|
||||||
store->addMultipleToStore(source,
|
store->addMultipleToStore(source,
|
||||||
RepairFlag{repair},
|
RepairFlag{repair},
|
||||||
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||||
|
}
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -852,14 +854,14 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
|
|
||||||
else {
|
else {
|
||||||
std::unique_ptr<Source> source;
|
std::unique_ptr<Source> source;
|
||||||
|
StringSink saved;
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
|
||||||
source = std::make_unique<TunnelSource>(from, to);
|
source = std::make_unique<TunnelSource>(from, to);
|
||||||
else {
|
else {
|
||||||
StringSink saved;
|
|
||||||
TeeSource tee { from, saved };
|
TeeSource tee { from, saved };
|
||||||
ParseSink ether;
|
ParseSink ether;
|
||||||
parseDump(ether, tee);
|
parseDump(ether, tee);
|
||||||
source = std::make_unique<StringSource>(std::move(*saved.s));
|
source = std::make_unique<StringSource>(saved.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
|
@ -920,6 +922,22 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case wopAddBuildLog: {
|
||||||
|
StorePath path{readString(from)};
|
||||||
|
logger->startWork();
|
||||||
|
if (!trusted)
|
||||||
|
throw Error("you are not privileged to add logs");
|
||||||
|
{
|
||||||
|
FramedSource source(from);
|
||||||
|
StringSink sink;
|
||||||
|
source.drainInto(sink);
|
||||||
|
store->addBuildLog(path, sink.s);
|
||||||
|
}
|
||||||
|
logger->stopWork();
|
||||||
|
to << 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Error("invalid operation %1%", op);
|
throw Error("invalid operation %1%", op);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,20 +75,20 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
||||||
|
|
||||||
auto references = worker_proto::read(*this, source, Phantom<StorePathSet> {});
|
auto references = worker_proto::read(*this, source, Phantom<StorePathSet> {});
|
||||||
auto deriver = readString(source);
|
auto deriver = readString(source);
|
||||||
auto narHash = hashString(htSHA256, *saved.s);
|
auto narHash = hashString(htSHA256, saved.s);
|
||||||
|
|
||||||
ValidPathInfo info { path, narHash };
|
ValidPathInfo info { path, narHash };
|
||||||
if (deriver != "")
|
if (deriver != "")
|
||||||
info.deriver = parseStorePath(deriver);
|
info.deriver = parseStorePath(deriver);
|
||||||
info.references = references;
|
info.references = references;
|
||||||
info.narSize = saved.s->size();
|
info.narSize = saved.s.size();
|
||||||
|
|
||||||
// Ignore optional legacy signature.
|
// Ignore optional legacy signature.
|
||||||
if (readInt(source) == 1)
|
if (readInt(source) == 1)
|
||||||
readString(source);
|
readString(source);
|
||||||
|
|
||||||
// Can't use underlying source, which would have been exhausted
|
// Can't use underlying source, which would have been exhausted
|
||||||
auto source = StringSource { *saved.s };
|
auto source = StringSource(saved.s);
|
||||||
addToStore(info, source, NoRepair, checkSigs);
|
addToStore(info, source, NoRepair, checkSigs);
|
||||||
|
|
||||||
res.push_back(info.path);
|
res.push_back(info.path);
|
||||||
|
|
|
@ -106,7 +106,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
this->request.dataCallback(data);
|
this->request.dataCallback(data);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
this->result.data->append(data);
|
this->result.data.append(data);
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if (!request.expectedETag.empty())
|
if (!request.expectedETag.empty())
|
||||||
|
@ -195,7 +195,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (std::regex_match(line, match, statusLine)) {
|
if (std::regex_match(line, match, statusLine)) {
|
||||||
result.etag = "";
|
result.etag = "";
|
||||||
result.data = std::make_shared<std::string>();
|
result.data.clear();
|
||||||
result.bodySize = 0;
|
result.bodySize = 0;
|
||||||
statusMsg = trim(match[1]);
|
statusMsg = trim(match[1]);
|
||||||
acceptRanges = false;
|
acceptRanges = false;
|
||||||
|
@ -340,7 +340,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
if (writtenToSink)
|
if (writtenToSink)
|
||||||
curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink);
|
curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink);
|
||||||
|
|
||||||
result.data = std::make_shared<std::string>();
|
result.data.clear();
|
||||||
result.bodySize = 0;
|
result.bodySize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,21 +434,21 @@ struct curlFileTransfer : public FileTransfer
|
||||||
|
|
||||||
attempt++;
|
attempt++;
|
||||||
|
|
||||||
std::shared_ptr<std::string> response;
|
std::optional<std::string> response;
|
||||||
if (errorSink)
|
if (errorSink)
|
||||||
response = errorSink->s;
|
response = std::move(errorSink->s);
|
||||||
auto exc =
|
auto exc =
|
||||||
code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted
|
code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted
|
||||||
? FileTransferError(Interrupted, response, "%s of '%s' was interrupted", request.verb(), request.uri)
|
? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri)
|
||||||
: httpStatus != 0
|
: httpStatus != 0
|
||||||
? FileTransferError(err,
|
? FileTransferError(err,
|
||||||
response,
|
std::move(response),
|
||||||
fmt("unable to %s '%s': HTTP error %d ('%s')",
|
fmt("unable to %s '%s': HTTP error %d ('%s')",
|
||||||
request.verb(), request.uri, httpStatus, statusMsg)
|
request.verb(), request.uri, httpStatus, statusMsg)
|
||||||
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
||||||
)
|
)
|
||||||
: FileTransferError(err,
|
: FileTransferError(err,
|
||||||
response,
|
std::move(response),
|
||||||
fmt("unable to %s '%s': %s (%d)",
|
fmt("unable to %s '%s': %s (%d)",
|
||||||
request.verb(), request.uri, curl_easy_strerror(code), code));
|
request.verb(), request.uri, curl_easy_strerror(code), code));
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
FileTransferResult res;
|
FileTransferResult res;
|
||||||
if (!s3Res.data)
|
if (!s3Res.data)
|
||||||
throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
|
throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
|
||||||
res.data = s3Res.data;
|
res.data = std::move(*s3Res.data);
|
||||||
callback(std::move(res));
|
callback(std::move(res));
|
||||||
#else
|
#else
|
||||||
throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri);
|
throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri);
|
||||||
|
@ -859,7 +859,7 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
FileTransferError::FileTransferError(FileTransfer::Error error, std::shared_ptr<string> response, const Args & ... args)
|
FileTransferError::FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args)
|
||||||
: Error(args...), error(error), response(response)
|
: Error(args...), error(error), response(response)
|
||||||
{
|
{
|
||||||
const auto hf = hintfmt(args...);
|
const auto hf = hintfmt(args...);
|
||||||
|
|
|
@ -59,7 +59,7 @@ struct FileTransferRequest
|
||||||
unsigned int baseRetryTimeMs = 250;
|
unsigned int baseRetryTimeMs = 250;
|
||||||
ActivityId parentAct;
|
ActivityId parentAct;
|
||||||
bool decompress = true;
|
bool decompress = true;
|
||||||
std::shared_ptr<std::string> data;
|
std::optional<std::string> data;
|
||||||
std::string mimeType;
|
std::string mimeType;
|
||||||
std::function<void(std::string_view data)> dataCallback;
|
std::function<void(std::string_view data)> dataCallback;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ struct FileTransferResult
|
||||||
bool cached = false;
|
bool cached = false;
|
||||||
std::string etag;
|
std::string etag;
|
||||||
std::string effectiveUri;
|
std::string effectiveUri;
|
||||||
std::shared_ptr<std::string> data;
|
std::string data;
|
||||||
uint64_t bodySize = 0;
|
uint64_t bodySize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,10 +119,10 @@ class FileTransferError : public Error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileTransfer::Error error;
|
FileTransfer::Error error;
|
||||||
std::shared_ptr<string> response; // intentionally optional
|
std::optional<string> response; // intentionally optional
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
FileTransferError(FileTransfer::Error error, std::shared_ptr<string> response, const Args & ... args);
|
FileTransferError(FileTransfer::Error error, std::optional<string> response, const Args & ... args);
|
||||||
|
|
||||||
virtual const char* sname() const override { return "FileTransferError"; }
|
virtual const char* sname() const override { return "FileTransferError"; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -966,6 +966,13 @@ public:
|
||||||
|
|
||||||
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
||||||
"Whether to accept nix configuration from a flake without prompting."};
|
"Whether to accept nix configuration from a flake without prompting."};
|
||||||
|
|
||||||
|
Setting<std::string> commitLockFileSummary{
|
||||||
|
this, "", "commit-lockfile-summary",
|
||||||
|
R"(
|
||||||
|
The commit summary to use when commiting changed flake lock files. If
|
||||||
|
empty, the summary is generated based on the action performed.
|
||||||
|
)"};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ protected:
|
||||||
const std::string & mimeType) override
|
const std::string & mimeType) override
|
||||||
{
|
{
|
||||||
auto req = makeRequest(path);
|
auto req = makeRequest(path);
|
||||||
req.data = std::make_shared<string>(StreamToSourceAdapter(istream).drain());
|
req.data = StreamToSourceAdapter(istream).drain();
|
||||||
req.mimeType = mimeType;
|
req.mimeType = mimeType;
|
||||||
try {
|
try {
|
||||||
getFileTransfer()->upload(req);
|
getFileTransfer()->upload(req);
|
||||||
|
@ -159,7 +159,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
void getFile(const std::string & path,
|
void getFile(const std::string & path,
|
||||||
Callback<std::shared_ptr<std::string>> callback) noexcept override
|
Callback<std::optional<std::string>> callback) noexcept override
|
||||||
{
|
{
|
||||||
checkEnabled();
|
checkEnabled();
|
||||||
|
|
||||||
|
@ -170,10 +170,10 @@ protected:
|
||||||
getFileTransfer()->enqueueFileTransfer(request,
|
getFileTransfer()->enqueueFileTransfer(request,
|
||||||
{[callbackPtr, this](std::future<FileTransferResult> result) {
|
{[callbackPtr, this](std::future<FileTransferResult> result) {
|
||||||
try {
|
try {
|
||||||
(*callbackPtr)(result.get().data);
|
(*callbackPtr)(std::move(result.get().data));
|
||||||
} catch (FileTransferError & e) {
|
} catch (FileTransferError & e) {
|
||||||
if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden)
|
if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden)
|
||||||
return (*callbackPtr)(std::shared_ptr<std::string>());
|
return (*callbackPtr)({});
|
||||||
maybeDisable();
|
maybeDisable();
|
||||||
callbackPtr->rethrow();
|
callbackPtr->rethrow();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|
|
@ -94,7 +94,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
|
||||||
conn->sshConn->in.close();
|
conn->sshConn->in.close();
|
||||||
auto msg = conn->from.drain();
|
auto msg = conn->from.drain();
|
||||||
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
|
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
|
||||||
host, chomp(*saved.s + msg));
|
host, chomp(saved.s + msg));
|
||||||
}
|
}
|
||||||
conn->remoteVersion = readInt(conn->from);
|
conn->remoteVersion = readInt(conn->from);
|
||||||
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
|
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
|
||||||
|
|
|
@ -96,6 +96,7 @@ void LocalBinaryCacheStore::init()
|
||||||
createDirs(binaryCacheDir + "/" + realisationsPrefix);
|
createDirs(binaryCacheDir + "/" + realisationsPrefix);
|
||||||
if (writeDebugInfo)
|
if (writeDebugInfo)
|
||||||
createDirs(binaryCacheDir + "/debuginfo");
|
createDirs(binaryCacheDir + "/debuginfo");
|
||||||
|
createDirs(binaryCacheDir + "/log");
|
||||||
BinaryCacheStore::init();
|
BinaryCacheStore::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,34 +87,32 @@ void LocalFSStore::narFromPath(const StorePath & path, Sink & sink)
|
||||||
|
|
||||||
const string LocalFSStore::drvsLogDir = "drvs";
|
const string LocalFSStore::drvsLogDir = "drvs";
|
||||||
|
|
||||||
|
std::optional<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
|
||||||
|
|
||||||
std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
|
|
||||||
{
|
{
|
||||||
auto path = path_;
|
auto path = path_;
|
||||||
|
|
||||||
if (!path.isDerivation()) {
|
if (!path.isDerivation()) {
|
||||||
try {
|
try {
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
if (!info->deriver) return nullptr;
|
if (!info->deriver) return std::nullopt;
|
||||||
path = *info->deriver;
|
path = *info->deriver;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto baseName = std::string(baseNameOf(printStorePath(path)));
|
auto baseName = path.to_string();
|
||||||
|
|
||||||
for (int j = 0; j < 2; j++) {
|
for (int j = 0; j < 2; j++) {
|
||||||
|
|
||||||
Path logPath =
|
Path logPath =
|
||||||
j == 0
|
j == 0
|
||||||
? fmt("%s/%s/%s/%s", logDir, drvsLogDir, string(baseName, 0, 2), string(baseName, 2))
|
? fmt("%s/%s/%s/%s", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2))
|
||||||
: fmt("%s/%s/%s", logDir, drvsLogDir, baseName);
|
: fmt("%s/%s/%s", logDir, drvsLogDir, baseName);
|
||||||
Path logBz2Path = logPath + ".bz2";
|
Path logBz2Path = logPath + ".bz2";
|
||||||
|
|
||||||
if (pathExists(logPath))
|
if (pathExists(logPath))
|
||||||
return std::make_shared<std::string>(readFile(logPath));
|
return readFile(logPath);
|
||||||
|
|
||||||
else if (pathExists(logBz2Path)) {
|
else if (pathExists(logBz2Path)) {
|
||||||
try {
|
try {
|
||||||
|
@ -124,7 +122,7 @@ std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,8 @@ public:
|
||||||
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
|
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
|
std::optional<std::string> getBuildLog(const StorePath & path) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "topo-sort.hh"
|
#include "topo-sort.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
#include "compression.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -1461,12 +1462,12 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
||||||
|
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpString(s, sink);
|
dumpString(s, sink);
|
||||||
auto narHash = hashString(htSHA256, *sink.s);
|
auto narHash = hashString(htSHA256, sink.s);
|
||||||
|
|
||||||
optimisePath(realPath, repair);
|
optimisePath(realPath, repair);
|
||||||
|
|
||||||
ValidPathInfo info { dstPath, narHash };
|
ValidPathInfo info { dstPath, narHash };
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s.size();
|
||||||
info.references = references;
|
info.references = references;
|
||||||
info.ca = TextHash { .hash = hash };
|
info.ca = TextHash { .hash = hash };
|
||||||
registerValidPath(info);
|
registerValidPath(info);
|
||||||
|
@ -1898,4 +1899,24 @@ FixedOutputHash LocalStore::hashCAPath(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
|
{
|
||||||
|
assert(drvPath.isDerivation());
|
||||||
|
|
||||||
|
auto baseName = drvPath.to_string();
|
||||||
|
|
||||||
|
auto logPath = fmt("%s/%s/%s/%s.bz2", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2));
|
||||||
|
|
||||||
|
if (pathExists(logPath)) return;
|
||||||
|
|
||||||
|
createDirs(dirOf(logPath));
|
||||||
|
|
||||||
|
auto tmpFile = fmt("%s.tmp.%d", logPath, getpid());
|
||||||
|
|
||||||
|
writeFile(tmpFile, compress("bzip2", log));
|
||||||
|
|
||||||
|
if (rename(tmpFile.c_str(), logPath.c_str()) != 0)
|
||||||
|
throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
@ -280,6 +280,8 @@ private:
|
||||||
const std::string_view pathHash
|
const std::string_view pathHash
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||||
|
|
||||||
friend struct LocalDerivationGoal;
|
friend struct LocalDerivationGoal;
|
||||||
friend struct PathSubstitutionGoal;
|
friend struct PathSubstitutionGoal;
|
||||||
friend struct SubstitutionGoal;
|
friend struct SubstitutionGoal;
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct NarMember
|
||||||
|
|
||||||
struct NarAccessor : public FSAccessor
|
struct NarAccessor : public FSAccessor
|
||||||
{
|
{
|
||||||
std::shared_ptr<const std::string> nar;
|
std::optional<const std::string> nar;
|
||||||
|
|
||||||
GetNarBytes getNarBytes;
|
GetNarBytes getNarBytes;
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ struct NarAccessor : public FSAccessor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NarAccessor(ref<const std::string> nar) : nar(nar)
|
NarAccessor(std::string && _nar) : nar(_nar)
|
||||||
{
|
{
|
||||||
StringSource source(*nar);
|
StringSource source(*nar);
|
||||||
NarIndexer indexer(*this, source);
|
NarIndexer indexer(*this, source);
|
||||||
|
@ -224,9 +224,9 @@ struct NarAccessor : public FSAccessor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<FSAccessor> makeNarAccessor(ref<const std::string> nar)
|
ref<FSAccessor> makeNarAccessor(std::string && nar)
|
||||||
{
|
{
|
||||||
return make_ref<NarAccessor>(nar);
|
return make_ref<NarAccessor>(std::move(nar));
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<FSAccessor> makeNarAccessor(Source & source)
|
ref<FSAccessor> makeNarAccessor(Source & source)
|
||||||
|
|
|
@ -10,7 +10,7 @@ struct Source;
|
||||||
|
|
||||||
/* Return an object that provides access to the contents of a NAR
|
/* Return an object that provides access to the contents of a NAR
|
||||||
file. */
|
file. */
|
||||||
ref<FSAccessor> makeNarAccessor(ref<const std::string> nar);
|
ref<FSAccessor> makeNarAccessor(std::string && nar);
|
||||||
|
|
||||||
ref<FSAccessor> makeNarAccessor(Source & source);
|
ref<FSAccessor> makeNarAccessor(Source & source);
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ Realisation Realisation::fromJSON(
|
||||||
auto fieldIterator = json.find(fieldName);
|
auto fieldIterator = json.find(fieldName);
|
||||||
if (fieldIterator == json.end())
|
if (fieldIterator == json.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return *fieldIterator;
|
return {*fieldIterator};
|
||||||
};
|
};
|
||||||
auto getField = [&](std::string fieldName) -> std::string {
|
auto getField = [&](std::string fieldName) -> std::string {
|
||||||
if (auto field = getOptionalField(fieldName))
|
if (auto field = getOptionalField(fieldName))
|
||||||
|
|
|
@ -22,9 +22,18 @@ Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::strin
|
||||||
return fmt("%s/%s.%s", cacheDir, hashPart, ext);
|
return fmt("%s/%s.%s", cacheDir, hashPart, ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string & nar,
|
ref<FSAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar)
|
||||||
ref<FSAccessor> narAccessor)
|
|
||||||
{
|
{
|
||||||
|
if (cacheDir != "") {
|
||||||
|
try {
|
||||||
|
/* FIXME: do this asynchronously. */
|
||||||
|
writeFile(makeCacheFile(hashPart, "nar"), nar);
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto narAccessor = makeNarAccessor(std::move(nar));
|
||||||
nars.emplace(hashPart, narAccessor);
|
nars.emplace(hashPart, narAccessor);
|
||||||
|
|
||||||
if (cacheDir != "") {
|
if (cacheDir != "") {
|
||||||
|
@ -33,14 +42,12 @@ void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string &
|
||||||
JSONPlaceholder jsonRoot(str);
|
JSONPlaceholder jsonRoot(str);
|
||||||
listNar(jsonRoot, narAccessor, "", true);
|
listNar(jsonRoot, narAccessor, "", true);
|
||||||
writeFile(makeCacheFile(hashPart, "ls"), str.str());
|
writeFile(makeCacheFile(hashPart, "ls"), str.str());
|
||||||
|
|
||||||
/* FIXME: do this asynchronously. */
|
|
||||||
writeFile(makeCacheFile(hashPart, "nar"), nar);
|
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return narAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath)
|
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath)
|
||||||
|
@ -55,7 +62,6 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
|
||||||
auto i = nars.find(std::string(storePath.hashPart()));
|
auto i = nars.find(std::string(storePath.hashPart()));
|
||||||
if (i != nars.end()) return {i->second, restPath};
|
if (i != nars.end()) return {i->second, restPath};
|
||||||
|
|
||||||
StringSink sink;
|
|
||||||
std::string listing;
|
std::string listing;
|
||||||
Path cacheFile;
|
Path cacheFile;
|
||||||
|
|
||||||
|
@ -86,19 +92,15 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
|
||||||
} catch (SysError &) { }
|
} catch (SysError &) { }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
*sink.s = nix::readFile(cacheFile);
|
auto narAccessor = makeNarAccessor(nix::readFile(cacheFile));
|
||||||
|
|
||||||
auto narAccessor = makeNarAccessor(sink.s);
|
|
||||||
nars.emplace(storePath.hashPart(), narAccessor);
|
nars.emplace(storePath.hashPart(), narAccessor);
|
||||||
return {narAccessor, restPath};
|
return {narAccessor, restPath};
|
||||||
|
|
||||||
} catch (SysError &) { }
|
} catch (SysError &) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringSink sink;
|
||||||
store->narFromPath(storePath, sink);
|
store->narFromPath(storePath, sink);
|
||||||
auto narAccessor = makeNarAccessor(sink.s);
|
return {addToCache(storePath.hashPart(), std::move(sink.s)), restPath};
|
||||||
addToCache(storePath.hashPart(), *sink.s, narAccessor);
|
|
||||||
return {narAccessor, restPath};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FSAccessor::Stat RemoteFSAccessor::stat(const Path & path)
|
FSAccessor::Stat RemoteFSAccessor::stat(const Path & path)
|
||||||
|
|
|
@ -20,8 +20,7 @@ class RemoteFSAccessor : public FSAccessor
|
||||||
|
|
||||||
Path makeCacheFile(std::string_view hashPart, const std::string & ext);
|
Path makeCacheFile(std::string_view hashPart, const std::string & ext);
|
||||||
|
|
||||||
void addToCache(std::string_view hashPart, const std::string & nar,
|
ref<FSAccessor> addToCache(std::string_view hashPart, std::string && nar);
|
||||||
ref<FSAccessor> narAccessor);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ void RemoteStore::initConnection(Connection & conn)
|
||||||
it. */
|
it. */
|
||||||
conn.closeWrite();
|
conn.closeWrite();
|
||||||
auto msg = conn.from.drain();
|
auto msg = conn.from.drain();
|
||||||
throw Error("protocol mismatch, got '%s'", chomp(*saved.s + msg));
|
throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.from >> conn.daemonVersion;
|
conn.from >> conn.daemonVersion;
|
||||||
|
@ -908,6 +908,18 @@ void RemoteStore::queryMissing(const std::vector<DerivedPath> & targets,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RemoteStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
|
{
|
||||||
|
auto conn(getConnection());
|
||||||
|
conn->to << wopAddBuildLog << drvPath.to_string();
|
||||||
|
StringSource source(log);
|
||||||
|
conn.withFramedSink([&](Sink & sink) {
|
||||||
|
source.drainInto(sink);
|
||||||
|
});
|
||||||
|
readInt(conn->from);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::connect()
|
void RemoteStore::connect()
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
|
|
@ -116,6 +116,8 @@ public:
|
||||||
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
|
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
|
||||||
uint64_t & downloadSize, uint64_t & narSize) override;
|
uint64_t & downloadSize, uint64_t & narSize) override;
|
||||||
|
|
||||||
|
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||||
|
|
||||||
void connect() override;
|
void connect() override;
|
||||||
|
|
||||||
unsigned int getProtocol() override;
|
unsigned int getProtocol() override;
|
||||||
|
|
|
@ -385,7 +385,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
|
||||||
auto compress = [&](std::string compression)
|
auto compress = [&](std::string compression)
|
||||||
{
|
{
|
||||||
auto compressed = nix::compress(compression, StreamToSourceAdapter(istream).drain());
|
auto compressed = nix::compress(compression, StreamToSourceAdapter(istream).drain());
|
||||||
return std::make_shared<std::stringstream>(std::move(*compressed));
|
return std::make_shared<std::stringstream>(std::move(compressed));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
|
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include "ref.hh"
|
#include "ref.hh"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace Aws { namespace Client { class ClientConfiguration; } }
|
namespace Aws { namespace Client { class ClientConfiguration; } }
|
||||||
namespace Aws { namespace S3 { class S3Client; } }
|
namespace Aws { namespace S3 { class S3Client; } }
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ struct S3Helper
|
||||||
|
|
||||||
struct FileTransferResult
|
struct FileTransferResult
|
||||||
{
|
{
|
||||||
std::shared_ptr<std::string> data;
|
std::optional<std::string> data;
|
||||||
unsigned int durationMs;
|
unsigned int durationMs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1109,6 +1109,21 @@ void copyClosure(
|
||||||
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
|
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copyClosure(
|
||||||
|
Store & srcStore,
|
||||||
|
Store & dstStore,
|
||||||
|
const StorePathSet & storePaths,
|
||||||
|
RepairFlag repair,
|
||||||
|
CheckSigsFlag checkSigs,
|
||||||
|
SubstituteFlag substitute)
|
||||||
|
{
|
||||||
|
if (&srcStore == &dstStore) return;
|
||||||
|
|
||||||
|
StorePathSet closure;
|
||||||
|
srcStore.computeFSClosure(storePaths, closure);
|
||||||
|
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istream & str, std::optional<HashResult> hashGiven)
|
std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istream & str, std::optional<HashResult> hashGiven)
|
||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
|
@ -724,8 +724,11 @@ public:
|
||||||
|
|
||||||
/* Return the build log of the specified store path, if available,
|
/* Return the build log of the specified store path, if available,
|
||||||
or null otherwise. */
|
or null otherwise. */
|
||||||
virtual std::shared_ptr<std::string> getBuildLog(const StorePath & path)
|
virtual std::optional<std::string> getBuildLog(const StorePath & path)
|
||||||
{ return nullptr; }
|
{ return std::nullopt; }
|
||||||
|
|
||||||
|
virtual void addBuildLog(const StorePath & path, std::string_view log)
|
||||||
|
{ unsupported("addBuildLog"); }
|
||||||
|
|
||||||
/* Hack to allow long-running processes like hydra-queue-runner to
|
/* Hack to allow long-running processes like hydra-queue-runner to
|
||||||
occasionally flush their path info cache. */
|
occasionally flush their path info cache. */
|
||||||
|
@ -812,6 +815,13 @@ void copyClosure(
|
||||||
CheckSigsFlag checkSigs = CheckSigs,
|
CheckSigsFlag checkSigs = CheckSigs,
|
||||||
SubstituteFlag substitute = NoSubstitute);
|
SubstituteFlag substitute = NoSubstitute);
|
||||||
|
|
||||||
|
void copyClosure(
|
||||||
|
Store & srcStore, Store & dstStore,
|
||||||
|
const StorePathSet & paths,
|
||||||
|
RepairFlag repair = NoRepair,
|
||||||
|
CheckSigsFlag checkSigs = CheckSigs,
|
||||||
|
SubstituteFlag substitute = NoSubstitute);
|
||||||
|
|
||||||
/* Remove the temporary roots file for this process. Any temporary
|
/* Remove the temporary roots file for this process. Any temporary
|
||||||
root becomes garbage after this point unless it has been registered
|
root becomes garbage after this point unless it has been registered
|
||||||
as a (permanent) root. */
|
as a (permanent) root. */
|
||||||
|
|
|
@ -56,6 +56,7 @@ typedef enum {
|
||||||
wopRegisterDrvOutput = 42,
|
wopRegisterDrvOutput = 42,
|
||||||
wopQueryRealisation = 43,
|
wopQueryRealisation = 43,
|
||||||
wopAddMultipleToStore = 44,
|
wopAddMultipleToStore = 44,
|
||||||
|
wopAddBuildLog = 45,
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -190,13 +190,13 @@ struct BrotliDecompressionSink : ChunkedCompressionSink
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<std::string> decompress(const std::string & method, const std::string & in)
|
std::string decompress(const std::string & method, std::string_view in)
|
||||||
{
|
{
|
||||||
StringSink ssink;
|
StringSink ssink;
|
||||||
auto sink = makeDecompressionSink(method, ssink);
|
auto sink = makeDecompressionSink(method, ssink);
|
||||||
(*sink)(in);
|
(*sink)(in);
|
||||||
sink->finish();
|
sink->finish();
|
||||||
return ssink.s;
|
return std::move(ssink.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink)
|
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink)
|
||||||
|
@ -281,13 +281,13 @@ ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & next
|
||||||
throw UnknownCompressionMethod("unknown compression method '%s'", method);
|
throw UnknownCompressionMethod("unknown compression method '%s'", method);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel, int level)
|
std::string compress(const std::string & method, std::string_view in, const bool parallel, int level)
|
||||||
{
|
{
|
||||||
StringSink ssink;
|
StringSink ssink;
|
||||||
auto sink = makeCompressionSink(method, ssink, parallel, level);
|
auto sink = makeCompressionSink(method, ssink, parallel, level);
|
||||||
(*sink)(in);
|
(*sink)(in);
|
||||||
sink->finish();
|
sink->finish();
|
||||||
return ssink.s;
|
return std::move(ssink.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,11 @@ struct CompressionSink : BufferedSink, FinishSink
|
||||||
using FinishSink::finish;
|
using FinishSink::finish;
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<std::string> decompress(const std::string & method, const std::string & in);
|
std::string decompress(const std::string & method, std::string_view in);
|
||||||
|
|
||||||
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink);
|
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink);
|
||||||
|
|
||||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel = false, int level = -1);
|
std::string compress(const std::string & method, std::string_view in, const bool parallel = false, int level = -1);
|
||||||
|
|
||||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1);
|
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1);
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ public:
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
BaseError(const std::string & fs, const Args & ... args)
|
explicit BaseError(const std::string & fs, const Args & ... args)
|
||||||
: err { .level = lvlError, .msg = hintfmt(fs, args...) }
|
: err { .level = lvlError, .msg = hintfmt(fs, args...) }
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,38 @@ namespace nix {
|
||||||
|
|
||||||
void toJSON(std::ostream & str, const char * start, const char * end)
|
void toJSON(std::ostream & str, const char * start, const char * end)
|
||||||
{
|
{
|
||||||
str << '"';
|
constexpr size_t BUF_SIZE = 4096;
|
||||||
for (auto i = start; i != end; i++)
|
char buf[BUF_SIZE + 7]; // BUF_SIZE + largest single sequence of puts
|
||||||
if (*i == '\"' || *i == '\\') str << '\\' << *i;
|
size_t bufPos = 0;
|
||||||
else if (*i == '\n') str << "\\n";
|
|
||||||
else if (*i == '\r') str << "\\r";
|
const auto flush = [&] {
|
||||||
else if (*i == '\t') str << "\\t";
|
str.write(buf, bufPos);
|
||||||
else if (*i >= 0 && *i < 32)
|
bufPos = 0;
|
||||||
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
|
};
|
||||||
else str << *i;
|
const auto put = [&] (char c) {
|
||||||
str << '"';
|
buf[bufPos++] = c;
|
||||||
|
};
|
||||||
|
|
||||||
|
put('"');
|
||||||
|
for (auto i = start; i != end; i++) {
|
||||||
|
if (bufPos >= BUF_SIZE) flush();
|
||||||
|
if (*i == '\"' || *i == '\\') { put('\\'); put(*i); }
|
||||||
|
else if (*i == '\n') { put('\\'); put('n'); }
|
||||||
|
else if (*i == '\r') { put('\\'); put('r'); }
|
||||||
|
else if (*i == '\t') { put('\\'); put('t'); }
|
||||||
|
else if (*i >= 0 && *i < 32) {
|
||||||
|
const char hex[17] = "0123456789abcdef";
|
||||||
|
put('\\');
|
||||||
|
put('u');
|
||||||
|
put(hex[(uint16_t(*i) >> 12) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 8) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 4) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 0) & 0xf]);
|
||||||
|
}
|
||||||
|
else put(*i);
|
||||||
|
}
|
||||||
|
put('"');
|
||||||
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void toJSON(std::ostream & str, const char * s)
|
void toJSON(std::ostream & str, const char * s)
|
||||||
|
|
|
@ -110,7 +110,7 @@ std::string Source::drain()
|
||||||
{
|
{
|
||||||
StringSink s;
|
StringSink s;
|
||||||
drainInto(s);
|
drainInto(s);
|
||||||
return *s.s;
|
return std::move(s.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ void writeString(std::string_view data, Sink & sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sink & operator << (Sink & sink, const string & s)
|
Sink & operator << (Sink & sink, std::string_view s)
|
||||||
{
|
{
|
||||||
writeString(s, sink);
|
writeString(s, sink);
|
||||||
return sink;
|
return sink;
|
||||||
|
@ -450,11 +450,11 @@ Error readError(Source & source)
|
||||||
void StringSink::operator () (std::string_view data)
|
void StringSink::operator () (std::string_view data)
|
||||||
{
|
{
|
||||||
static bool warned = false;
|
static bool warned = false;
|
||||||
if (!warned && s->size() > threshold) {
|
if (!warned && s.size() > threshold) {
|
||||||
warnLargeDump();
|
warnLargeDump();
|
||||||
warned = true;
|
warned = true;
|
||||||
}
|
}
|
||||||
s->append(data);
|
s.append(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ChainSource::read(char * data, size_t len)
|
size_t ChainSource::read(char * data, size_t len)
|
||||||
|
|
|
@ -154,12 +154,13 @@ private:
|
||||||
/* A sink that writes data to a string. */
|
/* A sink that writes data to a string. */
|
||||||
struct StringSink : Sink
|
struct StringSink : Sink
|
||||||
{
|
{
|
||||||
ref<std::string> s;
|
std::string s;
|
||||||
StringSink() : s(make_ref<std::string>()) { };
|
StringSink() { }
|
||||||
explicit StringSink(const size_t reservedSize) : s(make_ref<std::string>()) {
|
explicit StringSink(const size_t reservedSize)
|
||||||
s->reserve(reservedSize);
|
{
|
||||||
|
s.reserve(reservedSize);
|
||||||
};
|
};
|
||||||
StringSink(ref<std::string> s) : s(s) { };
|
StringSink(std::string && s) : s(std::move(s)) { };
|
||||||
void operator () (std::string_view data) override;
|
void operator () (std::string_view data) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,9 +168,9 @@ struct StringSink : Sink
|
||||||
/* A source that reads data from a string. */
|
/* A source that reads data from a string. */
|
||||||
struct StringSource : Source
|
struct StringSource : Source
|
||||||
{
|
{
|
||||||
const string & s;
|
std::string_view s;
|
||||||
size_t pos;
|
size_t pos;
|
||||||
StringSource(const string & _s) : s(_s), pos(0) { }
|
StringSource(std::string_view s) : s(s), pos(0) { }
|
||||||
size_t read(char * data, size_t len) override;
|
size_t read(char * data, size_t len) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -317,10 +318,10 @@ inline Sink & operator << (Sink & sink, uint64_t n)
|
||||||
return sink;
|
return sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sink & operator << (Sink & sink, const string & s);
|
Sink & operator << (Sink & in, const Error & ex);
|
||||||
|
Sink & operator << (Sink & sink, std::string_view s);
|
||||||
Sink & operator << (Sink & sink, const Strings & s);
|
Sink & operator << (Sink & sink, const Strings & s);
|
||||||
Sink & operator << (Sink & sink, const StringSet & s);
|
Sink & operator << (Sink & sink, const StringSet & s);
|
||||||
Sink & operator << (Sink & in, const Error & ex);
|
|
||||||
|
|
||||||
|
|
||||||
MakeError(SerialisationError, Error);
|
MakeError(SerialisationError, Error);
|
||||||
|
|
|
@ -12,17 +12,17 @@ namespace nix {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(compress, noneMethodDoesNothingToTheInput) {
|
TEST(compress, noneMethodDoesNothingToTheInput) {
|
||||||
ref<std::string> o = compress("none", "this-is-a-test");
|
auto o = compress("none", "this-is-a-test");
|
||||||
|
|
||||||
ASSERT_EQ(*o, "this-is-a-test");
|
ASSERT_EQ(o, "this-is-a-test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressNoneCompressed) {
|
TEST(decompress, decompressNoneCompressed) {
|
||||||
auto method = "none";
|
auto method = "none";
|
||||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||||
ref<std::string> o = decompress(method, str);
|
auto o = decompress(method, str);
|
||||||
|
|
||||||
ASSERT_EQ(*o, str);
|
ASSERT_EQ(o, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressEmptyCompressed) {
|
TEST(decompress, decompressEmptyCompressed) {
|
||||||
|
@ -30,33 +30,33 @@ namespace nix {
|
||||||
// (Content-Encoding == "").
|
// (Content-Encoding == "").
|
||||||
auto method = "";
|
auto method = "";
|
||||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||||
ref<std::string> o = decompress(method, str);
|
auto o = decompress(method, str);
|
||||||
|
|
||||||
ASSERT_EQ(*o, str);
|
ASSERT_EQ(o, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressXzCompressed) {
|
TEST(decompress, decompressXzCompressed) {
|
||||||
auto method = "xz";
|
auto method = "xz";
|
||||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||||
ref<std::string> o = decompress(method, *compress(method, str));
|
auto o = decompress(method, compress(method, str));
|
||||||
|
|
||||||
ASSERT_EQ(*o, str);
|
ASSERT_EQ(o, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressBzip2Compressed) {
|
TEST(decompress, decompressBzip2Compressed) {
|
||||||
auto method = "bzip2";
|
auto method = "bzip2";
|
||||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||||
ref<std::string> o = decompress(method, *compress(method, str));
|
auto o = decompress(method, compress(method, str));
|
||||||
|
|
||||||
ASSERT_EQ(*o, str);
|
ASSERT_EQ(o, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressBrCompressed) {
|
TEST(decompress, decompressBrCompressed) {
|
||||||
auto method = "br";
|
auto method = "br";
|
||||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||||
ref<std::string> o = decompress(method, *compress(method, str));
|
auto o = decompress(method, compress(method, str));
|
||||||
|
|
||||||
ASSERT_EQ(*o, str);
|
ASSERT_EQ(o, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(decompress, decompressInvalidInputThrowsCompressionError) {
|
TEST(decompress, decompressInvalidInputThrowsCompressionError) {
|
||||||
|
@ -77,7 +77,7 @@ namespace nix {
|
||||||
(*sink)(inputString);
|
(*sink)(inputString);
|
||||||
sink->finish();
|
sink->finish();
|
||||||
|
|
||||||
ASSERT_STREQ((*strSink.s).c_str(), inputString);
|
ASSERT_STREQ(strSink.s.c_str(), inputString);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(makeCompressionSink, compressAndDecompress) {
|
TEST(makeCompressionSink, compressAndDecompress) {
|
||||||
|
@ -90,7 +90,7 @@ namespace nix {
|
||||||
sink->finish();
|
sink->finish();
|
||||||
decompressionSink->finish();
|
decompressionSink->finish();
|
||||||
|
|
||||||
ASSERT_STREQ((*strSink.s).c_str(), inputString);
|
ASSERT_STREQ(strSink.s.c_str(), inputString);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ typedef std::map<string, string> StringMap;
|
||||||
/* Paths are just strings. */
|
/* Paths are just strings. */
|
||||||
|
|
||||||
typedef string Path;
|
typedef string Path;
|
||||||
|
typedef std::string_view PathView;
|
||||||
typedef list<Path> Paths;
|
typedef list<Path> Paths;
|
||||||
typedef set<Path> PathSet;
|
typedef set<Path> PathSet;
|
||||||
|
|
||||||
|
|
|
@ -106,16 +106,16 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path canonPath(const Path & path, bool resolveSymlinks)
|
Path canonPath(PathView path, bool resolveSymlinks)
|
||||||
{
|
{
|
||||||
assert(path != "");
|
assert(path != "");
|
||||||
|
|
||||||
string s;
|
string s;
|
||||||
|
s.reserve(256);
|
||||||
|
|
||||||
if (path[0] != '/')
|
if (path[0] != '/')
|
||||||
throw Error("not an absolute path: '%1%'", path);
|
throw Error("not an absolute path: '%1%'", path);
|
||||||
|
|
||||||
string::const_iterator i = path.begin(), end = path.end();
|
|
||||||
string temp;
|
string temp;
|
||||||
|
|
||||||
/* Count the number of times we follow a symlink and stop at some
|
/* Count the number of times we follow a symlink and stop at some
|
||||||
|
@ -125,33 +125,37 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
/* Skip slashes. */
|
/* Skip slashes. */
|
||||||
while (i != end && *i == '/') i++;
|
while (!path.empty() && path[0] == '/') path.remove_prefix(1);
|
||||||
if (i == end) break;
|
if (path.empty()) break;
|
||||||
|
|
||||||
/* Ignore `.'. */
|
/* Ignore `.'. */
|
||||||
if (*i == '.' && (i + 1 == end || i[1] == '/'))
|
if (path == "." || path.substr(0, 2) == "./")
|
||||||
i++;
|
path.remove_prefix(1);
|
||||||
|
|
||||||
/* If `..', delete the last component. */
|
/* If `..', delete the last component. */
|
||||||
else if (*i == '.' && i + 1 < end && i[1] == '.' &&
|
else if (path == ".." || path.substr(0, 3) == "../")
|
||||||
(i + 2 == end || i[2] == '/'))
|
|
||||||
{
|
{
|
||||||
if (!s.empty()) s.erase(s.rfind('/'));
|
if (!s.empty()) s.erase(s.rfind('/'));
|
||||||
i += 2;
|
path.remove_prefix(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normal component; copy it. */
|
/* Normal component; copy it. */
|
||||||
else {
|
else {
|
||||||
s += '/';
|
s += '/';
|
||||||
while (i != end && *i != '/') s += *i++;
|
if (const auto slash = path.find('/'); slash == string::npos) {
|
||||||
|
s += path;
|
||||||
|
path = {};
|
||||||
|
} else {
|
||||||
|
s += path.substr(0, slash);
|
||||||
|
path = path.substr(slash + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* If s points to a symlink, resolve it and continue from there */
|
/* If s points to a symlink, resolve it and continue from there */
|
||||||
if (resolveSymlinks && isLink(s)) {
|
if (resolveSymlinks && isLink(s)) {
|
||||||
if (++followCount >= maxFollow)
|
if (++followCount >= maxFollow)
|
||||||
throw Error("infinite symlink recursion in path '%1%'", path);
|
throw Error("infinite symlink recursion in path '%1%'", path);
|
||||||
temp = readLink(s) + string(i, end);
|
temp = concatStrings(readLink(s), path);
|
||||||
i = temp.begin();
|
path = temp;
|
||||||
end = temp.end();
|
|
||||||
if (!temp.empty() && temp[0] == '/') {
|
if (!temp.empty() && temp[0] == '/') {
|
||||||
s.clear(); /* restart for symlinks pointing to absolute path */
|
s.clear(); /* restart for symlinks pointing to absolute path */
|
||||||
} else {
|
} else {
|
||||||
|
@ -164,7 +168,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.empty() ? "/" : s;
|
return s.empty() ? "/" : std::move(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -668,9 +672,11 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
|
||||||
|
|
||||||
string drainFD(int fd, bool block, const size_t reserveSize)
|
string drainFD(int fd, bool block, const size_t reserveSize)
|
||||||
{
|
{
|
||||||
StringSink sink(reserveSize);
|
// the parser needs two extra bytes to append terminating characters, other users will
|
||||||
|
// not care very much about the extra memory.
|
||||||
|
StringSink sink(reserveSize + 2);
|
||||||
drainFD(fd, sink, block);
|
drainFD(fd, sink, block);
|
||||||
return std::move(*sink.s);
|
return std::move(sink.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1055,7 +1061,7 @@ std::pair<int, std::string> runProgram(RunOptions && options)
|
||||||
status = e.status;
|
status = e.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {status, std::move(*sink.s)};
|
return {status, std::move(sink.s)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void runProgram2(const RunOptions & options)
|
void runProgram2(const RunOptions & options)
|
||||||
|
@ -1229,23 +1235,22 @@ void _interrupted()
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
template<class C> C tokenizeString(std::string_view s, const string & separators)
|
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
||||||
{
|
{
|
||||||
C result;
|
C result;
|
||||||
string::size_type pos = s.find_first_not_of(separators, 0);
|
string::size_type pos = s.find_first_not_of(separators, 0);
|
||||||
while (pos != string::npos) {
|
while (pos != string::npos) {
|
||||||
string::size_type end = s.find_first_of(separators, pos + 1);
|
string::size_type end = s.find_first_of(separators, pos + 1);
|
||||||
if (end == string::npos) end = s.size();
|
if (end == string::npos) end = s.size();
|
||||||
string token(s, pos, end - pos);
|
result.insert(result.end(), string(s, pos, end - pos));
|
||||||
result.insert(result.end(), token);
|
|
||||||
pos = s.find_first_not_of(separators, end);
|
pos = s.find_first_not_of(separators, end);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template Strings tokenizeString(std::string_view s, const string & separators);
|
template Strings tokenizeString(std::string_view s, std::string_view separators);
|
||||||
template StringSet tokenizeString(std::string_view s, const string & separators);
|
template StringSet tokenizeString(std::string_view s, std::string_view separators);
|
||||||
template vector<string> tokenizeString(std::string_view s, const string & separators);
|
template vector<string> tokenizeString(std::string_view s, std::string_view separators);
|
||||||
|
|
||||||
|
|
||||||
string chomp(std::string_view s)
|
string chomp(std::string_view s)
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -54,7 +56,7 @@ Path absPath(Path path,
|
||||||
double or trailing slashes. Optionally resolves all symlink
|
double or trailing slashes. Optionally resolves all symlink
|
||||||
components such that each component of the resulting path is *not*
|
components such that each component of the resulting path is *not*
|
||||||
a symbolic link. */
|
a symbolic link. */
|
||||||
Path canonPath(const Path & path, bool resolveSymlinks = false);
|
Path canonPath(PathView path, bool resolveSymlinks = false);
|
||||||
|
|
||||||
/* Return the directory part of the given canonical path, i.e.,
|
/* Return the directory part of the given canonical path, i.e.,
|
||||||
everything before the final `/'. If the path is the root or an
|
everything before the final `/'. If the path is the root or an
|
||||||
|
@ -366,15 +368,19 @@ MakeError(FormatError, Error);
|
||||||
|
|
||||||
|
|
||||||
/* String tokenizer. */
|
/* String tokenizer. */
|
||||||
template<class C> C tokenizeString(std::string_view s, const string & separators = " \t\n\r");
|
template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||||
|
|
||||||
|
|
||||||
/* Concatenate the given strings with a separator between the
|
/* Concatenate the given strings with a separator between the
|
||||||
elements. */
|
elements. */
|
||||||
template<class C>
|
template<class C>
|
||||||
string concatStringsSep(const string & sep, const C & ss)
|
string concatStringsSep(const std::string_view sep, const C & ss)
|
||||||
{
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
// need a cast to string_view since this is also called with Symbols
|
||||||
|
for (const auto & s : ss) size += sep.size() + std::string_view(s).size();
|
||||||
string s;
|
string s;
|
||||||
|
s.reserve(size);
|
||||||
for (auto & i : ss) {
|
for (auto & i : ss) {
|
||||||
if (s.size() != 0) s += sep;
|
if (s.size() != 0) s += sep;
|
||||||
s += i;
|
s += i;
|
||||||
|
@ -382,6 +388,14 @@ string concatStringsSep(const string & sep, const C & ss)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class ... Parts>
|
||||||
|
auto concatStrings(Parts && ... parts)
|
||||||
|
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), string>
|
||||||
|
{
|
||||||
|
std::string_view views[sizeof...(parts)] = { parts... };
|
||||||
|
return concatStringsSep({}, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Add quotes around a collection of strings. */
|
/* Add quotes around a collection of strings. */
|
||||||
template<class C> Strings quoteStrings(const C & c)
|
template<class C> Strings quoteStrings(const C & c)
|
||||||
|
@ -419,21 +433,21 @@ bool statusOk(int status);
|
||||||
|
|
||||||
/* Parse a string into an integer. */
|
/* Parse a string into an integer. */
|
||||||
template<class N>
|
template<class N>
|
||||||
std::optional<N> string2Int(const std::string & s)
|
std::optional<N> string2Int(const std::string_view s)
|
||||||
{
|
{
|
||||||
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
std::istringstream str(s);
|
try {
|
||||||
N n;
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
str >> n;
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
if (str && str.get() == EOF) return n;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
||||||
'T' denoting a binary unit prefix. */
|
'T' denoting a binary unit prefix. */
|
||||||
template<class N>
|
template<class N>
|
||||||
N string2IntWithUnitPrefix(std::string s)
|
N string2IntWithUnitPrefix(std::string_view s)
|
||||||
{
|
{
|
||||||
N multiplier = 1;
|
N multiplier = 1;
|
||||||
if (!s.empty()) {
|
if (!s.empty()) {
|
||||||
|
@ -444,7 +458,7 @@ N string2IntWithUnitPrefix(std::string s)
|
||||||
else if (u == 'G') multiplier = 1ULL << 30;
|
else if (u == 'G') multiplier = 1ULL << 30;
|
||||||
else if (u == 'T') multiplier = 1ULL << 40;
|
else if (u == 'T') multiplier = 1ULL << 40;
|
||||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||||
s.resize(s.size() - 1);
|
s.remove_suffix(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto n = string2Int<N>(s))
|
if (auto n = string2Int<N>(s))
|
||||||
|
@ -454,14 +468,14 @@ N string2IntWithUnitPrefix(std::string s)
|
||||||
|
|
||||||
/* Parse a string into a float. */
|
/* Parse a string into a float. */
|
||||||
template<class N>
|
template<class N>
|
||||||
std::optional<N> string2Float(const string & s)
|
std::optional<N> string2Float(const std::string_view s)
|
||||||
{
|
{
|
||||||
std::istringstream str(s);
|
try {
|
||||||
N n;
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
str >> n;
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
if (str && str.get() == EOF) return n;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return true iff `s' starts with `prefix'. */
|
/* Return true iff `s' starts with `prefix'. */
|
||||||
|
|
|
@ -292,7 +292,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
else
|
else
|
||||||
for (auto i : left) {
|
for (auto i : left) {
|
||||||
if (fromArgs)
|
if (fromArgs)
|
||||||
exprs.push_back(state->parseExprFromString(i, absPath(".")));
|
exprs.push_back(state->parseExprFromString(std::move(i), absPath(".")));
|
||||||
else {
|
else {
|
||||||
auto absolute = i;
|
auto absolute = i;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpPath(path, sink);
|
dumpPath(path, sink);
|
||||||
|
|
||||||
auto narHash = hashString(htSHA256, *sink.s);
|
auto narHash = hashString(htSHA256, sink.s);
|
||||||
|
|
||||||
Hash hash = narHash;
|
Hash hash = narHash;
|
||||||
if (ingestionMethod == FileIngestionMethod::Flat) {
|
if (ingestionMethod == FileIngestionMethod::Flat) {
|
||||||
|
@ -45,14 +45,14 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
||||||
store->makeFixedOutputPath(ingestionMethod, hash, *namePart),
|
store->makeFixedOutputPath(ingestionMethod, hash, *namePart),
|
||||||
narHash,
|
narHash,
|
||||||
};
|
};
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s.size();
|
||||||
info.ca = std::optional { FixedOutputHash {
|
info.ca = std::optional { FixedOutputHash {
|
||||||
.method = ingestionMethod,
|
.method = ingestionMethod,
|
||||||
.hash = hash,
|
.hash = hash,
|
||||||
} };
|
} };
|
||||||
|
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
auto source = StringSource { *sink.s };
|
auto source = StringSource(sink.s);
|
||||||
store->addToStore(info, source);
|
store->addToStore(info, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct InstallableDerivedPath : Installable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string what() override { return derivedPath.to_string(*store); }
|
std::string what() const override { return derivedPath.to_string(*store); }
|
||||||
|
|
||||||
DerivedPaths toDerivedPaths() override
|
DerivedPaths toDerivedPaths() override
|
||||||
{
|
{
|
||||||
|
|
|
@ -78,7 +78,7 @@ struct CmdCatNar : StoreCommand, MixCat
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
void run(ref<Store> store) override
|
||||||
{
|
{
|
||||||
cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
|
cat(makeNarAccessor(readFile(narPath)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "sync.hh"
|
|
||||||
#include "thread-pool.hh"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
struct CmdCopy : BuiltPathsCommand
|
struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand
|
||||||
{
|
{
|
||||||
std::string srcUri, dstUri;
|
|
||||||
|
|
||||||
CheckSigsFlag checkSigs = CheckSigs;
|
CheckSigsFlag checkSigs = CheckSigs;
|
||||||
|
|
||||||
SubstituteFlag substitute = NoSubstitute;
|
SubstituteFlag substitute = NoSubstitute;
|
||||||
|
@ -21,20 +15,6 @@ struct CmdCopy : BuiltPathsCommand
|
||||||
CmdCopy()
|
CmdCopy()
|
||||||
: BuiltPathsCommand(true)
|
: BuiltPathsCommand(true)
|
||||||
{
|
{
|
||||||
addFlag({
|
|
||||||
.longName = "from",
|
|
||||||
.description = "URL of the source Nix store.",
|
|
||||||
.labels = {"store-uri"},
|
|
||||||
.handler = {&srcUri},
|
|
||||||
});
|
|
||||||
|
|
||||||
addFlag({
|
|
||||||
.longName = "to",
|
|
||||||
.description = "URL of the destination Nix store.",
|
|
||||||
.labels = {"store-uri"},
|
|
||||||
.handler = {&dstUri},
|
|
||||||
});
|
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "no-check-sigs",
|
.longName = "no-check-sigs",
|
||||||
.description = "Do not require that paths are signed by trusted keys.",
|
.description = "Do not require that paths are signed by trusted keys.",
|
||||||
|
@ -65,22 +45,9 @@ struct CmdCopy : BuiltPathsCommand
|
||||||
|
|
||||||
Category category() override { return catSecondary; }
|
Category category() override { return catSecondary; }
|
||||||
|
|
||||||
ref<Store> createStore() override
|
|
||||||
{
|
|
||||||
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
|
||||||
{
|
|
||||||
if (srcUri.empty() && dstUri.empty())
|
|
||||||
throw UsageError("you must pass '--from' and/or '--to'");
|
|
||||||
|
|
||||||
BuiltPathsCommand::run(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(ref<Store> srcStore, BuiltPaths && paths) override
|
void run(ref<Store> srcStore, BuiltPaths && paths) override
|
||||||
{
|
{
|
||||||
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
|
auto dstStore = getDstStore();
|
||||||
|
|
||||||
RealisedPath::Set stuffToCopy;
|
RealisedPath::Set stuffToCopy;
|
||||||
|
|
||||||
|
|
|
@ -137,15 +137,6 @@ Currently the `type` attribute can be one of the following:
|
||||||
*path* must be a directory in the file system containing a file
|
*path* must be a directory in the file system containing a file
|
||||||
named `flake.nix`.
|
named `flake.nix`.
|
||||||
|
|
||||||
If the directory or any of its parents is a Git repository, then
|
|
||||||
this is essentially equivalent to `git+file://<path>` (see below),
|
|
||||||
except that the `dir` parameter is derived automatically. For
|
|
||||||
example, if `/foo/bar` is a Git repository, then the flake reference
|
|
||||||
`/foo/bar/flake` is equivalent to `/foo/bar?dir=flake`.
|
|
||||||
|
|
||||||
If the directory is not inside a Git repository, then the flake
|
|
||||||
contents is the entire contents of *path*.
|
|
||||||
|
|
||||||
*path* generally must be an absolute path. However, on the command
|
*path* generally must be an absolute path. However, on the command
|
||||||
line, it can be a relative path (e.g. `.` or `./foo`) which is
|
line, it can be a relative path (e.g. `.` or `./foo`) which is
|
||||||
interpreted as relative to the current directory. In this case, it
|
interpreted as relative to the current directory. In this case, it
|
||||||
|
|
|
@ -157,7 +157,7 @@ struct CmdLsNar : Command, MixLs
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
list(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
|
list(makeNarAccessor(readFile(narPath)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ struct HelpRequested { };
|
||||||
|
|
||||||
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
{
|
{
|
||||||
bool printBuildLogs = false;
|
|
||||||
bool useNet = true;
|
bool useNet = true;
|
||||||
bool refresh = false;
|
bool refresh = false;
|
||||||
bool showVersion = false;
|
bool showVersion = false;
|
||||||
|
@ -270,11 +269,15 @@ void mainWrapped(int argc, char * * argv)
|
||||||
if (legacy) return legacy(argc, argv);
|
if (legacy) return legacy(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
verbosity = lvlNotice;
|
|
||||||
settings.verboseBuild = false;
|
|
||||||
evalSettings.pureEval = true;
|
evalSettings.pureEval = true;
|
||||||
|
|
||||||
setLogFormat("bar");
|
setLogFormat("bar");
|
||||||
|
settings.verboseBuild = false;
|
||||||
|
if (isatty(STDERR_FILENO)) {
|
||||||
|
verbosity = lvlNotice;
|
||||||
|
} else {
|
||||||
|
verbosity = lvlInfo;
|
||||||
|
}
|
||||||
|
|
||||||
Finally f([] { logger->stop(); });
|
Finally f([] { logger->stop(); });
|
||||||
|
|
||||||
|
|
|
@ -61,10 +61,10 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*sink.s = rewriteStrings(*sink.s, rewrites);
|
sink.s = rewriteStrings(sink.s, rewrites);
|
||||||
|
|
||||||
HashModuloSink hashModuloSink(htSHA256, oldHashPart);
|
HashModuloSink hashModuloSink(htSHA256, oldHashPart);
|
||||||
hashModuloSink(*sink.s);
|
hashModuloSink(sink.s);
|
||||||
|
|
||||||
auto narHash = hashModuloSink.finish().first;
|
auto narHash = hashModuloSink.finish().first;
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
|
||||||
};
|
};
|
||||||
info.references = std::move(references);
|
info.references = std::move(references);
|
||||||
if (hasSelfReference) info.references.insert(info.path);
|
if (hasSelfReference) info.references.insert(info.path);
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s.size();
|
||||||
info.ca = FixedOutputHash {
|
info.ca = FixedOutputHash {
|
||||||
.method = FileIngestionMethod::Recursive,
|
.method = FileIngestionMethod::Recursive,
|
||||||
.hash = info.narHash,
|
.hash = info.narHash,
|
||||||
|
@ -85,7 +85,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
|
||||||
|
|
||||||
auto source = sinkToSource([&](Sink & nextSink) {
|
auto source = sinkToSource([&](Sink & nextSink) {
|
||||||
RewritingSink rsink2(oldHashPart, std::string(info.path.hashPart()), nextSink);
|
RewritingSink rsink2(oldHashPart, std::string(info.path.hashPart()), nextSink);
|
||||||
rsink2(*sink.s);
|
rsink2(sink.s);
|
||||||
rsink2.flush();
|
rsink2.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,46 @@ the Nix store. Here are the recognised types of installables:
|
||||||
flake reference and *attrpath* is an optional attribute path. For
|
flake reference and *attrpath* is an optional attribute path. For
|
||||||
more information on flakes, see [the `nix flake` manual
|
more information on flakes, see [the `nix flake` manual
|
||||||
page](./nix3-flake.md). Flake references are most commonly a flake
|
page](./nix3-flake.md). Flake references are most commonly a flake
|
||||||
identifier in the flake registry (e.g. `nixpkgs`) or a path
|
identifier in the flake registry (e.g. `nixpkgs`), or a raw path
|
||||||
(e.g. `/path/to/my-flake` or `.`).
|
(e.g. `/path/to/my-flake` or `.` or `../foo`), or a full URL
|
||||||
|
(e.g. `github:nixos/nixpkgs` or `path:.`)
|
||||||
|
|
||||||
|
When the flake reference is a raw path (a path without any URL
|
||||||
|
scheme), it is interpreted as a `path:` or `git+file:` url in the following
|
||||||
|
way:
|
||||||
|
|
||||||
|
- If the path is within a Git repository, then the url will be of the form
|
||||||
|
`git+file://[GIT_REPO_ROOT]?dir=[RELATIVE_FLAKE_DIR_PATH]`
|
||||||
|
where `GIT_REPO_ROOT` is the path to the root of the git repository,
|
||||||
|
and `RELATIVE_FLAKE_DIR_PATH` is the path (relative to the directory
|
||||||
|
root) of the closest parent of the given path that contains a `flake.nix` within
|
||||||
|
the git repository.
|
||||||
|
If no such directory exists, then Nix will error-out.
|
||||||
|
|
||||||
|
Note that the search will only include files indexed by git. In particular, files
|
||||||
|
which are matched by `.gitignore` or have never been `git add`-ed will not be
|
||||||
|
available in the flake. If this is undesireable, specify `path:<directory>` explicitly;
|
||||||
|
|
||||||
|
For example, if `/foo/bar` is a git repository with the following structure:
|
||||||
|
```
|
||||||
|
.
|
||||||
|
└── baz
|
||||||
|
├── blah
|
||||||
|
│ └── file.txt
|
||||||
|
└── flake.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `/foo/bar/baz/blah` will resolve to `git+file:///foo/bar?dir=baz`
|
||||||
|
|
||||||
|
- If the supplied path is not a git repository, then the url will have the form
|
||||||
|
`path:FLAKE_DIR_PATH` where `FLAKE_DIR_PATH` is the closest parent
|
||||||
|
of the supplied path that contains a `flake.nix` file (within the same file-system).
|
||||||
|
If no such directory exists, then Nix will error-out.
|
||||||
|
|
||||||
|
For example, if `/foo/bar/flake.nix` exists, then `/foo/bar/baz/` will resolve to
|
||||||
|
`path:/foo/bar`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If *attrpath* is omitted, Nix tries some default values; for most
|
If *attrpath* is omitted, Nix tries some default values; for most
|
||||||
subcommands, the default is `defaultPackage.`*system*
|
subcommands, the default is `defaultPackage.`*system*
|
||||||
|
|
|
@ -157,17 +157,17 @@ struct ProfileManifest
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpPath(tempDir, sink);
|
dumpPath(tempDir, sink);
|
||||||
|
|
||||||
auto narHash = hashString(htSHA256, *sink.s);
|
auto narHash = hashString(htSHA256, sink.s);
|
||||||
|
|
||||||
ValidPathInfo info {
|
ValidPathInfo info {
|
||||||
store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references),
|
store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references),
|
||||||
narHash,
|
narHash,
|
||||||
};
|
};
|
||||||
info.references = std::move(references);
|
info.references = std::move(references);
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s.size();
|
||||||
info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash };
|
info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash };
|
||||||
|
|
||||||
auto source = StringSource { *sink.s };
|
StringSource source(sink.s);
|
||||||
store->addToStore(info, source);
|
store->addToStore(info, source);
|
||||||
|
|
||||||
return std::move(info.path);
|
return std::move(info.path);
|
||||||
|
|
|
@ -703,7 +703,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||||
|
|
||||||
Expr * NixRepl::parseString(string s)
|
Expr * NixRepl::parseString(string s)
|
||||||
{
|
{
|
||||||
Expr * e = state->parseExprFromString(s, curDir, staticEnv);
|
Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
46
src/nix/store-copy-log.cc
Normal file
46
src/nix/store-copy-log.cc
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "command.hh"
|
||||||
|
#include "shared.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
#include "thread-pool.hh"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "copy build logs between Nix stores";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "store-copy-log.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override { return catUtility; }
|
||||||
|
|
||||||
|
void run(ref<Store> srcStore) override
|
||||||
|
{
|
||||||
|
auto dstStore = getDstStore();
|
||||||
|
|
||||||
|
StorePathSet drvPaths;
|
||||||
|
|
||||||
|
for (auto & i : installables)
|
||||||
|
for (auto & drvPath : i->toDrvPaths(getEvalStore()))
|
||||||
|
drvPaths.insert(drvPath);
|
||||||
|
|
||||||
|
for (auto & drvPath : drvPaths) {
|
||||||
|
if (auto log = srcStore->getBuildLog(drvPath))
|
||||||
|
dstStore->addBuildLog(drvPath, *log);
|
||||||
|
else
|
||||||
|
throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdCopyLog = registerCommand2<CmdCopyLog>({"store", "copy-log"});
|
33
src/nix/store-copy-log.md
Normal file
33
src/nix/store-copy-log.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* To copy the build log of the `hello` package from
|
||||||
|
https://cache.nixos.org to the local store:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix store copy-log --from https://cache.nixos.org --eval-store auto nixpkgs#hello
|
||||||
|
```
|
||||||
|
|
||||||
|
You can verify that the log is available locally:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix log --substituters '' nixpkgs#hello
|
||||||
|
```
|
||||||
|
|
||||||
|
(The flag `--substituters ''` avoids querying
|
||||||
|
`https://cache.nixos.org` for the log.)
|
||||||
|
|
||||||
|
* To copy the log for a specific store derivation via SSH:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix store copy-log --to ssh-ng://machine /nix/store/ilgm50plpmcgjhcp33z6n4qbnpqfhxym-glibc-2.33-59.drv
|
||||||
|
```
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
`nix store copy-log` copies build logs between two Nix stores. The
|
||||||
|
source store is specified using `--from` and the destination using
|
||||||
|
`--to`. If one of these is omitted, it defaults to the local store.
|
||||||
|
|
||||||
|
)""
|
|
@ -140,7 +140,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
||||||
|
|
||||||
auto state = std::make_unique<EvalState>(Strings(), store);
|
auto state = std::make_unique<EvalState>(Strings(), store);
|
||||||
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).first;
|
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
{
|
{
|
||||||
std::string _package, _dependency;
|
std::string _package, _dependency;
|
||||||
bool all = false;
|
bool all = false;
|
||||||
|
bool precise = false;
|
||||||
|
|
||||||
CmdWhyDepends()
|
CmdWhyDepends()
|
||||||
{
|
{
|
||||||
|
@ -56,6 +57,12 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
.description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
|
.description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
|
||||||
.handler = {&all, true},
|
.handler = {&all, true},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "precise",
|
||||||
|
.description = "For each edge in the dependency graph, show the files in the parent that cause the dependency.",
|
||||||
|
.handler = {&precise, true},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
@ -158,11 +165,19 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
auto pathS = store->printStorePath(node.path);
|
auto pathS = store->printStorePath(node.path);
|
||||||
|
|
||||||
assert(node.dist != inf);
|
assert(node.dist != inf);
|
||||||
|
if (precise) {
|
||||||
logger->cout("%s%s%s%s" ANSI_NORMAL,
|
logger->cout("%s%s%s%s" ANSI_NORMAL,
|
||||||
firstPad,
|
firstPad,
|
||||||
node.visited ? "\e[38;5;244m" : "",
|
node.visited ? "\e[38;5;244m" : "",
|
||||||
firstPad != "" ? "→ " : "",
|
firstPad != "" ? "→ " : "",
|
||||||
pathS);
|
pathS);
|
||||||
|
} else {
|
||||||
|
logger->cout("%s%s%s%s" ANSI_NORMAL,
|
||||||
|
firstPad,
|
||||||
|
node.visited ? "\e[38;5;244m" : "",
|
||||||
|
firstPad != "" ? treeLast : "",
|
||||||
|
pathS);
|
||||||
|
}
|
||||||
|
|
||||||
if (node.path == dependencyPath && !all
|
if (node.path == dependencyPath && !all
|
||||||
&& packagePath != dependencyPath)
|
&& packagePath != dependencyPath)
|
||||||
|
@ -237,9 +252,8 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
|
|
||||||
// FIXME: should use scanForReferences().
|
// FIXME: should use scanForReferences().
|
||||||
|
|
||||||
visitPath(pathS);
|
if (precise) visitPath(pathS);
|
||||||
|
|
||||||
RunPager pager;
|
|
||||||
for (auto & ref : refs) {
|
for (auto & ref : refs) {
|
||||||
std::string hash(ref.second->path.hashPart());
|
std::string hash(ref.second->path.hashPart());
|
||||||
|
|
||||||
|
@ -259,6 +273,7 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RunPager pager;
|
||||||
try {
|
try {
|
||||||
printNode(graph.at(packagePath), "", "");
|
printNode(graph.at(packagePath), "", "");
|
||||||
} catch (BailOut & ) { }
|
} catch (BailOut & ) { }
|
||||||
|
|
|
@ -14,6 +14,17 @@ outPath=$(nix-build dependencies.nix --no-out-link)
|
||||||
|
|
||||||
nix copy --to file://$cacheDir $outPath
|
nix copy --to file://$cacheDir $outPath
|
||||||
|
|
||||||
|
# Test copying build logs to the binary cache.
|
||||||
|
nix log --store file://$cacheDir $outPath 2>&1 | grep 'is not available'
|
||||||
|
nix store copy-log --to file://$cacheDir $outPath
|
||||||
|
nix log --store file://$cacheDir $outPath | grep FOO
|
||||||
|
rm -rf $TEST_ROOT/var/log/nix
|
||||||
|
nix log $outPath 2>&1 | grep 'is not available'
|
||||||
|
nix log --substituters file://$cacheDir $outPath | grep FOO
|
||||||
|
|
||||||
|
# Test copying build logs from the binary cache.
|
||||||
|
nix store copy-log --from file://$cacheDir $(nix-store -qd $outPath)
|
||||||
|
nix log $outPath | grep FOO
|
||||||
|
|
||||||
basicDownloadTests() {
|
basicDownloadTests() {
|
||||||
# No uploading tests bcause upload with force HTTP doesn't work.
|
# No uploading tests bcause upload with force HTTP doesn't work.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
mkdir $out
|
mkdir $out
|
||||||
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
|
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
|
||||||
|
|
||||||
ln -s $input2 $out/input-2
|
ln -s $input2 $out/reference-to-input-2
|
||||||
|
|
||||||
# Self-reference.
|
# Self-reference.
|
||||||
ln -s $out $out/self
|
ln -s $out $out/self
|
||||||
|
|
|
@ -27,6 +27,8 @@ let {
|
||||||
input1 = input1 + "/.";
|
input1 = input1 + "/.";
|
||||||
input2 = "${input2}/.";
|
input2 = "${input2}/.";
|
||||||
input1_drv = input1;
|
input1_drv = input1;
|
||||||
|
input2_drv = input2;
|
||||||
|
input0_drv = input0;
|
||||||
meta.description = "Random test package";
|
meta.description = "Random test package";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
45
tests/flake-searching.sh
Normal file
45
tests/flake-searching.sh
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
|
||||||
|
cd $TEST_HOME
|
||||||
|
mkdir -p foo/subdir
|
||||||
|
echo '{ outputs = _: {}; }' > foo/flake.nix
|
||||||
|
cat <<EOF > flake.nix
|
||||||
|
{
|
||||||
|
inputs.foo.url = "$PWD/foo";
|
||||||
|
outputs = a: {
|
||||||
|
defaultPackage.$system = import ./simple.nix;
|
||||||
|
packages.$system.test = import ./simple.nix;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
mkdir subdir
|
||||||
|
pushd subdir
|
||||||
|
|
||||||
|
success=("" . .# .#test ../subdir ../subdir#test "$PWD")
|
||||||
|
failure=("path:$PWD")
|
||||||
|
|
||||||
|
for i in "${success[@]}"; do
|
||||||
|
nix build $i || fail "flake should be found by searching up directories"
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in "${failure[@]}"; do
|
||||||
|
! nix build $i || fail "flake should not search up directories when using 'path:'"
|
||||||
|
done
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
nix build --override-input foo . || fail "flake should search up directories when not an installable"
|
||||||
|
|
||||||
|
sed "s,$PWD/foo,$PWD/foo/subdir,g" -i flake.nix
|
||||||
|
! nix build || fail "flake should not search upwards when part of inputs"
|
||||||
|
|
||||||
|
pushd subdir
|
||||||
|
git init
|
||||||
|
for i in "${success[@]}" "${failure[@]}"; do
|
||||||
|
! nix build $i || fail "flake should not search past a git repository"
|
||||||
|
done
|
||||||
|
rm -rf .git
|
||||||
|
popd
|
|
@ -18,7 +18,7 @@ if nix-store --gc --print-dead | grep -E $outPath$; then false; fi
|
||||||
|
|
||||||
nix-store --gc --print-dead
|
nix-store --gc --print-dead
|
||||||
|
|
||||||
inUse=$(readLink $outPath/input-2)
|
inUse=$(readLink $outPath/reference-to-input-2)
|
||||||
if nix-store --delete $inUse; then false; fi
|
if nix-store --delete $inUse; then false; fi
|
||||||
test -e $inUse
|
test -e $inUse
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ nix-collect-garbage
|
||||||
|
|
||||||
# Check that the root and its dependencies haven't been deleted.
|
# Check that the root and its dependencies haven't been deleted.
|
||||||
cat $outPath/foobar
|
cat $outPath/foobar
|
||||||
cat $outPath/input-2/bar
|
cat $outPath/reference-to-input-2/bar
|
||||||
|
|
||||||
# Check that the derivation has been GC'd.
|
# Check that the derivation has been GC'd.
|
||||||
if test -e $drvPath; then false; fi
|
if test -e $drvPath; then false; fi
|
||||||
|
|
|
@ -47,6 +47,7 @@ nix_tests = \
|
||||||
describe-stores.sh \
|
describe-stores.sh \
|
||||||
flakes.sh \
|
flakes.sh \
|
||||||
flake-local-settings.sh \
|
flake-local-settings.sh \
|
||||||
|
flake-searching.sh \
|
||||||
build.sh \
|
build.sh \
|
||||||
repl.sh ca/repl.sh \
|
repl.sh ca/repl.sh \
|
||||||
ca/build.sh \
|
ca/build.sh \
|
||||||
|
@ -60,7 +61,8 @@ nix_tests = \
|
||||||
ca/concurrent-builds.sh \
|
ca/concurrent-builds.sh \
|
||||||
ca/nix-copy.sh \
|
ca/nix-copy.sh \
|
||||||
eval-store.sh \
|
eval-store.sh \
|
||||||
readfile-context.sh
|
readfile-context.sh \
|
||||||
|
why-depends.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
|
||||||
ifeq ($(HAVE_LIBCPUID), 1)
|
ifeq ($(HAVE_LIBCPUID), 1)
|
||||||
|
|
21
tests/why-depends.sh
Normal file
21
tests/why-depends.sh
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
cp ./dependencies.nix ./dependencies.builder0.sh ./config.nix $TEST_HOME
|
||||||
|
|
||||||
|
cd $TEST_HOME
|
||||||
|
|
||||||
|
nix-build ./dependencies.nix -A input0_drv -o dep
|
||||||
|
nix-build ./dependencies.nix -o toplevel
|
||||||
|
|
||||||
|
FAST_WHY_DEPENDS_OUTPUT=$(nix why-depends ./toplevel ./dep)
|
||||||
|
PRECISE_WHY_DEPENDS_OUTPUT=$(nix why-depends ./toplevel ./dep --precise)
|
||||||
|
|
||||||
|
# Both outputs should show that `input-2` is in the dependency chain
|
||||||
|
echo "$FAST_WHY_DEPENDS_OUTPUT" | grep -q input-2
|
||||||
|
echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grep -q input-2
|
||||||
|
|
||||||
|
# But only the “precise” one should refere to `reference-to-input-2`
|
||||||
|
echo "$FAST_WHY_DEPENDS_OUTPUT" | (! grep -q reference-to-input-2)
|
||||||
|
echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grep -q reference-to-input-2
|
Loading…
Reference in a new issue