forked from lix-project/lix
Compare commits
17 commits
pipe-opera
...
main
Author | SHA1 | Date | |
---|---|---|---|
Artemis Tosini | 60a48311e8 | ||
jade | c4c7cb7613 | ||
Qyriad | 8d12e0fbb7 | ||
Artemis Tosini | 3b96b51cf4 | ||
jade | 98e8cf9c63 | ||
jade | 12a5838d11 | ||
jade | 2436f2110a | ||
jade | 916b5c68fb | ||
Artemis Tosini | 53f3e39815 | ||
Pierre Bourdon | 73c013a5df | ||
Pierre Bourdon | e76245f8e9 | ||
eldritch horrors | 472ff1b833 | ||
eldritch horrors | 7bf1aff44a | ||
eldritch horrors | 58a91d70c9 | ||
eldritch horrors | ad36fb43ad | ||
eldritch horrors | d70e045f90 | ||
eldritch horrors | 20f53346df |
|
@ -91,11 +91,6 @@ midnightveil:
|
|||
ncfavier:
|
||||
github: ncfavier
|
||||
|
||||
piegames:
|
||||
display_name: piegames
|
||||
forgejo: piegames
|
||||
github: piegamesde
|
||||
|
||||
puck:
|
||||
display_name: puck
|
||||
forgejo: puck
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
synopsis: Pipe operator `|>` (experimental)
|
||||
issues: [fj#438]
|
||||
cls: [1654]
|
||||
category: Features
|
||||
credits: [piegames, horrors]
|
||||
---
|
||||
|
||||
Implementation of the pipe operator (`|>`) in the language as described in [RFC 148](https://github.com/NixOS/rfcs/pull/148).
|
||||
The feature is still marked experimental, enable `--extra-experimental-features pipe-operator` to use it.
|
|
@ -26,8 +26,6 @@
|
|||
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
||||
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
|
||||
| [Logical implication] | *bool* `->` *bool* | none | 14 |
|
||||
| \[Experimental\] [Function piping] | *expr* |> *func* | left | 15 |
|
||||
| \[Experimental\] [Function piping] | *expr* <| *func* | right | 16 |
|
||||
|
||||
[string]: ./values.md#type-string
|
||||
[path]: ./values.md#type-path
|
||||
|
@ -217,33 +215,3 @@ nix-repl> let f = x: 1; s = { func = f; }; in [ (f == f) (s == s) ]
|
|||
Equivalent to `!`*b1* `||` *b2*.
|
||||
|
||||
[Logical implication]: #logical-implication
|
||||
|
||||
## \[Experimental\] Function piping
|
||||
|
||||
*This language feature is still experimental and may change at any time. Enable `--extra-experimental-features pipe-operator` to use it.*
|
||||
|
||||
Pipes are a dedicated operator for function application, but with reverse order and a lower binding strength.
|
||||
This allows you to chain function calls together in way that is more natural to read and requires less parentheses.
|
||||
|
||||
`a |> f b |> g` is equivalent to `g (f b a)`.
|
||||
`g <| f b <| a` is equivalent to `g (f b a)`.
|
||||
|
||||
Example code snippet:
|
||||
|
||||
```nix
|
||||
defaultPrefsFile = defaultPrefs
|
||||
|> lib.mapAttrsToList (
|
||||
key: value: ''
|
||||
// ${value.reason}
|
||||
pref("${key}", ${builtins.toJSON value.value});
|
||||
''
|
||||
)
|
||||
|> lib.concatStringsSep "\n"
|
||||
|> pkgs.writeText "nixos-default-prefs.js";
|
||||
```
|
||||
|
||||
Note how `mapAttrsToList` is called with two arguments (the lambda and `defaultPrefs`),
|
||||
but moving the last argument in front of the rest improves the reading flow.
|
||||
This is common for functions with long first argument, including all `map`-like functions.
|
||||
|
||||
[Function piping]: #experimental-function-piping
|
||||
|
|
|
@ -314,6 +314,10 @@ nlohmann_json = dependency('nlohmann_json', required : true)
|
|||
# *absolutely* are not going to make it work)
|
||||
lix_doc = declare_dependency(link_args : [ '-llix_doc' ])
|
||||
|
||||
if is_freebsd
|
||||
libprocstat = declare_dependency(link_args : [ '-lprocstat' ])
|
||||
endif
|
||||
|
||||
#
|
||||
# Build-time tools
|
||||
#
|
||||
|
|
14
package.nix
14
package.nix
|
@ -92,19 +92,7 @@ let
|
|||
|
||||
# Reimplementation of Nixpkgs' Meson cross file, with some additions to make
|
||||
# it actually work.
|
||||
mesonCrossFile =
|
||||
let
|
||||
cpuFamily =
|
||||
platform:
|
||||
with platform;
|
||||
if isAarch32 then
|
||||
"arm"
|
||||
else if isx86_32 then
|
||||
"x86"
|
||||
else
|
||||
platform.uname.processor;
|
||||
in
|
||||
builtins.toFile "lix-cross-file.conf" ''
|
||||
mesonCrossFile = builtins.toFile "lix-cross-file.conf" ''
|
||||
[properties]
|
||||
# Meson is convinced that if !buildPlatform.canExecute hostPlatform then we cannot
|
||||
# build anything at all, which is not at all correct. If we can't execute the host
|
||||
|
|
|
@ -236,9 +236,9 @@ static int main_build_remote(int argc, char * * argv)
|
|||
}
|
||||
|
||||
#if __APPLE__
|
||||
futimes(bestSlotLock.get(), NULL);
|
||||
futimes(bestSlotLock.get(), nullptr);
|
||||
#else
|
||||
futimens(bestSlotLock.get(), NULL);
|
||||
futimens(bestSlotLock.get(), nullptr);
|
||||
#endif
|
||||
|
||||
lock.reset();
|
||||
|
|
|
@ -2795,20 +2795,20 @@ Expr & EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta
|
|||
}
|
||||
|
||||
|
||||
Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings)
|
||||
Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
// NOTE this method (and parseStdin) must take care to *fully copy* their input
|
||||
// into their respective Pos::Origin until the parser stops overwriting its input
|
||||
// data.
|
||||
auto s = make_ref<std::string>(s_);
|
||||
s_.append("\0\0", 2);
|
||||
return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv, xpSettings);
|
||||
return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings)
|
||||
Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath)
|
||||
{
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv, xpSettings);
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -342,8 +342,8 @@ public:
|
|||
/**
|
||||
* Parse a Nix expression from the specified string.
|
||||
*/
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv);
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath);
|
||||
|
||||
Expr & parseStdin();
|
||||
|
||||
|
@ -566,8 +566,7 @@ private:
|
|||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "checked-arithmetic.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#include <functional> // std::less
|
||||
#include <utility> // std::pair
|
||||
|
@ -18,8 +20,6 @@
|
|||
#include <gc/gc_allocator.h>
|
||||
#include <gc/gc_cpp.h>
|
||||
|
||||
#include "checked-arithmetic.hh"
|
||||
|
||||
/// calloc, transparently GC-enabled.
|
||||
#define LIX_GC_CALLOC(size) GC_MALLOC(size)
|
||||
|
||||
|
|
|
@ -434,8 +434,6 @@ struct op {
|
|||
struct and_ : _op<TAO_PEGTL_STRING("&&"), 12> {};
|
||||
struct or_ : _op<TAO_PEGTL_STRING("||"), 13> {};
|
||||
struct implies : _op<TAO_PEGTL_STRING("->"), 14, kind::rightAssoc> {};
|
||||
struct pipe_right : _op<TAO_PEGTL_STRING("|>"), 15> {};
|
||||
struct pipe_left : _op<TAO_PEGTL_STRING("<|"), 16, kind::rightAssoc> {};
|
||||
};
|
||||
|
||||
struct _expr {
|
||||
|
@ -523,7 +521,6 @@ struct _expr {
|
|||
app
|
||||
> {};
|
||||
|
||||
/* Order matters here. The order is the parsing order, not the precedence order: '<=' must be parsed before '<'. */
|
||||
struct _binary_operator : sor<
|
||||
operator_<op::implies>,
|
||||
operator_<op::update>,
|
||||
|
@ -532,8 +529,6 @@ struct _expr {
|
|||
operator_<op::minus>,
|
||||
operator_<op::mul>,
|
||||
operator_<op::div>,
|
||||
operator_<op::pipe_right>,
|
||||
operator_<op::pipe_left>,
|
||||
operator_<op::less_eq>,
|
||||
operator_<op::greater_eq>,
|
||||
operator_<op::less>,
|
||||
|
@ -654,8 +649,6 @@ struct operator_semantics {
|
|||
grammar::op::minus,
|
||||
grammar::op::mul,
|
||||
grammar::op::div,
|
||||
grammar::op::pipe_right,
|
||||
grammar::op::pipe_left,
|
||||
has_attr
|
||||
> op;
|
||||
};
|
||||
|
|
|
@ -114,29 +114,6 @@ struct ExprState
|
|||
return std::make_unique<ExprCall>(pos, std::make_unique<ExprVar>(fn), std::move(args));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expr> pipe(PosIdx pos, State & state, bool flip = false)
|
||||
{
|
||||
if (!state.xpSettings.isEnabled(Xp::PipeOperator))
|
||||
throw ParseError({
|
||||
.msg = HintFmt("Pipe operator is disabled"),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
|
||||
// Reverse the order compared to normal function application: arg |> fn
|
||||
std::unique_ptr<Expr> fn, arg;
|
||||
if (flip) {
|
||||
fn = popExprOnly();
|
||||
arg = popExprOnly();
|
||||
} else {
|
||||
arg = popExprOnly();
|
||||
fn = popExprOnly();
|
||||
}
|
||||
std::vector<std::unique_ptr<Expr>> args{1};
|
||||
args[0] = std::move(arg);
|
||||
|
||||
return std::make_unique<ExprCall>(pos, std::move(fn), std::move(args));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expr> order(PosIdx pos, bool less, State & state)
|
||||
{
|
||||
return call(pos, state.s.lessThan, !less);
|
||||
|
@ -186,8 +163,6 @@ struct ExprState
|
|||
[&] (Op::concat) { return applyBinary<ExprOpConcatLists>(pos); },
|
||||
[&] (has_attr & a) { return applyUnary<ExprOpHasAttr>(std::move(a.path)); },
|
||||
[&] (Op::unary_minus) { return negate(pos, state); },
|
||||
[&] (Op::pipe_right) { return pipe(pos, state, true); },
|
||||
[&] (Op::pipe_left) { return pipe(pos, state); },
|
||||
})(op)
|
||||
};
|
||||
}
|
||||
|
@ -655,7 +630,7 @@ template<> struct BuildAST<grammar::expr::path> : p::maybe_nothing {};
|
|||
|
||||
template<> struct BuildAST<grammar::expr::uri> {
|
||||
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||
bool noURLLiterals = ps.xpSettings.isEnabled(Xp::NoUrlLiterals);
|
||||
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
||||
if (noURLLiterals)
|
||||
throw ParseError({
|
||||
.msg = HintFmt("URL literals are disabled"),
|
||||
|
@ -856,8 +831,7 @@ Expr * EvalState::parse(
|
|||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
parser::State s = {
|
||||
symbols,
|
||||
|
@ -865,7 +839,6 @@ Expr * EvalState::parse(
|
|||
basePath,
|
||||
positions.addOrigin(origin, length),
|
||||
exprSymbols,
|
||||
xpSettings
|
||||
};
|
||||
parser::ExprState x;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ struct State
|
|||
SourcePath basePath;
|
||||
PosTable::Origin origin;
|
||||
const Expr::AstSymbols & s;
|
||||
const ExperimentalFeatureSettings & xpSettings;
|
||||
|
||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||
|
|
|
@ -695,7 +695,7 @@ struct GitInputScheme : InputScheme
|
|||
});
|
||||
Finally const _wait([&] { proc.wait(); });
|
||||
|
||||
unpackTarfile(*proc.stdout(), tmpDir);
|
||||
unpackTarfile(*proc.getStdout(), tmpDir);
|
||||
}
|
||||
|
||||
auto storePath = store->addToStore(name, tmpDir, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
|
|
|
@ -932,7 +932,7 @@ void runPostBuildHook(
|
|||
Finally const _wait([&] { proc.wait(); });
|
||||
|
||||
// FIXME just process the data, without a wrapper sink class
|
||||
proc.stdout()->drainInto(sink);
|
||||
proc.getStdout()->drainInto(sink);
|
||||
}
|
||||
|
||||
void DerivationGoal::buildDone()
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "realisation.hh"
|
||||
#include <thread>
|
||||
#include <future>
|
||||
|
||||
namespace nix {
|
||||
|
@ -41,11 +40,6 @@ class DrvOutputSubstitutionGoal : public Goal {
|
|||
*/
|
||||
std::shared_ptr<Store> sub;
|
||||
|
||||
/**
|
||||
* The substituter thread.
|
||||
*/
|
||||
std::thread thr;
|
||||
|
||||
std::unique_ptr<MaintainCount<uint64_t>> maintainRunningSubstitutions;
|
||||
|
||||
struct DownloadState
|
||||
|
|
|
@ -22,7 +22,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
|||
if (ex)
|
||||
logError(i->ex->info());
|
||||
else
|
||||
ex = std::move(i->ex);
|
||||
ex = std::move(*i->ex);
|
||||
}
|
||||
if (i->exitCode != Goal::ecSuccess) {
|
||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
||||
|
|
|
@ -34,18 +34,10 @@ BuildResult Goal::getBuildResult(const DerivedPath & req) const {
|
|||
}
|
||||
|
||||
|
||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p)
|
||||
{
|
||||
if (goals.find(p) != goals.end())
|
||||
return;
|
||||
goals.insert(p);
|
||||
}
|
||||
|
||||
|
||||
void Goal::addWaitee(GoalPtr waitee)
|
||||
{
|
||||
waitees.insert(waitee);
|
||||
addToWeakGoals(waitee->waiters, shared_from_this());
|
||||
waitee->waiters.insert(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,15 +71,14 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
|
|||
void Goal::amDone(ExitCode result, std::optional<Error> ex)
|
||||
{
|
||||
trace("done");
|
||||
assert(exitCode == ecBusy);
|
||||
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
|
||||
assert(!exitCode.has_value());
|
||||
exitCode = result;
|
||||
|
||||
if (ex) {
|
||||
if (!waiters.empty())
|
||||
logError(ex->info());
|
||||
else
|
||||
this->ex = std::move(*ex);
|
||||
this->ex = std::make_unique<Error>(std::move(*ex));
|
||||
}
|
||||
|
||||
for (auto & i : waiters) {
|
||||
|
|
|
@ -53,7 +53,7 @@ enum struct JobCategory {
|
|||
|
||||
struct Goal : public std::enable_shared_from_this<Goal>
|
||||
{
|
||||
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||
typedef enum {ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||
|
||||
/**
|
||||
* Backlink to the worker.
|
||||
|
@ -96,7 +96,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
|
|||
/**
|
||||
* Whether the goal is finished.
|
||||
*/
|
||||
ExitCode exitCode = ecBusy;
|
||||
std::optional<ExitCode> exitCode;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
@ -121,7 +121,7 @@ public:
|
|||
/**
|
||||
* Exception containing an error message, if any.
|
||||
*/
|
||||
std::optional<Error> ex;
|
||||
std::unique_ptr<Error> ex;
|
||||
|
||||
Goal(Worker & worker, DerivedPath path)
|
||||
: worker(worker)
|
||||
|
@ -175,6 +175,4 @@ public:
|
|||
virtual JobCategory jobCategory() const = 0;
|
||||
};
|
||||
|
||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
||||
|
||||
}
|
||||
|
|
|
@ -1882,7 +1882,7 @@ void LocalDerivationGoal::runChild()
|
|||
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
|
||||
sandboxArgs.push_back("1");
|
||||
}
|
||||
if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
|
||||
if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), nullptr)) {
|
||||
writeFull(STDERR_FILENO, "failed to configure sandbox\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
|
|
@ -214,9 +214,7 @@ void PathSubstitutionGoal::tryToRun()
|
|||
|
||||
outPipe.create();
|
||||
|
||||
promise = std::promise<void>();
|
||||
|
||||
thr = std::thread([this]() {
|
||||
thr = std::async(std::launch::async, [this]() {
|
||||
auto & fetchPath = subPath ? *subPath : storePath;
|
||||
try {
|
||||
ReceiveInterrupts receiveInterrupts;
|
||||
|
@ -230,16 +228,12 @@ void PathSubstitutionGoal::tryToRun()
|
|||
copyStorePath(
|
||||
*sub, worker.store, fetchPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs
|
||||
);
|
||||
|
||||
promise.set_value();
|
||||
} catch (const EndOfFile &) {
|
||||
promise.set_exception(std::make_exception_ptr(EndOfFile(
|
||||
throw EndOfFile(
|
||||
"NAR for '%s' fetched from '%s' is incomplete",
|
||||
sub->printStorePath(fetchPath),
|
||||
sub->getUri()
|
||||
)));
|
||||
} catch (...) {
|
||||
promise.set_exception(std::current_exception());
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -253,11 +247,10 @@ void PathSubstitutionGoal::finished()
|
|||
{
|
||||
trace("substitute finished");
|
||||
|
||||
thr.join();
|
||||
worker.childTerminated(this);
|
||||
|
||||
try {
|
||||
promise.get_future().get();
|
||||
thr.get();
|
||||
} catch (std::exception & e) {
|
||||
printError(e.what());
|
||||
|
||||
|
@ -316,9 +309,9 @@ void PathSubstitutionGoal::handleEOF(int fd)
|
|||
void PathSubstitutionGoal::cleanup()
|
||||
{
|
||||
try {
|
||||
if (thr.joinable()) {
|
||||
if (thr.valid()) {
|
||||
// FIXME: signal worker thread to quit.
|
||||
thr.join();
|
||||
thr.get();
|
||||
worker.childTerminated(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,9 +50,7 @@ struct PathSubstitutionGoal : public Goal
|
|||
/**
|
||||
* The substituter thread.
|
||||
*/
|
||||
std::thread thr;
|
||||
|
||||
std::promise<void> promise;
|
||||
std::future<void> thr;
|
||||
|
||||
/**
|
||||
* Whether to try to repair a valid path.
|
||||
|
|
|
@ -157,21 +157,13 @@ void Worker::removeGoal(GoalPtr goal)
|
|||
if (goal->exitCode == Goal::ecFailed && !settings.keepGoing)
|
||||
topGoals.clear();
|
||||
}
|
||||
|
||||
/* Wake up goals waiting for any goal to finish. */
|
||||
for (auto & i : waitingForAnyGoal) {
|
||||
GoalPtr goal = i.lock();
|
||||
if (goal) wakeUp(goal);
|
||||
}
|
||||
|
||||
waitingForAnyGoal.clear();
|
||||
}
|
||||
|
||||
|
||||
void Worker::wakeUp(GoalPtr goal)
|
||||
{
|
||||
goal->trace("woken up");
|
||||
addToWeakGoals(awake, goal);
|
||||
awake.insert(goal);
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,7 +205,7 @@ void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
|
|||
}
|
||||
|
||||
|
||||
void Worker::childTerminated(Goal * goal, bool wakeSleepers)
|
||||
void Worker::childTerminated(Goal * goal)
|
||||
{
|
||||
auto i = std::find_if(children.begin(), children.end(),
|
||||
[&](const Child & child) { return child.goal2 == goal; });
|
||||
|
@ -236,8 +228,6 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers)
|
|||
|
||||
children.erase(i);
|
||||
|
||||
if (wakeSleepers) {
|
||||
|
||||
/* Wake up goals waiting for a build slot. */
|
||||
for (auto & j : wantingToBuild) {
|
||||
GoalPtr goal = j.lock();
|
||||
|
@ -245,7 +235,6 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers)
|
|||
}
|
||||
|
||||
wantingToBuild.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,21 +246,14 @@ void Worker::waitForBuildSlot(GoalPtr goal)
|
|||
(isSubstitutionGoal && getNrSubstitutions() < settings.maxSubstitutionJobs))
|
||||
wakeUp(goal); /* we can do it right away */
|
||||
else
|
||||
addToWeakGoals(wantingToBuild, goal);
|
||||
}
|
||||
|
||||
|
||||
void Worker::waitForAnyGoal(GoalPtr goal)
|
||||
{
|
||||
debug("wait for any goal");
|
||||
addToWeakGoals(waitingForAnyGoal, goal);
|
||||
wantingToBuild.insert(goal);
|
||||
}
|
||||
|
||||
|
||||
void Worker::waitForAWhile(GoalPtr goal)
|
||||
{
|
||||
debug("wait for a while");
|
||||
addToWeakGoals(waitingForAWhile, goal);
|
||||
waitingForAWhile.insert(goal);
|
||||
}
|
||||
|
||||
|
||||
|
@ -429,7 +411,7 @@ void Worker::waitForInput()
|
|||
GoalPtr goal = j->goal.lock();
|
||||
assert(goal);
|
||||
|
||||
if (goal->exitCode == Goal::ecBusy &&
|
||||
if (!goal->exitCode.has_value() &&
|
||||
0 != settings.maxSilentTime &&
|
||||
j->respectTimeouts &&
|
||||
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
|
||||
|
@ -440,7 +422,7 @@ void Worker::waitForInput()
|
|||
continue;
|
||||
}
|
||||
|
||||
else if (goal->exitCode == Goal::ecBusy &&
|
||||
else if (!goal->exitCode.has_value() &&
|
||||
0 != settings.buildTimeout &&
|
||||
j->respectTimeouts &&
|
||||
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
|
||||
|
|
|
@ -90,11 +90,6 @@ private:
|
|||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||
|
||||
/**
|
||||
* Goals waiting for busy paths to be unlocked.
|
||||
*/
|
||||
WeakGoals waitingForAnyGoal;
|
||||
|
||||
/**
|
||||
* Goals sleeping for a few seconds (polling a lock).
|
||||
*/
|
||||
|
@ -228,12 +223,9 @@ public:
|
|||
bool inBuildSlot, bool respectTimeouts);
|
||||
|
||||
/**
|
||||
* Unregisters a running child process. `wakeSleepers` should be
|
||||
* false if there is no sense in waking up goals that are sleeping
|
||||
* because they can't run yet (e.g., there is no free build slot,
|
||||
* or the hook would still say `postpone`).
|
||||
* Unregisters a running child process.
|
||||
*/
|
||||
void childTerminated(Goal * goal, bool wakeSleepers = true);
|
||||
void childTerminated(Goal * goal);
|
||||
|
||||
/**
|
||||
* Put `goal` to sleep until a build slot becomes available (which
|
||||
|
@ -241,12 +233,6 @@ public:
|
|||
*/
|
||||
void waitForBuildSlot(GoalPtr goal);
|
||||
|
||||
/**
|
||||
* Wait for any goal to finish. Pretty indiscriminate way to
|
||||
* wait for some resource that some other goal is holding.
|
||||
*/
|
||||
void waitForAnyGoal(GoalPtr goal);
|
||||
|
||||
/**
|
||||
* Wait for a few seconds and then retry this goal. Used when
|
||||
* waiting for a lock held by another process. This kind of
|
||||
|
|
|
@ -177,14 +177,14 @@ static bool hasVirt() {
|
|||
size_t size;
|
||||
|
||||
size = sizeof(hasVMM);
|
||||
if (sysctlbyname("kern.hv_vmm_present", &hasVMM, &size, NULL, 0) == 0) {
|
||||
if (sysctlbyname("kern.hv_vmm_present", &hasVMM, &size, nullptr, 0) == 0) {
|
||||
if (hasVMM)
|
||||
return false;
|
||||
}
|
||||
|
||||
// whether the kernel and hardware supports virt
|
||||
size = sizeof(hvSupport);
|
||||
if (sysctlbyname("kern.hv_support", &hvSupport, &size, NULL, 0) == 0) {
|
||||
if (sysctlbyname("kern.hv_support", &hvSupport, &size, nullptr, 0) == 0) {
|
||||
return hvSupport == 1;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -557,7 +557,7 @@ void LocalStore::openDB(State & state, bool create)
|
|||
if (sqlite3_exec(db, "pragma main.journal_size_limit = 1099511627776;", 0, 0, 0) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "setting journal_size_limit");
|
||||
int enable = 1;
|
||||
if (sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, &enable) != SQLITE_OK)
|
||||
if (sqlite3_file_control(db, nullptr, SQLITE_FCNTL_PERSIST_WAL, &enable) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "setting persistent WAL mode");
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,9 @@ if host_machine.system() == 'linux'
|
|||
elif host_machine.system() == 'darwin'
|
||||
libstore_sources += files('platform/darwin.cc')
|
||||
libstore_headers += files('platform/darwin.hh')
|
||||
elif host_machine.system() == 'freebsd'
|
||||
libstore_sources += files('platform/freebsd.cc')
|
||||
libstore_headers += files('platform/freebsd.hh')
|
||||
else
|
||||
libstore_sources += files('platform/fallback.cc')
|
||||
libstore_headers += files('platform/fallback.hh')
|
||||
|
@ -202,11 +205,7 @@ foreach name, value : cpp_str_defines
|
|||
]
|
||||
endforeach
|
||||
|
||||
libstore = library(
|
||||
'lixstore',
|
||||
libstore_generated_headers,
|
||||
libstore_sources,
|
||||
dependencies : [
|
||||
dependencies = [
|
||||
libarchive,
|
||||
liblixutil, # Internal.
|
||||
seccomp,
|
||||
|
@ -218,7 +217,17 @@ libstore = library(
|
|||
aws_s3,
|
||||
aws_sdk_transfer,
|
||||
nlohmann_json,
|
||||
],
|
||||
]
|
||||
|
||||
if host_machine.system() == 'freebsd'
|
||||
dependencies += [ libprocstat ]
|
||||
endif
|
||||
|
||||
libstore = library(
|
||||
'lixstore',
|
||||
libstore_generated_headers,
|
||||
libstore_sources,
|
||||
dependencies : dependencies,
|
||||
cpp_args : cpp_args,
|
||||
cpp_pch : cpp_pch,
|
||||
install : true,
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "platform/linux.hh"
|
||||
#elif __APPLE__
|
||||
#include "platform/darwin.hh"
|
||||
#elif __FreeBSD__
|
||||
#include "platform/freebsd.hh"
|
||||
#else
|
||||
#include "platform/fallback.hh"
|
||||
#endif
|
||||
|
@ -16,6 +18,8 @@ std::shared_ptr<LocalStore> LocalStore::makeLocalStore(const Params & params)
|
|||
return std::shared_ptr<LocalStore>(new LinuxLocalStore(params));
|
||||
#elif __APPLE__
|
||||
return std::shared_ptr<LocalStore>(new DarwinLocalStore(params));
|
||||
#elif __FreeBSD__
|
||||
return std::shared_ptr<LocalStore>(new FreeBSDLocalStore(params));
|
||||
#else
|
||||
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
|
||||
#endif
|
||||
|
@ -32,6 +36,8 @@ std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoa
|
|||
return std::make_shared<LinuxLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||
#elif __APPLE__
|
||||
return std::make_shared<DarwinLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||
#elif __FreeBSD__
|
||||
return std::make_shared<FreeBSDLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||
#else
|
||||
return std::make_shared<FallbackLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||
#endif
|
||||
|
@ -53,6 +59,10 @@ std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoa
|
|||
return std::make_shared<DarwinLocalDerivationGoal>(
|
||||
drvPath, drv, wantedOutputs, worker, buildMode
|
||||
);
|
||||
#elif __FreeBSD__
|
||||
return std::make_shared<FreeBSDLocalDerivationGoal>(
|
||||
drvPath, drv, wantedOutputs, worker, buildMode
|
||||
);
|
||||
#else
|
||||
return std::make_shared<FallbackLocalDerivationGoal>(
|
||||
drvPath, drv, wantedOutputs, worker, buildMode
|
||||
|
|
|
@ -235,15 +235,15 @@ void DarwinLocalDerivationGoal::execBuilder(std::string builder, Strings args, S
|
|||
if (drv->platform == "aarch64-darwin") {
|
||||
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
||||
int affinity = 0;
|
||||
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
||||
sysctlbyname("kern.curproc_arch_affinity", nullptr, nullptr, &affinity, sizeof(affinity));
|
||||
|
||||
cpu_type_t cpu = CPU_TYPE_ARM64;
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, nullptr);
|
||||
} else if (drv->platform == "x86_64-darwin") {
|
||||
cpu_type_t cpu = CPU_TYPE_X86_64;
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, nullptr);
|
||||
}
|
||||
|
||||
posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
posix_spawn(nullptr, builder.c_str(), nullptr, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
}
|
||||
}
|
||||
|
|
142
src/libstore/platform/freebsd.cc
Normal file
142
src/libstore/platform/freebsd.cc
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include "platform/freebsd.hh"
|
||||
#include "regex.hh"
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <libprocstat.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void readSysctlRoots(const char * name, UncheckedRoots & unchecked)
|
||||
{
|
||||
size_t len = 0;
|
||||
std::string value;
|
||||
if (int err = sysctlbyname(name, nullptr, &len, nullptr, 0) < 0) {
|
||||
if (err == ENOENT || err == EACCES) {
|
||||
return;
|
||||
} else {
|
||||
throw SysError(err, "sysctlbyname %1%", name);
|
||||
}
|
||||
}
|
||||
|
||||
value.resize(len, ' ');
|
||||
if (int err = sysctlbyname(name, value.data(), &len, nullptr, 0) < 0) {
|
||||
if (err == ENOENT || err == EACCES) {
|
||||
return;
|
||||
} else {
|
||||
throw SysError(err, "sysctlbyname %1%", name);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & path : tokenizeString<Strings>(value, ";")) {
|
||||
unchecked[path].emplace(fmt("{{sysctl:%1%}}", name));
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcstatDeleter
|
||||
{
|
||||
void operator()(struct procstat * ps)
|
||||
{
|
||||
procstat_close(ps);
|
||||
}
|
||||
};
|
||||
|
||||
template<auto del>
|
||||
struct ProcstatReferredDeleter
|
||||
{
|
||||
struct procstat * ps;
|
||||
|
||||
ProcstatReferredDeleter(struct procstat * ps) : ps(ps) {}
|
||||
|
||||
template<typename T>
|
||||
void operator()(T * p)
|
||||
{
|
||||
del(ps, p);
|
||||
}
|
||||
};
|
||||
|
||||
void FreeBSDLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
|
||||
{
|
||||
readSysctlRoots("kern.module_path", unchecked);
|
||||
|
||||
auto storePathRegex = regex::storePathRegex(storeDir);
|
||||
|
||||
auto ps = std::unique_ptr<struct procstat, ProcstatDeleter>(procstat_open_sysctl());
|
||||
if (!ps) {
|
||||
throw SysError("procstat_open_sysctl");
|
||||
}
|
||||
|
||||
auto procs = std::unique_ptr<struct kinfo_proc[], ProcstatReferredDeleter<procstat_freeprocs>>(
|
||||
nullptr, ps.get()
|
||||
);
|
||||
auto files = std::unique_ptr<struct filestat_list, ProcstatReferredDeleter<procstat_freefiles>>(
|
||||
nullptr, ps.get()
|
||||
);
|
||||
|
||||
unsigned int numprocs = 0;
|
||||
procs.reset(procstat_getprocs(ps.get(), KERN_PROC_PROC, 0, &numprocs));
|
||||
if (!procs || numprocs == 0) {
|
||||
throw SysError("procstat_getprocs");
|
||||
};
|
||||
|
||||
for (unsigned int procidx = 0; procidx < numprocs; procidx++) {
|
||||
// Includes file descriptors, executable, cwd,
|
||||
// and mmapped files (including dynamic libraries)
|
||||
files.reset(procstat_getfiles(ps.get(), &procs[procidx], 1));
|
||||
// We only have permission if we're root so just skip it if we fail
|
||||
if (!files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (struct filestat * file = files->stqh_first; file; file = file->next.stqe_next) {
|
||||
if (!file->fs_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string role;
|
||||
if (file->fs_uflags & PS_FST_UFLAG_CTTY) {
|
||||
role = "ctty";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_CDIR) {
|
||||
role = "cwd";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_JAIL) {
|
||||
role = "jail";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_RDIR) {
|
||||
role = "root";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_TEXT) {
|
||||
role = "text";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_TRACE) {
|
||||
role = "trace";
|
||||
} else if (file->fs_uflags & PS_FST_UFLAG_MMAP) {
|
||||
role = "mmap";
|
||||
} else {
|
||||
role = fmt("fd/%1%", file->fs_fd);
|
||||
}
|
||||
|
||||
unchecked[file->fs_path].emplace(fmt("{procstat:%1%/%2%}", procs[procidx].ki_pid, role)
|
||||
);
|
||||
}
|
||||
|
||||
auto env_name = fmt("{procstat:%1%/env}", procs[procidx].ki_pid);
|
||||
// No need to free, the buffer is reused on next call and deallocated in procstat_close
|
||||
char ** env = procstat_getenvv(ps.get(), &procs[procidx], 0);
|
||||
if (env == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; env[i]; i++) {
|
||||
auto envString = std::string(env[i]);
|
||||
|
||||
auto envEnd = std::sregex_iterator{};
|
||||
for (auto match =
|
||||
std::sregex_iterator{envString.begin(), envString.end(), storePathRegex};
|
||||
match != envEnd;
|
||||
match++)
|
||||
{
|
||||
unchecked[match->str()].emplace(env_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
src/libstore/platform/freebsd.hh
Normal file
47
src/libstore/platform/freebsd.hh
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "build/local-derivation-goal.hh"
|
||||
#include "gc-store.hh"
|
||||
#include "local-store.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* FreeBSD-specific implementation of LocalStore
|
||||
*/
|
||||
class FreeBSDLocalStore : public LocalStore
|
||||
{
|
||||
public:
|
||||
FreeBSDLocalStore(const Params & params)
|
||||
: StoreConfig(params)
|
||||
, LocalFSStoreConfig(params)
|
||||
, LocalStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, LocalStore(params)
|
||||
{
|
||||
}
|
||||
FreeBSDLocalStore(const std::string scheme, std::string path, const Params & params)
|
||||
: FreeBSDLocalStore(params)
|
||||
{
|
||||
throw UnimplementedError("FreeBSDLocalStore");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* FreeBSD-specific implementation of LocalDerivationGoal
|
||||
*/
|
||||
class FreeBSDLocalDerivationGoal : public LocalDerivationGoal
|
||||
{
|
||||
public:
|
||||
using LocalDerivationGoal::LocalDerivationGoal;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
|
@ -15,6 +15,11 @@
|
|||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#if __FreeBSD__
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <cgroup.hh>
|
||||
|
||||
|
@ -102,6 +107,24 @@ std::optional<Path> getSelfExe()
|
|||
return buf;
|
||||
else
|
||||
return std::nullopt;
|
||||
#elif __FreeBSD__
|
||||
int sysctlName[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PATHNAME,
|
||||
-1,
|
||||
};
|
||||
size_t pathLen = 0;
|
||||
if (sysctl(sysctlName, sizeof(sysctlName) / sizeof(sysctlName[0]), nullptr, &pathLen, nullptr, 0) < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<char> path(pathLen);
|
||||
if (sysctl(sysctlName, sizeof(sysctlName) / sizeof(sysctlName[0]), path.data(), &pathLen, nullptr, 0) < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Path(path.begin(), path.end());
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
|
|
|
@ -166,16 +166,6 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
|
|||
may confuse external tooling.
|
||||
)",
|
||||
},
|
||||
{
|
||||
.tag = Xp::PipeOperator,
|
||||
.name = "pipe-operator",
|
||||
.description = R"(
|
||||
Enable new operators for function application to "pipe" arguments through a chain of functions similar to `lib.pipe`.
|
||||
This implementation is based on Nix [RFC 148](https://github.com/NixOS/rfcs/pull/148).
|
||||
|
||||
Tracking issue: https://git.lix.systems/lix-project/lix/issues/438
|
||||
)",
|
||||
},
|
||||
{
|
||||
.tag = Xp::FetchClosure,
|
||||
.name = "fetch-closure",
|
||||
|
|
|
@ -21,7 +21,6 @@ enum struct ExperimentalFeature
|
|||
NixCommand,
|
||||
RecursiveNix,
|
||||
NoUrlLiterals,
|
||||
PipeOperator,
|
||||
FetchClosure,
|
||||
ReplFlake,
|
||||
AutoAllocateUids,
|
||||
|
|
|
@ -21,23 +21,15 @@ Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
{
|
||||
if (path.empty() || path[0] != '/') {
|
||||
if (!dir) {
|
||||
#ifdef __GNU__
|
||||
/* GNU (aka. GNU/Hurd) doesn't have any limitation on path
|
||||
lengths and doesn't define `PATH_MAX'. */
|
||||
char *buf = getcwd(NULL, 0);
|
||||
if (buf == NULL)
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
if (!getcwd(buf, sizeof(buf)))
|
||||
#endif
|
||||
if (!getcwd(buf, sizeof(buf))) {
|
||||
throw SysError("cannot get cwd");
|
||||
}
|
||||
path = concatStrings(buf, "/", path);
|
||||
#ifdef __GNU__
|
||||
free(buf);
|
||||
#endif
|
||||
} else
|
||||
} else {
|
||||
path = concatStrings(*dir, "/", path);
|
||||
}
|
||||
}
|
||||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ std::pair<int, std::string> runProgram(RunOptions && options)
|
|||
try {
|
||||
auto proc = runProgram2(options);
|
||||
Finally const _wait([&] { proc.wait(); });
|
||||
stdout = proc.stdout()->drain();
|
||||
stdout = proc.getStdout()->drain();
|
||||
} catch (ExecError & e) {
|
||||
status = e.status;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
|
||||
void wait();
|
||||
|
||||
Source * stdout() const { return stdoutSource.get(); }
|
||||
Source * getStdout() const { return stdoutSource.get(); };
|
||||
};
|
||||
|
||||
std::pair<int, std::string> runProgram(RunOptions && options);
|
||||
|
|
|
@ -54,7 +54,7 @@ TarArchive::TarArchive(Source & source, bool raw) : buffer(65536)
|
|||
archive_read_support_format_raw(archive);
|
||||
archive_read_support_format_empty(archive);
|
||||
}
|
||||
archive_read_set_option(archive, NULL, "mac-ext", NULL);
|
||||
archive_read_set_option(archive, nullptr, "mac-ext", nullptr);
|
||||
check(archive_read_open(archive, (void *)this, callback_open, callback_read, callback_close), "Failed to open archive (%s)");
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ TarArchive::TarArchive(const Path & path)
|
|||
|
||||
archive_read_support_filter_all(archive);
|
||||
archive_read_support_format_all(archive);
|
||||
archive_read_set_option(archive, NULL, "mac-ext", NULL);
|
||||
archive_read_set_option(archive, nullptr, "mac-ext", nullptr);
|
||||
check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s");
|
||||
}
|
||||
|
||||
|
|
|
@ -24,24 +24,17 @@ GroupedPaths getClosureInfo(ref<Store> store, const StorePath & toplevel)
|
|||
|
||||
GroupedPaths groupedPaths;
|
||||
|
||||
for (auto & path : closure) {
|
||||
for (auto const & path : closure) {
|
||||
/* Strip the output name. Unfortunately this is ambiguous (we
|
||||
can't distinguish between output names like "bin" and
|
||||
version suffixes like "unstable"). */
|
||||
static std::regex regex("(.*)-([a-z]+|lib32|lib64)");
|
||||
std::smatch match;
|
||||
std::cmatch match;
|
||||
std::string name{path.name()};
|
||||
// Used to keep name alive through being potentially overwritten below
|
||||
// (to not invalidate the references from the regex result)
|
||||
//
|
||||
// n.b. cannot be just path.name().{begin,end}() since that returns const
|
||||
// char *, which does not, for some reason, convert as required on
|
||||
// libstdc++. Seems like a libstdc++ bug or standard bug to me... we
|
||||
// can afford the allocation in any case.
|
||||
const std::string origName{path.name()};
|
||||
std::string_view const origName = path.name();
|
||||
std::string outputName;
|
||||
|
||||
if (std::regex_match(origName, match, regex)) {
|
||||
if (std::regex_match(origName.begin(), origName.end(), match, regex)) {
|
||||
name = match[1];
|
||||
outputName = match[2];
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ std::set<std::string> runResolver(const Path & filename)
|
|||
return {};
|
||||
}
|
||||
|
||||
char* obj = (char*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
|
||||
char* obj = (char*) mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
|
||||
if (!obj)
|
||||
throw SysError("mmapping '%s'", filename);
|
||||
|
||||
|
|
|
@ -26,9 +26,9 @@ namespace nix {
|
|||
, state({}, store)
|
||||
{
|
||||
}
|
||||
Value eval(std::string input, bool forceValue = true, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings) {
|
||||
Value eval(std::string input, bool forceValue = true) {
|
||||
Value v;
|
||||
Expr & e = state.parseExprFromString(input, state.rootPath(CanonPath::root), xpSettings);
|
||||
Expr & e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
|
||||
state.eval(e, v);
|
||||
if (forceValue)
|
||||
state.forceValue(v, noPos);
|
||||
|
|
|
@ -59,11 +59,6 @@ namespace nix {
|
|||
ASSERT_THAT(v, IsFloatEq(1.234));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, pointfloat) {
|
||||
auto v = eval(".234");
|
||||
ASSERT_THAT(v, IsFloatEq(0.234));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, updateAttrs) {
|
||||
auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
@ -86,18 +81,6 @@ namespace nix {
|
|||
ASSERT_THAT(v, IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, urlLiteral) {
|
||||
auto v = eval("https://nixos.org");
|
||||
ASSERT_THAT(v, IsStringEq("https://nixos.org"));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, noUrlLiteral) {
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "no-url-literals");
|
||||
|
||||
ASSERT_THROW(eval("https://nixos.org", true, mockXpSettings), Error);
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, withFound) {
|
||||
auto v = eval("with { a = 23; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(23));
|
||||
|
@ -210,40 +193,4 @@ namespace nix {
|
|||
TEST_F(TrivialExpressionTest, orCantBeUsed) {
|
||||
ASSERT_THROW(eval("let or = 1; in or"), Error);
|
||||
}
|
||||
|
||||
// pipes are gated behind an experimental feature flag
|
||||
TEST_F(TrivialExpressionTest, pipeDisabled) {
|
||||
ASSERT_THROW(eval("let add = l: r: l + r; in ''a'' |> add ''b''"), Error);
|
||||
ASSERT_THROW(eval("let add = l: r: l + r; in ''a'' <| add ''b''"), Error);
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, pipeRight) {
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "pipe-operator");
|
||||
|
||||
auto v = eval("let add = l: r: l + r; in ''a'' |> add ''b''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("ba"));
|
||||
v = eval("let add = l: r: l + r; in ''a'' |> add ''b'' |> add ''c''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("cba"));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, pipeLeft) {
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "pipe-operator");
|
||||
|
||||
auto v = eval("let add = l: r: l + r; in add ''a'' <| ''b''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("ab"));
|
||||
v = eval("let add = l: r: l + r; in add ''a'' <| add ''b'' <| ''c''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("abc"));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, pipeMixed) {
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "pipe-operator");
|
||||
|
||||
auto v = eval("let add = l: r: l + r; in add ''a'' <| ''b'' |> add ''c''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("acb"));
|
||||
v = eval("let add = l: r: l + r; in ''a'' |> add <| ''c''", true, mockXpSettings);
|
||||
ASSERT_THAT(v, IsStringEq("ac"));
|
||||
}
|
||||
} /* namespace nix */
|
||||
|
|
Loading…
Reference in a new issue