Compare commits

..

17 commits

Author SHA1 Message Date
Artemis Tosini 60a48311e8 Merge "libutil: Support getSelfExe on FreeBSD" into main 2024-07-25 23:10:30 +00:00
jade c4c7cb7613 Merge changes Ic0dfcfe2,Ibe73851f,Ia7a8df1c,I400b2031 into main
* changes:
  package.nix: remove dead code
  diff-closures: remove gratuitous copy
  tree-wide: NULL -> nullptr
  libutil: rip out GNU Hurd support code
2024-07-25 18:05:41 +00:00
Qyriad 8d12e0fbb7 fix building with Musl, fixing static builds
Musl stdout macro expands¹ to something that isn't a valid identifier,
so we get syntax errors when compiling usage of a method called stdout
with Musl's stdio.h.

[1]: https://git.musl-libc.org/cgit/musl/tree/include/stdio.h?id=ab31e9d6a0fa7c5c408856c89df2dfb12c344039#n67

Change-Id: I10e6f6a49504399bf8edd59c5d9e4e62449469e8
2024-07-24 17:21:40 +00:00
Artemis Tosini 3b96b51cf4
libutil: Support getSelfExe on FreeBSD
getSelfExe is used in a few places re-execute nix.
Current code in this file uses ifdefs to support several
platforms, just keep doing that

Change-Id: Iecc2ada0101aea0c30524e3a1218594f919d74bf
2024-07-24 01:28:03 +00:00
jade 98e8cf9c63 package.nix: remove dead code
Change-Id: Ic0dfcfe27dbf13da4f7f74f5fab8ce6fa718d28f
2024-07-23 21:53:43 +02:00
jade 12a5838d11 diff-closures: remove gratuitous copy
This was done originally because std::smatch does not accept `const char
*` as iterators. However, this was because we should have been using
std::cmatch instead.

Change-Id: Ibe73851fd39755e883df2d33d22fed72ac0a04ae
2024-07-23 21:45:30 +02:00
jade 2436f2110a tree-wide: NULL -> nullptr
This is slightly more type safe and is more in line with modern C++.

Change-Id: Ia7a8df1c7788085020d1bdc941d6f9cee356144e
2024-07-23 21:06:55 +02:00
jade 916b5c68fb libutil: rip out GNU Hurd support code
Nobody has stepped up to add further support for Hurd since this code
appeared in 2010 or 2014. We don't need it.

Change-Id: I400b2031a225551ea3c71a3ef3ea9fdb599dfba3
2024-07-23 20:52:04 +02:00
Artemis Tosini 53f3e39815
libstore: Add FreeBSD findPlatformRoots
Use libprocstat to find garbage collector roots on FreeBSD.
Tested working on a FreeBSD machine, although there is no CI yet

Change-Id: Id36bac8c3de6cc4de94e2d76e9663dd4b76068a9
2024-07-23 17:49:33 +00:00
Pierre Bourdon 73c013a5df Merge "libexpr/gc-alloc: fix compilation with !HAVE_BOEHMGC" into main 2024-07-22 23:14:59 +00:00
Pierre Bourdon e76245f8e9
libexpr/gc-alloc: fix compilation with !HAVE_BOEHMGC
Fixes: 72ee25b402
Change-Id: Ib59386af1415a8ed4b53af24ec22a4ffa5e5877d
2024-07-23 00:50:09 +02:00
eldritch horrors 472ff1b833 libstore: keep Goal errors as unique_ptrs
Error is pretty large, and most goals do not fail. this alone more than
halves the size of Goal on x86_64-linux, from 720 bytes down to 344. in
derived classes the difference is not as dramatic, but even the largest
derived class (`LocalDerivationGoal`) loses almost 20% of its footprint

Change-Id: Ifda8f94c81b6566eeb3e52d55d9796ec40c7bce8
2024-07-22 19:01:40 +00:00
eldritch horrors 7bf1aff44a libstore: remove an always-defaulted argument
Change-Id: I3c7f17d5492a16bb54480fa1aa384b96fba72d61
2024-07-22 19:01:40 +00:00
eldritch horrors 58a91d70c9 libstore: use std::async instead of Goal threads
the goals are either already using std::async and merely forgot to
remove std::thread vestiges or they emulate async with threads and
promises. we can simply use async directly everywhere for clarity.

Change-Id: I3f05098310a25984f10fff1e68c573329002b500
2024-07-22 19:01:40 +00:00
eldritch horrors ad36fb43ad libstore: remove addToWeakGoals
under owner_less it's equivalent to insert(), only sometimes a little
bit faster because it does not construct a weak_ptr if the goal is in
the set already. this small difference in performance does not matter
here and c++23 will make insert transparent anyway, so we can drop it

Change-Id: I7cbd7d6e0daa95d67145ec58183162f6c4743b15
2024-07-22 19:01:40 +00:00
eldritch horrors d70e045f90 libstore: remove Goal::ecBusy
this should be an optional. "busy" is not an *exit* code!

Change-Id: Ic231cb27b022312b1a7a7b9602f32845b7a9c934
2024-07-22 19:01:40 +00:00
eldritch horrors 20f53346df libstore: remove unused Worker::waitForAnyGoal
Change-Id: Ia3ebd434b17052b6760ce74d8e20025a72148613
2024-07-22 19:01:40 +00:00
41 changed files with 326 additions and 323 deletions

View file

@ -91,11 +91,6 @@ midnightveil:
ncfavier:
github: ncfavier
piegames:
display_name: piegames
forgejo: piegames
github: piegamesde
puck:
display_name: puck
forgejo: puck

View file

@ -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.

View file

@ -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

View file

@ -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
#

View file

@ -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

View file

@ -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();

View file

@ -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);
}

View file

@ -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.

View file

@ -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)

View file

@ -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;
};

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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()

View file

@ -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

View file

@ -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()))

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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.

View file

@ -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))

View file

@ -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

View file

@ -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;

View file

@ -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");
}

View file

@ -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,

View file

@ -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

View file

@ -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());
}
}

View 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);
}
}
}
}
}

View 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:
};
}

View file

@ -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

View file

@ -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",

View file

@ -21,7 +21,6 @@ enum struct ExperimentalFeature
NixCommand,
RecursiveNix,
NoUrlLiterals,
PipeOperator,
FetchClosure,
ReplFlake,
AutoAllocateUids,

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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");
}

View file

@ -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];
}

View file

@ -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);

View file

@ -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);

View file

@ -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 */