Compare commits

..
Author SHA1 Message Date
Sergei Zimmerman 2e8ce8e21f libutil: Remove really dead code for git hashing
It's not being excersised by anything and already contains
bugs (like the fact that copyRecursive only handles the first entry
in a directory). Evidently, it can just be removed and was never used
by anything.
2025-11-20 22:13:58 +03:00
John Ericson 5caebab63a Merge pull request #14600 from edef1c/push-tvmtozyqsmno
Simplify `Derivation::type()`
2025-11-20 07:36:10 +00:00
edef 19d83d2605 Simplify Derivation::type()
We don't use the various set<string_view>s that we construct,
and all we really care about is ensuring that all outputs are
of a single, consistent type.
2025-11-20 03:50:26 +00:00
Sergei Zimmerman 70b9fbd76c Merge pull request #14597 from NixOS/restore-sink-openat
libutil: Make RestoreSink use *at system calls on UNIX
2025-11-20 01:50:10 +00:00
Sergei Zimmerman 40b25153b8 libutil: Implement second overload of createDirectory for RestoreSink
Now the intermediate symlink following issue should be completely plugged.
2025-11-20 04:01:38 +03:00
Sergei Zimmerman 09755e696a libutil: Add callback-based FileSystemObjectSink::createDirectory 2025-11-20 04:01:37 +03:00
Sergei Zimmerman fa380e0991 libutil: Make RestoreSink use *at system calls on UNIX
This is necessary to ban symlink following. It can be considered
a defense in depth against issues similar to CVE-2024-45593. By
slightly changing the API in a follow-up commit we will be able
to mitigate the symlink following issue for good.
2025-11-20 04:01:36 +03:00
John Ericson f7de5b326a Merge pull request #14506 from obsidiansystems/derivation-options-parse-paths
Parse deriving paths in `DerivationOptions`
2025-11-19 21:41:15 +00:00
Sergei Zimmermanandeldritch horrors 533cced249 libutil: Add requireCString, make renderUrlPathEnsureLegal error on NUL bytes better
Same utility as in lix's change I3caf476e59dcb7899ac5a3d83dfa3fb7ceaaabf0.

Co-authored-by: eldritch horrors <pennae@lix.systems>
2025-11-20 00:31:10 +03:00
Eelco Dolstra 8b167ea89b Merge pull request #14567 from pkpbynum/pb/fix-c-api-ctx-err-leak
C Util API: Fix leak of demangled error name
2025-11-19 20:49:54 +00:00
76bd600302 Parse deriving paths in DerivationOptions
This is an example of "Parse, don't validate" principle [1].

Before, we had a number of `StringSet`s in `DerivationOptions` that
were not *actually* allowed to be arbitrary sets of strings. Instead,
each set member had to be one of:

- a store path

- a CA "downstream placeholder"

- an output name

Only later, in the code that checks outputs, would these strings be
further parsed to match these cases. (Actually, only 2 by that point,
because the placeholders must be rewritten away by then.)

Now, we fully parse everything up front, and have an "honest" data type
that reflects these invariants:

- store paths are parsed, stored as (opaque) deriving paths

- CA "downstream placeholders" are rewritten to the output deriving
  paths they denote

- output names are the only arbitrary strings left

Since the first two cases both become deriving paths, that leaves us
with a `std::variant<SingleDerivedPath, String>` data type, which we use
in our sets instead.

Getting rid of placeholders is especially nice because we are replacing
them with something much more internally-structured / transparent.

[1]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
2025-11-19 15:48:10 -05:00
Eelco Dolstra b975f719b1 Merge pull request #14595 from NixOS/registry-resolve
Add `nix registry resolve` command
2025-11-19 20:37:21 +00:00
Eelco Dolstra 063cdb5508 Add nix registry resolve command 2025-11-19 20:55:42 +01:00
Eelco Dolstra 72dbd43882 Merge pull request #14594 from NixOS/registry-drop-settings
Registry: Drop settings field
2025-11-19 16:05:33 +00:00
Eelco Dolstra fb989bd93f Merge pull request #14585 from NixOS/dependabot/github_actions/cachix/install-nix-action-31.8.4
build(deps): bump cachix/install-nix-action from 31.8.3 to 31.8.4
2025-11-19 12:20:50 +00:00
Eelco Dolstra b309826a48 Merge pull request #14593 from juhp/patch-3
docs: fixup a few relative links to use ./ prefix for consistency
2025-11-19 12:20:22 +00:00
Eelco Dolstra bed0570629 Registry: Drop settings field
It's not used anywhere.
2025-11-19 11:52:15 +01:00
Jens Petersen ef6dbe76dc docs: fixup some rellinks to use ./ prefix for consistency
"./" prefix is already used almost everywhere
2025-11-19 15:50:43 +08:00
John Ericson dfac44cdfb Merge pull request #14591 from NixOS/filetransfer-error-handling
libstore/filetransfer: Improve error handling
2025-11-19 01:38:17 +00:00
Sergei Zimmerman 36f4e290d0 libstore/filetransfer: Add more context to error message
Now the error message looks something like:

error:
       … during upload of 'file:///tmp/storeabc/4yxrw9flcvca7f3fs7c5igl2ica39zaw.narinfo'

       error: blah blah

Also makes fail and failEx themselves noexcept, since all the operations they
do are noexcept and we don't want exceptions escaping from them.
2025-11-19 02:30:33 +03:00
Sergei Zimmerman bd0b338e15 libstore/filetransfer: Swallow exceptions in debugCallback 2025-11-19 02:24:38 +03:00
Sergei Zimmerman b3dfe37aea libstore/filetransfer: Handle exceptions in progressCallback 2025-11-19 02:24:37 +03:00
Sergei Zimmerman 87d3c3ba1a libstore/filetransfer: Handle exceptions in headerCallback
Callbacks *must* never throw exceptions on the curl thread!
2025-11-19 02:24:35 +03:00
Sergei Zimmerman 1e42e55fb4 libstore/filetransfer: Set callbackException on exceptions in read/seek callbacks
This would provide better error messages if seeking/reading ever fails.
2025-11-19 02:24:34 +03:00
Sergei Zimmerman e704b8eeed libstore/filetransfer: Rename writeException -> callbackException 2025-11-19 02:24:33 +03:00
Sergei Zimmermanandeldritch horrors 6d65f8eea2 libstore: Slightly deindent writeCallback by wrapping it in try/catch
The indentation level of the code is already high enough. We can just
wrap the whole function in a try/catch and mark it noexcept.

Partially cherry-picked from https://gerrit.lix.systems/c/lix/+/2133

Co-authored-by: eldritch horrors <pennae@lix.systems>
2025-11-19 02:23:12 +03:00
John Ericson f4989b118b Merge pull request #14590 from NixOS/fix-win-shell
packaging: Unbork win shells with unavailable dependencies
2025-11-18 22:19:16 +00:00
Sergei Zimmerman 2de742155a packaging: Unbork win shells with unavailable dependencies
Makes the cross-x86_64-w64-mingw32 devshell slightly less
broken. It still needs a bit of massaging to function, but
that's much less cumbersome now that the generic machinery
with genericClosure that evaluates drvPath doesn't barf on
unavailable packages.
2025-11-19 00:43:28 +03:00
John Ericson 09d6847490 Merge pull request #14589 from lovesegfault/fix-fetchers-substitute-test
tests: fix fetchers-substitute test for new narHash JSON format
2025-11-18 17:48:07 +00:00
Bernardo Meurer Costa 53af1119fb tests: fix fetchers-substitute test for new narHash JSON format
The test was failing because nix path-info --json now returns narHash as
a structured dictionary {"algorithm": "sha256", "format": "base64",
"hash": "..."} instead of an SRI string "sha256-...".

This change was introduced in commit 5e7ee808d. The functional test
path-info.sh was updated at that time, but this NixOS test was missed.

The fix converts the dictionary format to SRI format inline:
  tarball_hash_sri = f"{narHash_obj['algorithm']}-{narHash_obj['hash']}"
2025-11-18 16:36:27 +00:00
John Ericson 68d2292f3a Merge pull request #14539 from Radvendii/exprattrs-alloc-shvach
libexpr: move ExprAttrs data into Exprs::alloc (take 2)
2025-11-18 02:36:53 +00:00
John Ericson 16f0279d4f Merge pull request #14587 from NixOS/fix-mingw
treewide: Fix MinGW build
2025-11-18 02:17:38 +00:00
Sergei Zimmerman 8165419a0c treewide: Fix MinGW build
Several bugs to squash:

- Apparently DELETE is an already used macro with Win32. We can avoid it
  by using Camel case instead (slightly hacky but also fits the naming
  convention better)

- Gets rid of the raw usage of isatty. Added an isTTY impl to abstract over
  the raw API.
2025-11-18 04:30:57 +03:00
John Ericson 7721fa6df4 Merge pull request #14586 from NixOS/less-create-at-root
treewide: Reduce usage of PosixSourceAccessor::createAtRoot
2025-11-18 01:15:34 +00:00
Sergei Zimmerman cb5d97a607 Merge pull request #14580 from NixOS/fix-devshell
packaging/dev-shell: Fix configurePhase
2025-11-18 00:25:46 +00:00
Sergei Zimmerman 436bc1f39e treewide: Reduce usage of PosixSourceAccessor::createAtRoot
Replaces the usage of createAtRoot, which goes as far up the
directory tree as possible with rooted variant makeFSSourceAccessor.

The changes in this patch should be safe wrt to not asserting on relative
paths. Arguments passed to makeFSSourceAccessor here should already be using
absolute paths.
2025-11-18 03:22:27 +03:00
dependabot[bot] ae4ed24257 build(deps): bump cachix/install-nix-action from 31.8.3 to 31.8.4
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 31.8.3 to 31.8.4.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md)
- [Commits](https://github.com/cachix/install-nix-action/compare/7ec16f2c061ab07b235a7245e06ed46fe9a1cab6...0b0e072294b088b73964f1d72dfdac0951439dbd)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-version: 31.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 22:01:06 +00:00
Taeer Bar-Yam fcf3bdcac8 move ExprAttrs data into Exprs::alloc 2025-11-17 22:19:45 +01:00
Taeer Bar-Yam 4b97f1130a libexpr: ExprAttrs::attrs and ExprAttrs::dynamicAttrs -> std::optional
without this, there is no way to swap them out for structures using a
different allocator. This should be reverted as part of redesiging
ExprAttrs to use an ExprAttrsBuilder
2025-11-17 22:19:45 +01:00
Taeer Bar-Yam 614e143a20 libexpr: switch ExprAttrs to std::pmr::{vector,map} 2025-11-17 22:19:45 +01:00
John Ericson 77982c55b2 Merge pull request #14582 from NixOS/ref-to-reference
libfetchers: Convert ref<Store> -> Store &
2025-11-17 20:15:28 +00:00
John Ericson acacdf87b4 Merge pull request #14583 from NixOS/repl-typo
repl: Fix incorrect error message
2025-11-17 20:05:18 +00:00
John Ericson d5b6e1a0fc Merge pull request #14579 from obsidiansystems/store-c-header-split
libstore-c: Organize API into separate headers
2025-11-17 19:41:38 +00:00
Eelco Dolstra 3511a919b4 repl: Fix incorrect error message 2025-11-17 20:31:53 +01:00
Eelco Dolstra f6aa8c0486 Merge pull request #14581 from NixOS/clone-all
nix flake clone: Support all input types
2025-11-17 19:28:19 +00:00
Eelco Dolstra cd5cac0c40 libfetchers: Convert ref<Store> -> Store & 2025-11-17 20:08:51 +01:00
John Ericson 958866b9a6 Merge pull request #9732 from NixOS/systematize-fetchTree-docs
Systematize `builtins.fetchTree` docs
2025-11-17 18:58:48 +00:00
Eelco Dolstra d07c24f4c8 nix flake clone: Support all input types
For input types that have no concept of cloning, we now default to
copying the entire source tree.
2025-11-17 19:50:50 +01:00
Eelco Dolstra 95da93c05b Input::clone(): Use std::filesystem::path 2025-11-17 19:44:24 +01:00
John Ericson bae1ca257a Systematize builtins.fetchTree docs
And also render the docs nicely.

I would like to use a markdown AST for this, but to avoid new deps
(lowdown's AST doesn't suffice) I am just doing crude string
manipulations for now.
2025-11-17 13:10:03 -05:00
Eelco Dolstra f8141a2c26 Merge pull request #14574 from pkpbynum/pb/fix-registry-pin
Fix registry pin ref lookup
2025-11-17 18:09:13 +00:00
Sergei Zimmerman bdeaf976bd packaging/dev-shell: Fix configurePhase
Since 918c1a9e58 configurePhase variable points to cmakeConfigurePhase
and runPhase configurePhase does the wrong thing.

configurePhase function on the other hand still worked correctly.
2025-11-17 20:58:27 +03:00
John Ericson cdba2534cf libstore-c: Organize API into separate headers
Move StorePath and Derivation declarations to their own headers in a
backwards compatible way:

- Created nix_api_store/store_path.h for StorePath operations

- Created nix_api_store/derivation.h for Derivation operations

- Main nix_api_store.h includes both headers for backwards compatibility

This reorganization improves modularity and hopefully makes the API
easier to navigate.
2025-11-17 12:23:57 -05:00
Peter Bynum 8642c0a9a2 Fix registry pin ref lookup 2025-11-15 14:42:09 -08:00
Peter Bynum 70e56a41ce fmt 2025-11-15 08:34:16 -08:00
Peter Bynum a235b454cc Free alloc of demangled error name 2025-11-14 07:51:11 -08:00
110 changed files with 2436 additions and 1400 deletions

No files matched your search

+1 -1
View File
@@ -174,7 +174,7 @@ jobs:
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT" echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)" TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT" echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@7ec16f2c061ab07b235a7245e06ed46fe9a1cab6 # v31.8.3 - uses: cachix/install-nix-action@0b0e072294b088b73964f1d72dfdac0951439dbd # v31.8.4
if: ${{ !matrix.experimental-installer }} if: ${{ !matrix.experimental-installer }}
with: with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }} install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
+3 -3
View File
@@ -36,7 +36,7 @@ to a temporary location. The tarball must include a single top-level
directory containing at least a file named `default.nix`. directory containing at least a file named `default.nix`.
`nix-build` is essentially a wrapper around `nix-build` is essentially a wrapper around
[`nix-instantiate`](nix-instantiate.md) (to translate a high-level Nix [`nix-instantiate`](./nix-instantiate.md) (to translate a high-level Nix
expression to a low-level [store derivation]) and [`nix-store expression to a low-level [store derivation]) and [`nix-store
--realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store --realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store
derivation). derivation).
@@ -52,8 +52,8 @@ derivation).
# Options # Options
All options not listed here are passed to All options not listed here are passed to
[`nix-store --realise`](nix-store/realise.md), [`nix-store --realise`](./nix-store/realise.md),
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md). except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](./nix-instantiate.md).
- <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span> - <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span>
@@ -32,7 +32,7 @@ standard input.
- `--add-root` *path* - `--add-root` *path*
See the [corresponding option](nix-store.md) in `nix-store`. See the [corresponding option](./nix-store.md) in `nix-store`.
- `--parse` - `--parse`
+22 -16
View File
@@ -130,15 +130,19 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix;
ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags; ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags;
availableComponents = lib.filterAttrs (
k: v: lib.meta.availableOn pkgs.hostPlatform v
) allComponents;
activeComponents = buildInputsClosureCond isInternal ( activeComponents = buildInputsClosureCond isInternal (
lib.attrValues (finalAttrs.passthru.config.getComponents allComponents) lib.attrValues (finalAttrs.passthru.config.getComponents availableComponents)
); );
allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2; allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2;
internalDrvs = byDrvPath ( internalDrvs = byDrvPath (
# Drop the attr names (not present in buildInputs anyway) # Drop the attr names (not present in buildInputs anyway)
lib.attrValues allComponents lib.attrValues availableComponents
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues allComponents) ++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues availableComponents)
); );
isInternal = isInternal =
@@ -187,19 +191,19 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
); );
small = small =
(finalAttrs.finalPackage.withActiveComponents (c: { (finalAttrs.finalPackage.withActiveComponents (
inherit (c) c:
nix-cli lib.intersectAttrs (lib.genAttrs [
nix-util-tests "nix-cli"
nix-store-tests "nix-util-tests"
nix-expr-tests "nix-store-tests"
nix-fetchers-tests "nix-expr-tests"
nix-flake-tests "nix-fetchers-tests"
nix-functional-tests "nix-flake-tests"
# Currently required "nix-functional-tests"
nix-perl-bindings "nix-perl-bindings"
; ] (_: null)) c
})).overrideAttrs )).overrideAttrs
(o: { (o: {
mesonFlags = o.mesonFlags ++ [ mesonFlags = o.mesonFlags ++ [
# TODO: infer from activeComponents or vice versa # TODO: infer from activeComponents or vice versa
@@ -269,6 +273,8 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
CXX_LD = "mold"; CXX_LD = "mold";
}; };
dontUseCmakeConfigure = true;
mesonFlags = mesonFlags =
map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents2.nix-util.mesonFlags) map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents2.nix-util.mesonFlags)
++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents2.nix-store.mesonFlags) ++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents2.nix-store.mesonFlags)
+1 -1
View File
@@ -65,6 +65,6 @@ mkMesonDerivation (finalAttrs: {
''; '';
meta = { meta = {
platforms = lib.platforms.all; platforms = lib.platforms.unix;
}; };
}) })
+5 -4
View File
@@ -34,7 +34,7 @@ EvalSettings evalSettings{
auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false);
debug("fetching flake search path element '%s''", rest); debug("fetching flake search path element '%s''", rest);
auto [accessor, lockedRef] = auto [accessor, lockedRef] =
flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store); flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
auto storePath = nix::fetchToStore( auto storePath = nix::fetchToStore(
state.fetchSettings, state.fetchSettings,
*state.store, *state.store,
@@ -132,7 +132,7 @@ MixEvalArgs::MixEvalArgs()
fetchers::Attrs extraAttrs; fetchers::Attrs extraAttrs;
if (to.subdir != "") if (to.subdir != "")
extraAttrs["dir"] = to.subdir; extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(fetchSettings, from.input, to.input, extraAttrs); fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, openStore(), prefix); completeFlakeRef(completions, openStore(), prefix);
@@ -180,7 +180,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir) SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
{ {
if (EvalSettings::isPseudoUrl(s)) { if (EvalSettings::isPseudoUrl(s)) {
auto accessor = fetchers::downloadTarball(state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s)); auto accessor = fetchers::downloadTarball(*state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy);
return state.storePath(storePath); return state.storePath(storePath);
} }
@@ -188,7 +188,8 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
else if (hasPrefix(s, "flake:")) { else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
auto [accessor, lockedRef] = flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store); auto [accessor, lockedRef] =
flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
auto storePath = nix::fetchToStore( auto storePath = nix::fetchToStore(
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName()); state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath); state.allowPath(storePath);
+1 -2
View File
@@ -185,7 +185,6 @@ MixFlakeOptions::MixFlakeOptions()
} }
overrideRegistry( overrideRegistry(
fetchSettings,
fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}), fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}),
input3->lockedRef.input, input3->lockedRef.input,
extraAttrs); extraAttrs);
@@ -410,7 +409,7 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
Args::completeDir(completions, 0, prefix); Args::completeDir(completions, 0, prefix);
/* Look for registry entries that match the prefix. */ /* Look for registry entries that match the prefix. */
for (auto & registry : fetchers::getRegistries(fetchSettings, store)) { for (auto & registry : fetchers::getRegistries(fetchSettings, *store)) {
for (auto & entry : registry->entries) { for (auto & entry : registry->entries) {
auto from = entry.from.to_string(); auto from = entry.from.to_string();
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
+2 -2
View File
@@ -656,7 +656,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
+ "\n\n"; + "\n\n";
} }
markdown += stripIndentation(doc->doc.view()); markdown += stripIndentation(doc->doc);
logger->cout(trim(renderMarkdownToTerminal(markdown))); logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else if (fallbackPos) { } else if (fallbackPos) {
@@ -739,7 +739,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true); auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings)) if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings))
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); throw Error("cannot use ':load-flake' on unlocked flake reference '%s' (use --impure to override)", flakeRefS);
Value v; Value v;
+1 -1
View File
@@ -134,7 +134,7 @@ PrimOp * nix_alloc_primop(
.name = name, .name = name,
.args = {}, .args = {},
.arity = (size_t) arity, .arity = (size_t) arity,
.doc = &nix::StringData::make(doc), .doc = doc,
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)}; .fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)};
if (args) if (args)
for (size_t i = 0; args[i]; i++) for (size_t i = 0; args[i]; i++)
+1 -1
View File
@@ -1,6 +1,6 @@
#include "nix/expr/tests/libexpr.hh" #include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh" #include "nix/expr/value-to-json.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
namespace nix { namespace nix {
// Testing the conversion to JSON // Testing the conversion to JSON
+1 -1
View File
@@ -1,5 +1,5 @@
#include "nix/expr/tests/libexpr.hh" #include "nix/expr/tests/libexpr.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/print.hh" #include "nix/expr/print.hh"
+1 -1
View File
@@ -1,5 +1,5 @@
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
#include "nix/store/tests/libstore.hh" #include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h> #include <gtest/gtest.h>
+56 -23
View File
@@ -51,6 +51,36 @@ using json = nlohmann::json;
namespace nix { namespace nix {
/**
* Just for doc strings. Not for regular string values.
*/
static char * allocString(size_t size)
{
char * t;
t = (char *) GC_MALLOC_ATOMIC(size);
if (!t)
throw std::bad_alloc();
return t;
}
// When there's no need to write to the string, we can optimize away empty
// string allocations.
// This function handles makeImmutableString(std::string_view()) by returning
// the empty string.
/**
* Just for doc strings. Not for regular string values.
*/
static const char * makeImmutableString(std::string_view s)
{
const size_t size = s.size();
if (size == 0)
return "";
auto t = allocString(size + 1);
memcpy(t, s.data(), size);
t[size] = '\0';
return t;
}
StringData & StringData::alloc(size_t size) StringData & StringData::alloc(size_t size)
{ {
void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1); void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1);
@@ -175,7 +205,7 @@ bool Value::isTrivial() const
{ {
return !isa<tApp, tPrimOpApp>() return !isa<tApp, tPrimOpApp>()
&& (!isa<tThunk>() && (!isa<tThunk>()
|| (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty()) || (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs->empty())
|| dynamic_cast<ExprLambda *>(thunk().expr) || dynamic_cast<ExprList *>(thunk().expr)); || dynamic_cast<ExprLambda *>(thunk().expr) || dynamic_cast<ExprList *>(thunk().expr));
} }
@@ -487,15 +517,16 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
if (primOp.arity == 0) { if (primOp.arity == 0) {
primOp.arity = 1; primOp.arity = 1;
auto vPrimOp = allocValue(); auto vPrimOp = allocValue();
vPrimOp->mkPrimOp(new PrimOp(primOp)); vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
Value v; Value v;
v.mkApp(vPrimOp, vPrimOp); v.mkApp(vPrimOp, vPrimOp);
auto & primOp1 = *vPrimOp->primOp();
return addConstant( return addConstant(
primOp.name, primOp1.name,
v, v,
{ {
.type = nThunk, // FIXME .type = nThunk, // FIXME
.doc = primOp.doc, .doc = primOp1.doc ? primOp1.doc->c_str() : nullptr,
}); });
} }
@@ -535,13 +566,14 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
{ {
if (v.isPrimOp()) { if (v.isPrimOp()) {
auto v2 = &v; auto v2 = &v;
if (auto * doc = v2->primOp()->doc) auto & primOp = *v2->primOp();
if (primOp.doc)
return Doc{ return Doc{
.pos = {}, .pos = {},
.name = v2->primOp()->name, .name = primOp.name,
.arity = v2->primOp()->arity, .arity = primOp.arity,
.args = v2->primOp()->args, .args = primOp.args,
.doc = *doc, .doc = primOp.doc->c_str(),
}; };
} }
if (v.isLambda()) { if (v.isLambda()) {
@@ -583,8 +615,9 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = name, .name = name,
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though... .arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
.args = {}, .args = {},
/* NOTE: memory leak when compiled without GC. */ /* N.B. Can't use StringData here, because that would lead to an interior pointer.
.doc = StringData::make(s.view()), NOTE: memory leak when compiled without GC. */
.doc = makeImmutableString(s.view()),
}; };
} }
if (isFunctor(v)) { if (isFunctor(v)) {
@@ -1193,26 +1226,26 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{ {
auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size()); auto bindings = state.buildBindings(attrs->size() + dynamicAttrs->size());
auto dynamicEnv = &env; auto dynamicEnv = &env;
bool sort = false; bool sort = false;
if (recursive) { if (recursive) {
/* Create a new environment that contains the attributes in /* Create a new environment that contains the attributes in
this `rec'. */ this `rec'. */
Env & env2(state.mem.allocEnv(attrs.size())); Env & env2(state.mem.allocEnv(attrs->size()));
env2.up = &env; env2.up = &env;
dynamicEnv = &env2; dynamicEnv = &env2;
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr; Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
AttrDefs::iterator overrides = attrs.find(state.s.overrides); AttrDefs::iterator overrides = attrs->find(state.s.overrides);
bool hasOverrides = overrides != attrs.end(); bool hasOverrides = overrides != attrs->end();
/* The recursive attributes are evaluated in the new /* The recursive attributes are evaluated in the new
environment, while the inherited attributes are evaluated environment, while the inherited attributes are evaluated
in the original environment. */ in the original environment. */
Displacement displ = 0; Displacement displ = 0;
for (auto & i : attrs) { for (auto & i : *attrs) {
Value * vAttr; Value * vAttr;
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) { if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
vAttr = state.allocValue(); vAttr = state.allocValue();
@@ -1239,8 +1272,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
"while evaluating the `__overrides` attribute"); "while evaluating the `__overrides` attribute");
bindings.grow(state.buildBindings(bindings.capacity() + vOverrides->attrs()->size())); bindings.grow(state.buildBindings(bindings.capacity() + vOverrides->attrs()->size()));
for (auto & i : *vOverrides->attrs()) { for (auto & i : *vOverrides->attrs()) {
AttrDefs::iterator j = attrs.find(i.name); AttrDefs::iterator j = attrs->find(i.name);
if (j != attrs.end()) { if (j != attrs->end()) {
(*bindings.bindings)[j->second.displ] = i; (*bindings.bindings)[j->second.displ] = i;
env2.values[j->second.displ] = i.value; env2.values[j->second.displ] = i.value;
} else } else
@@ -1252,13 +1285,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else { else {
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr; Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
for (auto & i : attrs) for (auto & i : *attrs)
bindings.insert( bindings.insert(
i.first, i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.pos); i.first, i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.pos);
} }
/* Dynamic attrs apply *after* rec and __overrides. */ /* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) { for (auto & i : *dynamicAttrs) {
Value nameVal; Value nameVal;
i.nameExpr->eval(state, *dynamicEnv, nameVal); i.nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal, i.pos); state.forceValue(nameVal, i.pos);
@@ -1292,7 +1325,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
{ {
/* Create a new environment that contains the attributes in this /* Create a new environment that contains the attributes in this
`let'. */ `let'. */
Env & env2(state.mem.allocEnv(attrs->attrs.size())); Env & env2(state.mem.allocEnv(attrs->attrs->size()));
env2.up = &env; env2.up = &env;
Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr; Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
@@ -1301,7 +1334,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
while the inherited attributes are evaluated in the original while the inherited attributes are evaluated in the original
environment. */ environment. */
Displacement displ = 0; Displacement displ = 0;
for (auto & i : attrs->attrs) { for (auto & i : *attrs->attrs) {
env2.values[displ++] = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv)); env2.values[displ++] = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
} }
@@ -3157,7 +3190,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
if (EvalSettings::isPseudoUrl(value)) { if (EvalSettings::isPseudoUrl(value)) {
try { try {
auto accessor = fetchers::downloadTarball(store, fetchSettings, EvalSettings::resolvePseudoUrl(value)); auto accessor = fetchers::downloadTarball(*store, fetchSettings, EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(fetchSettings, *store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(fetchSettings, *store, SourcePath(accessor), FetchMode::Copy);
return finish(this->storePath(storePath)); return finish(this->storePath(storePath));
} catch (Error & e) { } catch (Error & e) {
+7 -3
View File
@@ -108,7 +108,7 @@ struct PrimOp
/** /**
* Optional free-form documentation about the primop. * Optional free-form documentation about the primop.
*/ */
const StringData * doc = nullptr; std::optional<std::string> doc;
/** /**
* Add a trace item, while calling the `<name>` builtin. * Add a trace item, while calling the `<name>` builtin.
@@ -156,7 +156,7 @@ struct Constant
/** /**
* Optional free-form documentation about the constant. * Optional free-form documentation about the constant.
*/ */
const StringData * doc = nullptr; const char * doc = nullptr;
/** /**
* Whether the constant is impure, and not available in pure mode. * Whether the constant is impure, and not available in pure mode.
@@ -837,7 +837,11 @@ public:
std::optional<std::string> name; std::optional<std::string> name;
size_t arity; size_t arity;
std::vector<std::string> args; std::vector<std::string> args;
const StringData & doc; /**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
}; };
/** /**
+1 -2
View File
@@ -31,8 +31,7 @@ headers = [ config_pub_h ] + files(
'print.hh', 'print.hh',
'repl-exit-status.hh', 'repl-exit-status.hh',
'search-path.hh', 'search-path.hh',
'string-data.hh', 'static-string-data.hh',
'string-data-static.hh',
'symbol-table.hh', 'symbol-table.hh',
'value-to-json.hh', 'value-to-json.hh',
'value-to-xml.hh', 'value-to-xml.hh',
+19 -8
View File
@@ -12,7 +12,7 @@
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/symbol-table.hh" #include "nix/expr/symbol-table.hh"
#include "nix/expr/eval-error.hh" #include "nix/expr/eval-error.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
#include "nix/util/pos-idx.hh" #include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh" #include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh" #include "nix/util/pos-table.hh"
@@ -395,9 +395,13 @@ struct ExprAttrs : Expr
} }
}; };
typedef std::map<Symbol, AttrDef> AttrDefs; typedef std::pmr::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs; /**
std::unique_ptr<std::vector<Expr *>> inheritFromExprs; * attrs will never be null. we use std::optional so that we can call emplace() to re-initialize the value with a
* new pmr::map using a different allocator (move assignment will copy into the old allocator)
*/
std::optional<AttrDefs> attrs;
std::unique_ptr<std::pmr::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef struct DynamicAttrDef
{ {
@@ -409,13 +413,20 @@ struct ExprAttrs : Expr
, pos(pos) {}; , pos(pos) {};
}; };
typedef std::vector<DynamicAttrDef> DynamicAttrDefs; typedef std::pmr::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs; /**
* dynamicAttrs will never be null. See comment on AttrDefs above.
*/
std::optional<DynamicAttrDefs> dynamicAttrs;
ExprAttrs(const PosIdx & pos) ExprAttrs(const PosIdx & pos)
: recursive(false) : recursive(false)
, pos(pos) {}; , pos(pos)
, attrs(AttrDefs{})
, dynamicAttrs(DynamicAttrDefs{}) {};
ExprAttrs() ExprAttrs()
: recursive(false) {}; : recursive(false)
, attrs(AttrDefs{})
, dynamicAttrs(DynamicAttrDefs{}) {};
PosIdx getPos() const override PosIdx getPos() const override
{ {
+17 -17
View File
@@ -5,7 +5,7 @@
#include "nix/expr/eval.hh" #include "nix/expr/eval.hh"
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
namespace nix { namespace nix {
@@ -126,8 +126,8 @@ inline void ParserState::addAttr(
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
ExprAttrs * nested; ExprAttrs * nested;
if (i->symbol) { if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(i->symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs->end()) {
nested = dynamic_cast<ExprAttrs *>(j->second.e); nested = dynamic_cast<ExprAttrs *>(j->second.e);
if (!nested) { if (!nested) {
attrPath.erase(i + 1, attrPath.end()); attrPath.erase(i + 1, attrPath.end());
@@ -135,11 +135,11 @@ inline void ParserState::addAttr(
} }
} else { } else {
nested = exprs.add<ExprAttrs>(); nested = exprs.add<ExprAttrs>();
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); (*attrs->attrs)[i->symbol] = ExprAttrs::AttrDef(nested, pos);
} }
} else { } else {
nested = exprs.add<ExprAttrs>(); nested = exprs.add<ExprAttrs>();
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
} }
attrs = nested; attrs = nested;
} }
@@ -148,7 +148,7 @@ inline void ParserState::addAttr(
if (i->symbol) { if (i->symbol) {
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos)); addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
} else { } else {
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
} }
auto it = lexerState.positionToDocComment.find(pos); auto it = lexerState.positionToDocComment.find(pos);
@@ -165,8 +165,8 @@ inline void ParserState::addAttr(
inline void inline void
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def) ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
{ {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol); ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs->end()) {
// This attr path is already defined. However, if both // This attr path is already defined. However, if both
// e and the expr pointed by the attr path are two attribute sets, // e and the expr pointed by the attr path are two attribute sets,
// we want to merge them. // we want to merge them.
@@ -181,8 +181,8 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
// See https://github.com/NixOS/nix/issues/9020. // See https://github.com/NixOS/nix/issues/9020.
if (jAttrs && ae) { if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs) if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); jAttrs->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
for (auto & ad : ae->attrs) { for (auto & ad : *ae->attrs) {
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e); auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e); auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
@@ -192,12 +192,12 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second)); addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
attrPath.pop_back(); attrPath.pop_back();
} }
ae->attrs.clear(); ae->attrs->clear();
jAttrs->dynamicAttrs.insert( jAttrs->dynamicAttrs->insert(
jAttrs->dynamicAttrs.end(), jAttrs->dynamicAttrs->end(),
std::make_move_iterator(ae->dynamicAttrs.begin()), std::make_move_iterator(ae->dynamicAttrs->begin()),
std::make_move_iterator(ae->dynamicAttrs.end())); std::make_move_iterator(ae->dynamicAttrs->end()));
ae->dynamicAttrs.clear(); ae->dynamicAttrs->clear();
if (ae->inheritFromExprs) { if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert( jAttrs->inheritFromExprs->insert(
jAttrs->inheritFromExprs->end(), jAttrs->inheritFromExprs->end(),
@@ -210,7 +210,7 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
} }
} else { } else {
// This attr path is not defined. Let's create it. // This attr path is not defined. Let's create it.
attrs->attrs.emplace(symbol, def); attrs->attrs->emplace(symbol, def);
def.e->setName(symbol); def.e->setName(symbol);
} }
} }
@@ -1,7 +1,7 @@
#pragma once #pragma once
///@file ///@file
#include "nix/expr/string-data.hh" #include "nix/expr/value.hh"
namespace nix { namespace nix {
@@ -1,96 +0,0 @@
#pragma once
///@file
#include <cstddef>
#include <cstring>
#include <memory_resource>
#include <string_view>
namespace nix {
class StringData
{
public:
using size_type = std::size_t;
size_type size_;
char data_[];
/*
* This in particular ensures that we cannot have a `StringData`
* that we use by value, which is just what we want!
*
* Dynamically sized types aren't a thing in C++ and even flexible array
* members are a language extension and beyond the realm of standard C++.
* Technically, sizeof data_ member is 0 and the intended way to use flexible
* array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes
* and the compiler will consider alignment restrictions for the FAM.
*
*/
StringData(StringData &&) = delete;
StringData & operator=(StringData &&) = delete;
StringData(const StringData &) = delete;
StringData & operator=(const StringData &) = delete;
~StringData() = default;
private:
StringData() = delete;
explicit StringData(size_type size)
: size_(size)
{
}
public:
/**
* Allocate StringData on the (possibly) GC-managed heap and copy
* the contents of s to it.
*/
static const StringData & make(std::string_view s);
/**
* Allocate StringData on the (possibly) GC-managed heap.
* @param size Length of the string (without the NUL terminator).
*/
static StringData & alloc(size_t size);
size_t size() const
{
return size_;
}
char * data() noexcept
{
return data_;
}
const char * data() const noexcept
{
return data_;
}
const char * c_str() const noexcept
{
return data_;
}
constexpr std::string_view view() const noexcept
{
return std::string_view(data_, size_);
}
template<size_t N>
struct Static;
static StringData & make(std::pmr::memory_resource & resource, std::string_view s)
{
auto & res =
*new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size());
std::memcpy(res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
}
};
} // namespace nix
+1 -1
View File
@@ -3,7 +3,7 @@
#include <memory_resource> #include <memory_resource>
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
#include "nix/util/chunked-vector.hh" #include "nix/util/chunked-vector.hh"
#include "nix/util/error.hh" #include "nix/util/error.hh"
+88 -1
View File
@@ -3,14 +3,16 @@
#include <bit> #include <bit>
#include <cassert> #include <cassert>
#include <cstddef>
#include <cstring>
#include <memory> #include <memory>
#include <memory_resource>
#include <span> #include <span>
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>
#include <concepts> #include <concepts>
#include "nix/expr/eval-gc.hh" #include "nix/expr/eval-gc.hh"
#include "nix/expr/string-data.hh"
#include "nix/expr/value/context.hh" #include "nix/expr/value/context.hh"
#include "nix/util/source-path.hh" #include "nix/util/source-path.hh"
#include "nix/expr/print-options.hh" #include "nix/expr/print-options.hh"
@@ -191,6 +193,91 @@ public:
friend struct Value; friend struct Value;
}; };
class StringData
{
public:
using size_type = std::size_t;
size_type size_;
char data_[];
/*
* This in particular ensures that we cannot have a `StringData`
* that we use by value, which is just what we want!
*
* Dynamically sized types aren't a thing in C++ and even flexible array
* members are a language extension and beyond the realm of standard C++.
* Technically, sizeof data_ member is 0 and the intended way to use flexible
* array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes
* and the compiler will consider alignment restrictions for the FAM.
*
*/
StringData(StringData &&) = delete;
StringData & operator=(StringData &&) = delete;
StringData(const StringData &) = delete;
StringData & operator=(const StringData &) = delete;
~StringData() = default;
private:
StringData() = delete;
explicit StringData(size_type size)
: size_(size)
{
}
public:
/**
* Allocate StringData on the (possibly) GC-managed heap and copy
* the contents of s to it.
*/
static const StringData & make(std::string_view s);
/**
* Allocate StringData on the (possibly) GC-managed heap.
* @param size Length of the string (without the NUL terminator).
*/
static StringData & alloc(size_t size);
size_t size() const
{
return size_;
}
char * data() noexcept
{
return data_;
}
const char * data() const noexcept
{
return data_;
}
const char * c_str() const noexcept
{
return data_;
}
constexpr std::string_view view() const noexcept
{
return std::string_view(data_, size_);
}
template<size_t N>
struct Static;
static StringData & make(std::pmr::memory_resource & resource, std::string_view s)
{
auto & res =
*new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size());
std::memcpy(res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
}
};
namespace detail { namespace detail {
/** /**
+23 -12
View File
@@ -74,9 +74,9 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
{ {
typedef const decltype(attrs)::value_type * Attr; typedef const AttrDefs::value_type * Attr;
std::vector<Attr> sorted; std::vector<Attr> sorted;
for (auto & i : attrs) for (auto & i : *attrs)
sorted.push_back(&i); sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) { std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
std::string_view sa = symbols[a->first], sb = symbols[b->first]; std::string_view sa = symbols[a->first], sb = symbols[b->first];
@@ -122,7 +122,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
str << "; "; str << "; ";
} }
} }
for (auto & i : dynamicAttrs) { for (auto & i : *dynamicAttrs) {
str << "\"${"; str << "\"${";
i.nameExpr->show(symbols, str); i.nameExpr->show(symbols, str);
str << "}\" = "; str << "}\" = ";
@@ -401,15 +401,26 @@ ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr<const Static
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{ {
// Move storage into the Exprs arena
{
auto arena = es.mem.exprs.alloc;
AttrDefs newAttrs{std::move(*attrs), arena};
attrs.emplace(std::move(newAttrs), arena);
DynamicAttrDefs newDynamicAttrs{std::move(*dynamicAttrs), arena};
dynamicAttrs.emplace(std::move(newDynamicAttrs), arena);
if (inheritFromExprs)
inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>(std::move(*inheritFromExprs), arena);
}
if (es.debugRepl) if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) { if (recursive) {
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> { auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size()); auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->size());
Displacement displ = 0; Displacement displ = 0;
for (auto & i : attrs) for (auto & i : *attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++); newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv; return newEnv;
}(); }();
@@ -417,20 +428,20 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
// No need to sort newEnv since attrs is in sorted order. // No need to sort newEnv since attrs is in sorted order.
auto inheritFromEnv = bindInheritSources(es, newEnv); auto inheritFromEnv = bindInheritSources(es, newEnv);
for (auto & i : attrs) for (auto & i : *attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
for (auto & i : dynamicAttrs) { for (auto & i : *dynamicAttrs) {
i.nameExpr->bindVars(es, newEnv); i.nameExpr->bindVars(es, newEnv);
i.valueExpr->bindVars(es, newEnv); i.valueExpr->bindVars(es, newEnv);
} }
} else { } else {
auto inheritFromEnv = bindInheritSources(es, env); auto inheritFromEnv = bindInheritSources(es, env);
for (auto & i : attrs) for (auto & i : *attrs)
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv)); i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
for (auto & i : dynamicAttrs) { for (auto & i : *dynamicAttrs) {
i.nameExpr->bindVars(es, env); i.nameExpr->bindVars(es, env);
i.valueExpr->bindVars(es, env); i.valueExpr->bindVars(es, env);
} }
@@ -486,10 +497,10 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{ {
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> { auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size()); auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs->size());
Displacement displ = 0; Displacement displ = 0;
for (auto & i : attrs->attrs) for (auto & i : *attrs->attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++); newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv; return newEnv;
}(); }();
@@ -497,7 +508,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
// No need to sort newEnv since attrs->attrs is in sorted order. // No need to sort newEnv since attrs->attrs is in sorted order.
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv); auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
for (auto & i : attrs->attrs) for (auto & i : *attrs->attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
if (es.debugRepl) if (es.debugRepl)
+8 -8
View File
@@ -211,7 +211,7 @@ expr_function
| WITH expr ';' expr_function | WITH expr ';' expr_function
{ $$ = state->exprs.add<ExprWith>(CUR_POS, $2, $4); } { $$ = state->exprs.add<ExprWith>(CUR_POS, $2, $4); }
| LET binds IN_KW expr_function | LET binds IN_KW expr_function
{ if (!$2->dynamicAttrs.empty()) { if (!$2->dynamicAttrs->empty())
throw ParseError({ throw ParseError({
.msg = HintFmt("dynamic attributes not allowed in let"), .msg = HintFmt("dynamic attributes not allowed in let"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
@@ -413,9 +413,9 @@ binds1
| binds[accum] INHERIT attrs ';' | binds[accum] INHERIT attrs ';'
{ $$ = $accum; { $$ = $accum;
for (auto & [i, iPos] : $attrs) { for (auto & [i, iPos] : $attrs) {
if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
$accum->attrs.emplace( $accum->attrs->emplace(
i.symbol, i.symbol,
ExprAttrs::AttrDef(state->exprs.add<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); ExprAttrs::AttrDef(state->exprs.add<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
} }
@@ -423,13 +423,13 @@ binds1
| binds[accum] INHERIT '(' expr ')' attrs ';' | binds[accum] INHERIT '(' expr ')' attrs ';'
{ $$ = $accum; { $$ = $accum;
if (!$accum->inheritFromExprs) if (!$accum->inheritFromExprs)
$accum->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); $accum->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
$accum->inheritFromExprs->push_back($expr); $accum->inheritFromExprs->push_back($expr);
auto from = state->exprs.add<ExprInheritFrom>(state->at(@expr), $accum->inheritFromExprs->size() - 1); auto from = state->exprs.add<ExprInheritFrom>(state->at(@expr), $accum->inheritFromExprs->size() - 1);
for (auto & [i, iPos] : $attrs) { for (auto & [i, iPos] : $attrs) {
if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
$accum->attrs.emplace( $accum->attrs->emplace(
i.symbol, i.symbol,
ExprAttrs::AttrDef( ExprAttrs::AttrDef(
state->exprs.add<ExprSelect>(state->exprs.alloc, iPos, from, i.symbol), state->exprs.add<ExprSelect>(state->exprs.alloc, iPos, from, i.symbol),
+201 -201
View File
File diff suppressed because it is too large. Load diff
+10 -11
View File
@@ -1,6 +1,5 @@
#include "nix/expr/primops.hh" #include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh" #include "nix/expr/eval-inline.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/derivations.hh" #include "nix/store/derivations.hh"
#include "nix/store/store-api.hh" #include "nix/store/store-api.hh"
#include "nix/store/globals.hh" #include "nix/store/globals.hh"
@@ -18,9 +17,9 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
static RegisterPrimOp primop_unsafeDiscardStringContext({ static RegisterPrimOp primop_unsafeDiscardStringContext({
.name = "__unsafeDiscardStringContext", .name = "__unsafeDiscardStringContext",
.args = {"s"}, .args = {"s"},
.doc = &R"( .doc = R"(
Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string. Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string.
)"_sds, )",
.fun = prim_unsafeDiscardStringContext, .fun = prim_unsafeDiscardStringContext,
}); });
@@ -34,7 +33,7 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args,
static RegisterPrimOp primop_hasContext( static RegisterPrimOp primop_hasContext(
{.name = "__hasContext", {.name = "__hasContext",
.args = {"s"}, .args = {"s"},
.doc = &R"( .doc = R"(
Return `true` if string *s* has a non-empty context. Return `true` if string *s* has a non-empty context.
The context can be obtained with The context can be obtained with
[`getContext`](#builtins-getContext). [`getContext`](#builtins-getContext).
@@ -51,7 +50,7 @@ static RegisterPrimOp primop_hasContext(
> then throw "package name cannot contain string context" > then throw "package name cannot contain string context"
> else { ${name} = meta; } > else { ${name} = meta; }
> ``` > ```
)"_sds, )",
.fun = prim_hasContext}); .fun = prim_hasContext});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -76,7 +75,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
static RegisterPrimOp primop_unsafeDiscardOutputDependency( static RegisterPrimOp primop_unsafeDiscardOutputDependency(
{.name = "__unsafeDiscardOutputDependency", {.name = "__unsafeDiscardOutputDependency",
.args = {"s"}, .args = {"s"},
.doc = &R"( .doc = R"(
Create a copy of the given string where every Create a copy of the given string where every
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
string context element is turned into a string context element is turned into a
@@ -92,7 +91,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency(
Replacing a constant string context element with a "derivation deep" element is a safe operation that just enlargens the string context without forgetting anything. Replacing a constant string context element with a "derivation deep" element is a safe operation that just enlargens the string context without forgetting anything.
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies [`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)"_sds, )",
.fun = prim_unsafeDiscardOutputDependency}); .fun = prim_unsafeDiscardOutputDependency});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v) static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -144,7 +143,7 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
static RegisterPrimOp primop_addDrvOutputDependencies( static RegisterPrimOp primop_addDrvOutputDependencies(
{.name = "__addDrvOutputDependencies", {.name = "__addDrvOutputDependencies",
.args = {"s"}, .args = {"s"},
.doc = &R"( .doc = R"(
Create a copy of the given string where a single Create a copy of the given string where a single
[constant](@docroot@/language/string-context.md#string-context-constant) [constant](@docroot@/language/string-context.md#string-context-constant)
string context element is turned into a string context element is turned into a
@@ -157,7 +156,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies(
The latter is supported so this function is idempotent. The latter is supported so this function is idempotent.
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)"_sds, )",
.fun = prim_addDrvOutputDependencies}); .fun = prim_addDrvOutputDependencies});
/* Extract the context of a string as a structured Nix value. /* Extract the context of a string as a structured Nix value.
@@ -231,7 +230,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args,
static RegisterPrimOp primop_getContext( static RegisterPrimOp primop_getContext(
{.name = "__getContext", {.name = "__getContext",
.args = {"s"}, .args = {"s"},
.doc = &R"( .doc = R"(
Return the string context of *s*. Return the string context of *s*.
The string context tracks references to derivations within a string. The string context tracks references to derivations within a string.
@@ -249,7 +248,7 @@ static RegisterPrimOp primop_getContext(
``` ```
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; } { "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
``` ```
)"_sds, )",
.fun = prim_getContext}); .fun = prim_getContext});
/* Append the given context to a given string. /* Append the given context to a given string.
+2 -3
View File
@@ -1,5 +1,4 @@
#include "nix/expr/primops.hh" #include "nix/expr/primops.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-open.hh" #include "nix/store/store-open.hh"
#include "nix/store/realisation.hh" #include "nix/store/realisation.hh"
#include "nix/store/make-content-addressed.hh" #include "nix/store/make-content-addressed.hh"
@@ -217,7 +216,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args
static RegisterPrimOp primop_fetchClosure({ static RegisterPrimOp primop_fetchClosure({
.name = "__fetchClosure", .name = "__fetchClosure",
.args = {"args"}, .args = {"args"},
.doc = &R"( .doc = R"(
Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context. Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context.
This function can be invoked in three ways that we will discuss in order of preference. This function can be invoked in three ways that we will discuss in order of preference.
@@ -285,7 +284,7 @@ static RegisterPrimOp primop_fetchClosure({
`fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression. `fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression.
However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched. However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched.
Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity. Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity.
)"_sds, )",
.fun = prim_fetchClosure, .fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure, .experimentalFeature = Xp::FetchClosure,
}); });
+1 -1
View File
@@ -81,7 +81,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar
attrs.insert_or_assign("rev", rev->gitRev()); attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
auto [storePath, input2] = input.fetchToStore(state.fetchSettings, state.store); auto [storePath, input2] = input.fetchToStore(state.fetchSettings, *state.store);
auto attrs2 = state.buildBindings(8); auto attrs2 = state.buildBindings(8);
state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath)); state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath));
+102 -205
View File
@@ -2,7 +2,6 @@
#include "nix/expr/primops.hh" #include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh" #include "nix/expr/eval-inline.hh"
#include "nix/expr/eval-settings.hh" #include "nix/expr/eval-settings.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-api.hh" #include "nix/store/store-api.hh"
#include "nix/fetchers/fetchers.hh" #include "nix/fetchers/fetchers.hh"
#include "nix/store/filetransfer.hh" #include "nix/store/filetransfer.hh"
@@ -195,7 +194,7 @@ static void fetchTree(
} }
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
input = lookupInRegistries(state.fetchSettings, state.store, input, fetchers::UseRegistries::Limited).first; input = lookupInRegistries(state.fetchSettings, *state.store, input, fetchers::UseRegistries::Limited).first;
if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) { if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) {
if (input.getNarHash()) if (input.getNarHash())
@@ -221,7 +220,7 @@ static void fetchTree(
} }
auto cachedInput = auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, state.store, input, fetchers::UseRegistries::No); state.inputCache->getAccessor(state.fetchSettings, *state.store, input, fetchers::UseRegistries::No);
auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor); auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor);
@@ -236,229 +235,127 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, V
static RegisterPrimOp primop_fetchTree({ static RegisterPrimOp primop_fetchTree({
.name = "fetchTree", .name = "fetchTree",
.args = {"input"}, .args = {"input"},
.doc = &R"( .doc = []() -> std::string {
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with: std::string doc = stripIndentation(R"(
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
- the resulting fixed-output [store path](@docroot@/store/store-path.md) - the resulting fixed-output [store path](@docroot@/store/store-path.md)
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash - the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes --> - backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
*input* must be an attribute set with the following attributes: *input* must be an attribute set with the following attributes:
- `type` (String, required) - `type` (String, required)
One of the [supported source types](#source-types). One of the [supported source types](#source-types).
This determines other required and allowed input attributes. This determines other required and allowed input attributes.
- `narHash` (String, optional) - `narHash` (String, optional)
The `narHash` parameter can be used to substitute the source of the tree. The `narHash` parameter can be used to substitute the source of the tree.
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism. It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available. If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again. A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
That is, `fetchTree` is idempotent. That is, `fetchTree` is idempotent.
Downloads are cached in `$XDG_CACHE_HOME/nix`. Downloads are cached in `$XDG_CACHE_HOME/nix`.
The remote source is fetched from the network if both are true: The remote source is fetched from the network if both are true:
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store - A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
> **Note** > **Note**
>
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
## Source types
The following source types and associated input attributes are supported.
<!-- TODO: It would be soooo much more predictable to work with (and
document) if `fetchTree` was a curried call with the first parameter for
`type` or an attribute like `builtins.fetchTree.git`! -->
- `"file"`
Place a plain file into the Nix store.
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
- `url` (String, required)
Supported protocols:
- `https`
> **Example**
> >
> ```nix > [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
> fetchTree {
> type = "file";
> url = "https://example.com/index.html";
> }
> ```
- `http` - There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
Insecure HTTP transfer for legacy sources. ## Source types
> **Warning** The following source types and associated input attributes are supported.
>
> HTTP performs no encryption or authentication.
> Use a `narHash` known in advance to ensure the output has expected contents.
- `file` <!-- TODO: It would be soooo much more predictable to work with (and
document) if `fetchTree` was a curried call with the first parameter for
`type` or an attribute like `builtins.fetchTree.git`! -->
)");
A file on the local file system. auto indentString = [](std::string const & str, std::string const & indent) {
std::string result;
std::istringstream stream(str);
std::string line;
bool first = true;
while (std::getline(stream, line)) {
if (!first)
result += "\n";
result += indent + line;
first = false;
}
return result;
};
> **Example** for (const auto & [schemeName, scheme] : fetchers::getAllInputSchemes()) {
> doc += "\n- `" + quoteString(schemeName, '"') + "`\n\n";
> ```nix doc += indentString(scheme->schemeDescription(), " ");
> fetchTree { if (!doc.empty() && doc.back() != '\n')
> type = "file"; doc += "\n";
> url = "file:///home/eelco/nix/README.md";
> }
> ```
- `"git"` for (const auto & [attrName, attribute] : scheme->allowedAttrs()) {
doc += "\n - `" + attrName + "` (" + attribute.type + ", "
+ (attribute.required ? "required" : "optional") + ")\n\n";
doc += indentString(stripIndentation(attribute.doc), " ");
if (!doc.empty() && doc.back() != '\n')
doc += "\n";
}
}
Fetch a Git tree and copy it to the Nix store. doc += "\n" + stripIndentation(R"(
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit). The following input types are still subject to change:
- `allRefs` (Bool, optional) - `"path"`
- `"github"`
- `"gitlab"`
- `"sourcehut"`
- `"mercurial"`
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled. *input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
Whether to fetch all references (eg. branches and tags) of the repository.
With this argument being true, it's possible to load a `rev` from *any* `ref`.
(Without setting this option, only `rev`s from the specified `ref` are supported).
Default: `false`
- `lastModified` (Integer, optional)
Unix timestamp of the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
- `lfs` (Bool, optional)
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
- `ref` (String, optional)
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
Default: `"HEAD"`
- `rev` (String, optional)
A Git revision; a commit hash.
Default: the tip of `ref`
- `revCount` (Integer, optional)
Number of revisions in the history of the Git repository before the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
- `shallow` (Bool, optional)
Make a shallow clone when fetching the Git tree.
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
Default: `true`
- `submodules` (Bool, optional)
Also fetch submodules if available.
Default: `false`
- `url` (String, required)
The URL formats supported are the same as for Git itself.
> **Example** > **Example**
> >
> Fetch a GitHub repository using the attribute set representation:
>
> ```nix > ```nix
> fetchTree { > builtins.fetchTree {
> type = "git"; > type = "github";
> url = "git@github.com:NixOS/nixpkgs.git"; > owner = "NixOS";
> repo = "nixpkgs";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> }
> ```
>
> This evaluates to the following attribute set:
>
> ```nix
> {
> lastModified = 1686503798;
> lastModifiedDate = "20230611171638";
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> shortRev = "ae2e6b3";
> } > }
> ``` > ```
> **Note** > **Example**
> >
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but use the *current file contents* of the Git working directory. > Fetch the same GitHub repository using the URL-like syntax:
>
> ```nix
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
> ```
)");
- `"tarball"` return doc;
}(),
Download a tar archive and extract it into the Nix store.
This has the same underlying implementation as [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball)
- `url` (String, required)
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
The following input types are still subject to change:
- `"path"`
- `"github"`
- `"gitlab"`
- `"sourcehut"`
- `"mercurial"`
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
> **Example**
>
> Fetch a GitHub repository using the attribute set representation:
>
> ```nix
> builtins.fetchTree {
> type = "github";
> owner = "NixOS";
> repo = "nixpkgs";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> }
> ```
>
> This evaluates to the following attribute set:
>
> ```nix
> {
> lastModified = 1686503798;
> lastModifiedDate = "20230611171638";
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> shortRev = "ae2e6b3";
> }
> ```
> **Example**
>
> Fetch the same GitHub repository using the URL-like syntax:
>
> ```nix
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
> ```
)"_sds,
.fun = prim_fetchTree, .fun = prim_fetchTree,
.experimentalFeature = Xp::FetchTree, .experimentalFeature = Xp::FetchTree,
}); });
@@ -583,10 +480,10 @@ static void fetch(
auto storePath = unpack ? fetchToStore( auto storePath = unpack ? fetchToStore(
state.fetchSettings, state.fetchSettings,
*state.store, *state.store,
fetchers::downloadTarball(state.store, state.fetchSettings, *url), fetchers::downloadTarball(*state.store, state.fetchSettings, *url),
FetchMode::Copy, FetchMode::Copy,
name) name)
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath; : fetchers::downloadFile(*state.store, state.fetchSettings, *url, name).storePath;
if (expectedHash) { if (expectedHash) {
auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash
@@ -618,7 +515,7 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchurl({ static RegisterPrimOp primop_fetchurl({
.name = "__fetchurl", .name = "__fetchurl",
.args = {"arg"}, .args = {"arg"},
.doc = &R"( .doc = R"(
Download the specified URL and return the path of the downloaded file. Download the specified URL and return the path of the downloaded file.
`arg` can be either a string denoting the URL, or an attribute set with the following attributes: `arg` can be either a string denoting the URL, or an attribute set with the following attributes:
@@ -632,7 +529,7 @@ static RegisterPrimOp primop_fetchurl({
characters that are invalid for the store. characters that are invalid for the store.
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)"_sds, )",
.fun = prim_fetchurl, .fun = prim_fetchurl,
}); });
@@ -644,7 +541,7 @@ static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args
static RegisterPrimOp primop_fetchTarball({ static RegisterPrimOp primop_fetchTarball({
.name = "fetchTarball", .name = "fetchTarball",
.args = {"args"}, .args = {"args"},
.doc = &R"( .doc = R"(
Download the specified URL, unpack it and return the path of the Download the specified URL, unpack it and return the path of the
unpacked tree. The file must be a tape archive (`.tar`) compressed unpacked tree. The file must be a tape archive (`.tar`) compressed
with `gzip`, `bzip2` or `xz`. If the tarball consists of a with `gzip`, `bzip2` or `xz`. If the tarball consists of a
@@ -682,7 +579,7 @@ static RegisterPrimOp primop_fetchTarball({
``` ```
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)"_sds, )",
.fun = prim_fetchTarball, .fun = prim_fetchTarball,
}); });
@@ -695,7 +592,7 @@ static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchGit({ static RegisterPrimOp primop_fetchGit({
.name = "fetchGit", .name = "fetchGit",
.args = {"args"}, .args = {"args"},
.doc = &R"( .doc = R"(
Fetch a path from git. *args* can be a URL, in which case the HEAD Fetch a path from git. *args* can be a URL, in which case the HEAD
of the repo at that URL is fetched. Otherwise, it can be an of the repo at that URL is fetched. Otherwise, it can be an
attribute with the following attributes (all except `url` optional): attribute with the following attributes (all except `url` optional):
@@ -898,7 +795,7 @@ static RegisterPrimOp primop_fetchGit({
given, `fetchGit` uses the current content of the checked-out given, `fetchGit` uses the current content of the checked-out
files, even if they are not committed or added to Git's index. It files, even if they are not committed or added to Git's index. It
only considers files added to the Git repository, as listed by `git ls-files`. only considers files added to the Git repository, as listed by `git ls-files`.
)"_sds, )",
.fun = prim_fetchGit, .fun = prim_fetchGit,
}); });
+7 -8
View File
@@ -1,6 +1,6 @@
#include "nix/expr/primops.hh" #include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh" #include "nix/expr/eval-inline.hh"
#include "nix/expr/string-data-static.hh" #include "nix/expr/static-string-data.hh"
#include "expr-config-private.hh" #include "expr-config-private.hh"
@@ -170,10 +170,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
} }
} }
static RegisterPrimOp primop_fromTOML({ static RegisterPrimOp primop_fromTOML(
.name = "fromTOML", {.name = "fromTOML",
.args = {"e"}, .args = {"e"},
.doc = &R"( .doc = R"(
Convert a TOML string to a Nix value. For example, Convert a TOML string to a Nix value. For example,
```nix ```nix
@@ -186,8 +186,7 @@ static RegisterPrimOp primop_fromTOML({
``` ```
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`. returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)"_sds, )",
.fun = prim_fromTOML, .fun = prim_fromTOML});
});
} // namespace nix } // namespace nix
+1 -1
View File
@@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport)
{"ref", "main"}, {"ref", "main"},
}); });
auto [accessor, i] = input.getAccessor(settings, store); auto [accessor, i] = input.getAccessor(settings, *store);
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n"); ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
} }
+28 -26
View File
@@ -6,6 +6,7 @@
#include "nix/fetchers/fetch-settings.hh" #include "nix/fetchers/fetch-settings.hh"
#include "nix/fetchers/fetch-to-store.hh" #include "nix/fetchers/fetch-to-store.hh"
#include "nix/util/url.hh" #include "nix/util/url.hh"
#include "nix/util/archive.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@@ -26,18 +27,9 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
throw Error("Input scheme with name %s already registered", schemeName); throw Error("Input scheme with name %s already registered", schemeName);
} }
nlohmann::json dumpRegisterInputSchemeInfo() const InputSchemeMap & getAllInputSchemes()
{ {
using nlohmann::json; return inputSchemes();
auto res = json::object();
for (auto & [name, scheme] : inputSchemes()) {
auto & r = res[name] = json::object();
r["allowedAttrs"] = scheme->allowedAttrs();
}
return res;
} }
Input Input::fromURL(const Settings & settings, const std::string & url, bool requireTree) Input Input::fromURL(const Settings & settings, const std::string & url, bool requireTree)
@@ -119,7 +111,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
return std::move(*res); return std::move(*res);
} }
std::optional<std::string> Input::getFingerprint(ref<Store> store) const std::optional<std::string> Input::getFingerprint(Store & store) const
{ {
if (!scheme) if (!scheme)
return std::nullopt; return std::nullopt;
@@ -198,7 +190,7 @@ bool Input::contains(const Input & other) const
} }
// FIXME: remove // FIXME: remove
std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, ref<Store> store) const std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, Store & store) const
{ {
if (!scheme) if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
@@ -208,9 +200,9 @@ std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, ref<S
auto [accessor, result] = getAccessorUnchecked(settings, store); auto [accessor, result] = getAccessorUnchecked(settings, store);
auto storePath = auto storePath =
nix::fetchToStore(settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName()); nix::fetchToStore(settings, store, SourcePath(accessor), FetchMode::Copy, result.getName());
auto narHash = store->queryPathInfo(storePath)->narHash; auto narHash = store.queryPathInfo(storePath)->narHash;
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
result.attrs.insert_or_assign("__final", Explicit<bool>(true)); result.attrs.insert_or_assign("__final", Explicit<bool>(true));
@@ -297,7 +289,7 @@ void Input::checkLocks(Input specified, Input & result)
} }
} }
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settings, ref<Store> store) const std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settings, Store & store) const
{ {
try { try {
auto [accessor, result] = getAccessorUnchecked(settings, store); auto [accessor, result] = getAccessorUnchecked(settings, store);
@@ -313,7 +305,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settin
} }
} }
std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings & settings, ref<Store> store) const std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings & settings, Store & store) const
{ {
// FIXME: cache the accessor // FIXME: cache the accessor
@@ -333,13 +325,13 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
*/ */
if (isFinal() && getNarHash()) { if (isFinal() && getNarHash()) {
try { try {
auto storePath = computeStorePath(*store); auto storePath = computeStorePath(store);
store->ensurePath(storePath); store.ensurePath(storePath);
debug("using substituted/cached input '%s' in '%s'", to_string(), store->printStorePath(storePath)); debug("using substituted/cached input '%s' in '%s'", to_string(), store.printStorePath(storePath));
auto accessor = store->requireStoreObjectAccessor(storePath); auto accessor = store.requireStoreObjectAccessor(storePath);
accessor->fingerprint = getFingerprint(store); accessor->fingerprint = getFingerprint(store);
@@ -349,7 +341,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
if (accessor->fingerprint) { if (accessor->fingerprint) {
ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive; ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive;
auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/"); auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/");
settings.getCache()->upsert(cacheKey, *store, {}, storePath); settings.getCache()->upsert(cacheKey, store, {}, storePath);
} }
accessor->setPathDisplay("«" + to_string() + "»"); accessor->setPathDisplay("«" + to_string() + "»");
@@ -377,10 +369,10 @@ Input Input::applyOverrides(std::optional<std::string> ref, std::optional<Hash>
return scheme->applyOverrides(*this, ref, rev); return scheme->applyOverrides(*this, ref, rev);
} }
void Input::clone(const Settings & settings, const Path & destDir) const void Input::clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const
{ {
assert(scheme); assert(scheme);
scheme->clone(settings, *this, destDir); scheme->clone(settings, store, *this, destDir);
} }
std::optional<std::filesystem::path> Input::getSourcePath() const std::optional<std::filesystem::path> Input::getSourcePath() const
@@ -493,9 +485,19 @@ void InputScheme::putFile(
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path); throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
} }
void InputScheme::clone(const Settings & settings, const Input & input, const Path & destDir) const void InputScheme::clone(
const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const
{ {
throw Error("do not know how to clone input '%s'", input.to_string()); if (std::filesystem::exists(destDir))
throw Error("cannot clone into existing path %s", destDir);
auto [accessor, input2] = getAccessor(settings, store, input);
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to %s...", input2.to_string(), destDir));
auto source = sinkToSource([&](Sink & sink) { accessor->dumpPath(CanonPath::root, sink); });
restorePath(destDir, *source);
} }
std::optional<ExperimentalFeature> InputScheme::experimentalFeature() const std::optional<ExperimentalFeature> InputScheme::experimentalFeature() const
+1 -1
View File
@@ -209,7 +209,7 @@ std::vector<nlohmann::json> Fetch::fetchUrls(const std::vector<Pointer> & pointe
auto url = api.endpoint + "/objects/batch"; auto url = api.endpoint + "/objects/batch";
const auto & authHeader = api.authHeader; const auto & authHeader = api.authHeader;
FileTransferRequest request(parseURL(url)); FileTransferRequest request(parseURL(url));
request.method = HttpMethod::POST; request.method = HttpMethod::Post;
Headers headers; Headers headers;
if (authHeader.has_value()) if (authHeader.has_value())
headers.push_back({"Authorization", *authHeader}); headers.push_back({"Authorization", *authHeader});
+181 -25
View File
@@ -194,28 +194,183 @@ struct GitInputScheme : InputScheme
return "git"; return "git";
} }
StringSet allowedAttrs() const override std::string schemeDescription() const override
{ {
return { return stripIndentation(R"(
"url", Fetch a Git tree and copy it to the Nix store.
"ref", This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
"rev", )");
"shallow", }
"submodules",
"lfs", const std::map<std::string, AttributeInfo> & allowedAttrs() const override
"exportIgnore", {
"lastModified", static const std::map<std::string, AttributeInfo> attrs = {
"revCount", {
"narHash", "url",
"allRefs", {
"name", .type = "String",
"dirtyRev", .required = true,
"dirtyShortRev", .doc = R"(
"verifyCommit", The URL formats supported are the same as for Git itself.
"keytype",
"publicKey", > **Example**
"publicKeys", >
> ```nix
> fetchTree {
> type = "git";
> url = "git@github.com:NixOS/nixpkgs.git";
> }
> ```
> **Note**
>
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but uses the *current file contents* of the Git working directory.
)",
},
},
{
"ref",
{
.type = "String",
.required = false,
.doc = R"(
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
Default: `"HEAD"`
)",
},
},
{
"rev",
{
.type = "String",
.required = false,
.doc = R"(
A Git revision; a commit hash.
Default: the tip of `ref`
)",
},
},
{
"shallow",
{
.type = "Bool",
.required = false,
.doc = R"(
Make a shallow clone when fetching the Git tree.
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
Default: `true`
)",
},
},
{
"submodules",
{
.type = "Bool",
.required = false,
.doc = R"(
Also fetch submodules if available.
Default: `false`
)",
},
},
{
"lfs",
{
.type = "Bool",
.required = false,
.doc = R"(
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
)",
},
},
{
"exportIgnore",
{},
},
{
"lastModified",
{
.type = "Integer",
.required = false,
.doc = R"(
Unix timestamp of the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
)",
},
},
{
"revCount",
{
.type = "Integer",
.required = false,
.doc = R"(
Number of revisions in the history of the Git repository before the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
)",
},
},
{
"narHash",
{},
},
{
"allRefs",
{
.type = "Bool",
.required = false,
.doc = R"(
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
Whether to fetch all references (eg. branches and tags) of the repository.
With this argument being true, it's possible to load a `rev` from *any* `ref`.
(Without setting this option, only `rev`s from the specified `ref` are supported).
Default: `false`
)",
},
},
{
"name",
{},
},
{
"dirtyRev",
{},
},
{
"dirtyShortRev",
{},
},
{
"verifyCommit",
{},
},
{
"keytype",
{},
},
{
"publicKey",
{},
},
{
"publicKeys",
{},
},
}; };
return attrs;
} }
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -278,7 +433,8 @@ struct GitInputScheme : InputScheme
return res; return res;
} }
void clone(const Settings & settings, const Input & input, const Path & destDir) const override void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
{ {
auto repoInfo = getRepoInfo(input); auto repoInfo = getRepoInfo(input);
@@ -623,7 +779,7 @@ struct GitInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessorFromCommit(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const getAccessorFromCommit(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
{ {
assert(!repoInfo.workdirInfo.isDirty); assert(!repoInfo.workdirInfo.isDirty);
@@ -797,7 +953,7 @@ struct GitInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessorFromWorkdir(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const getAccessorFromWorkdir(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
{ {
auto repoPath = repoInfo.getPath().value(); auto repoPath = repoInfo.getPath().value();
@@ -881,7 +1037,7 @@ struct GitInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
Input input(_input); Input input(_input);
@@ -903,7 +1059,7 @@ struct GitInputScheme : InputScheme
return {accessor, std::move(final)}; return {accessor, std::move(final)};
} }
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
{ {
auto makeFingerprint = [&](const Hash & rev) { auto makeFingerprint = [&](const Hash & rev) {
return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "") return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "")
+73 -27
View File
@@ -110,18 +110,43 @@ struct GitArchiveInputScheme : InputScheme
return input; return input;
} }
StringSet allowedAttrs() const override const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{ {
return { static const std::map<std::string, AttributeInfo> attrs = {
"owner", {
"repo", "owner",
"ref", {},
"rev", },
"narHash", {
"lastModified", "repo",
"host", {},
"treeHash", },
{
"ref",
{},
},
{
"rev",
{},
},
{
"narHash",
{},
},
{
"lastModified",
{},
},
{
"host",
{},
},
{
"treeHash",
{},
},
}; };
return attrs;
} }
std::optional<Input> inputFromAttrs(const fetchers::Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const fetchers::Settings & settings, const Attrs & attrs) const override
@@ -233,7 +258,7 @@ struct GitArchiveInputScheme : InputScheme
std::optional<Hash> treeHash; std::optional<Hash> treeHash;
}; };
virtual RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const = 0; virtual RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0; virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0;
@@ -243,7 +268,7 @@ struct GitArchiveInputScheme : InputScheme
time_t lastModified; time_t lastModified;
}; };
std::pair<Input, TarballInfo> downloadArchive(const Settings & settings, ref<Store> store, Input input) const std::pair<Input, TarballInfo> downloadArchive(const Settings & settings, Store & store, Input input) const
{ {
if (!maybeGetStrAttr(input.attrs, "ref")) if (!maybeGetStrAttr(input.attrs, "ref"))
input.attrs.insert_or_assign("ref", "HEAD"); input.attrs.insert_or_assign("ref", "HEAD");
@@ -316,7 +341,7 @@ struct GitArchiveInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
auto [input, tarballInfo] = downloadArchive(settings, store, _input); auto [input, tarballInfo] = downloadArchive(settings, store, _input);
@@ -345,7 +370,7 @@ struct GitArchiveInputScheme : InputScheme
return Xp::Flakes; return Xp::Flakes;
} }
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
{ {
if (auto rev = input.getRev()) if (auto rev = input.getRev())
return rev->gitRev(); return rev->gitRev();
@@ -361,6 +386,12 @@ struct GitHubInputScheme : GitArchiveInputScheme
return "github"; return "github";
} }
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{ {
// Github supports PAT/OAuth2 tokens and HTTP Basic // Github supports PAT/OAuth2 tokens and HTTP Basic
@@ -387,7 +418,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
return getStrAttr(input.attrs, "repo"); return getStrAttr(input.attrs, "repo");
} }
RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const override RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
{ {
auto host = getHost(input); auto host = getHost(input);
auto url = fmt( auto url = fmt(
@@ -401,7 +432,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
auto downloadResult = downloadFile(store, settings, url, "source", headers); auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto json = nlohmann::json::parse( auto json = nlohmann::json::parse(
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root)); store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
return RefInfo{ return RefInfo{
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1), .rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
@@ -426,12 +457,13 @@ struct GitHubInputScheme : GitArchiveInputScheme
return DownloadUrl{parseURL(url), headers}; return DownloadUrl{parseURL(url), headers};
} }
void clone(const Settings & settings, const Input & input, const Path & destDir) const override void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
{ {
auto host = getHost(input); auto host = getHost(input);
Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input))) Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir); .clone(settings, store, destDir);
} }
}; };
@@ -442,6 +474,12 @@ struct GitLabInputScheme : GitArchiveInputScheme
return "gitlab"; return "gitlab";
} }
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{ {
// Gitlab supports 4 kinds of authorization, two of which are // Gitlab supports 4 kinds of authorization, two of which are
@@ -461,7 +499,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
return std::make_pair(token.substr(0, fldsplit), token.substr(fldsplit + 1)); return std::make_pair(token.substr(0, fldsplit), token.substr(fldsplit + 1));
} }
RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const override RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
{ {
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// See rate limiting note below // See rate limiting note below
@@ -476,7 +514,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
auto downloadResult = downloadFile(store, settings, url, "source", headers); auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto json = nlohmann::json::parse( auto json = nlohmann::json::parse(
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root)); store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) { if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)}; return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)};
@@ -507,7 +545,8 @@ struct GitLabInputScheme : GitArchiveInputScheme
return DownloadUrl{parseURL(url), headers}; return DownloadUrl{parseURL(url), headers};
} }
void clone(const Settings & settings, const Input & input, const Path & destDir) const override void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
{ {
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// FIXME: get username somewhere // FIXME: get username somewhere
@@ -515,7 +554,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
settings, settings,
fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir); .clone(settings, store, destDir);
} }
}; };
@@ -526,6 +565,12 @@ struct SourceHutInputScheme : GitArchiveInputScheme
return "sourcehut"; return "sourcehut";
} }
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{ {
// SourceHut supports both PAT and OAuth2. See // SourceHut supports both PAT and OAuth2. See
@@ -536,7 +581,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
// Once it is implemented, however, should work as expected. // Once it is implemented, however, should work as expected.
} }
RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const override RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
{ {
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial // TODO: In the future, when the sourcehut graphql API is implemented for mercurial
// and with anonymous access, this method should use it instead. // and with anonymous access, this method should use it instead.
@@ -552,7 +597,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
std::string refUri; std::string refUri;
if (ref == "HEAD") { if (ref == "HEAD") {
auto downloadFileResult = downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers); auto downloadFileResult = downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root); auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto remoteLine = git::parseLsRemoteLine(getLine(contents).first); auto remoteLine = git::parseLsRemoteLine(getLine(contents).first);
if (!remoteLine) { if (!remoteLine) {
@@ -565,7 +610,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
std::regex refRegex(refUri); std::regex refRegex(refUri);
auto downloadFileResult = downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers); auto downloadFileResult = downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root); auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
std::istringstream is(contents); std::istringstream is(contents);
std::string line; std::string line;
@@ -596,14 +641,15 @@ struct SourceHutInputScheme : GitArchiveInputScheme
return DownloadUrl{parseURL(url), headers}; return DownloadUrl{parseURL(url), headers};
} }
void clone(const Settings & settings, const Input & input, const Path & destDir) const override void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
{ {
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL( Input::fromURL(
settings, settings,
fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir); .clone(settings, store, destDir);
} }
}; };
@@ -113,7 +113,7 @@ public:
* Fetch the entire input into the Nix store, returning the * Fetch the entire input into the Nix store, returning the
* location in the Nix store and the locked input. * location in the Nix store and the locked input.
*/ */
std::pair<StorePath, Input> fetchToStore(const Settings & settings, ref<Store> store) const; std::pair<StorePath, Input> fetchToStore(const Settings & settings, Store & store) const;
/** /**
* Check the locking attributes in `result` against * Check the locking attributes in `result` against
@@ -133,17 +133,17 @@ public:
* input without copying it to the store. Also return a possibly * input without copying it to the store. Also return a possibly
* unlocked input. * unlocked input.
*/ */
std::pair<ref<SourceAccessor>, Input> getAccessor(const Settings & settings, ref<Store> store) const; std::pair<ref<SourceAccessor>, Input> getAccessor(const Settings & settings, Store & store) const;
private: private:
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, ref<Store> store) const; std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, Store & store) const;
public: public:
Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const; Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const;
void clone(const Settings & settings, const Path & destDir) const; void clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const;
std::optional<std::filesystem::path> getSourcePath() const; std::optional<std::filesystem::path> getSourcePath() const;
@@ -173,7 +173,7 @@ public:
* *
* This is not a stable identifier between Nix versions, but not guaranteed to change either. * This is not a stable identifier between Nix versions, but not guaranteed to change either.
*/ */
std::optional<std::string> getFingerprint(ref<Store> store) const; std::optional<std::string> getFingerprint(Store & store) const;
}; };
/** /**
@@ -203,20 +203,34 @@ struct InputScheme
*/ */
virtual std::string_view schemeName() const = 0; virtual std::string_view schemeName() const = 0;
/**
* Longform description of this scheme, for documentation purposes.
*/
virtual std::string schemeDescription() const = 0;
// TODO remove these defaults
struct AttributeInfo
{
const char * type = "String";
bool required = true;
const char * doc = "";
};
/** /**
* Allowed attributes in an attribute set that is converted to an * Allowed attributes in an attribute set that is converted to an
* input. * input, and documentation for each attribute.
* *
* `type` is not included from this set, because the `type` field is * `type` is not included from this map, because the `type` field is
parsed first to choose which scheme; `type` is always required. parsed first to choose which scheme; `type` is always required.
*/ */
virtual StringSet allowedAttrs() const = 0; virtual const std::map<std::string, AttributeInfo> & allowedAttrs() const = 0;
virtual ParsedURL toURL(const Input & input) const; virtual ParsedURL toURL(const Input & input) const;
virtual Input applyOverrides(const Input & input, std::optional<std::string> ref, std::optional<Hash> rev) const; virtual Input applyOverrides(const Input & input, std::optional<std::string> ref, std::optional<Hash> rev) const;
virtual void clone(const Settings & settings, const Input & input, const Path & destDir) const; virtual void
clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const;
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const; virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
@@ -227,7 +241,7 @@ struct InputScheme
std::optional<std::string> commitMsg) const; std::optional<std::string> commitMsg) const;
virtual std::pair<ref<SourceAccessor>, Input> virtual std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const = 0; getAccessor(const Settings & settings, Store & store, const Input & input) const = 0;
/** /**
* Is this `InputScheme` part of an experimental feature? * Is this `InputScheme` part of an experimental feature?
@@ -239,7 +253,7 @@ struct InputScheme
return true; return true;
} }
virtual std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const virtual std::optional<std::string> getFingerprint(Store & store, const Input & input) const
{ {
return std::nullopt; return std::nullopt;
} }
@@ -263,7 +277,12 @@ struct InputScheme
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher); void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
nlohmann::json dumpRegisterInputSchemeInfo(); using InputSchemeMap = std::map<std::string_view, std::shared_ptr<InputScheme>>;
/**
* Use this for docs, not for finding a specific scheme
*/
const InputSchemeMap & getAllInputSchemes();
struct PublicKey struct PublicKey
{ {
@@ -16,7 +16,7 @@ struct InputCache
}; };
CachedResult CachedResult
getAccessor(const Settings & settings, ref<Store> store, const Input & originalInput, UseRegistries useRegistries); getAccessor(const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries);
struct CachedInput struct CachedInput
{ {
@@ -13,8 +13,6 @@ namespace nix::fetchers {
struct Registry struct Registry
{ {
const Settings & settings;
enum RegistryType { enum RegistryType {
Flag = 0, Flag = 0,
User = 1, User = 1,
@@ -34,9 +32,8 @@ struct Registry
std::vector<Entry> entries; std::vector<Entry> entries;
Registry(const Settings & settings, RegistryType type) Registry(RegistryType type)
: settings{settings} : type{type}
, type{type}
{ {
} }
@@ -57,9 +54,9 @@ std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Pat
Path getUserRegistryPath(); Path getUserRegistryPath();
Registries getRegistries(const Settings & settings, ref<Store> store); Registries getRegistries(const Settings & settings, Store & store);
void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs); void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs);
enum class UseRegistries : int { enum class UseRegistries : int {
No, No,
@@ -72,6 +69,6 @@ enum class UseRegistries : int {
* use the registries for which the filter function returns true. * use the registries for which the filter function returns true.
*/ */
std::pair<Input, Attrs> std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & input, UseRegistries useRegistries); lookupInRegistries(const Settings & settings, Store & store, const Input & input, UseRegistries useRegistries);
} // namespace nix::fetchers } // namespace nix::fetchers
@@ -25,7 +25,7 @@ struct DownloadFileResult
}; };
DownloadFileResult downloadFile( DownloadFileResult downloadFile(
ref<Store> store, Store & store,
const Settings & settings, const Settings & settings,
const std::string & url, const std::string & url,
const std::string & name, const std::string & name,
@@ -43,6 +43,6 @@ struct DownloadTarballResult
* Download and import a tarball into the Git cache. The result is the * Download and import a tarball into the Git cache. The result is the
* Git tree hash of the root directory. * Git tree hash of the root directory.
*/ */
ref<SourceAccessor> downloadTarball(ref<Store> store, const Settings & settings, const std::string & url); ref<SourceAccessor> downloadTarball(Store & store, const Settings & settings, const std::string & url);
} // namespace nix::fetchers } // namespace nix::fetchers
+26 -7
View File
@@ -60,14 +60,33 @@ struct IndirectInputScheme : InputScheme
return "indirect"; return "indirect";
} }
StringSet allowedAttrs() const override std::string schemeDescription() const override
{ {
return { // TODO
"id", return "";
"ref", }
"rev",
"narHash", const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"id",
{},
},
{
"ref",
{},
},
{
"rev",
{},
},
{
"narHash",
{},
},
}; };
return attrs;
} }
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -107,7 +126,7 @@ struct IndirectInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const override getAccessor(const Settings & settings, Store & store, const Input & input) const override
{ {
throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
} }
+1 -1
View File
@@ -6,7 +6,7 @@
namespace nix::fetchers { namespace nix::fetchers {
InputCache::CachedResult InputCache::getAccessor( InputCache::CachedResult InputCache::getAccessor(
const Settings & settings, ref<Store> store, const Input & originalInput, UseRegistries useRegistries) const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries)
{ {
auto fetched = lookup(originalInput); auto fetched = lookup(originalInput);
Input resolvedInput = originalInput; Input resolvedInput = originalInput;
+43 -18
View File
@@ -68,16 +68,41 @@ struct MercurialInputScheme : InputScheme
return "hg"; return "hg";
} }
StringSet allowedAttrs() const override std::string schemeDescription() const override
{ {
return { // TODO
"url", return "";
"ref", }
"rev",
"revCount", const std::map<std::string, AttributeInfo> & allowedAttrs() const override
"narHash", {
"name", static const std::map<std::string, AttributeInfo> attrs = {
{
"url",
{},
},
{
"ref",
{},
},
{
"rev",
{},
},
{
"revCount",
{},
},
{
"narHash",
{},
},
{
"name",
{},
},
}; };
return attrs;
} }
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -154,7 +179,7 @@ struct MercurialInputScheme : InputScheme
return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()}; return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()};
} }
StorePath fetchToStore(const Settings & settings, ref<Store> store, Input & input) const StorePath fetchToStore(const Settings & settings, Store & store, Input & input) const
{ {
auto origRev = input.getRev(); auto origRev = input.getRev();
@@ -205,7 +230,7 @@ struct MercurialInputScheme : InputScheme
return files.count(file); return files.count(file);
}; };
auto storePath = store->addToStore( auto storePath = store.addToStore(
input.getName(), input.getName(),
{getFSSourceAccessor(), CanonPath(actualPath)}, {getFSSourceAccessor(), CanonPath(actualPath)},
ContentAddressMethod::Raw::NixArchive, ContentAddressMethod::Raw::NixArchive,
@@ -226,7 +251,7 @@ struct MercurialInputScheme : InputScheme
"Hash '%s' is not supported by Mercurial. Only sha1 is supported.", "Hash '%s' is not supported by Mercurial. Only sha1 is supported.",
rev.to_string(HashFormat::Base16, true)); rev.to_string(HashFormat::Base16, true));
return Cache::Key{"hgRev", {{"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()}}}; return Cache::Key{"hgRev", {{"store", store.storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()}}};
}; };
auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath { auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath {
@@ -246,7 +271,7 @@ struct MercurialInputScheme : InputScheme
/* If we have a rev, check if we have a cached store path. */ /* If we have a rev, check if we have a cached store path. */
if (auto rev = input.getRev()) { if (auto rev = input.getRev()) {
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), *store)) if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), store))
return makeResult(res->value, res->storePath); return makeResult(res->value, res->storePath);
} }
@@ -300,7 +325,7 @@ struct MercurialInputScheme : InputScheme
/* Now that we have the rev, check the cache again for a /* Now that we have the rev, check the cache again for a
cached store path. */ cached store path. */
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), *store)) if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), store))
return makeResult(res->value, res->storePath); return makeResult(res->value, res->storePath);
Path tmpDir = createTempDir(); Path tmpDir = createTempDir();
@@ -310,7 +335,7 @@ struct MercurialInputScheme : InputScheme
deletePath(tmpDir + "/.hg_archival.txt"); deletePath(tmpDir + "/.hg_archival.txt");
auto storePath = store->addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)}); auto storePath = store.addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)});
Attrs infoAttrs({ Attrs infoAttrs({
{"revCount", (uint64_t) revCount}, {"revCount", (uint64_t) revCount},
@@ -319,18 +344,18 @@ struct MercurialInputScheme : InputScheme
if (!origRev) if (!origRev)
settings.getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}}); settings.getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}});
settings.getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath); settings.getCache()->upsert(revInfoKey(rev), store, infoAttrs, storePath);
return makeResult(infoAttrs, std::move(storePath)); return makeResult(infoAttrs, std::move(storePath));
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
Input input(_input); Input input(_input);
auto storePath = fetchToStore(settings, store, input); auto storePath = fetchToStore(settings, store, input);
auto accessor = store->requireStoreObjectAccessor(storePath); auto accessor = store.requireStoreObjectAccessor(storePath);
accessor->setPathDisplay("«" + input.to_string() + "»"); accessor->setPathDisplay("«" + input.to_string() + "»");
@@ -342,7 +367,7 @@ struct MercurialInputScheme : InputScheme
return (bool) input.getRev(); return (bool) input.getRev();
} }
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
{ {
if (auto rev = input.getRev()) if (auto rev = input.getRev())
return rev->gitRev(); return rev->gitRev();
+38 -16
View File
@@ -40,20 +40,42 @@ struct PathInputScheme : InputScheme
return "path"; return "path";
} }
StringSet allowedAttrs() const override std::string schemeDescription() const override
{ {
return { // TODO
"path", return "";
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"path",
{},
},
/* Allow the user to pass in "fake" tree info /* Allow the user to pass in "fake" tree info
attributes. This is useful for making a pinned tree work attributes. This is useful for making a pinned tree work
the same as the repository from which is exported (e.g. the same as the repository from which is exported (e.g.
path:/nix/store/...-source?lastModified=1585388205&rev=b0c285...). path:/nix/store/...-source?lastModified=1585388205&rev=b0c285...).
*/ */
"rev", {
"revCount", "rev",
"lastModified", {},
"narHash", },
{
"revCount",
{},
},
{
"lastModified",
{},
},
{
"narHash",
{},
},
}; };
return attrs;
} }
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -117,7 +139,7 @@ struct PathInputScheme : InputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
Input input(_input); Input input(_input);
auto path = getStrAttr(input.attrs, "path"); auto path = getStrAttr(input.attrs, "path");
@@ -125,31 +147,31 @@ struct PathInputScheme : InputScheme
auto absPath = getAbsPath(input); auto absPath = getAbsPath(input);
// FIXME: check whether access to 'path' is allowed. // FIXME: check whether access to 'path' is allowed.
auto storePath = store->maybeParseStorePath(absPath.string()); auto storePath = store.maybeParseStorePath(absPath.string());
if (storePath) if (storePath)
store->addTempRoot(*storePath); store.addTempRoot(*storePath);
time_t mtime = 0; time_t mtime = 0;
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) { if (!storePath || storePath->name() != "source" || !store.isValidPath(*storePath)) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying %s to the store", absPath)); Activity act(*logger, lvlTalkative, actUnknown, fmt("copying %s to the store", absPath));
// FIXME: try to substitute storePath. // FIXME: try to substitute storePath.
auto src = sinkToSource( auto src = sinkToSource(
[&](Sink & sink) { mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter); }); [&](Sink & sink) { mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter); });
storePath = store->addToStoreFromDump(*src, "source"); storePath = store.addToStoreFromDump(*src, "source");
} }
auto accessor = store->requireStoreObjectAccessor(*storePath); auto accessor = store.requireStoreObjectAccessor(*storePath);
// To prevent `fetchToStore()` copying the path again to Nix // To prevent `fetchToStore()` copying the path again to Nix
// store, pre-create an entry in the fetcher cache. // store, pre-create an entry in the fetcher cache.
auto info = store->queryPathInfo(*storePath); auto info = store.queryPathInfo(*storePath);
accessor->fingerprint = accessor->fingerprint =
fmt("path:%s", store->queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true)); fmt("path:%s", store.queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true));
settings.getCache()->upsert( settings.getCache()->upsert(
makeFetchToStoreCacheKey( makeFetchToStoreCacheKey(
input.getName(), *accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"), input.getName(), *accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"),
*store, store,
{}, {},
*storePath); *storePath);
+13 -13
View File
@@ -14,10 +14,10 @@ std::shared_ptr<Registry> Registry::read(const Settings & settings, const Source
{ {
debug("reading registry '%s'", path); debug("reading registry '%s'", path);
auto registry = std::make_shared<Registry>(settings, type); auto registry = std::make_shared<Registry>(type);
if (!path.pathExists()) if (!path.pathExists())
return std::make_shared<Registry>(settings, type); return std::make_shared<Registry>(type);
try { try {
@@ -125,23 +125,23 @@ std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Pat
return customRegistry; return customRegistry;
} }
std::shared_ptr<Registry> getFlagRegistry(const Settings & settings) std::shared_ptr<Registry> getFlagRegistry()
{ {
static auto flagRegistry = std::make_shared<Registry>(settings, Registry::Flag); static auto flagRegistry = std::make_shared<Registry>(Registry::Flag);
return flagRegistry; return flagRegistry;
} }
void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs) void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs)
{ {
getFlagRegistry(settings)->add(from, to, extraAttrs); getFlagRegistry()->add(from, to, extraAttrs);
} }
static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, ref<Store> store) static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, Store & store)
{ {
static auto reg = [&]() { static auto reg = [&]() {
auto path = settings.flakeRegistry.get(); auto path = settings.flakeRegistry.get();
if (path == "") { if (path == "") {
return std::make_shared<Registry>(settings, Registry::Global); // empty registry return std::make_shared<Registry>(Registry::Global); // empty registry
} }
return Registry::read( return Registry::read(
@@ -149,9 +149,9 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, re
[&] -> SourcePath { [&] -> SourcePath {
if (!isAbsolute(path)) { if (!isAbsolute(path)) {
auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath; auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) if (auto store2 = dynamic_cast<LocalFSStore *>(&store))
store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json"); store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
return {store->requireStoreObjectAccessor(storePath)}; return {store.requireStoreObjectAccessor(storePath)};
} else { } else {
return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks(); return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks();
} }
@@ -162,10 +162,10 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, re
return reg; return reg;
} }
Registries getRegistries(const Settings & settings, ref<Store> store) Registries getRegistries(const Settings & settings, Store & store)
{ {
Registries registries; Registries registries;
registries.push_back(getFlagRegistry(settings)); registries.push_back(getFlagRegistry());
registries.push_back(getUserRegistry(settings)); registries.push_back(getUserRegistry(settings));
registries.push_back(getSystemRegistry(settings)); registries.push_back(getSystemRegistry(settings));
registries.push_back(getGlobalRegistry(settings, store)); registries.push_back(getGlobalRegistry(settings, store));
@@ -173,7 +173,7 @@ Registries getRegistries(const Settings & settings, ref<Store> store)
} }
std::pair<Input, Attrs> std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & _input, UseRegistries useRegistries) lookupInRegistries(const Settings & settings, Store & store, const Input & _input, UseRegistries useRegistries)
{ {
Attrs extraAttrs; Attrs extraAttrs;
int n = 0; int n = 0;
+123 -22
View File
@@ -13,7 +13,7 @@
namespace nix::fetchers { namespace nix::fetchers {
DownloadFileResult downloadFile( DownloadFileResult downloadFile(
ref<Store> store, Store & store,
const Settings & settings, const Settings & settings,
const std::string & url, const std::string & url,
const std::string & name, const std::string & name,
@@ -28,7 +28,7 @@ DownloadFileResult downloadFile(
{"name", name}, {"name", name},
}}}; }}};
auto cached = settings.getCache()->lookupStorePath(key, *store); auto cached = settings.getCache()->lookupStorePath(key, store);
auto useCached = [&]() -> DownloadFileResult { auto useCached = [&]() -> DownloadFileResult {
return { return {
@@ -74,7 +74,7 @@ DownloadFileResult downloadFile(
dumpString(res.data, sink); dumpString(res.data, sink);
auto hash = hashString(HashAlgorithm::SHA256, res.data); auto hash = hashString(HashAlgorithm::SHA256, res.data);
auto info = ValidPathInfo::makeFromCA( auto info = ValidPathInfo::makeFromCA(
*store, store,
name, name,
FixedOutputInfo{ FixedOutputInfo{
.method = FileIngestionMethod::Flat, .method = FileIngestionMethod::Flat,
@@ -84,7 +84,7 @@ DownloadFileResult downloadFile(
hashString(HashAlgorithm::SHA256, sink.s)); hashString(HashAlgorithm::SHA256, sink.s));
info.narSize = sink.s.size(); info.narSize = sink.s.size();
auto source = StringSource{sink.s}; auto source = StringSource{sink.s};
store->addToStore(info, source, NoRepair, NoCheckSigs); store.addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path); storePath = std::move(info.path);
} }
@@ -93,7 +93,7 @@ DownloadFileResult downloadFile(
key.second.insert_or_assign("url", url); key.second.insert_or_assign("url", url);
assert(!res.urls.empty()); assert(!res.urls.empty());
infoAttrs.insert_or_assign("url", *res.urls.rbegin()); infoAttrs.insert_or_assign("url", *res.urls.rbegin());
settings.getCache()->upsert(key, *store, infoAttrs, *storePath); settings.getCache()->upsert(key, store, infoAttrs, *storePath);
} }
return { return {
@@ -214,7 +214,7 @@ static DownloadTarballResult downloadTarball_(
return attrsToResult(infoAttrs); return attrsToResult(infoAttrs);
} }
ref<SourceAccessor> downloadTarball(ref<Store> store, const Settings & settings, const std::string & url) ref<SourceAccessor> downloadTarball(Store & store, const Settings & settings, const std::string & url)
{ {
/* Go through Input::getAccessor() to ensure that the resulting /* Go through Input::getAccessor() to ensure that the resulting
accessor has a fingerprint. */ accessor has a fingerprint. */
@@ -278,7 +278,7 @@ struct CurlInputScheme : InputScheme
HTTP request. Now that we've processed the Nix-specific HTTP request. Now that we've processed the Nix-specific
attributes above, remove them so we don't also send them as attributes above, remove them so we don't also send them as
part of the HTTP request. */ part of the HTTP request. */
for (auto & param : allowedAttrs()) for (auto & [param, _] : allowedAttrs())
url.query.erase(param); url.query.erase(param);
input.attrs.insert_or_assign("type", std::string{schemeName()}); input.attrs.insert_or_assign("type", std::string{schemeName()});
@@ -286,18 +286,83 @@ struct CurlInputScheme : InputScheme
return input; return input;
} }
StringSet allowedAttrs() const override static const std::map<std::string, AttributeInfo> & allowedAttrsImpl()
{ {
return { static const std::map<std::string, AttributeInfo> attrs = {
"type", {
"url", "url",
"narHash", {
"name", .type = "String",
"unpack", .required = true,
"rev", .doc = R"(
"revCount", Supported protocols:
"lastModified",
- `https`
> **Example**
>
> ```nix
> fetchTree {
> type = "file";
> url = "https://example.com/index.html";
> }
> ```
- `http`
Insecure HTTP transfer for legacy sources.
> **Warning**
>
> HTTP performs no encryption or authentication.
> Use a `narHash` known in advance to ensure the output has expected contents.
- `file`
A file on the local file system.
> **Example**
>
> ```nix
> fetchTree {
> type = "file";
> url = "file:///home/eelco/nix/README.md";
> }
> ```
)",
},
},
{
"narHash",
{},
},
{
"name",
{},
},
{
"unpack",
{},
},
{
"rev",
{},
},
{
"revCount",
{},
},
{
"lastModified",
{},
},
}; };
return attrs;
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
return allowedAttrsImpl();
} }
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -332,6 +397,14 @@ struct FileInputScheme : CurlInputScheme
return "file"; return "file";
} }
std::string schemeDescription() const override
{
return stripIndentation(R"(
Place a plain file into the Nix store.
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
)");
}
bool isValidURL(const ParsedURL & url, bool requireTree) const override bool isValidURL(const ParsedURL & url, bool requireTree) const override
{ {
auto parsedUrlScheme = parseUrlScheme(url.scheme); auto parsedUrlScheme = parseUrlScheme(url.scheme);
@@ -341,7 +414,7 @@ struct FileInputScheme : CurlInputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
auto input(_input); auto input(_input);
@@ -351,10 +424,10 @@ struct FileInputScheme : CurlInputScheme
tarballs. */ tarballs. */
auto file = downloadFile(store, settings, getStrAttr(input.attrs, "url"), input.getName()); auto file = downloadFile(store, settings, getStrAttr(input.attrs, "url"), input.getName());
auto narHash = store->queryPathInfo(file.storePath)->narHash; auto narHash = store.queryPathInfo(file.storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
auto accessor = ref{store->getFSAccessor(file.storePath)}; auto accessor = ref{store.getFSAccessor(file.storePath)};
accessor->setPathDisplay("«" + input.to_string() + "»"); accessor->setPathDisplay("«" + input.to_string() + "»");
@@ -369,6 +442,34 @@ struct TarballInputScheme : CurlInputScheme
return "tarball"; return "tarball";
} }
std::string schemeDescription() const override
{
return stripIndentation(R"(
Download a tar archive and extract it into the Nix store.
This has the same underlying implementation as [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball)
)");
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = [] {
auto attrs = CurlInputScheme::allowedAttrsImpl();
// Override the "url" attribute to add tarball-specific example
attrs["url"].doc = R"(
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
)";
return attrs;
}();
return attrs;
}
bool isValidURL(const ParsedURL & url, bool requireTree) const override bool isValidURL(const ParsedURL & url, bool requireTree) const override
{ {
auto parsedUrlScheme = parseUrlScheme(url.scheme); auto parsedUrlScheme = parseUrlScheme(url.scheme);
@@ -379,7 +480,7 @@ struct TarballInputScheme : CurlInputScheme
} }
std::pair<ref<SourceAccessor>, Input> std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override getAccessor(const Settings & settings, Store & store, const Input & _input) const override
{ {
auto input(_input); auto input(_input);
@@ -404,7 +505,7 @@ struct TarballInputScheme : CurlInputScheme
return {result.accessor, input}; return {result.accessor, input};
} }
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
{ {
if (auto narHash = input.getNarHash()) if (auto narHash = input.getNarHash())
return narHash->to_string(HashFormat::SRI, true); return narHash->to_string(HashFormat::SRI, true);
+6 -7
View File
@@ -19,7 +19,6 @@
#include "nix/expr/eval-settings.hh" #include "nix/expr/eval-settings.hh"
#include "nix/expr/symbol-table.hh" #include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/fetchers/attrs.hh" #include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh" #include "nix/fetchers/fetchers.hh"
#include "nix/util/configuration.hh" #include "nix/util/configuration.hh"
@@ -63,7 +62,7 @@ PrimOp getFlake(const Settings & settings)
return PrimOp{ return PrimOp{
.name = "__getFlake", .name = "__getFlake",
.args = {"args"}, .args = {"args"},
.doc = &R"( .doc = R"(
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
```nix ```nix
@@ -77,7 +76,7 @@ PrimOp getFlake(const Settings & settings)
```nix ```nix
(builtins.getFlake "github:edolstra/dwarffs").rev (builtins.getFlake "github:edolstra/dwarffs").rev
``` ```
)"_sds, )",
.fun = prim_getFlake, .fun = prim_getFlake,
.experimentalFeature = Xp::Flakes, .experimentalFeature = Xp::Flakes,
}; };
@@ -105,7 +104,7 @@ static void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value ** arg
nix::PrimOp parseFlakeRef({ nix::PrimOp parseFlakeRef({
.name = "__parseFlakeRef", .name = "__parseFlakeRef",
.args = {"flake-ref"}, .args = {"flake-ref"},
.doc = &R"( .doc = R"(
Parse a flake reference, and return its exploded form. Parse a flake reference, and return its exploded form.
For example: For example:
@@ -119,7 +118,7 @@ nix::PrimOp parseFlakeRef({
```nix ```nix
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
``` ```
)"_sds, )",
.fun = prim_parseFlakeRef, .fun = prim_parseFlakeRef,
.experimentalFeature = Xp::Flakes, .experimentalFeature = Xp::Flakes,
}); });
@@ -163,7 +162,7 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value **
nix::PrimOp flakeRefToString({ nix::PrimOp flakeRefToString({
.name = "__flakeRefToString", .name = "__flakeRefToString",
.args = {"attrs"}, .args = {"attrs"},
.doc = &R"( .doc = R"(
Convert a flake reference from attribute set format to URL format. Convert a flake reference from attribute set format to URL format.
For example: For example:
@@ -179,7 +178,7 @@ nix::PrimOp flakeRefToString({
```nix ```nix
"github:NixOS/nixpkgs/23.05?dir=lib" "github:NixOS/nixpkgs/23.05?dir=lib"
``` ```
)"_sds, )",
.fun = prim_flakeRefToString, .fun = prim_flakeRefToString,
.experimentalFeature = Xp::Flakes, .experimentalFeature = Xp::Flakes,
}); });
+5 -5
View File
@@ -373,7 +373,7 @@ static Flake getFlake(
{ {
// Fetch a lazy tree first. // Fetch a lazy tree first.
auto cachedInput = auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, state.store, originalRef.input, useRegistries); state.inputCache->getAccessor(state.fetchSettings, *state.store, originalRef.input, useRegistries);
auto subdir = fetchers::maybeGetStrAttr(cachedInput.extraAttrs, "dir").value_or(originalRef.subdir); auto subdir = fetchers::maybeGetStrAttr(cachedInput.extraAttrs, "dir").value_or(originalRef.subdir);
auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), subdir); auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), subdir);
@@ -390,7 +390,7 @@ static Flake getFlake(
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'. // FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
newLockedRef.input.attrs.erase("narHash"); newLockedRef.input.attrs.erase("narHash");
auto cachedInput2 = state.inputCache->getAccessor( auto cachedInput2 = state.inputCache->getAccessor(
state.fetchSettings, state.store, newLockedRef.input, fetchers::UseRegistries::No); state.fetchSettings, *state.store, newLockedRef.input, fetchers::UseRegistries::No);
cachedInput.accessor = cachedInput2.accessor; cachedInput.accessor = cachedInput2.accessor;
lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir); lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir);
} }
@@ -756,7 +756,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
return {*resolvedPath, *input.ref}; return {*resolvedPath, *input.ref};
} else { } else {
auto cachedInput = state.inputCache->getAccessor( auto cachedInput = state.inputCache->getAccessor(
state.fetchSettings, state.store, input.ref->input, useRegistriesInputs); state.fetchSettings, *state.store, input.ref->input, useRegistriesInputs);
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir); auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);
@@ -975,7 +975,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes)
state.callFunction(*vCallFlake, args, vRes, noPos); state.callFunction(*vCallFlake, args, vRes, noPos);
} }
std::optional<Fingerprint> LockedFlake::getFingerprint(ref<Store> store, const fetchers::Settings & fetchSettings) const std::optional<Fingerprint> LockedFlake::getFingerprint(Store & store, const fetchers::Settings & fetchSettings) const
{ {
if (lockFile.isUnlocked(fetchSettings)) if (lockFile.isUnlocked(fetchSettings))
return std::nullopt; return std::nullopt;
@@ -1005,7 +1005,7 @@ Flake::~Flake() {}
ref<eval_cache::EvalCache> openEvalCache(EvalState & state, ref<const LockedFlake> lockedFlake) ref<eval_cache::EvalCache> openEvalCache(EvalState & state, ref<const LockedFlake> lockedFlake)
{ {
auto fingerprint = state.settings.useEvalCache && state.settings.pureEval auto fingerprint = state.settings.useEvalCache && state.settings.pureEval
? lockedFlake->getFingerprint(state.store, state.fetchSettings) ? lockedFlake->getFingerprint(*state.store, state.fetchSettings)
: std::nullopt; : std::nullopt;
auto rootLoader = [&state, lockedFlake]() { auto rootLoader = [&state, lockedFlake]() {
/* For testing whether the evaluation cache is /* For testing whether the evaluation cache is
+3 -3
View File
@@ -64,8 +64,8 @@ std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef)
return str; return str;
} }
FlakeRef FlakeRef::resolve( FlakeRef
const fetchers::Settings & fetchSettings, ref<Store> store, fetchers::UseRegistries useRegistries) const FlakeRef::resolve(const fetchers::Settings & fetchSettings, Store & store, fetchers::UseRegistries useRegistries) const
{ {
auto [input2, extraAttrs] = lookupInRegistries(fetchSettings, store, input, useRegistries); auto [input2, extraAttrs] = lookupInRegistries(fetchSettings, store, input, useRegistries);
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
@@ -289,7 +289,7 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Settings & fetchSettings, const fet
} }
std::pair<ref<SourceAccessor>, FlakeRef> std::pair<ref<SourceAccessor>, FlakeRef>
FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, Store & store) const
{ {
auto [accessor, lockedInput] = input.getAccessor(fetchSettings, store); auto [accessor, lockedInput] = input.getAccessor(fetchSettings, store);
return {accessor, FlakeRef(std::move(lockedInput), subdir)}; return {accessor, FlakeRef(std::move(lockedInput), subdir)};
+1 -1
View File
@@ -135,7 +135,7 @@ struct LockedFlake
*/ */
std::map<ref<Node>, SourcePath> nodePaths; std::map<ref<Node>, SourcePath> nodePaths;
std::optional<Fingerprint> getFingerprint(ref<Store> store, const fetchers::Settings & fetchSettings) const; std::optional<Fingerprint> getFingerprint(Store & store, const fetchers::Settings & fetchSettings) const;
}; };
struct LockFlags struct LockFlags
+2 -3
View File
@@ -73,13 +73,12 @@ struct FlakeRef
FlakeRef resolve( FlakeRef resolve(
const fetchers::Settings & fetchSettings, const fetchers::Settings & fetchSettings,
ref<Store> store, Store & store,
fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const; fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const;
static FlakeRef fromAttrs(const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs); static FlakeRef fromAttrs(const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs);
std::pair<ref<SourceAccessor>, FlakeRef> std::pair<ref<SourceAccessor>, FlakeRef> lazyFetch(const fetchers::Settings & fetchSettings, Store & store) const;
lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const;
/** /**
* Canonicalize a flakeref for the purpose of comparing "old" and * Canonicalize a flakeref for the purpose of comparing "old" and
+2
View File
@@ -35,6 +35,8 @@ include_dirs = [ include_directories('.') ]
headers = files( headers = files(
'nix_api_store.h', 'nix_api_store.h',
'nix_api_store/derivation.h',
'nix_api_store/store_path.h',
) )
# TODO don't install this once tests don't use it and/or move the header into `libstore`, non-`c` # TODO don't install this once tests don't use it and/or move the header into `libstore`, non-`c`
+2 -36
View File
@@ -12,6 +12,8 @@
*/ */
#include "nix_api_util.h" #include "nix_api_util.h"
#include "nix_api_store/store_path.h"
#include "nix_api_store/derivation.h"
#include <stdbool.h> #include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
@@ -21,10 +23,6 @@ extern "C" {
/** @brief Reference to a Nix store */ /** @brief Reference to a Nix store */
typedef struct Store Store; typedef struct Store Store;
/** @brief Nix store path */
typedef struct StorePath StorePath;
/** @brief Nix Derivation */
typedef struct nix_derivation nix_derivation;
/** /**
* @brief Initializes the Nix store library * @brief Initializes the Nix store library
@@ -118,30 +116,6 @@ nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_ca
*/ */
StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path); StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path);
/**
* @brief Get the path name (e.g. "name" in /nix/store/...-name)
*
* @param[in] store_path the path to get the name from
* @param[in] callback called with the name
* @param[in] user_data arbitrary data, passed to the callback when it's called.
*/
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data);
/**
* @brief Copy a StorePath
*
* @param[in] p the path to copy
* @return a new StorePath
*/
StorePath * nix_store_path_clone(const StorePath * p);
/** @brief Deallocate a StorePath
*
* Does not fail.
* @param[in] p the path to free
*/
void nix_store_path_free(StorePath * p);
/** /**
* @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in * @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in
* the store) * the store)
@@ -229,14 +203,6 @@ nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store
*/ */
StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_derivation * derivation); StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_derivation * derivation);
/**
* @brief Deallocate a `nix_derivation`
*
* Does not fail.
* @param[in] drv the derivation to free
*/
void nix_derivation_free(nix_derivation * drv);
/** /**
* @brief Copy the closure of `path` from `srcStore` to `dstStore`. * @brief Copy the closure of `path` from `srcStore` to `dstStore`.
* *
+38
View File
@@ -0,0 +1,38 @@
#ifndef NIX_API_STORE_DERIVATION_H
#define NIX_API_STORE_DERIVATION_H
/**
* @defgroup libstore_derivation Derivation
* @ingroup libstore
* @brief Derivation operations that don't require a Store
* @{
*/
/** @file
* @brief Derivation operations
*/
#include "nix_api_util.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
/** @brief Nix Derivation */
typedef struct nix_derivation nix_derivation;
/**
* @brief Deallocate a `nix_derivation`
*
* Does not fail.
* @param[in] drv the derivation to free
*/
void nix_derivation_free(nix_derivation * drv);
// cffi end
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif // NIX_API_STORE_DERIVATION_H
+54
View File
@@ -0,0 +1,54 @@
#ifndef NIX_API_STORE_STORE_PATH_H
#define NIX_API_STORE_STORE_PATH_H
/**
* @defgroup libstore_storepath StorePath
* @ingroup libstore
* @brief Store path operations that don't require a Store
* @{
*/
/** @file
* @brief Store path operations
*/
#include "nix_api_util.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
/** @brief Nix store path */
typedef struct StorePath StorePath;
/**
* @brief Copy a StorePath
*
* @param[in] p the path to copy
* @return a new StorePath
*/
StorePath * nix_store_path_clone(const StorePath * p);
/** @brief Deallocate a StorePath
*
* Does not fail.
* @param[in] p the path to free
*/
void nix_store_path_free(StorePath * p);
/**
* @brief Get the path name (e.g. "<name>" in /nix/store/<hash>-<name>)
*
* @param[in] store_path the path to get the name from
* @param[in] callback called with the name
* @param[in] user_data arbitrary data, passed to the callback when it's called.
*/
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data);
// cffi end
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif // NIX_API_STORE_STORE_PATH_H
@@ -4,10 +4,13 @@
"allowSubstitutes": false, "allowSubstitutes": false,
"exportReferencesGraph": { "exportReferencesGraph": {
"refs1": [ "refs1": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" {
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
], ],
"refs2": [ "refs2": [
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
] ]
}, },
"impureEnvVars": [ "impureEnvVars": [
@@ -20,18 +23,36 @@
"outputChecks": { "outputChecks": {
"forAllOutputs": { "forAllOutputs": {
"allowedReferences": [ "allowedReferences": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" {
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
], ],
"allowedRequisites": [ "allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", {
"bin" "drvPath": "self",
"output": "bin"
},
{
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "dev"
}
], ],
"disallowedReferences": [ "disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", {
"dev" "drvPath": "self",
"output": "dev"
},
{
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "out"
}
], ],
"disallowedRequisites": [ "disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8" {
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "dev"
}
], ],
"ignoreSelfRefs": true, "ignoreSelfRefs": true,
"maxClosureSize": null, "maxClosureSize": null,
@@ -4,10 +4,13 @@
"allowSubstitutes": false, "allowSubstitutes": false,
"exportReferencesGraph": { "exportReferencesGraph": {
"refs1": [ "refs1": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" {
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
], ],
"refs2": [ "refs2": [
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
] ]
}, },
"impureEnvVars": [ "impureEnvVars": [
@@ -23,11 +26,20 @@
"allowedReferences": null, "allowedReferences": null,
"allowedRequisites": null, "allowedRequisites": null,
"disallowedReferences": [ "disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", {
"dev" "drvPath": "self",
"output": "dev"
},
{
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "out"
}
], ],
"disallowedRequisites": [ "disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8" {
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "dev"
}
], ],
"ignoreSelfRefs": false, "ignoreSelfRefs": false,
"maxClosureSize": null, "maxClosureSize": null,
@@ -44,11 +56,20 @@
}, },
"out": { "out": {
"allowedReferences": [ "allowedReferences": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" {
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
], ],
"allowedRequisites": [ "allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", {
"bin" "drvPath": "self",
"output": "bin"
},
{
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "dev"
}
], ],
"disallowedReferences": [], "disallowedReferences": [],
"disallowedRequisites": [], "disallowedRequisites": [],
@@ -4,10 +4,10 @@
"allowSubstitutes": false, "allowSubstitutes": false,
"exportReferencesGraph": { "exportReferencesGraph": {
"refs1": [ "refs1": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
], ],
"refs2": [ "refs2": [
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
] ]
}, },
"impureEnvVars": [ "impureEnvVars": [
@@ -20,18 +20,24 @@
"outputChecks": { "outputChecks": {
"forAllOutputs": { "forAllOutputs": {
"allowedReferences": [ "allowedReferences": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
], ],
"allowedRequisites": [ "allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", {
"bin" "drvPath": "self",
"output": "bin"
},
"z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
], ],
"disallowedReferences": [ "disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", {
"dev" "drvPath": "self",
"output": "dev"
},
"r5cff30838majxk5mp3ip2diffi8vpaj-bar"
], ],
"disallowedRequisites": [ "disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" "9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
], ],
"ignoreSelfRefs": true, "ignoreSelfRefs": true,
"maxClosureSize": null, "maxClosureSize": null,
@@ -4,10 +4,10 @@
"allowSubstitutes": false, "allowSubstitutes": false,
"exportReferencesGraph": { "exportReferencesGraph": {
"refs1": [ "refs1": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
], ],
"refs2": [ "refs2": [
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
] ]
}, },
"impureEnvVars": [ "impureEnvVars": [
@@ -23,11 +23,14 @@
"allowedReferences": null, "allowedReferences": null,
"allowedRequisites": null, "allowedRequisites": null,
"disallowedReferences": [ "disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", {
"dev" "drvPath": "self",
"output": "dev"
},
"r5cff30838majxk5mp3ip2diffi8vpaj-bar"
], ],
"disallowedRequisites": [ "disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" "9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
], ],
"ignoreSelfRefs": false, "ignoreSelfRefs": false,
"maxClosureSize": null, "maxClosureSize": null,
@@ -44,11 +47,14 @@
}, },
"out": { "out": {
"allowedReferences": [ "allowedReferences": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
], ],
"allowedRequisites": [ "allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", {
"bin" "drvPath": "self",
"output": "bin"
},
"z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
], ],
"disallowedReferences": [], "disallowedReferences": [],
"disallowedRequisites": [], "disallowedRequisites": [],
+96 -69
View File
@@ -3,7 +3,7 @@
#include "nix/util/experimental-features.hh" #include "nix/util/experimental-features.hh"
#include "nix/store/derivations.hh" #include "nix/store/derivations.hh"
#include "nix/store/derivations.hh" #include "nix/store/derived-path.hh"
#include "nix/store/derivation-options.hh" #include "nix/store/derivation-options.hh"
#include "nix/store/parsed-derivations.hh" #include "nix/store/parsed-derivations.hh"
#include "nix/util/types.hh" #include "nix/util/types.hh"
@@ -17,7 +17,7 @@ namespace nix {
using namespace nlohmann; using namespace nlohmann;
class DerivationAdvancedAttrsTest : public JsonCharacterizationTest<Derivation>, class DerivationAdvancedAttrsTest : public JsonCharacterizationTest<Derivation>,
public JsonCharacterizationTest<DerivationOptions>, public JsonCharacterizationTest<DerivationOptions<SingleDerivedPath>>,
public LibStoreTest public LibStoreTest
{ {
protected: protected:
@@ -42,7 +42,8 @@ public:
{ {
this->readTest(fileName, [&](auto encoded) { this->readTest(fileName, [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedFeatures); EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedFeatures);
}); });
} }
@@ -51,11 +52,14 @@ public:
* Helper function to test DerivationOptions parsing and comparison * Helper function to test DerivationOptions parsing and comparison
*/ */
void testDerivationOptions( void testDerivationOptions(
const std::string & fileName, const DerivationOptions & expected, const StringSet & expectedSystemFeatures) const std::string & fileName,
const DerivationOptions<SingleDerivedPath> & expected,
const StringSet & expectedSystemFeatures)
{ {
this->readTest(fileName, [&](auto encoded) { this->readTest(fileName, [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_EQ(options, expected); EXPECT_EQ(options, expected);
EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedSystemFeatures); EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedSystemFeatures);
@@ -131,22 +135,38 @@ TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attribute
* Since these are both repeated and sensative opaque values, it makes * Since these are both repeated and sensative opaque values, it makes
* sense to give them names in this file. * sense to give them names in this file.
*/ */
static std::string pathFoo = "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo", static SingleDerivedPath
pathFooDev = "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", pathFoo = SingleDerivedPath::Opaque{StorePath{"p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}},
pathBar = "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", pathFooDev = SingleDerivedPath::Opaque{StorePath{"z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"}},
pathBarDev = "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev", pathBar = SingleDerivedPath::Opaque{StorePath{"r5cff30838majxk5mp3ip2diffi8vpaj-bar"}},
pathBarDrvIA = "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv", pathBarDev = SingleDerivedPath::Opaque{StorePath{"9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"}},
pathBarDrvCA = "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", pathBarDrvIA = SingleDerivedPath::Opaque{StorePath{"vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}},
placeholderFoo = "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9", pathBarDrvCA = SingleDerivedPath::Opaque{StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}},
placeholderFooDev = "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", placeholderFoo =
placeholderBar = "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", SingleDerivedPath::Built{
placeholderBarDev = "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"; .drvPath = makeConstantStorePathRef(StorePath{"j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv"}),
.output = "out",
},
placeholderFooDev =
SingleDerivedPath::Built{
.drvPath = makeConstantStorePathRef(StorePath{"j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv"}),
.output = "dev",
},
placeholderBar =
SingleDerivedPath::Built{
.drvPath = makeConstantStorePathRef(StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}),
.output = "out",
},
placeholderBarDev = SingleDerivedPath::Built{
.drvPath = makeConstantStorePathRef(StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}),
.output = "dev",
};
using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph); using ExportReferencesMap = decltype(DerivationOptions<SingleDerivedPath>::exportReferencesGraph);
static const DerivationOptions advancedAttributes_defaults = { static const DerivationOptions<SingleDerivedPath> advancedAttributes_defaults = {
.outputChecks = .outputChecks =
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.ignoreSelfRefs = true, .ignoreSelfRefs = true,
}, },
.unsafeDiscardReferences = {}, .unsafeDiscardReferences = {},
@@ -167,7 +187,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_TRUE(!got.structuredAttrs); EXPECT_TRUE(!got.structuredAttrs);
@@ -192,9 +213,9 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults)
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
{ {
DerivationOptions expected = { DerivationOptions<SingleDerivedPath> expected = {
.outputChecks = .outputChecks =
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.ignoreSelfRefs = true, .ignoreSelfRefs = true,
}, },
.unsafeDiscardReferences = {}, .unsafeDiscardReferences = {},
@@ -212,12 +233,13 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
this->readTest("advanced-attributes.drv", [&](auto encoded) { this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_TRUE(!got.structuredAttrs); EXPECT_TRUE(!got.structuredAttrs);
// Reset fields that vary between test cases to enable whole-object comparison // Reset fields that vary between test cases to enable whole-object comparison
options.outputChecks = DerivationOptions::OutputChecks{.ignoreSelfRefs = true}; options.outputChecks = DerivationOptions<SingleDerivedPath>::OutputChecks{.ignoreSelfRefs = true};
options.exportReferencesGraph = {}; options.exportReferencesGraph = {};
EXPECT_EQ(options, expected); EXPECT_EQ(options, expected);
@@ -227,14 +249,14 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
}); });
}; };
DerivationOptions advancedAttributes_ia = { DerivationOptions<SingleDerivedPath> advancedAttributes_ia = {
.outputChecks = .outputChecks =
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.ignoreSelfRefs = true, .ignoreSelfRefs = true,
.allowedReferences = StringSet{pathFoo}, .allowedReferences = std::set<DrvRef<SingleDerivedPath>>{pathFoo},
.disallowedReferences = StringSet{pathBar, "dev"}, .disallowedReferences = std::set<DrvRef<SingleDerivedPath>>{pathBar, OutputName{"dev"}},
.allowedRequisites = StringSet{pathFooDev, "bin"}, .allowedRequisites = std::set<DrvRef<SingleDerivedPath>>{pathFooDev, OutputName{"bin"}},
.disallowedRequisites = StringSet{pathBarDev}, .disallowedRequisites = std::set<DrvRef<SingleDerivedPath>>{pathBarDev},
}, },
.unsafeDiscardReferences = {}, .unsafeDiscardReferences = {},
.passAsFile = {}, .passAsFile = {},
@@ -257,14 +279,14 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_ia)
testDerivationOptions("advanced-attributes.drv", advancedAttributes_ia, {"rainbow", "uid-range"}); testDerivationOptions("advanced-attributes.drv", advancedAttributes_ia, {"rainbow", "uid-range"});
}; };
DerivationOptions advancedAttributes_ca = { DerivationOptions<SingleDerivedPath> advancedAttributes_ca = {
.outputChecks = .outputChecks =
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.ignoreSelfRefs = true, .ignoreSelfRefs = true,
.allowedReferences = StringSet{placeholderFoo}, .allowedReferences = std::set<DrvRef<SingleDerivedPath>>{placeholderFoo},
.disallowedReferences = StringSet{placeholderBar, "dev"}, .disallowedReferences = std::set<DrvRef<SingleDerivedPath>>{placeholderBar, OutputName{"dev"}},
.allowedRequisites = StringSet{placeholderFooDev, "bin"}, .allowedRequisites = std::set<DrvRef<SingleDerivedPath>>{placeholderFooDev, OutputName{"bin"}},
.disallowedRequisites = StringSet{placeholderBarDev}, .disallowedRequisites = std::set<DrvRef<SingleDerivedPath>>{placeholderBarDev},
}, },
.unsafeDiscardReferences = {}, .unsafeDiscardReferences = {},
.passAsFile = {}, .passAsFile = {},
@@ -287,8 +309,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes)
testDerivationOptions("advanced-attributes.drv", advancedAttributes_ca, {"rainbow", "uid-range", "ca-derivations"}); testDerivationOptions("advanced-attributes.drv", advancedAttributes_ca, {"rainbow", "uid-range", "ca-derivations"});
}; };
DerivationOptions advancedAttributes_structuredAttrs_defaults = { DerivationOptions<SingleDerivedPath> advancedAttributes_structuredAttrs_defaults = {
.outputChecks = std::map<std::string, DerivationOptions::OutputChecks>{}, .outputChecks = std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks>{},
.unsafeDiscardReferences = {}, .unsafeDiscardReferences = {},
.passAsFile = {}, .passAsFile = {},
.exportReferencesGraph = {}, .exportReferencesGraph = {},
@@ -307,7 +329,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_TRUE(got.structuredAttrs); EXPECT_TRUE(got.structuredAttrs);
@@ -332,11 +355,11 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_default
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
{ {
DerivationOptions expected = { DerivationOptions<SingleDerivedPath> expected = {
.outputChecks = .outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{ std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks>{
{"dev", {"dev",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.maxSize = 789, .maxSize = 789,
.maxClosureSize = 5909, .maxClosureSize = 5909,
}}, }},
@@ -357,7 +380,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); auto options = derivationOptionsFromStructuredAttrs(
*this->store, got.inputDrvs, got.env, get(got.structuredAttrs), true, this->mockXpSettings);
EXPECT_TRUE(got.structuredAttrs); EXPECT_TRUE(got.structuredAttrs);
@@ -365,7 +389,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
{ {
// Delete all keys but "dev" in options.outputChecks // Delete all keys but "dev" in options.outputChecks
auto * outputChecksMapP = auto * outputChecksMapP =
std::get_if<std::map<std::string, DerivationOptions::OutputChecks>>(&options.outputChecks); std::get_if<std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks>>(
&options.outputChecks);
ASSERT_TRUE(outputChecksMapP); ASSERT_TRUE(outputChecksMapP);
auto & outputChecksMap = *outputChecksMapP; auto & outputChecksMap = *outputChecksMapP;
auto devEntry = outputChecksMap.find("dev"); auto devEntry = outputChecksMap.find("dev");
@@ -385,21 +410,21 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
}); });
}; };
DerivationOptions advancedAttributes_structuredAttrs_ia = { DerivationOptions<SingleDerivedPath> advancedAttributes_structuredAttrs_ia = {
.outputChecks = .outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{ std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks>{
{"out", {"out",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.allowedReferences = StringSet{pathFoo}, .allowedReferences = std::set<DrvRef<SingleDerivedPath>>{pathFoo},
.allowedRequisites = StringSet{pathFooDev, "bin"}, .allowedRequisites = std::set<DrvRef<SingleDerivedPath>>{pathFooDev, OutputName{"bin"}},
}}, }},
{"bin", {"bin",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.disallowedReferences = StringSet{pathBar, "dev"}, .disallowedReferences = std::set<DrvRef<SingleDerivedPath>>{pathBar, OutputName{"dev"}},
.disallowedRequisites = StringSet{pathBarDev}, .disallowedRequisites = std::set<DrvRef<SingleDerivedPath>>{pathBarDev},
}}, }},
{"dev", {"dev",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.maxSize = 789, .maxSize = 789,
.maxClosureSize = 5909, .maxClosureSize = 5909,
}}, }},
@@ -427,21 +452,21 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
"advanced-attributes-structured-attrs.drv", advancedAttributes_structuredAttrs_ia, {"rainbow", "uid-range"}); "advanced-attributes-structured-attrs.drv", advancedAttributes_structuredAttrs_ia, {"rainbow", "uid-range"});
}; };
DerivationOptions advancedAttributes_structuredAttrs_ca = { DerivationOptions<SingleDerivedPath> advancedAttributes_structuredAttrs_ca = {
.outputChecks = .outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{ std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks>{
{"out", {"out",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.allowedReferences = StringSet{placeholderFoo}, .allowedReferences = std::set<DrvRef<SingleDerivedPath>>{placeholderFoo},
.allowedRequisites = StringSet{placeholderFooDev, "bin"}, .allowedRequisites = std::set<DrvRef<SingleDerivedPath>>{placeholderFooDev, OutputName{"bin"}},
}}, }},
{"bin", {"bin",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.disallowedReferences = StringSet{placeholderBar, "dev"}, .disallowedReferences = std::set<DrvRef<SingleDerivedPath>>{placeholderBar, OutputName{"dev"}},
.disallowedRequisites = StringSet{placeholderBarDev}, .disallowedRequisites = std::set<DrvRef<SingleDerivedPath>>{placeholderBarDev},
}}, }},
{"dev", {"dev",
DerivationOptions::OutputChecks{ DerivationOptions<SingleDerivedPath>::OutputChecks{
.maxSize = 789, .maxSize = 789,
.maxClosureSize = 5909, .maxClosureSize = 5909,
}}, }},
@@ -471,14 +496,16 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{"rainbow", "uid-range", "ca-derivations"}); {"rainbow", "uid-range", "ca-derivations"});
}; };
#define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \ #define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \
TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \ TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \
{ \ { \
this->JsonCharacterizationTest<DerivationOptions>::readJsonTest(#VAR, advancedAttributes_##VAR2); \ this->JsonCharacterizationTest<DerivationOptions<SingleDerivedPath>>::readJsonTest( \
} \ #VAR, advancedAttributes_##VAR2); \
TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \ } \
{ \ TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \
this->JsonCharacterizationTest<DerivationOptions>::writeJsonTest(#VAR, advancedAttributes_##VAR2); \ { \
this->JsonCharacterizationTest<DerivationOptions<SingleDerivedPath>>::writeJsonTest( \
#VAR, advancedAttributes_##VAR2); \
} }
TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, defaults, defaults) TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, defaults, defaults)
+41 -16
View File
@@ -32,14 +32,6 @@ DerivationBuildingGoal::DerivationBuildingGoal(
, drv{std::make_unique<Derivation>(drv)} , drv{std::make_unique<Derivation>(drv)}
, buildMode(buildMode) , buildMode(buildMode)
{ {
try {
drvOptions =
std::make_unique<DerivationOptions>(DerivationOptions::fromStructuredAttrs(drv.env, drv.structuredAttrs));
} catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath));
throw;
}
name = fmt("building derivation '%s'", worker.store.printStorePath(drvPath)); name = fmt("building derivation '%s'", worker.store.printStorePath(drvPath));
trace("created"); trace("created");
@@ -206,6 +198,38 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution(bool storeDerivation)
Goal::Co DerivationBuildingGoal::tryToBuild() Goal::Co DerivationBuildingGoal::tryToBuild()
{ {
auto drvOptions = [&] {
DerivationOptions<SingleDerivedPath> temp;
try {
temp =
derivationOptionsFromStructuredAttrs(worker.store, drv->inputDrvs, drv->env, get(drv->structuredAttrs));
} catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath));
throw;
}
auto res = tryResolve(
temp,
[&](ref<const SingleDerivedPath> drvPath, const std::string & outputName) -> std::optional<StorePath> {
try {
return resolveDerivedPath(
worker.store, SingleDerivedPath::Built{drvPath, outputName}, &worker.evalStore);
} catch (Error &) {
return std::nullopt;
}
});
/* The derivation must have all of its inputs gotten this point,
so the resolution will surely succeed.
(Actually, we shouldn't even enter this goal until we have a
resolved derivation, or derivation with only input addressed
transitive inputs, so this should be a no-opt anyways.)
*/
assert(res);
return *res;
}();
std::map<std::string, InitialOutput> initialOutputs; std::map<std::string, InitialOutput> initialOutputs;
/* Recheck at this point. In particular, whereas before we were /* Recheck at this point. In particular, whereas before we were
@@ -344,13 +368,13 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
/* Don't do a remote build if the derivation has the attribute /* Don't do a remote build if the derivation has the attribute
`preferLocalBuild' set. Also, check and repair modes are only `preferLocalBuild' set. Also, check and repair modes are only
supported for local builds. */ supported for local builds. */
bool buildLocally = (buildMode != bmNormal || drvOptions->willBuildLocally(worker.store, *drv)) bool buildLocally = (buildMode != bmNormal || drvOptions.willBuildLocally(worker.store, *drv))
&& settings.maxBuildJobs.get() != 0; && settings.maxBuildJobs.get() != 0;
if (buildLocally) { if (buildLocally) {
useHook = false; useHook = false;
} else { } else {
switch (tryBuildHook(initialOutputs)) { switch (tryBuildHook(initialOutputs, drvOptions)) {
case rpAccept: case rpAccept:
/* Yes, it has started doing so. Wait until we get /* Yes, it has started doing so. Wait until we get
EOF from the hook. */ EOF from the hook. */
@@ -379,7 +403,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
externalBuilder = settings.findExternalDerivationBuilderIfSupported(*drv); externalBuilder = settings.findExternalDerivationBuilderIfSupported(*drv);
if (!externalBuilder && !drvOptions->canBuildLocally(worker.store, *drv)) { if (!externalBuilder && !drvOptions.canBuildLocally(worker.store, *drv)) {
auto msg = auto msg =
fmt("Cannot build '%s'.\n" fmt("Cannot build '%s'.\n"
"Reason: " ANSI_RED "required system or feature not available" ANSI_NORMAL "Reason: " ANSI_RED "required system or feature not available" ANSI_NORMAL
@@ -388,7 +412,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
"Current system: '%s' with features {%s}", "Current system: '%s' with features {%s}",
Magenta(worker.store.printStorePath(drvPath)), Magenta(worker.store.printStorePath(drvPath)),
Magenta(drv->platform), Magenta(drv->platform),
concatStringsSep(", ", drvOptions->getRequiredSystemFeatures(*drv)), concatStringsSep(", ", drvOptions.getRequiredSystemFeatures(*drv)),
Magenta(settings.thisSystem), Magenta(settings.thisSystem),
concatStringsSep<StringSet>(", ", worker.store.Store::config.systemFeatures)); concatStringsSep<StringSet>(", ", worker.store.Store::config.systemFeatures));
@@ -586,7 +610,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
} }
try { try {
desugaredEnv = DesugaredEnv::create(worker.store, *drv, *drvOptions, inputPaths); desugaredEnv = DesugaredEnv::create(worker.store, *drv, drvOptions, inputPaths);
} catch (BuildError & e) { } catch (BuildError & e) {
outputLocks.unlock(); outputLocks.unlock();
worker.permanentFailure = true; worker.permanentFailure = true;
@@ -597,7 +621,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
.drvPath = drvPath, .drvPath = drvPath,
.buildResult = buildResult, .buildResult = buildResult,
.drv = *drv, .drv = *drv,
.drvOptions = *drvOptions, .drvOptions = drvOptions,
.inputPaths = inputPaths, .inputPaths = inputPaths,
.initialOutputs = initialOutputs, .initialOutputs = initialOutputs,
.buildMode = buildMode, .buildMode = buildMode,
@@ -803,7 +827,8 @@ BuildError DerivationBuildingGoal::fixupBuilderFailureErrorMessage(BuilderFailur
return BuildError{e.status, msg}; return BuildError{e.status, msg};
} }
HookReply DerivationBuildingGoal::tryBuildHook(const std::map<std::string, InitialOutput> & initialOutputs) HookReply DerivationBuildingGoal::tryBuildHook(
const std::map<std::string, InitialOutput> & initialOutputs, const DerivationOptions<StorePath> & drvOptions)
{ {
#ifdef _WIN32 // TODO enable build hook on Windows #ifdef _WIN32 // TODO enable build hook on Windows
return rpDecline; return rpDecline;
@@ -820,7 +845,7 @@ HookReply DerivationBuildingGoal::tryBuildHook(const std::map<std::string, Initi
/* Send the request to the hook. */ /* Send the request to the hook. */
worker.hook->sink << "try" << (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0) << drv->platform worker.hook->sink << "try" << (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0) << drv->platform
<< worker.store.printStorePath(drvPath) << drvOptions->getRequiredSystemFeatures(*drv); << worker.store.printStorePath(drvPath) << drvOptions.getRequiredSystemFeatures(*drv);
worker.hook->sink.flush(); worker.hook->sink.flush();
/* Read the first line of input, which should be a word indicating /* Read the first line of input, which should be a word indicating
+26 -21
View File
@@ -11,7 +11,7 @@ void checkOutputs(
Store & store, Store & store,
const StorePath & drvPath, const StorePath & drvPath,
const decltype(Derivation::outputs) & drvOutputs, const decltype(Derivation::outputs) & drvOutputs,
const decltype(DerivationOptions::outputChecks) & outputChecks, const decltype(DerivationOptions<StorePath>::outputChecks) & outputChecks,
const std::map<std::string, ValidPathInfo> & outputs) const std::map<std::string, ValidPathInfo> & outputs)
{ {
std::map<Path, const ValidPathInfo &> outputsByPath; std::map<Path, const ValidPathInfo &> outputsByPath;
@@ -85,7 +85,7 @@ void checkOutputs(
return std::make_pair(std::move(pathsDone), closureSize); return std::make_pair(std::move(pathsDone), closureSize);
}; };
auto applyChecks = [&](const DerivationOptions::OutputChecks & checks) { auto applyChecks = [&](const DerivationOptions<StorePath>::OutputChecks & checks) {
if (checks.maxSize && info.narSize > *checks.maxSize) if (checks.maxSize && info.narSize > *checks.maxSize)
throw BuildError( throw BuildError(
BuildResult::Failure::OutputRejected, BuildResult::Failure::OutputRejected,
@@ -105,28 +105,33 @@ void checkOutputs(
*checks.maxClosureSize); *checks.maxClosureSize);
} }
auto checkRefs = [&](const StringSet & value, bool allowed, bool recursive) { auto checkRefs = [&](const std::set<DrvRef<StorePath>> & value, bool allowed, bool recursive) {
/* Parse a list of reference specifiers. Each element must /* Parse a list of reference specifiers. Each element must
either be a store path, or the symbolic name of the output either be a store path, or the symbolic name of the output
of the derivation (such as `out'). */ of the derivation (such as `out'). */
StorePathSet spec; StorePathSet spec;
for (auto & i : value) { for (auto & i : value) {
if (store.isStorePath(i)) std::visit(
spec.insert(store.parseStorePath(i)); overloaded{
else if (auto output = get(outputs, i)) [&](const StorePath & path) { spec.insert(path); },
spec.insert(output->path); [&](const OutputName & refOutputName) {
else { if (auto output = get(outputs, refOutputName))
std::string outputsListing = spec.insert(output->path);
concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; }); else {
throw BuildError( std::string outputsListing =
BuildResult::Failure::OutputRejected, concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; });
"derivation '%s' output check for '%s' contains an illegal reference specifier '%s'," throw BuildError(
" expected store path or output name (one of [%s])", BuildResult::Failure::OutputRejected,
store.printStorePath(drvPath), "derivation '%s' output check for '%s' contains output name '%s',"
outputName, " but this is not a valid output of this derivation."
i, " (Valid outputs are [%s].)",
outputsListing); store.printStorePath(drvPath),
} outputName,
refOutputName,
outputsListing);
}
}},
i);
} }
auto used = recursive ? getClosure(info.path).first : info.references; auto used = recursive ? getClosure(info.path).first : info.references;
@@ -180,8 +185,8 @@ void checkOutputs(
std::visit( std::visit(
overloaded{ overloaded{
[&](const DerivationOptions::OutputChecks & checks) { applyChecks(checks); }, [&](const DerivationOptions<StorePath>::OutputChecks & checks) { applyChecks(checks); },
[&](const std::map<std::string, DerivationOptions::OutputChecks> & checksPerOutput) { [&](const std::map<std::string, DerivationOptions<StorePath>::OutputChecks> & checksPerOutput) {
if (auto outputChecks = get(checksPerOutput, outputName)) if (auto outputChecks = get(checksPerOutput, outputName))
applyChecks(*outputChecks); applyChecks(*outputChecks);
+1 -1
View File
@@ -21,7 +21,7 @@ void checkOutputs(
Store & store, Store & store,
const StorePath & drvPath, const StorePath & drvPath,
const decltype(Derivation::outputs) & drvOutputs, const decltype(Derivation::outputs) & drvOutputs,
const decltype(DerivationOptions::outputChecks) & drvOptions, const decltype(DerivationOptions<StorePath>::outputChecks) & drvOptions,
const std::map<std::string, ValidPathInfo> & outputs); const std::map<std::string, ValidPathInfo> & outputs);
} // namespace nix } // namespace nix
+5 -2
View File
@@ -18,7 +18,10 @@ std::string & DesugaredEnv::atFileEnvPair(std::string_view name, std::string fil
} }
DesugaredEnv DesugaredEnv::create( DesugaredEnv DesugaredEnv::create(
Store & store, const Derivation & drv, const DerivationOptions & drvOptions, const StorePathSet & inputPaths) Store & store,
const Derivation & drv,
const DerivationOptions<StorePath> & drvOptions,
const StorePathSet & inputPaths)
{ {
DesugaredEnv res; DesugaredEnv res;
@@ -46,7 +49,7 @@ DesugaredEnv DesugaredEnv::create(
} }
/* Handle exportReferencesGraph(), if set. */ /* Handle exportReferencesGraph(), if set. */
for (auto & [fileName, storePaths] : drvOptions.getParsedExportReferencesGraph(store)) { for (auto & [fileName, storePaths] : drvOptions.exportReferencesGraph) {
/* Write closure info to <fileName>. */ /* Write closure info to <fileName>. */
res.extraFiles.insert_or_assign( res.extraFiles.insert_or_assign(
fileName, store.makeValidityRegistration(store.exportReferences(storePaths, inputPaths), false, false)); fileName, store.makeValidityRegistration(store.exportReferences(storePaths, inputPaths), false, false));
+3 -2
View File
@@ -64,9 +64,10 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
{ {
trace("have derivation"); trace("have derivation");
auto drvOptions = [&]() -> DerivationOptions { auto drvOptions = [&]() -> DerivationOptions<SingleDerivedPath> {
try { try {
return DerivationOptions::fromStructuredAttrs(drv->env, drv->structuredAttrs); return derivationOptionsFromStructuredAttrs(
worker.store, drv->inputDrvs, drv->env, get(drv->structuredAttrs));
} catch (Error & e) { } catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath)); e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath));
throw; throw;
+298 -67
View File
@@ -2,15 +2,18 @@
#include "nix/util/json-utils.hh" #include "nix/util/json-utils.hh"
#include "nix/store/parsed-derivations.hh" #include "nix/store/parsed-derivations.hh"
#include "nix/store/derivations.hh" #include "nix/store/derivations.hh"
#include "nix/store/derived-path.hh"
#include "nix/store/store-api.hh" #include "nix/store/store-api.hh"
#include "nix/util/types.hh" #include "nix/util/types.hh"
#include "nix/util/util.hh" #include "nix/util/util.hh"
#include "nix/store/globals.hh" #include "nix/store/globals.hh"
#include "nix/util/variant-wrapper.hh"
#include <optional> #include <optional>
#include <string> #include <string>
#include <variant> #include <variant>
#include <regex> #include <regex>
#include <ranges>
namespace nix { namespace nix {
@@ -90,14 +93,38 @@ getStringSetAttr(const StringMap & env, const StructuredAttrs * parsed, const st
return ss ? (std::optional{StringSet{ss->begin(), ss->end()}}) : (std::optional<StringSet>{}); return ss ? (std::optional{StringSet{ss->begin(), ss->end()}}) : (std::optional<StringSet>{});
} }
using OutputChecks = DerivationOptions::OutputChecks; template<typename Inputs>
using OutputChecks = DerivationOptions<Inputs>::OutputChecks;
using OutputChecksVariant = std::variant<OutputChecks, std::map<std::string, OutputChecks>>; template<typename Inputs>
using OutputChecksVariant = std::variant<OutputChecks<Inputs>, std::map<std::string, OutputChecks<Inputs>>>;
DerivationOptions DerivationOptions::fromStructuredAttrs( DerivationOptions<StorePath> derivationOptionsFromStructuredAttrs(
const StringMap & env, const std::optional<StructuredAttrs> & parsed, bool shouldWarn) const StoreDirConfig & store,
const StringMap & env,
const StructuredAttrs * parsed,
bool shouldWarn,
const ExperimentalFeatureSettings & mockXpSettings)
{ {
return fromStructuredAttrs(env, parsed ? &*parsed : nullptr); /* Use the SingleDerivedPath version with empty inputDrvs, then
resolve. */
DerivedPathMap<StringSet> emptyInputDrvs{};
auto singleDerivedPathOptions =
derivationOptionsFromStructuredAttrs(store, emptyInputDrvs, env, parsed, shouldWarn, mockXpSettings);
/* "Resolve" all SingleDerivedPath inputs to StorePath. */
auto resolved = tryResolve(
singleDerivedPathOptions,
[&](ref<const SingleDerivedPath> drvPath, const std::string & outputName) -> std::optional<StorePath> {
// there should be nothing to resolve
assert(false);
});
/* Since we should never need to call the call back, there should be
no way it fails. */
assert(resolved);
return *resolved;
} }
static void flatten(const nlohmann::json & value, StringSet & res) static void flatten(const nlohmann::json & value, StringSet & res)
@@ -111,10 +138,63 @@ static void flatten(const nlohmann::json & value, StringSet & res)
throw Error("'exportReferencesGraph' value is not an array or a string"); throw Error("'exportReferencesGraph' value is not an array or a string");
} }
DerivationOptions DerivationOptions<SingleDerivedPath> derivationOptionsFromStructuredAttrs(
DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn) const StoreDirConfig & store,
const DerivedPathMap<StringSet> & inputDrvs,
const StringMap & env,
const StructuredAttrs * parsed,
bool shouldWarn,
const ExperimentalFeatureSettings & mockXpSettings)
{ {
DerivationOptions defaults = {}; DerivationOptions<SingleDerivedPath> defaults = {};
std::map<std::string, SingleDerivedPath::Built> placeholders;
if (mockXpSettings.isEnabled(Xp::CaDerivations)) {
/* Initialize placeholder map from inputDrvs */
auto initPlaceholders = [&](this const auto & initPlaceholders,
ref<const SingleDerivedPath> basePath,
const DerivedPathMap<StringSet>::ChildNode & node) -> void {
for (const auto & outputName : node.value) {
auto built = SingleDerivedPath::Built{
.drvPath = basePath,
.output = outputName,
};
placeholders.insert_or_assign(
DownstreamPlaceholder::fromSingleDerivedPathBuilt(built, mockXpSettings).render(),
std::move(built));
}
for (const auto & [outputName, childNode] : node.childMap) {
initPlaceholders(
make_ref<const SingleDerivedPath>(SingleDerivedPath::Built{
.drvPath = basePath,
.output = outputName,
}),
childNode);
}
};
for (const auto & [drvPath, outputs] : inputDrvs.map) {
auto basePath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{drvPath});
initPlaceholders(basePath, outputs);
}
}
auto parseSingleDerivedPath = [&](const std::string & pathS) -> SingleDerivedPath {
if (auto it = placeholders.find(pathS); it != placeholders.end())
return it->second;
else
return SingleDerivedPath::Opaque{store.toStorePath(pathS).first};
};
auto parseRef = [&](const std::string & pathS) -> DrvRef<SingleDerivedPath> {
if (auto it = placeholders.find(pathS); it != placeholders.end())
return it->second;
if (store.isStorePath(pathS))
return SingleDerivedPath::Opaque{store.toStorePath(pathS).first};
else
return pathS;
};
if (shouldWarn && parsed) { if (shouldWarn && parsed) {
auto & structuredAttrs = parsed->structuredAttrs; auto & structuredAttrs = parsed->structuredAttrs;
@@ -146,14 +226,14 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
} }
return { return {
.outputChecks = [&]() -> OutputChecksVariant { .outputChecks = [&]() -> OutputChecksVariant<SingleDerivedPath> {
if (parsed) { if (parsed) {
auto & structuredAttrs = parsed->structuredAttrs; auto & structuredAttrs = parsed->structuredAttrs;
std::map<std::string, OutputChecks> res; std::map<std::string, OutputChecks<SingleDerivedPath>> res;
if (auto * outputChecks = get(structuredAttrs, "outputChecks")) { if (auto * outputChecks = get(structuredAttrs, "outputChecks")) {
for (auto & [outputName, output_] : getObject(*outputChecks)) { for (auto & [outputName, output_] : getObject(*outputChecks)) {
OutputChecks checks; OutputChecks<SingleDerivedPath> checks;
auto & output = getObject(output_); auto & output = getObject(output_);
@@ -163,13 +243,14 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
if (auto maxClosureSize = get(output, "maxClosureSize")) if (auto maxClosureSize = get(output, "maxClosureSize"))
checks.maxClosureSize = maxClosureSize->get<uint64_t>(); checks.maxClosureSize = maxClosureSize->get<uint64_t>();
auto get_ = [&output = output](const std::string & name) -> std::optional<StringSet> { auto get_ =
[&](const std::string & name) -> std::optional<std::set<DrvRef<SingleDerivedPath>>> {
if (auto i = get(output, name)) { if (auto i = get(output, name)) {
StringSet res; std::set<DrvRef<SingleDerivedPath>> res;
for (auto j = i->begin(); j != i->end(); ++j) { for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string()) if (!j->is_string())
throw Error("attribute '%s' must be a list of strings", name); throw Error("attribute '%s' must be a list of strings", name);
res.insert(j->get<std::string>()); res.insert(parseRef(j->get<std::string>()));
} }
return res; return res;
} }
@@ -178,7 +259,7 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
res.insert_or_assign( res.insert_or_assign(
outputName, outputName,
OutputChecks{ OutputChecks<SingleDerivedPath>{
.maxSize = [&]() -> std::optional<uint64_t> { .maxSize = [&]() -> std::optional<uint64_t> {
if (auto maxSize = get(output, "maxSize")) if (auto maxSize = get(output, "maxSize"))
return maxSize->get<uint64_t>(); return maxSize->get<uint64_t>();
@@ -192,21 +273,32 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
return std::nullopt; return std::nullopt;
}(), }(),
.allowedReferences = get_("allowedReferences"), .allowedReferences = get_("allowedReferences"),
.disallowedReferences = get_("disallowedReferences").value_or(StringSet{}), .disallowedReferences =
get_("disallowedReferences").value_or(std::set<DrvRef<SingleDerivedPath>>{}),
.allowedRequisites = get_("allowedRequisites"), .allowedRequisites = get_("allowedRequisites"),
.disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{}), .disallowedRequisites =
get_("disallowedRequisites").value_or(std::set<DrvRef<SingleDerivedPath>>{}),
}); });
} }
} }
return res; return res;
} else { } else {
return OutputChecks{ auto parseRefSet = [&](const std::optional<StringSet> optionalStringSet)
-> std::optional<std::set<DrvRef<SingleDerivedPath>>> {
if (!optionalStringSet)
return std::nullopt;
auto range = *optionalStringSet | std::views::transform(parseRef);
return std::set<DrvRef<SingleDerivedPath>>(range.begin(), range.end());
};
return OutputChecks<SingleDerivedPath>{
// legacy non-structured-attributes case // legacy non-structured-attributes case
.ignoreSelfRefs = true, .ignoreSelfRefs = true,
.allowedReferences = getStringSetAttr(env, parsed, "allowedReferences"), .allowedReferences = parseRefSet(getStringSetAttr(env, parsed, "allowedReferences")),
.disallowedReferences = getStringSetAttr(env, parsed, "disallowedReferences").value_or(StringSet{}), .disallowedReferences = parseRefSet(getStringSetAttr(env, parsed, "disallowedReferences"))
.allowedRequisites = getStringSetAttr(env, parsed, "allowedRequisites"), .value_or(std::set<DrvRef<SingleDerivedPath>>{}),
.disallowedRequisites = getStringSetAttr(env, parsed, "disallowedRequisites").value_or(StringSet{}), .allowedRequisites = parseRefSet(getStringSetAttr(env, parsed, "allowedRequisites")),
.disallowedRequisites = parseRefSet(getStringSetAttr(env, parsed, "disallowedRequisites"))
.value_or(std::set<DrvRef<SingleDerivedPath>>{}),
}; };
} }
}(), }(),
@@ -245,16 +337,19 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
}(), }(),
.exportReferencesGraph = .exportReferencesGraph =
[&] { [&] {
std::map<std::string, StringSet> ret; std::map<std::string, std::set<SingleDerivedPath>> ret;
if (parsed) { if (parsed) {
auto * e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph"); auto * e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph");
if (!e || !e->is_object()) if (!e || !e->is_object())
return ret; return ret;
for (auto & [key, value] : getObject(*e)) { for (auto & [key, storePathsJson] : getObject(*e)) {
StringSet ss; StringSet ss;
flatten(value, ss); flatten(storePathsJson, ss);
ret.insert_or_assign(key, std::move(ss)); std::set<SingleDerivedPath> storePaths;
for (auto & s : ss)
storePaths.insert(parseSingleDerivedPath(s));
ret.insert_or_assign(key, std::move(storePaths));
} }
} else { } else {
auto s = getOr(env, "exportReferencesGraph", ""); auto s = getOr(env, "exportReferencesGraph", "");
@@ -268,7 +363,7 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName); throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName);
auto & storePathS = *i++; auto & storePathS = *i++;
ret.insert_or_assign(std::move(fileName), StringSet{storePathS}); ret.insert_or_assign(std::move(fileName), std::set{parseSingleDerivedPath(storePathS)});
} }
} }
return ret; return ret;
@@ -286,28 +381,8 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
}; };
} }
std::map<std::string, StorePathSet> template<typename Input>
DerivationOptions::getParsedExportReferencesGraph(const StoreDirConfig & store) const StringSet DerivationOptions<Input>::getRequiredSystemFeatures(const BasicDerivation & drv) const
{
std::map<std::string, StorePathSet> res;
for (auto & [fileName, ss] : exportReferencesGraph) {
StorePathSet storePaths;
for (auto & storePathS : ss) {
if (!store.isInStore(storePathS))
throw BuildError(
BuildResult::Failure::InputRejected,
"'exportReferencesGraph' contains a non-store path '%1%'",
storePathS);
storePaths.insert(store.toStorePath(storePathS).first);
}
res.insert_or_assign(fileName, storePaths);
}
return res;
}
StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & drv) const
{ {
// FIXME: cache this? // FIXME: cache this?
StringSet res; StringSet res;
@@ -318,7 +393,8 @@ StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & d
return res; return res;
} }
bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivation & drv) const template<typename Input>
bool DerivationOptions<Input>::canBuildLocally(Store & localStore, const BasicDerivation & drv) const
{ {
if (drv.platform != settings.thisSystem.get() && !settings.extraPlatforms.get().count(drv.platform) if (drv.platform != settings.thisSystem.get() && !settings.extraPlatforms.get().count(drv.platform)
&& !drv.isBuiltin()) && !drv.isBuiltin())
@@ -334,42 +410,194 @@ bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivatio
return true; return true;
} }
bool DerivationOptions::willBuildLocally(Store & localStore, const BasicDerivation & drv) const template<typename Input>
bool DerivationOptions<Input>::willBuildLocally(Store & localStore, const BasicDerivation & drv) const
{ {
return preferLocalBuild && canBuildLocally(localStore, drv); return preferLocalBuild && canBuildLocally(localStore, drv);
} }
bool DerivationOptions::substitutesAllowed() const template<typename Input>
bool DerivationOptions<Input>::substitutesAllowed() const
{ {
return settings.alwaysAllowSubstitutes ? true : allowSubstitutes; return settings.alwaysAllowSubstitutes ? true : allowSubstitutes;
} }
bool DerivationOptions::useUidRange(const BasicDerivation & drv) const template<typename Input>
bool DerivationOptions<Input>::useUidRange(const BasicDerivation & drv) const
{ {
return getRequiredSystemFeatures(drv).count("uid-range"); return getRequiredSystemFeatures(drv).count("uid-range");
} }
std::optional<DerivationOptions<StorePath>> tryResolve(
const DerivationOptions<SingleDerivedPath> & drvOptions,
std::function<std::optional<StorePath>(ref<const SingleDerivedPath> drvPath, const std::string & outputName)>
queryResolutionChain)
{
auto tryResolvePath = [&](const SingleDerivedPath & input) -> std::optional<StorePath> {
return std::visit(
overloaded{
[](const SingleDerivedPath::Opaque & p) -> std::optional<StorePath> { return p.path; },
[&](const SingleDerivedPath::Built & p) -> std::optional<StorePath> {
return queryResolutionChain(p.drvPath, p.output);
}},
input.raw());
};
auto tryResolveRef = [&](const DrvRef<SingleDerivedPath> & ref) -> std::optional<DrvRef<StorePath>> {
return std::visit(
overloaded{
[](const OutputName & outputName) -> std::optional<DrvRef<StorePath>> { return outputName; },
[&](const SingleDerivedPath & input) -> std::optional<DrvRef<StorePath>> {
return tryResolvePath(input);
}},
ref);
};
auto tryResolveRefSet =
[&](const std::set<DrvRef<SingleDerivedPath>> & refSet) -> std::optional<std::set<DrvRef<StorePath>>> {
std::set<DrvRef<StorePath>> resolvedSet;
for (const auto & ref : refSet) {
auto resolvedRef = tryResolveRef(ref);
if (!resolvedRef)
return std::nullopt;
resolvedSet.insert(*resolvedRef);
}
return resolvedSet;
};
// Helper function to try resolving OutputChecks using functional style
auto tryResolveOutputChecks = [&](const DerivationOptions<SingleDerivedPath>::OutputChecks & checks)
-> std::optional<DerivationOptions<StorePath>::OutputChecks> {
std::optional<std::set<DrvRef<StorePath>>> resolvedAllowedReferences;
if (checks.allowedReferences) {
resolvedAllowedReferences = tryResolveRefSet(*checks.allowedReferences);
if (!resolvedAllowedReferences)
return std::nullopt;
}
std::optional<std::set<DrvRef<StorePath>>> resolvedAllowedRequisites;
if (checks.allowedRequisites) {
resolvedAllowedRequisites = tryResolveRefSet(*checks.allowedRequisites);
if (!resolvedAllowedRequisites)
return std::nullopt;
}
auto resolvedDisallowedReferences = tryResolveRefSet(checks.disallowedReferences);
if (!resolvedDisallowedReferences)
return std::nullopt;
auto resolvedDisallowedRequisites = tryResolveRefSet(checks.disallowedRequisites);
if (!resolvedDisallowedRequisites)
return std::nullopt;
return DerivationOptions<StorePath>::OutputChecks{
.ignoreSelfRefs = checks.ignoreSelfRefs,
.maxSize = checks.maxSize,
.maxClosureSize = checks.maxClosureSize,
.allowedReferences = resolvedAllowedReferences,
.disallowedReferences = *resolvedDisallowedReferences,
.allowedRequisites = resolvedAllowedRequisites,
.disallowedRequisites = *resolvedDisallowedRequisites,
};
};
// Helper function to resolve exportReferencesGraph using functional style
auto tryResolveExportReferencesGraph = [&](const std::map<std::string, std::set<SingleDerivedPath>> & exportGraph)
-> std::optional<std::map<std::string, std::set<StorePath>>> {
std::map<std::string, std::set<StorePath>> resolved;
for (const auto & [name, inputPaths] : exportGraph) {
std::set<StorePath> resolvedPaths;
for (const auto & inputPath : inputPaths) {
auto resolvedPath = tryResolvePath(inputPath);
if (!resolvedPath)
return std::nullopt;
resolvedPaths.insert(*resolvedPath);
}
resolved.emplace(name, std::move(resolvedPaths));
}
return resolved;
};
// Resolve outputChecks using functional style with std::visit
auto resolvedOutputChecks = std::visit(
overloaded{
[&](const DerivationOptions<SingleDerivedPath>::OutputChecks & checks)
-> std::optional<std::variant<
DerivationOptions<StorePath>::OutputChecks,
std::map<std::string, DerivationOptions<StorePath>::OutputChecks>>> {
auto resolved = tryResolveOutputChecks(checks);
if (!resolved)
return std::nullopt;
return std::variant<
DerivationOptions<StorePath>::OutputChecks,
std::map<std::string, DerivationOptions<StorePath>::OutputChecks>>(*resolved);
},
[&](const std::map<std::string, DerivationOptions<SingleDerivedPath>::OutputChecks> & checksMap)
-> std::optional<std::variant<
DerivationOptions<StorePath>::OutputChecks,
std::map<std::string, DerivationOptions<StorePath>::OutputChecks>>> {
std::map<std::string, DerivationOptions<StorePath>::OutputChecks> resolvedMap;
for (const auto & [outputName, checks] : checksMap) {
auto resolved = tryResolveOutputChecks(checks);
if (!resolved)
return std::nullopt;
resolvedMap.emplace(outputName, *resolved);
}
return std::variant<
DerivationOptions<StorePath>::OutputChecks,
std::map<std::string, DerivationOptions<StorePath>::OutputChecks>>(resolvedMap);
}},
drvOptions.outputChecks);
if (!resolvedOutputChecks)
return std::nullopt;
// Resolve exportReferencesGraph
auto resolvedExportGraph = tryResolveExportReferencesGraph(drvOptions.exportReferencesGraph);
if (!resolvedExportGraph)
return std::nullopt;
// Return resolved DerivationOptions using designated initializers
return DerivationOptions<StorePath>{
.outputChecks = *resolvedOutputChecks,
.unsafeDiscardReferences = drvOptions.unsafeDiscardReferences,
.passAsFile = drvOptions.passAsFile,
.exportReferencesGraph = *resolvedExportGraph,
.additionalSandboxProfile = drvOptions.additionalSandboxProfile,
.noChroot = drvOptions.noChroot,
.impureHostDeps = drvOptions.impureHostDeps,
.impureEnvVars = drvOptions.impureEnvVars,
.allowLocalNetworking = drvOptions.allowLocalNetworking,
.requiredSystemFeatures = drvOptions.requiredSystemFeatures,
.preferLocalBuild = drvOptions.preferLocalBuild,
.allowSubstitutes = drvOptions.allowSubstitutes,
};
}
template struct DerivationOptions<StorePath>;
template struct DerivationOptions<SingleDerivedPath>;
} // namespace nix } // namespace nix
namespace nlohmann { namespace nlohmann {
using namespace nix; using namespace nix;
DerivationOptions adl_serializer<DerivationOptions>::from_json(const json & json_) DerivationOptions<SingleDerivedPath> adl_serializer<DerivationOptions<SingleDerivedPath>>::from_json(const json & json_)
{ {
auto & json = getObject(json_); auto & json = getObject(json_);
return { return {
.outputChecks = [&]() -> OutputChecksVariant { .outputChecks = [&]() -> OutputChecksVariant<SingleDerivedPath> {
auto outputChecks = getObject(valueAt(json, "outputChecks")); auto outputChecks = getObject(valueAt(json, "outputChecks"));
auto forAllOutputsOpt = optionalValueAt(outputChecks, "forAllOutputs"); auto forAllOutputsOpt = optionalValueAt(outputChecks, "forAllOutputs");
auto perOutputOpt = optionalValueAt(outputChecks, "perOutput"); auto perOutputOpt = optionalValueAt(outputChecks, "perOutput");
if (forAllOutputsOpt && !perOutputOpt) { if (forAllOutputsOpt && !perOutputOpt) {
return static_cast<OutputChecks>(*forAllOutputsOpt); return static_cast<OutputChecks<SingleDerivedPath>>(*forAllOutputsOpt);
} else if (perOutputOpt && !forAllOutputsOpt) { } else if (perOutputOpt && !forAllOutputsOpt) {
return static_cast<std::map<std::string, OutputChecks>>(*perOutputOpt); return static_cast<std::map<std::string, OutputChecks<SingleDerivedPath>>>(*perOutputOpt);
} else { } else {
throw Error("Exactly one of 'perOutput' or 'forAllOutputs' is required"); throw Error("Exactly one of 'perOutput' or 'forAllOutputs' is required");
} }
@@ -377,7 +605,7 @@ DerivationOptions adl_serializer<DerivationOptions>::from_json(const json & json
.unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"), .unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"),
.passAsFile = getStringSet(valueAt(json, "passAsFile")), .passAsFile = getStringSet(valueAt(json, "passAsFile")),
.exportReferencesGraph = getMap<StringSet>(getObject(valueAt(json, "exportReferencesGraph")), getStringSet), .exportReferencesGraph = valueAt(json, "exportReferencesGraph"),
.additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")), .additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")),
.noChroot = getBoolean(valueAt(json, "noChroot")), .noChroot = getBoolean(valueAt(json, "noChroot")),
@@ -391,16 +619,17 @@ DerivationOptions adl_serializer<DerivationOptions>::from_json(const json & json
}; };
} }
void adl_serializer<DerivationOptions>::to_json(json & json, const DerivationOptions & o) void adl_serializer<DerivationOptions<SingleDerivedPath>>::to_json(
json & json, const DerivationOptions<SingleDerivedPath> & o)
{ {
json["outputChecks"] = std::visit( json["outputChecks"] = std::visit(
overloaded{ overloaded{
[&](const OutputChecks & checks) { [&](const OutputChecks<SingleDerivedPath> & checks) {
nlohmann::json outputChecks; nlohmann::json outputChecks;
outputChecks["forAllOutputs"] = checks; outputChecks["forAllOutputs"] = checks;
return outputChecks; return outputChecks;
}, },
[&](const std::map<std::string, OutputChecks> & checksPerOutput) { [&](const std::map<std::string, OutputChecks<SingleDerivedPath>> & checksPerOutput) {
nlohmann::json outputChecks; nlohmann::json outputChecks;
outputChecks["perOutput"] = checksPerOutput; outputChecks["perOutput"] = checksPerOutput;
return outputChecks; return outputChecks;
@@ -423,7 +652,7 @@ void adl_serializer<DerivationOptions>::to_json(json & json, const DerivationOpt
json["allowSubstitutes"] = o.allowSubstitutes; json["allowSubstitutes"] = o.allowSubstitutes;
} }
DerivationOptions::OutputChecks adl_serializer<DerivationOptions::OutputChecks>::from_json(const json & json_) OutputChecks<SingleDerivedPath> adl_serializer<OutputChecks<SingleDerivedPath>>::from_json(const json & json_)
{ {
auto & json = getObject(json_); auto & json = getObject(json_);
@@ -431,14 +660,16 @@ DerivationOptions::OutputChecks adl_serializer<DerivationOptions::OutputChecks>:
.ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")), .ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")),
.maxSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxSize"))), .maxSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxSize"))),
.maxClosureSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxClosureSize"))), .maxClosureSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxClosureSize"))),
.allowedReferences = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedReferences"))), .allowedReferences =
.disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")), ptrToOwned<std::set<DrvRef<SingleDerivedPath>>>(getNullable(valueAt(json, "allowedReferences"))),
.allowedRequisites = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedRequisites"))), .disallowedReferences = valueAt(json, "disallowedReferences"),
.disallowedRequisites = getStringSet(valueAt(json, "disallowedRequisites")), .allowedRequisites =
ptrToOwned<std::set<DrvRef<SingleDerivedPath>>>(getNullable(valueAt(json, "allowedRequisites"))),
.disallowedRequisites = valueAt(json, "disallowedRequisites"),
}; };
} }
void adl_serializer<DerivationOptions::OutputChecks>::to_json(json & json, const DerivationOptions::OutputChecks & c) void adl_serializer<OutputChecks<SingleDerivedPath>>::to_json(json & json, const OutputChecks<SingleDerivedPath> & c)
{ {
json["ignoreSelfRefs"] = c.ignoreSelfRefs; json["ignoreSelfRefs"] = c.ignoreSelfRefs;
json["maxSize"] = c.maxSize; json["maxSize"] = c.maxSize;
+46 -53
View File
@@ -13,6 +13,7 @@
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include <boost/unordered/concurrent_flat_map.hpp> #include <boost/unordered/concurrent_flat_map.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <optional>
namespace nix { namespace nix {
@@ -791,71 +792,63 @@ std::string outputPathName(std::string_view drvName, OutputNameView outputName)
DerivationType BasicDerivation::type() const DerivationType BasicDerivation::type() const
{ {
std::set<std::string_view> inputAddressedOutputs, fixedCAOutputs, floatingCAOutputs, deferredIAOutputs,
impureOutputs;
std::optional<HashAlgorithm> floatingHashAlgo; std::optional<HashAlgorithm> floatingHashAlgo;
std::optional<DerivationType> ty;
auto decide = [&](DerivationType newTy) {
if (!ty)
ty = newTy;
else if (ty.value() != newTy)
throw Error("can't mix derivation output types");
else if (ty.value() == DerivationType::ContentAddressed{.sandboxed = false, .fixed = true})
// FIXME: Experimental feature?
throw Error("only one fixed output is allowed for now");
};
for (auto & i : outputs) { for (auto & i : outputs) {
std::visit( std::visit(
overloaded{ overloaded{
[&](const DerivationOutput::InputAddressed &) { inputAddressedOutputs.insert(i.first); }, [&](const DerivationOutput::InputAddressed &) {
[&](const DerivationOutput::CAFixed &) { fixedCAOutputs.insert(i.first); }, decide(
[&](const DerivationOutput::CAFloating & dof) { DerivationType::InputAddressed{
floatingCAOutputs.insert(i.first); .deferred = false,
if (!floatingHashAlgo) { });
floatingHashAlgo = dof.hashAlgo;
} else {
if (*floatingHashAlgo != dof.hashAlgo)
throw Error("all floating outputs must use the same hash algorithm");
}
}, },
[&](const DerivationOutput::Deferred &) { deferredIAOutputs.insert(i.first); }, [&](const DerivationOutput::CAFixed &) {
[&](const DerivationOutput::Impure &) { impureOutputs.insert(i.first); }, decide(
DerivationType::ContentAddressed{
.sandboxed = false,
.fixed = true,
});
if (i.first != "out"sv)
throw Error("single fixed output must be named \"out\"");
},
[&](const DerivationOutput::CAFloating & dof) {
decide(
DerivationType::ContentAddressed{
.sandboxed = true,
.fixed = false,
});
if (!floatingHashAlgo)
floatingHashAlgo = dof.hashAlgo;
else if (*floatingHashAlgo != dof.hashAlgo)
throw Error("all floating outputs must use the same hash algorithm");
},
[&](const DerivationOutput::Deferred &) {
decide(
DerivationType::InputAddressed{
.deferred = true,
});
},
[&](const DerivationOutput::Impure &) { decide(DerivationType::Impure{}); },
}, },
i.second.raw); i.second.raw);
} }
if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() if (!ty)
&& deferredIAOutputs.empty() && impureOutputs.empty())
throw Error("must have at least one output"); throw Error("must have at least one output");
if (!inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() return ty.value();
&& deferredIAOutputs.empty() && impureOutputs.empty())
return DerivationType::InputAddressed{
.deferred = false,
};
if (inputAddressedOutputs.empty() && !fixedCAOutputs.empty() && floatingCAOutputs.empty()
&& deferredIAOutputs.empty() && impureOutputs.empty()) {
if (fixedCAOutputs.size() > 1)
// FIXME: Experimental feature?
throw Error("only one fixed output is allowed for now");
if (*fixedCAOutputs.begin() != "out"sv)
throw Error("single fixed output must be named \"out\"");
return DerivationType::ContentAddressed{
.sandboxed = false,
.fixed = true,
};
}
if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && !floatingCAOutputs.empty()
&& deferredIAOutputs.empty() && impureOutputs.empty())
return DerivationType::ContentAddressed{
.sandboxed = true,
.fixed = false,
};
if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty()
&& !deferredIAOutputs.empty() && impureOutputs.empty())
return DerivationType::InputAddressed{
.deferred = true,
};
if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty()
&& deferredIAOutputs.empty() && !impureOutputs.empty())
return DerivationType::Impure{};
throw Error("can't mix derivation output types");
} }
DrvHashes drvHashes; DrvHashes drvHashes;
+43
View File
@@ -1,5 +1,6 @@
#include "nix/store/downstream-placeholder.hh" #include "nix/store/downstream-placeholder.hh"
#include "nix/store/derivations.hh" #include "nix/store/derivations.hh"
#include "nix/util/json-utils.hh"
namespace nix { namespace nix {
@@ -49,3 +50,45 @@ DownstreamPlaceholder DownstreamPlaceholder::fromSingleDerivedPathBuilt(
} }
} // namespace nix } // namespace nix
namespace nlohmann {
using namespace nix;
template<typename Item>
DrvRef<Item> adl_serializer<DrvRef<Item>>::from_json(const json & json)
{
// OutputName case: { "drvPath": "self", "output": <output> }
if (json.type() == nlohmann::json::value_t::object) {
auto & obj = getObject(json);
if (auto * drvPath_ = get(obj, "drvPath")) {
auto & drvPath = *drvPath_;
if (drvPath.type() == nlohmann::json::value_t::string && getString(drvPath) == "self") {
return getString(valueAt(obj, "output"));
}
}
}
// Input case
return adl_serializer<Item>::from_json(json);
}
template<typename Item>
void adl_serializer<DrvRef<Item>>::to_json(json & json, const DrvRef<Item> & ref)
{
std::visit(
overloaded{
[&](const OutputName & outputName) {
json = nlohmann::json::object();
json["drvPath"] = "self";
json["output"] = outputName;
},
[&](const Item & item) { json = item; },
},
ref);
}
template struct adl_serializer<nix::DrvRef<StorePath>>;
template struct adl_serializer<nix::DrvRef<SingleDerivedPath>>;
} // namespace nlohmann
+65 -42
View File
@@ -151,15 +151,23 @@ struct curlFileTransfer : public FileTransfer
} }
} }
void failEx(std::exception_ptr ex) void failEx(std::exception_ptr ex) noexcept
{ {
assert(!done); assert(!done);
done = true; done = true;
try {
std::rethrow_exception(ex);
} catch (nix::Error & e) {
/* Add more context to the error message. */
e.addTrace({}, "during %s of '%s'", Uncolored(request.verb()), request.uri.to_string());
} catch (...) {
/* Can't add more context to the error. */
}
callback.rethrow(ex); callback.rethrow(ex);
} }
template<class T> template<class T>
void fail(T && e) void fail(T && e) noexcept
{ {
failEx(std::make_exception_ptr(std::forward<T>(e))); failEx(std::make_exception_ptr(std::forward<T>(e)));
} }
@@ -168,32 +176,30 @@ struct curlFileTransfer : public FileTransfer
std::shared_ptr<FinishSink> decompressionSink; std::shared_ptr<FinishSink> decompressionSink;
std::optional<StringSink> errorSink; std::optional<StringSink> errorSink;
std::exception_ptr writeException; std::exception_ptr callbackException;
size_t writeCallback(void * contents, size_t size, size_t nmemb) size_t writeCallback(void * contents, size_t size, size_t nmemb) noexcept
{ try {
try { size_t realSize = size * nmemb;
size_t realSize = size * nmemb; result.bodySize += realSize;
result.bodySize += realSize;
if (!decompressionSink) { if (!decompressionSink) {
decompressionSink = makeDecompressionSink(encoding, finalSink); decompressionSink = makeDecompressionSink(encoding, finalSink);
if (!successfulStatuses.count(getHTTPStatus())) { if (!successfulStatuses.count(getHTTPStatus())) {
// In this case we want to construct a TeeSink, to keep // In this case we want to construct a TeeSink, to keep
// the response around (which we figure won't be big // the response around (which we figure won't be big
// like an actual download should be) to improve error // like an actual download should be) to improve error
// messages. // messages.
errorSink = StringSink{}; errorSink = StringSink{};
}
} }
(*decompressionSink)({(char *) contents, realSize});
return realSize;
} catch (...) {
writeException = std::current_exception();
return 0;
} }
(*decompressionSink)({(char *) contents, realSize});
return realSize;
} catch (...) {
callbackException = std::current_exception();
return 0;
} }
static size_t writeCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) static size_t writeCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp)
@@ -209,8 +215,8 @@ struct curlFileTransfer : public FileTransfer
result.urls.push_back(effectiveUriCStr); result.urls.push_back(effectiveUriCStr);
} }
size_t headerCallback(void * contents, size_t size, size_t nmemb) size_t headerCallback(void * contents, size_t size, size_t nmemb) noexcept
{ try {
size_t realSize = size * nmemb; size_t realSize = size * nmemb;
std::string line((char *) contents, realSize); std::string line((char *) contents, realSize);
printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line)); printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line));
@@ -263,6 +269,15 @@ struct curlFileTransfer : public FileTransfer
} }
} }
return realSize; return realSize;
} catch (...) {
#if LIBCURL_VERSION_NUM >= 0x075700
/* https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html:
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. */
callbackException = std::current_exception();
return CURL_WRITEFUNC_ERROR;
#else
return realSize;
#endif
} }
static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp)
@@ -270,14 +285,17 @@ struct curlFileTransfer : public FileTransfer
return ((TransferItem *) userp)->headerCallback(contents, size, nmemb); return ((TransferItem *) userp)->headerCallback(contents, size, nmemb);
} }
int progressCallback(curl_off_t dltotal, curl_off_t dlnow) int progressCallback(curl_off_t dltotal, curl_off_t dlnow) noexcept
{ try {
try { act.progress(dlnow, dltotal);
act.progress(dlnow, dltotal);
} catch (nix::Interrupted &) {
assert(getInterrupted());
}
return getInterrupted(); return getInterrupted();
} catch (nix::Interrupted &) {
assert(getInterrupted());
return 1;
} catch (...) {
/* Something unexpected has happened like logger throwing an exception. */
callbackException = std::current_exception();
return 1;
} }
static int progressCallbackWrapper( static int progressCallbackWrapper(
@@ -288,11 +306,14 @@ struct curlFileTransfer : public FileTransfer
return item.progressCallback(isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow); return item.progressCallback(isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow);
} }
static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) noexcept
{ try {
if (type == CURLINFO_TEXT) if (type == CURLINFO_TEXT)
vomit("curl: %s", chomp(std::string(data, size))); vomit("curl: %s", chomp(std::string(data, size)));
return 0; return 0;
} catch (...) {
/* Swallow the exception. Nothing left to do. */
return 0;
} }
size_t readCallback(char * buffer, size_t size, size_t nitems) noexcept size_t readCallback(char * buffer, size_t size, size_t nitems) noexcept
@@ -302,6 +323,7 @@ struct curlFileTransfer : public FileTransfer
} catch (EndOfFile &) { } catch (EndOfFile &) {
return 0; return 0;
} catch (...) { } catch (...) {
callbackException = std::current_exception();
return CURL_READFUNC_ABORT; return CURL_READFUNC_ABORT;
} }
@@ -333,6 +355,7 @@ struct curlFileTransfer : public FileTransfer
} }
return CURL_SEEKFUNC_OK; return CURL_SEEKFUNC_OK;
} catch (...) { } catch (...) {
callbackException = std::current_exception();
return CURL_SEEKFUNC_FAIL; return CURL_SEEKFUNC_FAIL;
} }
@@ -386,17 +409,17 @@ struct curlFileTransfer : public FileTransfer
if (settings.downloadSpeed.get() > 0) if (settings.downloadSpeed.get() > 0)
curl_easy_setopt(req, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) (settings.downloadSpeed.get() * 1024)); curl_easy_setopt(req, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) (settings.downloadSpeed.get() * 1024));
if (request.method == HttpMethod::HEAD) if (request.method == HttpMethod::Head)
curl_easy_setopt(req, CURLOPT_NOBODY, 1); curl_easy_setopt(req, CURLOPT_NOBODY, 1);
if (request.method == HttpMethod::DELETE) if (request.method == HttpMethod::Delete)
curl_easy_setopt(req, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(req, CURLOPT_CUSTOMREQUEST, "DELETE");
if (request.data) { if (request.data) {
if (request.method == HttpMethod::POST) { if (request.method == HttpMethod::Post) {
curl_easy_setopt(req, CURLOPT_POST, 1L); curl_easy_setopt(req, CURLOPT_POST, 1L);
curl_easy_setopt(req, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) request.data->sizeHint); curl_easy_setopt(req, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) request.data->sizeHint);
} else if (request.method == HttpMethod::PUT) { } else if (request.method == HttpMethod::Put) {
curl_easy_setopt(req, CURLOPT_UPLOAD, 1L); curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->sizeHint); curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->sizeHint);
} else { } else {
@@ -476,7 +499,7 @@ struct curlFileTransfer : public FileTransfer
try { try {
decompressionSink->finish(); decompressionSink->finish();
} catch (...) { } catch (...) {
writeException = std::current_exception(); callbackException = std::current_exception();
} }
} }
@@ -485,8 +508,8 @@ struct curlFileTransfer : public FileTransfer
httpStatus = 304; httpStatus = 304;
} }
if (writeException) if (callbackException)
failEx(writeException); failEx(callbackException);
else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) { else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) {
result.cached = httpStatus == 304; result.cached = httpStatus == 304;
+2 -2
View File
@@ -120,7 +120,7 @@ bool HttpBinaryCacheStore::fileExists(const std::string & path)
try { try {
FileTransferRequest request(makeRequest(path)); FileTransferRequest request(makeRequest(path));
request.method = HttpMethod::HEAD; request.method = HttpMethod::Head;
getFileTransfer()->download(request); getFileTransfer()->download(request);
return true; return true;
} catch (FileTransferError & e) { } catch (FileTransferError & e) {
@@ -141,7 +141,7 @@ void HttpBinaryCacheStore::upload(
std::optional<Headers> headers) std::optional<Headers> headers)
{ {
auto req = makeRequest(path); auto req = makeRequest(path);
req.method = HttpMethod::PUT; req.method = HttpMethod::Put;
if (headers) { if (headers) {
req.headers.reserve(req.headers.size() + headers->size()); req.headers.reserve(req.headers.size() + headers->size());
@@ -69,7 +69,7 @@ struct DerivationBuilderParams
* *
* @todo this should be part of `Derivation`. * @todo this should be part of `Derivation`.
*/ */
const DerivationOptions & drvOptions; const DerivationOptions<StorePath> & drvOptions;
// The remainder is state held during the build. // The remainder is state held during the build.
@@ -52,8 +52,6 @@ private:
*/ */
std::unique_ptr<Derivation> drv; std::unique_ptr<Derivation> drv;
std::unique_ptr<DerivationOptions> drvOptions;
/** /**
* The remainder is state held during the build. * The remainder is state held during the build.
*/ */
@@ -115,7 +113,8 @@ private:
/** /**
* Is the build hook willing to perform the build? * Is the build hook willing to perform the build?
*/ */
HookReply tryBuildHook(const std::map<std::string, InitialOutput> & initialOutputs); HookReply tryBuildHook(
const std::map<std::string, InitialOutput> & initialOutputs, const DerivationOptions<StorePath> & drvOptions);
/** /**
* Open a log file and a pipe to it. * Open a log file and a pipe to it.
@@ -8,6 +8,7 @@ namespace nix {
class Store; class Store;
struct Derivation; struct Derivation;
template<typename Input>
struct DerivationOptions; struct DerivationOptions;
/** /**
@@ -77,7 +78,10 @@ struct DesugaredEnv
* just part of `Derivation`. * just part of `Derivation`.
*/ */
static DesugaredEnv create( static DesugaredEnv create(
Store & store, const Derivation & drv, const DerivationOptions & drvOptions, const StorePathSet & inputPaths); Store & store,
const Derivation & drv,
const DerivationOptions<StorePath> & drvOptions,
const StorePathSet & inputPaths);
}; };
} // namespace nix } // namespace nix
@@ -8,7 +8,8 @@
#include "nix/util/types.hh" #include "nix/util/types.hh"
#include "nix/util/json-impls.hh" #include "nix/util/json-impls.hh"
#include "nix/store/path.hh" #include "nix/store/store-dir-config.hh"
#include "nix/store/downstream-placeholder.hh"
namespace nix { namespace nix {
@@ -17,6 +18,9 @@ struct StoreDirConfig;
struct BasicDerivation; struct BasicDerivation;
struct StructuredAttrs; struct StructuredAttrs;
template<typename V>
struct DerivedPathMap;
/** /**
* This represents all the special options on a `Derivation`. * This represents all the special options on a `Derivation`.
* *
@@ -34,6 +38,7 @@ struct StructuredAttrs;
* separately. That would be nice to separate concerns, and not make any * separately. That would be nice to separate concerns, and not make any
* environment variable names magical. * environment variable names magical.
*/ */
template<typename Input>
struct DerivationOptions struct DerivationOptions
{ {
struct OutputChecks struct OutputChecks
@@ -41,13 +46,15 @@ struct DerivationOptions
bool ignoreSelfRefs = false; bool ignoreSelfRefs = false;
std::optional<uint64_t> maxSize, maxClosureSize; std::optional<uint64_t> maxSize, maxClosureSize;
using DrvRef = nix::DrvRef<Input>;
/** /**
* env: allowedReferences * env: allowedReferences
* *
* A value of `nullopt` indicates that the check is skipped. * A value of `nullopt` indicates that the check is skipped.
* This means that all references are allowed. * This means that all references are allowed.
*/ */
std::optional<StringSet> allowedReferences; std::optional<std::set<DrvRef>> allowedReferences;
/** /**
* env: disallowedReferences * env: disallowedReferences
@@ -55,21 +62,21 @@ struct DerivationOptions
* No needed for `std::optional`, because skipping the check is * No needed for `std::optional`, because skipping the check is
* the same as disallowing the references. * the same as disallowing the references.
*/ */
StringSet disallowedReferences; std::set<DrvRef> disallowedReferences;
/** /**
* env: allowedRequisites * env: allowedRequisites
* *
* See `allowedReferences` * See `allowedReferences`
*/ */
std::optional<StringSet> allowedRequisites; std::optional<std::set<DrvRef>> allowedRequisites;
/** /**
* env: disallowedRequisites * env: disallowedRequisites
* *
* See `disallowedReferences` * See `disallowedReferences`
*/ */
StringSet disallowedRequisites; std::set<DrvRef> disallowedRequisites;
bool operator==(const OutputChecks &) const = default; bool operator==(const OutputChecks &) const = default;
}; };
@@ -116,23 +123,7 @@ struct DerivationOptions
* attributes give to the builder. The set of paths in the original JSON * attributes give to the builder. The set of paths in the original JSON
* is replaced with a list of `PathInfo` in JSON format. * is replaced with a list of `PathInfo` in JSON format.
*/ */
std::map<std::string, StringSet> exportReferencesGraph; std::map<std::string, std::set<Input>> exportReferencesGraph;
/**
* Once a derivations is resolved, the strings in in
* `exportReferencesGraph` should all be store paths (with possible
* suffix paths, but those are discarded).
*
* @return The parsed path set for for each key in the map.
*
* @todo Ideally, `exportReferencesGraph` would just store
* `StorePath`s for this, but we can't just do that, because for CA
* derivations they is actually in general `DerivedPath`s (via
* placeholder strings) until the derivation is resolved and exact
* inputs store paths are known. We can use better types for that
* too, but that is a longer project.
*/
std::map<std::string, StorePathSet> getParsedExportReferencesGraph(const StoreDirConfig & store) const;
/** /**
* env: __sandboxProfile * env: __sandboxProfile
@@ -185,18 +176,6 @@ struct DerivationOptions
bool operator==(const DerivationOptions &) const = default; bool operator==(const DerivationOptions &) const = default;
/**
* Parse this information from its legacy encoding as part of the
* environment. This should not be used with nice greenfield formats
* (e.g. JSON) but is necessary for supporting old formats (e.g.
* ATerm).
*/
static DerivationOptions
fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn = true);
static DerivationOptions
fromStructuredAttrs(const StringMap & env, const std::optional<StructuredAttrs> & parsed, bool shouldWarn = true);
/** /**
* @param drv Must be the same derivation we parsed this from. In * @param drv Must be the same derivation we parsed this from. In
* the future we'll flip things around so a `BasicDerivation` has * the future we'll flip things around so a `BasicDerivation` has
@@ -222,7 +201,49 @@ struct DerivationOptions
bool useUidRange(const BasicDerivation & drv) const; bool useUidRange(const BasicDerivation & drv) const;
}; };
extern template struct DerivationOptions<StorePath>;
extern template struct DerivationOptions<SingleDerivedPath>;
struct DerivationOutput;
/**
* Parse this information from its legacy encoding as part of the
* environment. This should not be used with nice greenfield formats
* (e.g. JSON) but is necessary for supporting old formats (e.g.
* ATerm).
*/
DerivationOptions<SingleDerivedPath> derivationOptionsFromStructuredAttrs(
const StoreDirConfig & store,
const DerivedPathMap<StringSet> & inputDrvs,
const StringMap & env,
const StructuredAttrs * parsed,
bool shouldWarn = true,
const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings);
DerivationOptions<StorePath> derivationOptionsFromStructuredAttrs(
const StoreDirConfig & store,
const StringMap & env,
const StructuredAttrs * parsed,
bool shouldWarn = true,
const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings);
/**
* This is the counterpart of `Derivation::tryResolve`. In particular,
* it takes the same sort of callback, which is used to reolve
* non-constant deriving paths.
*
* We need this function when resolving a derivation, and we will use
* this as part of that if/when `Derivation` includes
* `DerivationOptions`
*/
std::optional<DerivationOptions<StorePath>> tryResolve(
const DerivationOptions<SingleDerivedPath> & drvOptions,
std::function<std::optional<StorePath>(ref<const SingleDerivedPath> drvPath, const std::string & outputName)>
queryResolutionChain);
}; // namespace nix }; // namespace nix
JSON_IMPL(DerivationOptions); JSON_IMPL(nix::DerivationOptions<nix::StorePath>);
JSON_IMPL(DerivationOptions::OutputChecks) JSON_IMPL(nix::DerivationOptions<nix::SingleDerivedPath>);
JSON_IMPL(nix::DerivationOptions<nix::StorePath>::OutputChecks)
JSON_IMPL(nix::DerivationOptions<nix::SingleDerivedPath>::OutputChecks)
@@ -2,11 +2,23 @@
///@file ///@file
#include "nix/util/hash.hh" #include "nix/util/hash.hh"
#include "nix/util/json-impls.hh"
#include "nix/store/path.hh" #include "nix/store/path.hh"
#include "nix/store/derived-path.hh" #include "nix/store/derived-path.hh"
namespace nix { namespace nix {
/**
* A reference is either to a to-be-registered output (by name),
* or to an already-registered store object (by `Input`).
*
* `Ref<SingleDerivedPath` is a representation of something that can be
* turned into a placeholder. (Regular own-output placeholder in the
* first case, `DownstreamPlaceholder` in the second case.)
*/
template<typename Input>
using DrvRef = std::variant<OutputName, Input>;
/** /**
* Downstream Placeholders are opaque and almost certainly unique values * Downstream Placeholders are opaque and almost certainly unique values
* used to allow derivations to refer to store objects which are yet to * used to allow derivations to refer to store objects which are yet to
@@ -92,3 +104,17 @@ public:
}; };
} // namespace nix } // namespace nix
namespace nlohmann {
template<typename Item>
struct adl_serializer<nix::DrvRef<Item>>
{
static nix::DrvRef<Item> from_json(const json & json);
static void to_json(json & json, const nix::DrvRef<Item> & t);
};
extern template struct adl_serializer<nix::DrvRef<nix::StorePath>>;
extern template struct adl_serializer<nix::DrvRef<nix::SingleDerivedPath>>;
} // namespace nlohmann
+11 -11
View File
@@ -87,11 +87,11 @@ extern const unsigned int RETRY_TIME_MS_DEFAULT;
* HTTP methods supported by FileTransfer. * HTTP methods supported by FileTransfer.
*/ */
enum struct HttpMethod { enum struct HttpMethod {
GET, Get,
PUT, Put,
HEAD, Head,
POST, Post,
DELETE, Delete,
}; };
/** /**
@@ -110,7 +110,7 @@ struct FileTransferRequest
VerbatimURL uri; VerbatimURL uri;
Headers headers; Headers headers;
std::string expectedETag; std::string expectedETag;
HttpMethod method = HttpMethod::GET; HttpMethod method = HttpMethod::Get;
size_t tries = fileTransferSettings.tries; size_t tries = fileTransferSettings.tries;
unsigned int baseRetryTimeMs = RETRY_TIME_MS_DEFAULT; unsigned int baseRetryTimeMs = RETRY_TIME_MS_DEFAULT;
ActivityId parentAct; ActivityId parentAct;
@@ -164,14 +164,14 @@ struct FileTransferRequest
std::string verb() const std::string verb() const
{ {
switch (method) { switch (method) {
case HttpMethod::HEAD: case HttpMethod::Head:
case HttpMethod::GET: case HttpMethod::Get:
return "download"; return "download";
case HttpMethod::PUT: case HttpMethod::Put:
case HttpMethod::POST: case HttpMethod::Post:
assert(data); assert(data);
return "upload"; return "upload";
case HttpMethod::DELETE: case HttpMethod::Delete:
return "delet"; return "delet";
} }
unreachable(); unreachable();
@@ -9,6 +9,7 @@
namespace nix { namespace nix {
class Store; class Store;
template<typename Input>
struct DerivationOptions; struct DerivationOptions;
struct DerivationOutput; struct DerivationOutput;
@@ -47,7 +48,7 @@ struct StructuredAttrs
nlohmann::json::object_t prepareStructuredAttrs( nlohmann::json::object_t prepareStructuredAttrs(
Store & store, Store & store,
const DerivationOptions & drvOptions, const DerivationOptions<StorePath> & drvOptions,
const StorePathSet & inputPaths, const StorePathSet & inputPaths,
const DerivationOutputs & outputs) const; const DerivationOutputs & outputs) const;
+5 -9
View File
@@ -1246,10 +1246,8 @@ StorePath LocalStore::addToStoreFromDump(
auto desc = ContentAddressWithReferences::fromParts( auto desc = ContentAddressWithReferences::fromParts(
hashMethod, hashMethod,
methodsMatch methodsMatch ? dumpHash
? dumpHash : hashPath(makeFSSourceAccessor(tempPath), hashMethod.getFileIngestionMethod(), hashAlgo).first,
: hashPath(PosixSourceAccessor::createAtRoot(tempPath), hashMethod.getFileIngestionMethod(), hashAlgo)
.first,
{ {
.others = references, .others = references,
// caller is not capable of creating a self-reference, because this is content-addressed without modulus // caller is not capable of creating a self-reference, because this is content-addressed without modulus
@@ -1385,11 +1383,9 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
checkInterrupt(); checkInterrupt();
auto name = link.path().filename(); auto name = link.path().filename();
printMsg(lvlTalkative, "checking contents of %s", name); printMsg(lvlTalkative, "checking contents of %s", name);
std::string hash = hashPath( std::string hash =
PosixSourceAccessor::createAtRoot(link.path()), hashPath(makeFSSourceAccessor(link.path()), FileIngestionMethod::NixArchive, HashAlgorithm::SHA256)
FileIngestionMethod::NixArchive, .first.to_string(HashFormat::Nix32, false);
HashAlgorithm::SHA256)
.first.to_string(HashFormat::Nix32, false);
if (hash != name.string()) { if (hash != name.string()) {
printError("link %s was modified! expected hash %s, got '%s'", link.path(), name, hash); printError("link %s was modified! expected hash %s, got '%s'", link.path(), name, hash);
if (repair) { if (repair) {
+3 -2
View File
@@ -225,11 +225,12 @@ MissingPaths Store::queryMissing(const std::vector<DerivedPath> & targets)
return; return;
auto drv = make_ref<Derivation>(derivationFromPath(drvPath)); auto drv = make_ref<Derivation>(derivationFromPath(drvPath));
DerivationOptions drvOptions; DerivationOptions<SingleDerivedPath> drvOptions;
try { try {
// FIXME: this is a lot of work just to get the value // FIXME: this is a lot of work just to get the value
// of `allowSubstitutes`. // of `allowSubstitutes`.
drvOptions = DerivationOptions::fromStructuredAttrs(drv->env, drv->structuredAttrs); drvOptions = derivationOptionsFromStructuredAttrs(
*this, drv->inputDrvs, drv->env, get(drv->structuredAttrs));
} catch (Error & e) { } catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", printStorePath(drvPath)); e.addTrace({}, "while parsing derivation '%s'", printStorePath(drvPath));
throw; throw;
+1 -1
View File
@@ -172,7 +172,7 @@ void LocalStore::optimisePath_(
auto stLink = lstat(linkPath.string()); auto stLink = lstat(linkPath.string());
if (st.st_size != stLink.st_size || (repair && hash != ({ if (st.st_size != stLink.st_size || (repair && hash != ({
hashPath( hashPath(
PosixSourceAccessor::createAtRoot(linkPath), makeFSSourceAccessor(linkPath),
FileSerialisationMethod::NixArchive, FileSerialisationMethod::NixArchive,
HashAlgorithm::SHA256) HashAlgorithm::SHA256)
.hash; .hash;
+3 -3
View File
@@ -100,7 +100,7 @@ static nlohmann::json pathInfoToJSON(Store & store, const StorePathSet & storePa
nlohmann::json::object_t StructuredAttrs::prepareStructuredAttrs( nlohmann::json::object_t StructuredAttrs::prepareStructuredAttrs(
Store & store, Store & store,
const DerivationOptions & drvOptions, const DerivationOptions<StorePath> & drvOptions,
const StorePathSet & inputPaths, const StorePathSet & inputPaths,
const DerivationOutputs & outputs) const const DerivationOutputs & outputs) const
{ {
@@ -114,8 +114,8 @@ nlohmann::json::object_t StructuredAttrs::prepareStructuredAttrs(
json["outputs"] = std::move(outputsJson); json["outputs"] = std::move(outputsJson);
/* Handle exportReferencesGraph. */ /* Handle exportReferencesGraph. */
for (auto & [key, storePaths] : drvOptions.getParsedExportReferencesGraph(store)) { for (auto & [key, storePaths] : drvOptions.exportReferencesGraph) {
json[key] = pathInfoToJSON(store, store.exportReferences(storePaths, storePaths)); json[key] = pathInfoToJSON(store, store.exportReferences(storePaths, inputPaths));
} }
return json; return json;
+4 -4
View File
@@ -295,7 +295,7 @@ std::string S3BinaryCacheStore::createMultipartUpload(
url.query["uploads"] = ""; url.query["uploads"] = "";
req.uri = VerbatimURL(url); req.uri = VerbatimURL(url);
req.method = HttpMethod::POST; req.method = HttpMethod::Post;
StringSource payload{std::string_view("")}; StringSource payload{std::string_view("")};
req.data = {payload}; req.data = {payload};
req.mimeType = mimeType; req.mimeType = mimeType;
@@ -325,7 +325,7 @@ S3BinaryCacheStore::uploadPart(std::string_view key, std::string_view uploadId,
} }
auto req = makeRequest(key); auto req = makeRequest(key);
req.method = HttpMethod::PUT; req.method = HttpMethod::Put;
req.setupForS3(); req.setupForS3();
auto url = req.uri.parsed(); auto url = req.uri.parsed();
@@ -355,7 +355,7 @@ void S3BinaryCacheStore::abortMultipartUpload(std::string_view key, std::string_
auto url = req.uri.parsed(); auto url = req.uri.parsed();
url.query["uploadId"] = uploadId; url.query["uploadId"] = uploadId;
req.uri = VerbatimURL(url); req.uri = VerbatimURL(url);
req.method = HttpMethod::DELETE; req.method = HttpMethod::Delete;
getFileTransfer()->enqueueFileTransfer(req).get(); getFileTransfer()->enqueueFileTransfer(req).get();
} catch (...) { } catch (...) {
@@ -372,7 +372,7 @@ void S3BinaryCacheStore::completeMultipartUpload(
auto url = req.uri.parsed(); auto url = req.uri.parsed();
url.query["uploadId"] = uploadId; url.query["uploadId"] = uploadId;
req.uri = VerbatimURL(url); req.uri = VerbatimURL(url);
req.method = HttpMethod::POST; req.method = HttpMethod::Post;
std::string xml = "<CompleteMultipartUpload>"; std::string xml = "<CompleteMultipartUpload>";
for (const auto & [idx, etag] : enumerate(partEtags)) { for (const auto & [idx, etag] : enumerate(partEtags)) {
+1 -1
View File
@@ -36,7 +36,7 @@ nix_err nix_context_error(nix_c_context * context)
const char * demangled = abi::__cxa_demangle(typeid(e).name(), 0, 0, &status); const char * demangled = abi::__cxa_demangle(typeid(e).name(), 0, 0, &status);
if (demangled) { if (demangled) {
context->name = demangled; context->name = demangled;
// todo: free(demangled); free((void *) demangled);
} else { } else {
context->name = typeid(e).name(); context->name = typeid(e).name();
} }
+39 -39
View File
@@ -200,54 +200,54 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
} }
else if (type == "directory") { else if (type == "directory") {
sink.createDirectory(path); sink.createDirectory(path, [&](FileSystemObjectSink & dirSink, const CanonPath & relDirPath) {
std::map<Path, int, CaseInsensitiveCompare> names;
std::map<Path, int, CaseInsensitiveCompare> names; std::string prevName;
std::string prevName; while (1) {
auto tag = getString();
while (1) { if (tag == ")")
auto tag = getString(); break;
if (tag == ")") if (tag != "entry")
break; throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
if (tag != "entry") expectTag("(");
throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
expectTag("("); expectTag("name");
expectTag("name"); auto name = getString();
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos
|| name.find((char) 0) != std::string::npos)
throw badArchive("NAR contains invalid file name '%1%'", name);
if (name <= prevName)
throw badArchive("NAR directory is not sorted");
prevName = name;
if (archiveSettings.useCaseHack) {
auto i = names.find(name);
if (i != names.end()) {
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 badArchive(
"NAR contains file name '%s' that collides with case-hacked file name '%s'",
prevName,
j->first);
} else
names[name] = 0;
}
auto name = getString(); expectTag("node");
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos
|| name.find((char) 0) != std::string::npos) parse(dirSink, source, relDirPath / name);
throw badArchive("NAR contains invalid file name '%1%'", name);
if (name <= prevName) expectTag(")");
throw badArchive("NAR directory is not sorted");
prevName = name;
if (archiveSettings.useCaseHack) {
auto i = names.find(name);
if (i != names.end()) {
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 badArchive(
"NAR contains file name '%s' that collides with case-hacked file name '%s'",
prevName,
j->first);
} else
names[name] = 0;
} }
});
expectTag("node");
parse(sink, source, path / name);
expectTag(")");
}
} }
else if (type == "symlink") { else if (type == "symlink") {
+64 -39
View File
@@ -14,44 +14,6 @@
namespace nix { namespace nix {
void copyRecursive(SourceAccessor & accessor, const CanonPath & from, FileSystemObjectSink & sink, const CanonPath & to)
{
auto stat = accessor.lstat(from);
switch (stat.type) {
case SourceAccessor::tSymlink: {
sink.createSymlink(to, accessor.readLink(from));
break;
}
case SourceAccessor::tRegular: {
sink.createRegularFile(to, [&](CreateRegularFileSink & crf) {
if (stat.isExecutable)
crf.isExecutable();
accessor.readFile(from, crf, [&](uint64_t size) { crf.preallocateContents(size); });
});
break;
}
case SourceAccessor::tDirectory: {
sink.createDirectory(to);
for (auto & [name, _] : accessor.readDirectory(from)) {
copyRecursive(accessor, from / name, sink, to / name);
break;
}
break;
}
case SourceAccessor::tChar:
case SourceAccessor::tBlock:
case SourceAccessor::tSocket:
case SourceAccessor::tFifo:
case SourceAccessor::tUnknown:
default:
throw Error("file '%1%' has an unsupported type of %2%", from, stat.typeString());
}
}
struct RestoreSinkSettings : Config struct RestoreSinkSettings : Config
{ {
Setting<bool> preallocateContents{ Setting<bool> preallocateContents{
@@ -70,11 +32,60 @@ static std::filesystem::path append(const std::filesystem::path & src, const Can
return dst; return dst;
} }
#ifndef _WIN32
void RestoreSink::createDirectory(const CanonPath & path, DirectoryCreatedCallback callback)
{
if (path.isRoot()) {
createDirectory(path);
callback(*this, path);
return;
}
createDirectory(path);
assert(dirFd); // If that's not true the above call must have thrown an exception.
RestoreSink dirSink{startFsync};
dirSink.dstPath = append(dstPath, path);
dirSink.dirFd = ::openat(dirFd.get(), path.rel_c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (!dirSink.dirFd)
throw SysError("opening directory '%s'", dirSink.dstPath.string());
callback(dirSink, CanonPath::root);
}
#endif
void RestoreSink::createDirectory(const CanonPath & path) void RestoreSink::createDirectory(const CanonPath & path)
{ {
auto p = append(dstPath, path); auto p = append(dstPath, path);
#ifndef _WIN32
if (dirFd) {
if (path.isRoot())
/* Trying to create a directory that we already have a file descriptor for. */
throw Error("path '%s' already exists", p.string());
if (::mkdirat(dirFd.get(), path.rel_c_str(), 0777) == -1)
throw SysError("creating directory '%s'", p.string());
return;
}
#endif
if (!std::filesystem::create_directory(p)) if (!std::filesystem::create_directory(p))
throw Error("path '%s' already exists", p.string()); throw Error("path '%s' already exists", p.string());
#ifndef _WIN32
if (path.isRoot()) {
assert(!dirFd); // Handled above
/* Open directory for further *at operations relative to the sink root
directory. */
dirFd = open(p.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (!dirFd)
throw SysError("creating directory '%1%'", p.string());
}
#endif
}; };
struct RestoreRegularFile : CreateRegularFileSink struct RestoreRegularFile : CreateRegularFileSink
@@ -114,7 +125,14 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
NULL) NULL)
#else #else
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666) [&]() {
/* O_EXCL together with O_CREAT ensures symbolic links in the last
component are not followed. */
constexpr int flags = O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC;
if (!dirFd)
return ::open(p.c_str(), flags, 0666);
return ::openat(dirFd.get(), path.rel_c_str(), flags, 0666);
}();
#endif #endif
; ;
if (!crf.fd) if (!crf.fd)
@@ -161,6 +179,13 @@ void RestoreRegularFile::operator()(std::string_view data)
void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) void RestoreSink::createSymlink(const CanonPath & path, const std::string & target)
{ {
auto p = append(dstPath, path); auto p = append(dstPath, path);
#ifndef _WIN32
if (dirFd) {
if (::symlinkat(requireCString(target), dirFd.get(), path.rel_c_str()) == -1)
throw SysError("creating symlink from '%1%' -> '%2%'", p.string(), target);
return;
}
#endif
nix::createSymlink(target, p.string()); nix::createSymlink(target, p.string());
} }
-23
View File
@@ -217,29 +217,6 @@ std::optional<Mode> convertMode(SourceAccessor::Type type)
} }
} }
void restore(FileSystemObjectSink & sink, Source & source, HashAlgorithm hashAlgo, std::function<RestoreHook> hook)
{
parse(sink, CanonPath::root, source, BlobMode::Regular, hashAlgo, [&](CanonPath name, TreeEntry entry) {
auto [accessor, from] = hook(entry.hash);
auto stat = accessor->lstat(from);
auto gotOpt = convertMode(stat.type);
if (!gotOpt)
throw Error(
"file '%s' (git hash %s) has an unsupported type",
from,
entry.hash.to_string(HashFormat::Base16, false));
auto & got = *gotOpt;
if (got != entry.mode)
throw Error(
"git mode of file '%s' (git hash %s) is %o but expected %o",
from,
entry.hash.to_string(HashFormat::Base16, false),
(RawMode) got,
(RawMode) entry.mode);
copyRecursive(*accessor, from, sink, name);
});
}
void dumpBlobPrefix(uint64_t size, Sink & sink, const ExperimentalFeatureSettings & xpSettings) void dumpBlobPrefix(uint64_t size, Sink & sink, const ExperimentalFeatureSettings & xpSettings)
{ {
xpSettings.require(Xp::GitHashing); xpSettings.require(Xp::GitHashing);
+33
View File
@@ -36,6 +36,23 @@ struct FileSystemObjectSink
virtual void createDirectory(const CanonPath & path) = 0; virtual void createDirectory(const CanonPath & path) = 0;
using DirectoryCreatedCallback = std::function<void(FileSystemObjectSink & dirSink, const CanonPath & dirRelPath)>;
/**
* Create a directory and invoke a callback with a pair of sink + CanonPath
* of the created subdirectory relative to dirSink.
*
* @note This allows for UNIX RestoreSink implementations to implement
* *at-style accessors that always keep an open file descriptor for the
* freshly created directory. Use this when it's important to disallow any
* intermediate path components from being symlinks.
*/
virtual void createDirectory(const CanonPath & path, DirectoryCreatedCallback callback)
{
createDirectory(path);
callback(*this, path);
}
/** /**
* This function in general is no re-entrant. Only one file can be * This function in general is no re-entrant. Only one file can be
* written at a time. * written at a time.
@@ -82,6 +99,18 @@ struct NullFileSystemObjectSink : FileSystemObjectSink
struct RestoreSink : FileSystemObjectSink struct RestoreSink : FileSystemObjectSink
{ {
std::filesystem::path dstPath; std::filesystem::path dstPath;
#ifndef _WIN32
/**
* File descriptor for the directory located at dstPath. Used for *at
* operations relative to this file descriptor. This sink must *never*
* follow intermediate symlinks (starting from dstPath) in case a file
* collision is encountered for various reasons like case-insensitivity or
* other types on normalization. using appropriate *at system calls and traversing
* only one path component at a time ensures that writing is race-free and is
* is not susceptible to symlink replacement.
*/
AutoCloseFD dirFd;
#endif
bool startFsync = false; bool startFsync = false;
explicit RestoreSink(bool startFsync) explicit RestoreSink(bool startFsync)
@@ -91,6 +120,10 @@ struct RestoreSink : FileSystemObjectSink
void createDirectory(const CanonPath & path) override; void createDirectory(const CanonPath & path) override;
#ifndef _WIN32
void createDirectory(const CanonPath & path, DirectoryCreatedCallback callback) override;
#endif
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override; void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override;
void createSymlink(const CanonPath & path, const std::string & target) override; void createSymlink(const CanonPath & path, const std::string & target) override;
-7
View File
@@ -136,13 +136,6 @@ std::optional<Mode> convertMode(SourceAccessor::Type type);
*/ */
using RestoreHook = SourcePath(Hash); using RestoreHook = SourcePath(Hash);
/**
* Wrapper around `parse` and `RestoreSink`
*
* @param hashAlgo must be `HashAlgo::SHA1` or `HashAlgo::SHA256` for now.
*/
void restore(FileSystemObjectSink & sink, Source & source, HashAlgorithm hashAlgo, std::function<RestoreHook> hook);
/** /**
* Dumps a single file to a sink * Dumps a single file to a sink
* *
+6
View File
@@ -166,4 +166,10 @@ public:
} }
}; };
/**
* Check that the string does not contain any NUL bytes and return c_str().
* @throws Error if str contains '\0' bytes.
*/
const char * requireCString(const std::string & str);
} // namespace nix } // namespace nix
+8
View File
@@ -4,7 +4,15 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include "nix/util/file-descriptor.hh"
namespace nix { namespace nix {
/**
* Determine whether \param fd is a terminal.
*/
bool isTTY(Descriptor fd);
/** /**
* Determine whether ANSI escape sequences are appropriate for the * Determine whether ANSI escape sequences are appropriate for the
* present output. * present output.
+22
View File
@@ -242,6 +242,28 @@ std::string stripIndentation(std::string_view s);
*/ */
std::pair<std::string_view, std::string_view> getLine(std::string_view s); std::pair<std::string_view, std::string_view> getLine(std::string_view s);
/**
* Get a pointer to the contents of a `std::optional` if it is set, or a
* null pointer otherise.
*
* Const version.
*/
template<class T>
const T * get(const std::optional<T> & opt)
{
return opt ? &*opt : nullptr;
}
/**
* Non-const counterpart of `const T * get(const std::optional<T>)`.
* Takes a mutable reference, but returns a mutable pointer.
*/
template<class T>
T * get(std::optional<T> & opt)
{
return opt ? &*opt : nullptr;
}
/** /**
* Get a value for the specified key from an associate container. * Get a value for the specified key from an associate container.
*/ */
+11
View File
@@ -5,6 +5,7 @@
#include "nix/util/strings-inline.hh" #include "nix/util/strings-inline.hh"
#include "nix/util/os-string.hh" #include "nix/util/os-string.hh"
#include "nix/util/error.hh" #include "nix/util/error.hh"
#include "nix/util/util.hh"
namespace nix { namespace nix {
@@ -152,4 +153,14 @@ std::string optionalBracket(std::string_view prefix, std::string_view content, s
return result; return result;
} }
const char * requireCString(const std::string & s)
{
if (std::memchr(s.data(), '\0', s.size())) [[unlikely]] {
using namespace std::string_view_literals;
auto str = replaceStrings(s, "\0"sv, "␀"sv);
throw Error("string '%s' with null (\\0) bytes used where it's not allowed", str);
}
return s.c_str();
}
} // namespace nix } // namespace nix
+10
View File
@@ -64,6 +64,16 @@ inline std::pair<int, size_t> charWidthUTF8Helper(std::string_view s)
namespace nix { namespace nix {
bool isTTY(Descriptor fd)
{
#ifndef _WIN32
return isatty(fd);
#else
DWORD mode;
return GetConsoleMode(fd, &mode);
#endif
}
bool isTTY() bool isTTY()
{ {
static const bool tty = isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" static const bool tty = isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"
+5 -2
View File
@@ -327,8 +327,11 @@ Path renderUrlPathEnsureLegal(const std::vector<std::string> & urlPath)
/* This is only really valid for UNIX. Windows has more restrictions. */ /* This is only really valid for UNIX. Windows has more restrictions. */
if (comp.contains('/')) if (comp.contains('/'))
throw BadURL("URL path component '%s' contains '/', which is not allowed in file names", comp); throw BadURL("URL path component '%s' contains '/', which is not allowed in file names", comp);
if (comp.contains(char(0))) if (comp.contains(char(0))) {
throw BadURL("URL path component '%s' contains NUL byte which is not allowed", comp); using namespace std::string_view_literals;
auto str = replaceStrings(comp, "\0"sv, "␀"sv);
throw BadURL("URL path component '%s' contains NUL byte which is not allowed", str);
}
} }
return concatStringsSep("/", urlPath); return concatStringsSep("/", urlPath);
+1 -1
View File
@@ -75,7 +75,7 @@ struct CmdCatNar : StoreCommand, MixCat
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
AutoCloseFD fd = open(narPath.c_str(), O_RDONLY); AutoCloseFD fd = toDescriptor(open(narPath.c_str(), O_RDONLY));
if (!fd) if (!fd)
throw SysError("opening NAR file '%s'", narPath); throw SysError("opening NAR file '%s'", narPath);
auto source = FdSource{fd.get()}; auto source = FdSource{fd.get()};
+2 -1
View File
@@ -1,13 +1,14 @@
#include "nix/cmd/command.hh" #include "nix/cmd/command.hh"
#include "nix/store/store-api.hh" #include "nix/store/store-api.hh"
#include "nix/util/archive.hh" #include "nix/util/archive.hh"
#include "nix/util/terminal.hh"
using namespace nix; using namespace nix;
static FdSink getNarSink() static FdSink getNarSink()
{ {
auto fd = getStandardOutput(); auto fd = getStandardOutput();
if (isatty(fd)) if (isTTY(fd))
throw UsageError("refusing to write NAR to a terminal"); throw UsageError("refusing to write NAR to a terminal");
return FdSink(std::move(fd)); return FdSink(std::move(fd));
} }
+1 -1
View File
@@ -45,7 +45,7 @@ struct CmdFlakePrefetchInputs : FlakeCommand
if (auto lockedNode = dynamic_cast<const LockedNode *>(&node)) { if (auto lockedNode = dynamic_cast<const LockedNode *>(&node)) {
try { try {
Activity act(*logger, lvlInfo, actUnknown, fmt("fetching '%s'", lockedNode->lockedRef)); Activity act(*logger, lvlInfo, actUnknown, fmt("fetching '%s'", lockedNode->lockedRef));
auto accessor = lockedNode->lockedRef.input.getAccessor(fetchSettings, store).first; auto accessor = lockedNode->lockedRef.input.getAccessor(fetchSettings, *store).first;
fetchToStore( fetchToStore(
fetchSettings, *store, accessor, FetchMode::Copy, lockedNode->lockedRef.input.getName()); fetchSettings, *store, accessor, FetchMode::Copy, lockedNode->lockedRef.input.getName());
} catch (Error & e) { } catch (Error & e) {
+7 -7
View File
@@ -240,7 +240,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
j["lastModified"] = *lastModified; j["lastModified"] = *lastModified;
j["path"] = storePath; j["path"] = storePath;
j["locks"] = lockedFlake.lockFile.toJSON().first; j["locks"] = lockedFlake.lockFile.toJSON().first;
if (auto fingerprint = lockedFlake.getFingerprint(store, fetchSettings)) if (auto fingerprint = lockedFlake.getFingerprint(*store, fetchSettings))
j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false);
printJSON(j); printJSON(j);
} else { } else {
@@ -260,7 +260,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
logger->cout( logger->cout(
ANSI_BOLD "Last modified:" ANSI_NORMAL " %s", ANSI_BOLD "Last modified:" ANSI_NORMAL " %s",
std::put_time(std::localtime(&*lastModified), "%F %T")); std::put_time(std::localtime(&*lastModified), "%F %T"));
if (auto fingerprint = lockedFlake.getFingerprint(store, fetchSettings)) if (auto fingerprint = lockedFlake.getFingerprint(*store, fetchSettings))
logger->cout( logger->cout(
ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s", fingerprint->to_string(HashFormat::Base16, false)); ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s", fingerprint->to_string(HashFormat::Base16, false));
@@ -1019,7 +1019,7 @@ struct CmdFlakeNew : CmdFlakeInitCommon
struct CmdFlakeClone : FlakeCommand struct CmdFlakeClone : FlakeCommand
{ {
Path destDir; std::filesystem::path destDir;
std::string description() override std::string description() override
{ {
@@ -1049,7 +1049,7 @@ struct CmdFlakeClone : FlakeCommand
if (destDir.empty()) if (destDir.empty())
throw Error("missing flag '--dest'"); throw Error("missing flag '--dest'");
getFlakeRef().resolve(fetchSettings, store).input.clone(fetchSettings, destDir); getFlakeRef().resolve(fetchSettings, *store).input.clone(fetchSettings, *store, destDir);
} }
}; };
@@ -1100,7 +1100,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun, MixNoCheckSigs
std::optional<StorePath> storePath; std::optional<StorePath> storePath;
if (!(*inputNode)->lockedRef.input.isRelative()) { if (!(*inputNode)->lockedRef.input.isRelative()) {
storePath = dryRun ? (*inputNode)->lockedRef.input.computeStorePath(*store) storePath = dryRun ? (*inputNode)->lockedRef.input.computeStorePath(*store)
: (*inputNode)->lockedRef.input.fetchToStore(fetchSettings, store).first; : (*inputNode)->lockedRef.input.fetchToStore(fetchSettings, *store).first;
sources.insert(*storePath); sources.insert(*storePath);
} }
if (json) { if (json) {
@@ -1498,8 +1498,8 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto originalRef = getFlakeRef(); auto originalRef = getFlakeRef();
auto resolvedRef = originalRef.resolve(fetchSettings, store); auto resolvedRef = originalRef.resolve(fetchSettings, *store);
auto [accessor, lockedRef] = resolvedRef.lazyFetch(getEvalState()->fetchSettings, store); auto [accessor, lockedRef] = resolvedRef.lazyFetch(getEvalState()->fetchSettings, *store);
auto storePath = auto storePath =
fetchToStore(getEvalState()->fetchSettings, *store, accessor, FetchMode::Copy, lockedRef.input.getName()); fetchToStore(getEvalState()->fetchSettings, *store, accessor, FetchMode::Copy, lockedRef.input.getName());
auto hash = store->queryPathInfo(storePath)->narHash; auto hash = store->queryPathInfo(storePath)->narHash;
+1 -1
View File
@@ -145,7 +145,7 @@ struct CmdLsNar : Command, MixLs
void run() override void run() override
{ {
AutoCloseFD fd = open(narPath.c_str(), O_RDONLY); AutoCloseFD fd = toDescriptor(open(narPath.c_str(), O_RDONLY));
if (!fd) if (!fd)
throw SysError("opening NAR file '%s'", narPath); throw SysError("opening NAR file '%s'", narPath);
auto source = FdSource{fd.get()}; auto source = FdSource{fd.get()};
+30 -12
View File
@@ -193,20 +193,38 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs
std::string dumpCli() std::string dumpCli()
{ {
auto res = nlohmann::json::object(); using nlohmann::json;
auto res = json::object();
res["args"] = toJSON(); res["args"] = toJSON();
auto stores = nlohmann::json::object(); {
for (auto & [storeName, implem] : Implementations::registered()) { auto & stores = res["stores"] = json::object();
auto & j = stores[storeName]; for (auto & [storeName, implem] : Implementations::registered()) {
j["doc"] = implem.doc; auto & j = stores[storeName];
j["uri-schemes"] = implem.uriSchemes; j["doc"] = implem.doc;
j["settings"] = implem.getConfig()->toJSON(); j["uri-schemes"] = implem.uriSchemes;
j["experimentalFeature"] = implem.experimentalFeature; j["settings"] = implem.getConfig()->toJSON();
j["experimentalFeature"] = implem.experimentalFeature;
}
} }
res["stores"] = std::move(stores);
res["fetchers"] = fetchers::dumpRegisterInputSchemeInfo(); {
auto & fetchers = res["fetchers"] = json::object();
for (const auto & [schemeName, scheme] : fetchers::getAllInputSchemes()) {
auto & s = fetchers[schemeName] = json::object();
s["description"] = scheme->schemeDescription();
auto & attrs = s["allowedAttrs"] = json::object();
for (auto & [fieldName, field] : scheme->allowedAttrs()) {
auto & f = attrs[fieldName] = json::object();
f["type"] = field.type;
f["required"] = field.required;
f["doc"] = stripIndentation(field.doc);
}
}
};
return res.dump(); return res.dump();
} }
@@ -440,7 +458,7 @@ void mainWrapped(int argc, char ** argv)
if (!primOp->doc) if (!primOp->doc)
continue; continue;
b["args"] = primOp->args; b["args"] = primOp->args;
b["doc"] = trim(stripIndentation(primOp->doc->view())); b["doc"] = trim(stripIndentation(*primOp->doc));
if (primOp->experimentalFeature) if (primOp->experimentalFeature)
b["experimental-feature"] = primOp->experimentalFeature; b["experimental-feature"] = primOp->experimentalFeature;
builtinsJson.emplace(state.symbols[builtin.name], std::move(b)); builtinsJson.emplace(state.symbols[builtin.name], std::move(b));
@@ -449,7 +467,7 @@ void mainWrapped(int argc, char ** argv)
auto b = nlohmann::json::object(); auto b = nlohmann::json::object();
if (!info.doc) if (!info.doc)
continue; continue;
b["doc"] = trim(stripIndentation(info.doc->view())); b["doc"] = trim(stripIndentation(info.doc));
b["type"] = showType(info.type, false); b["type"] = showType(info.type, false);
if (info.impureOnly) if (info.impureOnly)
b["impure-only"] = true; b["impure-only"] = true;
+2 -2
View File
@@ -554,9 +554,9 @@ static void main_nix_build(int argc, char ** argv)
env["NIX_STORE"] = store->storeDir; env["NIX_STORE"] = store->storeDir;
env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores ? settings.buildCores : settings.getDefaultCores()); env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores ? settings.buildCores : settings.getDefaultCores());
DerivationOptions drvOptions; DerivationOptions<StorePath> drvOptions;
try { try {
drvOptions = DerivationOptions::fromStructuredAttrs(drv.env, drv.structuredAttrs); drvOptions = derivationOptionsFromStructuredAttrs(*store, drv.env, get(drv.structuredAttrs));
} catch (Error & e) { } catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", store->printStorePath(packageInfo.requireDrvPath())); e.addTrace({}, "while parsing derivation '%s'", store->printStorePath(packageInfo.requireDrvPath()));
throw; throw;
+3 -3
View File
@@ -121,7 +121,7 @@ static void update(const StringSet & channelNames)
// We want to download the url to a file to see if it's a tarball while also checking if we // We want to download the url to a file to see if it's a tarball while also checking if we
// got redirected in the process, so that we can grab the various parts of a nix channel // got redirected in the process, so that we can grab the various parts of a nix channel
// definition from a consistent location if the redirect changes mid-download. // definition from a consistent location if the redirect changes mid-download.
auto result = fetchers::downloadFile(store, fetchSettings, url, std::string(baseNameOf(url))); auto result = fetchers::downloadFile(*store, fetchSettings, url, std::string(baseNameOf(url)));
url = result.effectiveUrl; url = result.effectiveUrl;
bool unpacked = false; bool unpacked = false;
@@ -139,10 +139,10 @@ static void update(const StringSet & channelNames)
if (!unpacked) { if (!unpacked) {
// Download the channel tarball. // Download the channel tarball.
try { try {
result = fetchers::downloadFile(store, fetchSettings, url + "/nixexprs.tar.xz", "nixexprs.tar.xz"); result = fetchers::downloadFile(*store, fetchSettings, url + "/nixexprs.tar.xz", "nixexprs.tar.xz");
} catch (FileTransferError & e) { } catch (FileTransferError & e) {
result = result =
fetchers::downloadFile(store, fetchSettings, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2"); fetchers::downloadFile(*store, fetchSettings, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2");
} }
} }
// Regardless of where it came from, add the expression representing this channel to accumulated expression // Regardless of where it came from, add the expression representing this channel to accumulated expression
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff. Show more