Compare commits
43 Commits
unix-sourc
...
2.24.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb11c14998 | ||
|
|
a6ad5565ef | ||
|
|
2e1cb495c1 | ||
|
|
e25410c788 | ||
|
|
25510ba66f | ||
|
|
d9b60b3902 | ||
|
|
a041688133 | ||
|
|
1288970496 | ||
|
|
0cfc9bf133 | ||
|
|
f160d3ac68 | ||
|
|
6187ee468f | ||
|
|
12fa019ae5 | ||
|
|
ae486b2910 | ||
|
|
437f7a0042 | ||
|
|
b53b07b30b | ||
|
|
8d0414d682 | ||
|
|
c5a0e624d9 | ||
|
|
0679505d8c | ||
|
|
ea7abb58b5 | ||
|
|
f1ab41b2bc | ||
|
|
c21f664e82 | ||
|
|
0c25bea7cc | ||
|
|
9d8669b14a | ||
|
|
cb0439f0c2 | ||
|
|
30a57328d2 | ||
|
|
79a43160ca | ||
|
|
7befd60c01 | ||
|
|
90fb4e8890 | ||
|
|
3ac5d736e2 | ||
|
|
4e707b8e57 | ||
|
|
20cae372f4 | ||
|
|
d550139191 | ||
|
|
5b62a1dbd6 | ||
|
|
450252c92c | ||
|
|
4036c3aafb | ||
|
|
935bf1157d | ||
|
|
b1941c9f8a | ||
|
|
40832b0a95 | ||
|
|
fa78d7f72f | ||
|
|
2382a52c84 | ||
|
|
fe6a7c805c | ||
|
|
0a167ffd1f | ||
|
|
206e32e2d7 |
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -49,6 +49,7 @@ jobs:
|
||||
done
|
||||
) &
|
||||
- run: nix --experimental-features 'nix-command flakes' flake check -L
|
||||
- run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json
|
||||
|
||||
# Steps to test CI automation in your own fork.
|
||||
# Cachix:
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
`pkgconfig` and the Boehm garbage collector, and pass the flag
|
||||
`--enable-gc` to `configure`.
|
||||
|
||||
For `bdw-gc` <= 8.2.4 Nix needs a [small patch](https://github.com/NixOS/nix/blob/ac4d2e7b857acdfeac35ac8a592bdecee2d29838/boehmgc-traceable_allocator-public.diff) to be applied.
|
||||
|
||||
- The `boost` library of version 1.66.0 or higher. It can be obtained
|
||||
from the official web site <https://www.boost.org/>.
|
||||
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -80,11 +80,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721548954,
|
||||
"narHash": "sha256-7cCC8+Tdq1+3OPyc3+gVo9dzUNkNIQfwSDJ2HSi2u3o=",
|
||||
"lastModified": 1723688146,
|
||||
"narHash": "sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz+NG82pbdg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "63d37ccd2d178d54e7fb691d7ec76000740ea24a",
|
||||
"rev": "c3d4ac725177c030b1e289015989da2ad9d56af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
versionSuffix =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
((NEW_NIX_FIRST_BUILD_UID=301))
|
||||
((NEW_NIX_FIRST_BUILD_UID=351))
|
||||
|
||||
id_available(){
|
||||
dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null
|
||||
|
||||
@@ -4,7 +4,17 @@ set -eu
|
||||
set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
|
||||
# Notes:
|
||||
# - up to macOS Big Sur we used the same GID/UIDs as Linux (30000:30001-32)
|
||||
# - we changed UID to 301 because Big Sur updates failed into recovery mode
|
||||
# we're targeting the 200-400 UID range for role users mentioned in the
|
||||
# usage note for sysadminctl
|
||||
# - we changed UID to 351 because Sequoia now uses UIDs 300-304 for its own
|
||||
# daemon users
|
||||
# - we changed GID to 350 alongside above just because it hides the nixbld
|
||||
# group from the Users & Groups settings panel :)
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-351}"
|
||||
export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-350}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
|
||||
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
@@ -23,10 +23,10 @@ readonly RED='\033[31m'
|
||||
# installer allows overriding build user count to speed up installation
|
||||
# as creating each user takes non-trivial amount of time on macos
|
||||
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
|
||||
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
|
||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||
# each system specific installer must set these:
|
||||
# NIX_FIRST_BUILD_UID
|
||||
# NIX_BUILD_GROUP_ID
|
||||
# NIX_BUILD_USER_NAME_TEMPLATE
|
||||
# Please don't change this. We don't support it, because the
|
||||
# default shell profile that comes with Nix doesn't support it.
|
||||
@@ -530,9 +530,7 @@ It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
|
||||
with the UID $primary_group_id. This script can't really handle
|
||||
that right now, so I'm going to give up.
|
||||
|
||||
You can fix this by editing this script and changing the
|
||||
NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
|
||||
to $primary_group_id and re-run.
|
||||
You can export NIX_BUILD_GROUP_ID=$primary_group_id and re-run.
|
||||
EOF
|
||||
else
|
||||
row " Exists" "Yes"
|
||||
|
||||
@@ -5,6 +5,7 @@ set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
||||
export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
|
||||
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
|
||||
|
||||
@@ -171,7 +171,9 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
|
||||
{
|
||||
if (EvalSettings::isPseudoUrl(s)) {
|
||||
auto accessor = fetchers::downloadTarball(
|
||||
EvalSettings::resolvePseudoUrl(s)).accessor;
|
||||
state.store,
|
||||
state.fetchSettings,
|
||||
EvalSettings::resolvePseudoUrl(s));
|
||||
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy);
|
||||
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,16 @@
|
||||
#include "nix_api_util.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef __has_c_attribute
|
||||
# define __has_c_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute(deprecated)
|
||||
# define NIX_DEPRECATED(msg) [[deprecated(msg)]]
|
||||
#else
|
||||
# define NIX_DEPRECATED(msg)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -45,7 +55,7 @@ typedef struct EvalState EvalState; // nix::EvalState
|
||||
* @see nix_value_incref, nix_value_decref
|
||||
*/
|
||||
typedef struct nix_value nix_value;
|
||||
[[deprecated("use nix_value instead")]] typedef nix_value Value;
|
||||
NIX_DEPRECATED("use nix_value instead") typedef nix_value Value;
|
||||
|
||||
// Function prototypes
|
||||
/**
|
||||
|
||||
@@ -32,122 +32,6 @@ static void * oomHandler(size_t requested)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
class BoehmGCStackAllocator : public StackAllocator
|
||||
{
|
||||
boost::coroutines2::protected_fixedsize_stack stack{
|
||||
// We allocate 8 MB, the default max stack size on NixOS.
|
||||
// A smaller stack might be quicker to allocate but reduces the stack
|
||||
// depth available for source filter expressions etc.
|
||||
std::max(boost::context::stack_traits::default_size(), static_cast<std::size_t>(8 * 1024 * 1024))};
|
||||
|
||||
// This is specific to boost::coroutines2::protected_fixedsize_stack.
|
||||
// The stack protection page is included in sctx.size, so we have to
|
||||
// subtract one page size from the stack size.
|
||||
std::size_t pfss_usable_stack_size(boost::context::stack_context & sctx)
|
||||
{
|
||||
return sctx.size - boost::context::stack_traits::page_size();
|
||||
}
|
||||
|
||||
public:
|
||||
boost::context::stack_context allocate() override
|
||||
{
|
||||
auto sctx = stack.allocate();
|
||||
|
||||
// Stacks generally start at a high address and grow to lower addresses.
|
||||
// Architectures that do the opposite are rare; in fact so rare that
|
||||
// boost_routine does not implement it.
|
||||
// So we subtract the stack size.
|
||||
GC_add_roots(static_cast<char *>(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp);
|
||||
return sctx;
|
||||
}
|
||||
|
||||
void deallocate(boost::context::stack_context sctx) override
|
||||
{
|
||||
GC_remove_roots(static_cast<char *>(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp);
|
||||
stack.deallocate(sctx);
|
||||
}
|
||||
};
|
||||
|
||||
static BoehmGCStackAllocator boehmGCStackAllocator;
|
||||
|
||||
/**
|
||||
* When a thread goes into a coroutine, we lose its original sp until
|
||||
* control flow returns to the thread.
|
||||
* While in the coroutine, the sp points outside the thread stack,
|
||||
* so we can detect this and push the entire thread stack instead,
|
||||
* as an approximation.
|
||||
* The coroutine's stack is covered by `BoehmGCStackAllocator`.
|
||||
* This is not an optimal solution, because the garbage is scanned when a
|
||||
* coroutine is active, for both the coroutine and the original thread stack.
|
||||
* However, the implementation is quite lean, and usually we don't have active
|
||||
* coroutines during evaluation, so this is acceptable.
|
||||
*/
|
||||
void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id)
|
||||
{
|
||||
void *& sp = *sp_ptr;
|
||||
auto pthread_id = reinterpret_cast<pthread_t>(_pthread_id);
|
||||
# ifndef __APPLE__
|
||||
pthread_attr_t pattr;
|
||||
# endif
|
||||
size_t osStackSize;
|
||||
// The low address of the stack, which grows down.
|
||||
void * osStackLimit;
|
||||
void * osStackBase;
|
||||
|
||||
# ifdef __APPLE__
|
||||
osStackSize = pthread_get_stacksize_np(pthread_id);
|
||||
osStackLimit = pthread_get_stackaddr_np(pthread_id);
|
||||
# else
|
||||
if (pthread_attr_init(&pattr)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_attr_init failed");
|
||||
}
|
||||
# ifdef HAVE_PTHREAD_GETATTR_NP
|
||||
if (pthread_getattr_np(pthread_id, &pattr)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_getattr_np failed");
|
||||
}
|
||||
# elif HAVE_PTHREAD_ATTR_GET_NP
|
||||
if (!pthread_attr_init(&pattr)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_attr_init failed");
|
||||
}
|
||||
if (!pthread_attr_get_np(pthread_id, &pattr)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed");
|
||||
}
|
||||
# else
|
||||
# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`"
|
||||
# endif
|
||||
if (pthread_attr_getstack(&pattr, &osStackLimit, &osStackSize)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed");
|
||||
}
|
||||
if (pthread_attr_destroy(&pattr)) {
|
||||
throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed");
|
||||
}
|
||||
# endif
|
||||
osStackBase = (char *) osStackLimit + osStackSize;
|
||||
// NOTE: We assume the stack grows down, as it does on all architectures we support.
|
||||
// Architectures that grow the stack up are rare.
|
||||
if (sp >= osStackBase || sp < osStackLimit) { // sp is outside the os stack
|
||||
sp = osStackLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable GC while this object lives. Used by CoroutineContext.
|
||||
*
|
||||
* Boehm keeps a count of GC_disable() and GC_enable() calls,
|
||||
* and only enables GC when the count matches.
|
||||
*/
|
||||
class BoehmDisableGC
|
||||
{
|
||||
public:
|
||||
BoehmDisableGC()
|
||||
{
|
||||
GC_disable();
|
||||
};
|
||||
~BoehmDisableGC()
|
||||
{
|
||||
GC_enable();
|
||||
};
|
||||
};
|
||||
|
||||
static inline void initGCReal()
|
||||
{
|
||||
/* Initialise the Boehm garbage collector. */
|
||||
@@ -168,24 +52,6 @@ static inline void initGCReal()
|
||||
|
||||
GC_set_oom_fn(oomHandler);
|
||||
|
||||
StackAllocator::defaultAllocator = &boehmGCStackAllocator;
|
||||
|
||||
// TODO: Remove __APPLE__ condition.
|
||||
// Comment suggests an implementation that works on darwin and windows
|
||||
// https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196
|
||||
# if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__)
|
||||
GC_set_sp_corrector(&fixupBoehmStackPointer);
|
||||
|
||||
if (!GC_get_sp_corrector()) {
|
||||
printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines");
|
||||
/* Used to disable GC when entering coroutines on macOS */
|
||||
create_coro_gc_hook = []() -> std::shared_ptr<void> { return std::make_shared<BoehmDisableGC>(); };
|
||||
}
|
||||
# else
|
||||
# warning \
|
||||
"BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later."
|
||||
# endif
|
||||
|
||||
/* Set the initial heap size to something fairly big (25% of
|
||||
physical RAM, up to a maximum of 384 MiB) so that in most cases
|
||||
we don't need to garbage collect at all. (Collection has a
|
||||
|
||||
@@ -3083,7 +3083,9 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
|
||||
if (EvalSettings::isPseudoUrl(value)) {
|
||||
try {
|
||||
auto accessor = fetchers::downloadTarball(
|
||||
EvalSettings::resolvePseudoUrl(value)).accessor;
|
||||
store,
|
||||
fetchSettings,
|
||||
EvalSettings::resolvePseudoUrl(value));
|
||||
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
|
||||
return finish(store->toRealPath(storePath));
|
||||
} catch (Error & e) {
|
||||
|
||||
@@ -501,7 +501,11 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
// https://github.com/NixOS/nix/issues/4313
|
||||
auto storePath =
|
||||
unpack
|
||||
? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name)
|
||||
? fetchToStore(
|
||||
*state.store,
|
||||
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
|
||||
FetchMode::Copy,
|
||||
name)
|
||||
: fetchers::downloadFile(state.store, *url, name).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
|
||||
@@ -102,7 +102,7 @@ DownloadFileResult downloadFile(
|
||||
};
|
||||
}
|
||||
|
||||
DownloadTarballResult downloadTarball(
|
||||
static DownloadTarballResult downloadTarball_(
|
||||
const std::string & url,
|
||||
const Headers & headers)
|
||||
{
|
||||
@@ -202,6 +202,22 @@ DownloadTarballResult downloadTarball(
|
||||
return attrsToResult(infoAttrs);
|
||||
}
|
||||
|
||||
ref<SourceAccessor> downloadTarball(
|
||||
ref<Store> store,
|
||||
const Settings & settings,
|
||||
const std::string & url)
|
||||
{
|
||||
/* Go through Input::getAccessor() to ensure that the resulting
|
||||
accessor has a fingerprint. */
|
||||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "tarball");
|
||||
attrs.insert_or_assign("url", url);
|
||||
|
||||
auto input = Input::fromAttrs(settings, std::move(attrs));
|
||||
|
||||
return input.getAccessor(store).first;
|
||||
}
|
||||
|
||||
// An input scheme corresponding to a curl-downloadable resource.
|
||||
struct CurlInputScheme : InputScheme
|
||||
{
|
||||
@@ -353,7 +369,7 @@ struct TarballInputScheme : CurlInputScheme
|
||||
{
|
||||
auto input(_input);
|
||||
|
||||
auto result = downloadTarball(getStrAttr(input.attrs, "url"), {});
|
||||
auto result = downloadTarball_(getStrAttr(input.attrs, "url"), {});
|
||||
|
||||
result.accessor->setPathDisplay("«" + input.to_string() + "»");
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ struct SourceAccessor;
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
struct Settings;
|
||||
|
||||
struct DownloadFileResult
|
||||
{
|
||||
StorePath storePath;
|
||||
@@ -40,8 +42,9 @@ struct DownloadTarballResult
|
||||
* Download and import a tarball into the Git cache. The result is the
|
||||
* Git tree hash of the root directory.
|
||||
*/
|
||||
DownloadTarballResult downloadTarball(
|
||||
const std::string & url,
|
||||
const Headers & headers = {});
|
||||
ref<SourceAccessor> downloadTarball(
|
||||
ref<Store> store,
|
||||
const Settings & settings,
|
||||
const std::string & url);
|
||||
|
||||
}
|
||||
|
||||
@@ -145,8 +145,10 @@ Goal::Co PathSubstitutionGoal::init()
|
||||
/* None left. Terminate this goal and let someone else deal
|
||||
with it. */
|
||||
|
||||
worker.failedSubstitutions++;
|
||||
worker.updateProgress();
|
||||
if (substituterFailed) {
|
||||
worker.failedSubstitutions++;
|
||||
worker.updateProgress();
|
||||
}
|
||||
|
||||
/* Hack: don't indicate failure if there were no substituters.
|
||||
In that case the calling derivation should just do a
|
||||
@@ -158,7 +160,7 @@ Goal::Co PathSubstitutionGoal::init()
|
||||
}
|
||||
|
||||
|
||||
Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub, std::shared_ptr<const ValidPathInfo> info, bool& substituterFailed)
|
||||
Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub, std::shared_ptr<const ValidPathInfo> info, bool & substituterFailed)
|
||||
{
|
||||
trace("all references realised");
|
||||
|
||||
@@ -181,7 +183,7 @@ Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub,
|
||||
/* Make sure that we are allowed to start a substitution. Note that even
|
||||
if maxSubstitutionJobs == 0, we still allow a substituter to run. This
|
||||
prevents infinite waiting. */
|
||||
if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) {
|
||||
while (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) {
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
co_await Suspend{};
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
*/
|
||||
Co init() override;
|
||||
Co gotInfo();
|
||||
Co tryToRun(StorePath subPath, nix::ref<Store> sub, std::shared_ptr<const ValidPathInfo> info, bool& substituterFailed);
|
||||
Co tryToRun(StorePath subPath, nix::ref<Store> sub, std::shared_ptr<const ValidPathInfo> info, bool & substituterFailed);
|
||||
Co finished();
|
||||
|
||||
/**
|
||||
|
||||
@@ -184,13 +184,13 @@ void Worker::wakeUp(GoalPtr goal)
|
||||
}
|
||||
|
||||
|
||||
unsigned Worker::getNrLocalBuilds()
|
||||
size_t Worker::getNrLocalBuilds()
|
||||
{
|
||||
return nrLocalBuilds;
|
||||
}
|
||||
|
||||
|
||||
unsigned Worker::getNrSubstitutions()
|
||||
size_t Worker::getNrSubstitutions()
|
||||
{
|
||||
return nrSubstitutions;
|
||||
}
|
||||
|
||||
@@ -92,12 +92,12 @@ private:
|
||||
* Number of build slots occupied. This includes local builds but does not
|
||||
* include substitutions or remote builds via the build hook.
|
||||
*/
|
||||
unsigned int nrLocalBuilds;
|
||||
size_t nrLocalBuilds;
|
||||
|
||||
/**
|
||||
* Number of substitution slots occupied.
|
||||
*/
|
||||
unsigned int nrSubstitutions;
|
||||
size_t nrSubstitutions;
|
||||
|
||||
/**
|
||||
* Maps used to prevent multiple instantiations of a goal for the
|
||||
@@ -235,12 +235,12 @@ public:
|
||||
* Return the number of local build processes currently running (but not
|
||||
* remote builds via the build hook).
|
||||
*/
|
||||
unsigned int getNrLocalBuilds();
|
||||
size_t getNrLocalBuilds();
|
||||
|
||||
/**
|
||||
* Return the number of substitution processes currently running.
|
||||
*/
|
||||
unsigned int getNrSubstitutions();
|
||||
size_t getNrSubstitutions();
|
||||
|
||||
/**
|
||||
* Registers a running child process. `inBuildSlot` means that
|
||||
|
||||
@@ -220,8 +220,6 @@ std::string S3BinaryCacheStoreConfig::doc()
|
||||
|
||||
struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore
|
||||
{
|
||||
std::string bucketName;
|
||||
|
||||
Stats stats;
|
||||
|
||||
S3Helper s3Helper;
|
||||
|
||||
@@ -210,14 +210,16 @@ StorePath Store::addToStore(
|
||||
fsm = FileSerialisationMethod::NixArchive;
|
||||
break;
|
||||
}
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
dumpPath(path, sink, fsm, filter);
|
||||
std::optional<StorePath> storePath;
|
||||
auto sink = sourceToSink([&](Source & source) {
|
||||
LengthSource lengthSource(source);
|
||||
storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair);
|
||||
if (lengthSource.total >= settings.warnLargePathThreshold)
|
||||
warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total));
|
||||
});
|
||||
LengthSource lengthSource(*source);
|
||||
auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair);
|
||||
if (lengthSource.total >= settings.warnLargePathThreshold)
|
||||
warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total));
|
||||
return storePath;
|
||||
dumpPath(path, *sink, fsm, filter);
|
||||
sink->finish();
|
||||
return storePath.value();
|
||||
}
|
||||
|
||||
void Store::addMultipleToStore(
|
||||
|
||||
@@ -49,6 +49,7 @@ R""(
|
||||
(if (param "_ALLOW_LOCAL_NETWORKING")
|
||||
(begin
|
||||
(allow network* (remote ip "localhost:*"))
|
||||
(allow network-inbound (local ip "*:*")) ; required to bind and listen
|
||||
|
||||
; Allow access to /etc/resolv.conf (which is a symlink to
|
||||
; /private/var/run/resolv.conf).
|
||||
|
||||
@@ -23,7 +23,7 @@ struct ArchiveSettings : Config
|
||||
false,
|
||||
#endif
|
||||
"use-case-hack",
|
||||
"Whether to enable a Darwin-specific hack for dealing with file name collisions."};
|
||||
"Whether to enable a macOS-specific hack for dealing with file name case collisions."};
|
||||
};
|
||||
|
||||
static ArchiveSettings archiveSettings;
|
||||
@@ -214,11 +214,13 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
||||
else if (t == "directory") {
|
||||
sink.createDirectory(path);
|
||||
|
||||
std::string prevName;
|
||||
|
||||
while (1) {
|
||||
s = getString();
|
||||
|
||||
if (s == "entry") {
|
||||
std::string name, prevName;
|
||||
std::string name;
|
||||
|
||||
s = getString();
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
@@ -241,6 +243,9 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
||||
debug("case collision between '%1%' and '%2%'", i->first, name);
|
||||
name += caseHackSuffix;
|
||||
name += std::to_string(++i->second);
|
||||
auto j = names.find(name);
|
||||
if (j != names.end())
|
||||
throw Error("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first);
|
||||
} else
|
||||
names[name] = 0;
|
||||
}
|
||||
|
||||
@@ -68,10 +68,19 @@ static RestoreSinkSettings restoreSinkSettings;
|
||||
|
||||
static GlobalConfig::Register r1(&restoreSinkSettings);
|
||||
|
||||
static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path)
|
||||
{
|
||||
auto dst = src;
|
||||
if (!path.rel().empty())
|
||||
dst /= path.rel();
|
||||
return dst;
|
||||
}
|
||||
|
||||
void RestoreSink::createDirectory(const CanonPath & path)
|
||||
{
|
||||
std::filesystem::create_directory(dstPath / path.rel());
|
||||
auto p = append(dstPath, path);
|
||||
if (!std::filesystem::create_directory(p))
|
||||
throw Error("path '%s' already exists", p.string());
|
||||
};
|
||||
|
||||
struct RestoreRegularFile : CreateRegularFileSink {
|
||||
@@ -82,14 +91,6 @@ struct RestoreRegularFile : CreateRegularFileSink {
|
||||
void preallocateContents(uint64_t size) override;
|
||||
};
|
||||
|
||||
static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path)
|
||||
{
|
||||
auto dst = src;
|
||||
if (!path.rel().empty())
|
||||
dst /= path.rel();
|
||||
return dst;
|
||||
}
|
||||
|
||||
void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)> func)
|
||||
{
|
||||
auto p = append(dstPath, path);
|
||||
|
||||
@@ -171,55 +171,6 @@ size_t StringSource::read(char * data, size_t len)
|
||||
#error Coroutines are broken in this version of Boost!
|
||||
#endif
|
||||
|
||||
/* A concrete datatype allow virtual dispatch of stack allocation methods. */
|
||||
struct VirtualStackAllocator {
|
||||
StackAllocator *allocator = StackAllocator::defaultAllocator;
|
||||
|
||||
boost::context::stack_context allocate() {
|
||||
return allocator->allocate();
|
||||
}
|
||||
|
||||
void deallocate(boost::context::stack_context sctx) {
|
||||
allocator->deallocate(sctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* This class reifies the default boost coroutine stack allocation strategy with
|
||||
a virtual interface. */
|
||||
class DefaultStackAllocator : public StackAllocator {
|
||||
boost::coroutines2::default_stack stack;
|
||||
|
||||
boost::context::stack_context allocate() override {
|
||||
return stack.allocate();
|
||||
}
|
||||
|
||||
void deallocate(boost::context::stack_context sctx) override {
|
||||
stack.deallocate(sctx);
|
||||
}
|
||||
};
|
||||
|
||||
static DefaultStackAllocator defaultAllocatorSingleton;
|
||||
|
||||
StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton;
|
||||
|
||||
|
||||
std::shared_ptr<void> (*create_coro_gc_hook)() = []() -> std::shared_ptr<void> {
|
||||
return {};
|
||||
};
|
||||
|
||||
/* This class is used for entry and exit hooks on coroutines */
|
||||
class CoroutineContext {
|
||||
/* Disable GC when entering the coroutine without the boehm patch,
|
||||
* since it doesn't find the main thread stack in this case.
|
||||
* std::shared_ptr<void> performs type-erasure, so it will call the right
|
||||
* deleter. */
|
||||
const std::shared_ptr<void> coro_gc_hook = create_coro_gc_hook();
|
||||
public:
|
||||
CoroutineContext() {};
|
||||
~CoroutineContext() {};
|
||||
};
|
||||
|
||||
std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun)
|
||||
{
|
||||
struct SourceToSink : FinishSink
|
||||
@@ -241,14 +192,12 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun)
|
||||
cur = in;
|
||||
|
||||
if (!coro) {
|
||||
CoroutineContext ctx;
|
||||
coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) {
|
||||
LambdaSource source([&](char *out, size_t out_len) {
|
||||
coro = coro_t::push_type([&](coro_t::pull_type & yield) {
|
||||
LambdaSource source([&](char * out, size_t out_len) {
|
||||
if (cur.empty()) {
|
||||
yield();
|
||||
if (yield.get()) {
|
||||
return (size_t)0;
|
||||
}
|
||||
if (yield.get())
|
||||
throw EndOfFile("coroutine has finished");
|
||||
}
|
||||
|
||||
size_t n = std::min(cur.size(), out_len);
|
||||
@@ -263,20 +212,14 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun)
|
||||
if (!*coro) { unreachable(); }
|
||||
|
||||
if (!cur.empty()) {
|
||||
CoroutineContext ctx;
|
||||
(*coro)(false);
|
||||
}
|
||||
}
|
||||
|
||||
void finish() override
|
||||
{
|
||||
if (!coro) return;
|
||||
if (!*coro) unreachable();
|
||||
{
|
||||
CoroutineContext ctx;
|
||||
if (coro && *coro)
|
||||
(*coro)(true);
|
||||
}
|
||||
if (*coro) unreachable();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,8 +250,7 @@ std::unique_ptr<Source> sinkToSource(
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
if (!coro) {
|
||||
CoroutineContext ctx;
|
||||
coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) {
|
||||
coro = coro_t::pull_type([&](coro_t::push_type & yield) {
|
||||
LambdaSink sink([&](std::string_view data) {
|
||||
if (!data.empty()) yield(std::string(data));
|
||||
});
|
||||
@@ -320,7 +262,6 @@ std::unique_ptr<Source> sinkToSource(
|
||||
|
||||
if (pos == cur.size()) {
|
||||
if (!cur.empty()) {
|
||||
CoroutineContext ctx;
|
||||
(*coro)();
|
||||
}
|
||||
cur = coro->get();
|
||||
|
||||
@@ -557,27 +557,4 @@ struct FramedSink : nix::BufferedSink
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stack allocation strategy for sinkToSource.
|
||||
* Mutable to avoid a boehm gc dependency in libutil.
|
||||
*
|
||||
* boost::context doesn't provide a virtual class, so we define our own.
|
||||
*/
|
||||
struct StackAllocator {
|
||||
virtual boost::context::stack_context allocate() = 0;
|
||||
virtual void deallocate(boost::context::stack_context sctx) = 0;
|
||||
|
||||
/**
|
||||
* The stack allocator to use in sinkToSource and potentially elsewhere.
|
||||
* It is reassigned by the initGC() method in libexpr.
|
||||
*/
|
||||
static StackAllocator *defaultAllocator;
|
||||
};
|
||||
|
||||
/* Disabling GC when entering a coroutine (without the boehm patch).
|
||||
mutable to avoid boehm gc dependency in libutil.
|
||||
*/
|
||||
extern std::shared_ptr<void> (*create_coro_gc_hook)();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||
script = argv[1];
|
||||
try {
|
||||
auto lines = tokenizeString<Strings>(readFile(script), "\n");
|
||||
if (std::regex_search(lines.front(), std::regex("^#!"))) {
|
||||
if (!lines.empty() && std::regex_search(lines.front(), std::regex("^#!"))) {
|
||||
lines.pop_front();
|
||||
inShebang = true;
|
||||
for (int i = 2; i < argc; ++i)
|
||||
|
||||
BIN
tests/functional/case-collision.nar
Normal file
BIN
tests/functional/case-collision.nar
Normal file
Binary file not shown.
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source common.sh
|
||||
|
||||
TODO_NixOS
|
||||
|
||||
clearStore
|
||||
|
||||
rm -rf "$TEST_ROOT/case"
|
||||
|
||||
opts=("--option" "use-case-hack" "true")
|
||||
|
||||
# Check whether restoring and dumping a NAR that contains case
|
||||
# collisions is round-tripping, even on a case-insensitive system.
|
||||
|
||||
nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case.nar
|
||||
nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > "$TEST_ROOT/case.nar"
|
||||
cmp case.nar "$TEST_ROOT/case.nar"
|
||||
[ "$(nix-hash "${opts[@]}" --type sha256 "$TEST_ROOT/case")" = "$(nix-hash --flat --type sha256 case.nar)" ]
|
||||
|
||||
# Check whether we detect true collisions (e.g. those remaining after
|
||||
# removal of the suffix).
|
||||
touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
|
||||
(! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null)
|
||||
BIN
tests/functional/duplicate.nar
Normal file
BIN
tests/functional/duplicate.nar
Normal file
Binary file not shown.
@@ -90,7 +90,7 @@ nix_tests = \
|
||||
derivation-advanced-attributes.sh \
|
||||
import-derivation.sh \
|
||||
nix_path.sh \
|
||||
case-hack.sh \
|
||||
nars.sh \
|
||||
placeholders.sh \
|
||||
ssh-relay.sh \
|
||||
build.sh \
|
||||
|
||||
94
tests/functional/nars.sh
Executable file
94
tests/functional/nars.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source common.sh
|
||||
|
||||
TODO_NixOS
|
||||
|
||||
clearStore
|
||||
|
||||
# Check that NARs with duplicate directory entries are rejected.
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "NAR directory is not sorted"
|
||||
|
||||
# Check that nix-store --restore fails if the output already exists.
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out' already exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
echo foo > "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "File exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "File exists"
|
||||
|
||||
mkdir -p "$TEST_ROOT/out2"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out' already exists"
|
||||
|
||||
# The same, but for a regular file.
|
||||
nix-store --dump ./nars.sh > "$TEST_ROOT/tmp.nar"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
mkdir -p "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
mkdir -p "$TEST_ROOT/out2"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
# The same, but for a symlink.
|
||||
ln -sfn foo "$TEST_ROOT/symlink"
|
||||
nix-store --dump "$TEST_ROOT/symlink" > "$TEST_ROOT/tmp.nar"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar"
|
||||
[[ -L "$TEST_ROOT/out" ]]
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
mkdir -p "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
mkdir -p "$TEST_ROOT/out2"
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
|
||||
|
||||
# Check whether restoring and dumping a NAR that contains case
|
||||
# collisions is round-tripping, even on a case-insensitive system.
|
||||
rm -rf "$TEST_ROOT/case"
|
||||
opts=("--option" "use-case-hack" "true")
|
||||
nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case.nar
|
||||
nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > "$TEST_ROOT/case.nar"
|
||||
cmp case.nar "$TEST_ROOT/case.nar"
|
||||
[ "$(nix-hash "${opts[@]}" --type sha256 "$TEST_ROOT/case")" = "$(nix-hash --flat --type sha256 case.nar)" ]
|
||||
|
||||
# Check whether we detect true collisions (e.g. those remaining after
|
||||
# removal of the suffix).
|
||||
touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
|
||||
(! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null)
|
||||
|
||||
# Detect NARs that have a directory entry that after case-hacking
|
||||
# collides with another entry (e.g. a directory containing 'Test',
|
||||
# 'Test~nix~case~hack~1' and 'test').
|
||||
rm -rf "$TEST_ROOT/case"
|
||||
expectStderr 1 nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case-collision.nar | grepQuiet "NAR contains file name 'test' that collides with case-hacked file name 'Test~nix~case~hack~1'"
|
||||
|
||||
# Deserializing a NAR that contains file names that Unicode-normalize
|
||||
# to the same name should fail on macOS but succeed on Linux.
|
||||
rm -rf "$TEST_ROOT/out"
|
||||
if [[ $(uname) = Darwin ]]; then
|
||||
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < unnormalized.nar | grepQuiet "path '.*/out/â' already exists"
|
||||
else
|
||||
nix-store --restore "$TEST_ROOT/out" < unnormalized.nar
|
||||
[[ -e $TEST_ROOT/out/â ]]
|
||||
[[ -e $TEST_ROOT/out/â ]]
|
||||
fi
|
||||
BIN
tests/functional/unnormalized.nar
Normal file
BIN
tests/functional/unnormalized.nar
Normal file
Binary file not shown.
Reference in New Issue
Block a user