Compare commits

..

3 Commits

Author SHA1 Message Date
John Ericson
156061409a Do some parsing of git hashes based on length.
Note that for Nix-native information we should *not* do length tricks,
but instead always rely on an explicit algorithm. This hack should be
only for foreign hash literals.
2025-11-09 18:52:35 -05:00
John Ericson
6da56d7ed4 Make changes to support libgit2 experimental API
This experimental interface is likely to become stable with libgit2 2.0.
2025-11-09 18:52:35 -05:00
John Ericson
9846305fee Switch to libgit2 experimental
Just patch the package for now, but we should make Nixpkgs provide what
we need (whether that the experimental variant, or a 2.x release with
the new API made non-experimental) before releasing this change.
2025-11-09 18:52:35 -05:00
205 changed files with 1277 additions and 3687 deletions

View File

@@ -174,7 +174,7 @@ jobs:
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@7ec16f2c061ab07b235a7245e06ed46fe9a1cab6 # v31.8.3
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31.8.2
if: ${{ !matrix.experimental-installer }}
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}

View File

@@ -58,8 +58,8 @@ mkMesonDerivation (finalAttrs: {
"man"
];
nativeBuildInputs = [
nix-cli
# Hack for sake of the dev shell
passthru.externalNativeBuildInputs = [
meson
ninja
(lib.getBin lowdown-unsandboxed)
@@ -78,6 +78,10 @@ mkMesonDerivation (finalAttrs: {
changelog-d
];
nativeBuildInputs = finalAttrs.passthru.externalNativeBuildInputs ++ [
nix-cli
];
preConfigure = ''
chmod u+w ./.version
echo ${finalAttrs.version} > ./.version

View File

@@ -22,15 +22,7 @@ The store path info JSON format has been updated from version 1 to version 2:
- New: `"ca": {"method": "nar", "hash": {"algorithm": "sha256", "format": "base64", "hash": "EMIJ+giQ..."}}`
- Still `null` values for input-addressed store objects
- **Structured hash fields**:
Hash values (`narHash` and `downloadHash`) are now structured JSON objects instead of strings:
- Old: `"narHash": "sha256:FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="`
- New: `"narHash": {"algorithm": "sha256", "format": "base64", "hash": "FePFYIlM..."}`
- Same structure applies to `downloadHash` in NAR info contexts
Nix currently only produces, and doesn't consume this format.
Version 1 format is still accepted when reading for backward compatibility.
**Affected command**: `nix path-info --json`

View File

@@ -1,21 +0,0 @@
---
synopsis: "S3 binary cache stores now support storage class configuration"
prs: [14464]
issues: [7015]
---
S3 binary cache stores now support configuring the storage class for uploaded objects via the `storage-class` parameter. This allows users to optimize costs by selecting appropriate storage tiers based on access patterns.
Example usage:
```bash
# Use Glacier storage for long-term archival
nix copy --to 's3://my-bucket?storage-class=GLACIER' /nix/store/...
# Use Intelligent Tiering for automatic cost optimization
nix copy --to 's3://my-bucket?storage-class=INTELLIGENT_TIERING' /nix/store/...
```
The storage class applies to both regular uploads and multipart uploads. When not specified, objects use the bucket's default storage class.
See the [S3 storage classes documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html) for available storage classes and their characteristics.

View File

@@ -29,7 +29,6 @@
- [Build Trace](store/build-trace.md)
- [Derivation Resolution](store/resolution.md)
- [Building](store/building.md)
- [Secrets](store/secrets.md)
- [Store Types](store/types/index.md)
{{#include ./store/types/SUMMARY.md}}
- [Appendix: Math notation](store/math-notation.md)

View File

@@ -71,7 +71,7 @@ $defs:
Note: This field may not be present in all contexts, such as when the path is used as the key and the the store object info the value in map.
narHash:
"$ref": "./hash-v1.yaml"
type: string
title: NAR Hash
description: |
Hash of the [file system object](@docroot@/store/file-system-object.md) part of the store object when serialized as a [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive).
@@ -229,7 +229,7 @@ $defs:
> This is an impure "`.narinfo`" field that may not be included in certain contexts.
downloadHash:
"$ref": "./hash-v1.yaml"
type: string
title: Download Hash
description: |
A digest for the compressed archive itself, as opposed to the data contained within.

View File

@@ -24,7 +24,7 @@ nar-obj-inner
| str("type"), str("directory") directory
;
regular = [ str("executable"), str("") ], str("contents"), str(contents);
regular = [ str("executable") ], str("contents"), str(contents);
symlink = str("target"), str(target);
@@ -52,4 +52,4 @@ The Nix Archive (NAR) format is also formally described using [Kaitai Struct](ht
{{#include nar.ksy}}
```
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.

View File

@@ -1,20 +0,0 @@
# Secrets
The store is readable to all users on the system. For this reason, it
is generally discouraged to allow secrets to make it into the store.
Even on a single-user system, separate system users isolate services
from each other and having secrets that all local users can read
weakens that isolation. When using external store caches the secrets
may end up there, and on multi-user systems the secrets will be
available to all those users.
Organize your derivations so that secrets are read from the filesystem
(with appropriate access controls) at run time. Place the secrets on
the filesystem manually or use a scheme that includes the secret in
the store in encrypted form, and decrypts it adding the relevant
access control on system activation.
Several such schemes for NixOS can in the
[comparison of secret managing schemes] on the wiki.
[comparison of secret managing schemes]: https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes

View File

@@ -60,9 +60,4 @@ if get_option('unit-tests')
subproject('libflake-tests')
endif
subproject('nix-functional-tests')
if get_option('json-schema-checks')
subproject('json-schema-checks')
endif
if get_option('kaitai-struct-checks')
subproject('kaitai-struct-checks')
endif
subproject('json-schema-checks')

View File

@@ -27,17 +27,3 @@ option(
value : false,
description : 'Build benchmarks (requires gbenchmark)',
)
option(
'kaitai-struct-checks',
type : 'boolean',
value : true,
description : 'Check the Kaitai Struct specifications (requires Kaitai Struct)',
)
option(
'json-schema-checks',
type : 'boolean',
value : true,
description : 'Check JSON schema validity of schemas and examples (requires jv)',
)

View File

@@ -74,4 +74,14 @@ scope: {
buildPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase;
installPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase;
});
libgit2 = pkgs.libgit2.overrideAttrs (attrs: {
cmakeFlags = (attrs.cmakeFlags or [ ]) ++ [
(lib.mesonBool "EXPERIMENTAL_SHA256" true)
];
postInstall = (attrs.postInstall or "") + ''
substituteInPlace $(find $dev/include -type f) --replace-quiet '#include "git2/' '#include "git2-experimental/'
'';
});
}

View File

@@ -3,118 +3,10 @@
devFlake,
}:
let
# Some helper functions
/**
Compute a filtered closure of build inputs.
Specifically, `buildInputsClosure cond startSet` computes the closure formed
by recursive application of `p: filter cond p.buildInputs ++ filter cond p.propagatedBuildInputs`
to `startSet`.
Example:
```nix
builtInputsClosure isInternal [ pkg1 pkg2 ]
=> [ pkg1 pkg3 pkg2 pkg10 ]
```
Note: order tbd
Note: `startSet` is *NOT* filtered.
*/
buildInputsClosureCond =
cond: startSet:
let
closure = builtins.genericClosure {
startSet = map (d: {
key = d.drvPath;
value = d;
}) startSet;
operator =
d:
let
r =
map
(d': {
key = d'.drvPath;
value = d';
})
(
lib.filter cond d.value.buildInputs or [ ] ++ lib.filter cond d.value.propagatedBuildInputs or [ ]
);
in
r;
};
in
map (item: item.value) closure;
/**
`[ pkg1 pkg2 ]` -> `{ "...-pkg2.drv" = null; "...-pkg1.drv" = null }`
Note: fairly arbitrary order (hash based). Use for efficient set membership test only.
*/
byDrvPath =
l:
lib.listToAttrs (
map (c: {
name =
# Just a lookup key
builtins.unsafeDiscardStringContext c.drvPath;
value = null;
}) l
);
/**
Stable dedup.
Unlike `listToAttrs` -> `attrValues`, this preserves the input ordering,
which is more predictable ("deterministic") than e.g. sorting store paths,
whose hashes affect the ordering on every change.
*/
# TODO: add to Nixpkgs lib, refer from uniqueStrings
dedupByString =
key: l:
let
r =
lib.foldl'
(
a@{ list, set }:
elem:
let
k = builtins.unsafeDiscardStringContext (key elem);
in
if set ? ${k} then
a
else
let
# Note: O(n²) copying. Use linkedLists to concat them in one go at the end.
# https://github.com/NixOS/nixpkgs/pull/452088
newList = [ elem ] ++ list;
newSet = set // {
${k} = null;
};
in
builtins.seq newList builtins.seq newSet {
list = newList;
set = newSet;
}
)
{
list = [ ];
set = { };
}
l;
in
r.list;
in
{ pkgs }:
# TODO: don't use nix-util for this?
pkgs.nixComponents2.nix-util.overrideAttrs (
finalAttrs: prevAttrs:
attrs:
let
stdenv = pkgs.nixDependencies2.stdenv;
@@ -129,89 +21,13 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
"-D${prefix}:${rest}";
havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix;
ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags;
activeComponents = buildInputsClosureCond isInternal (
lib.attrValues (finalAttrs.passthru.config.getComponents allComponents)
);
allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2;
internalDrvs = byDrvPath (
# Drop the attr names (not present in buildInputs anyway)
lib.attrValues allComponents
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues allComponents)
);
isInternal =
dep: internalDrvs ? ${builtins.unsafeDiscardStringContext dep.drvPath or "_non-existent_"};
in
{
pname = "shell-for-nix";
passthru = {
inherit activeComponents;
# We use this attribute to store non-derivation values like functions and
# perhaps other things that are primarily for overriding and not the shell.
config = {
# Default getComponents
getComponents =
c:
builtins.removeAttrs c (
lib.optionals (!havePerl) [ "nix-perl-bindings" ]
++ lib.optionals (!buildCanExecuteHost) [ "nix-manual" ]
);
};
/**
Produce a devShell for a given set of nix components
Example:
```nix
shell.withActiveComponents (c: {
inherit (c) nix-util;
})
```
*/
withActiveComponents =
f2:
finalAttrs.finalPackage.overrideAttrs (
finalAttrs: prevAttrs: {
passthru = prevAttrs.passthru // {
config = prevAttrs.passthru.config // {
getComponents = f2;
};
};
}
);
small =
(finalAttrs.finalPackage.withActiveComponents (c: {
inherit (c)
nix-cli
nix-util-tests
nix-store-tests
nix-expr-tests
nix-fetchers-tests
nix-flake-tests
nix-functional-tests
# Currently required
nix-perl-bindings
;
})).overrideAttrs
(o: {
mesonFlags = o.mesonFlags ++ [
# TODO: infer from activeComponents or vice versa
"-Dkaitai-struct-checks=false"
"-Djson-schema-checks=false"
];
});
};
pname = "shell-for-" + attrs.pname;
# Remove the version suffix to avoid unnecessary attempts to substitute in nix develop
version = lib.fileContents ../.version;
name = finalAttrs.pname;
name = attrs.pname;
installFlags = "sysconfdir=$(out)/etc";
shellHook = ''
@@ -282,9 +98,17 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
nativeBuildInputs =
let
inputs =
dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.nativeBuildInputs) activeComponents)
)
attrs.nativeBuildInputs or [ ]
++ pkgs.nixComponents2.nix-util.nativeBuildInputs
++ pkgs.nixComponents2.nix-store.nativeBuildInputs
++ pkgs.nixComponents2.nix-fetchers.nativeBuildInputs
++ pkgs.nixComponents2.nix-expr.nativeBuildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.nativeBuildInputs
++ lib.optionals buildCanExecuteHost pkgs.nixComponents2.nix-manual.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-internal-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
++ lib.optional (
!buildCanExecuteHost
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
@@ -293,7 +117,9 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
&& lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)
) pkgs.buildPackages.mesonEmulatorHook
++ [
pkgs.buildPackages.cmake
pkgs.buildPackages.gnused
pkgs.buildPackages.changelog-d
modular.pre-commit.settings.package
(pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript)
pkgs.buildPackages.nixfmt-rfc-style
@@ -310,22 +136,18 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
# from making its way into NIX_CFLAGS_COMPILE.
lib.filter (p: !lib.hasInfix "separate-debug-info" p) inputs;
propagatedNativeBuildInputs = dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (
lib.lists.concatMap (c: c.propagatedNativeBuildInputs) activeComponents
)
);
buildInputs = [
pkgs.gbenchmark
]
++ dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.buildInputs) activeComponents)
)
++ attrs.buildInputs or [ ]
++ pkgs.nixComponents2.nix-util.buildInputs
++ pkgs.nixComponents2.nix-store.buildInputs
++ pkgs.nixComponents2.nix-store-tests.externalBuildInputs
++ pkgs.nixComponents2.nix-fetchers.buildInputs
++ pkgs.nixComponents2.nix-expr.buildInputs
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
++ pkgs.nixComponents2.nix-cmd.buildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
++ lib.optional havePerl pkgs.perl;
propagatedBuildInputs = dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.propagatedBuildInputs) activeComponents)
);
}
)

View File

@@ -34,11 +34,15 @@ mkMesonDerivation (finalAttrs: {
outputs = [ "out" ];
passthru.externalNativeBuildInputs = [
jsonschema
];
nativeBuildInputs = [
meson
ninja
jsonschema
];
]
++ finalAttrs.passthru.externalNativeBuildInputs;
doCheck = true;

View File

@@ -37,15 +37,7 @@ mkMesonDerivation (finalAttrs: {
outputs = [ "out" ];
buildInputs = [
gtest
kaitai-struct-cpp-stl-runtime
];
nativeBuildInputs = [
meson
ninja
pkg-config
passthru.externalNativeBuildInputs = [
# This can go away when we bump up to 25.11
(kaitai-struct-compiler.overrideAttrs (finalAttrs: {
version = "0.11";
@@ -56,6 +48,20 @@ mkMesonDerivation (finalAttrs: {
}))
];
passthru.externalBuildInputs = [
gtest
kaitai-struct-cpp-stl-runtime
];
buildInputs = finalAttrs.passthru.externalBuildInputs;
nativeBuildInputs = [
meson
ninja
pkg-config
]
++ finalAttrs.passthru.externalNativeBuildInputs;
doCheck = true;
mesonCheckFlags = [ "--print-errorlogs" ];

View File

@@ -2,7 +2,6 @@
#include <nlohmann/json.hpp>
#include "nix/cmd/command.hh"
#include "nix/cmd/legacy.hh"
#include "nix/cmd/markdown.hh"
#include "nix/store/store-open.hh"
#include "nix/store/local-fs-store.hh"
@@ -15,18 +14,6 @@
namespace nix {
RegisterCommand::Commands & RegisterCommand::commands()
{
static RegisterCommand::Commands commands;
return commands;
}
RegisterLegacyCommand::Commands & RegisterLegacyCommand::commands()
{
static RegisterLegacyCommand::Commands commands;
return commands;
}
nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
{
nix::Commands res;

View File

@@ -33,8 +33,7 @@ EvalSettings evalSettings{
// FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false);
debug("fetching flake search path element '%s''", rest);
auto [accessor, lockedRef] =
flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store);
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings,
*state.store,
@@ -132,7 +131,7 @@ MixEvalArgs::MixEvalArgs()
fetchers::Attrs extraAttrs;
if (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) {
completeFlakeRef(completions, openStore(), prefix);
@@ -188,7 +187,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
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(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath);

View File

@@ -286,7 +286,11 @@ struct RegisterCommand
{
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
static Commands & commands();
static Commands & commands()
{
static Commands commands;
return commands;
}
RegisterCommand(std::vector<std::string> && name, std::function<ref<Command>()> command)
{

View File

@@ -13,7 +13,11 @@ struct RegisterLegacyCommand
{
typedef std::map<std::string, MainFunction> Commands;
static Commands & commands();
static Commands & commands()
{
static Commands commands;
return commands;
}
RegisterLegacyCommand(const std::string & name, MainFunction fun)
{

View File

@@ -185,7 +185,6 @@ MixFlakeOptions::MixFlakeOptions()
}
overrideRegistry(
fetchSettings,
fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}),
input3->lockedRef.input,
extraAttrs);

View File

@@ -656,7 +656,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
+ "\n\n";
}
markdown += stripIndentation(doc->doc.view());
markdown += stripIndentation(doc->doc);
logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else if (fallbackPos) {
@@ -738,7 +738,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
}
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings))
if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
Value v;

View File

@@ -137,8 +137,6 @@ nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Sto
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
{
if (builder)
builder->~nix_eval_state_builder();
operator delete(builder, static_cast<std::align_val_t>(alignof(nix_eval_state_builder)));
}
@@ -205,8 +203,6 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
void nix_state_free(EvalState * state)
{
if (state)
state->~EvalState();
operator delete(state, static_cast<std::align_val_t>(alignof(EvalState)));
}

View File

@@ -134,7 +134,7 @@ PrimOp * nix_alloc_primop(
.name = name,
.args = {},
.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)};
if (args)
for (size_t i = 0; args[i]; i++)

View File

@@ -104,7 +104,6 @@ MATCHER(IsAttrs, "")
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
{
if (arg.type() != nString) {
*result_listener << "Expected a string got " << arg.type();
return false;
}
return arg.string_view() == s;

View File

@@ -1,6 +1,5 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/string-data-static.hh"
namespace nix {
// Testing the conversion to JSON
@@ -55,7 +54,7 @@ TEST_F(JSONValueTest, IntNegative)
TEST_F(JSONValueTest, String)
{
Value v;
v.mkStringNoCopy("test"_sds);
v.mkStringNoCopy("test");
ASSERT_EQ(getJSONValue(v), "\"test\"");
}
@@ -63,7 +62,7 @@ TEST_F(JSONValueTest, StringQuotes)
{
Value v;
v.mkStringNoCopy("test\""_sds);
v.mkStringNoCopy("test\"");
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
}

View File

@@ -14,7 +14,7 @@
namespace nixC {
TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
{
auto tmpDir = nix::createTempDir();
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
@@ -42,16 +42,12 @@ TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
nix_expr_eval_from_string(ctx, state, "builtins.seq <nixos-config> <nixpkgs>", ".", value);
assert_ctx_ok();
nix_state_free(state);
ASSERT_EQ(nix_get_type(ctx, value), NIX_TYPE_PATH);
assert_ctx_ok();
auto pathStr = nix_get_path_string(ctx, value);
assert_ctx_ok();
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
nix_gc_decref(nullptr, value);
}
TEST_F(nix_api_expr_test, nix_expr_eval_from_string)

View File

@@ -661,14 +661,8 @@ INSTANTIATE_TEST_SUITE_P(
CASE(R"(null)", ""),
CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
CASE(R"({ outPath = "foo"; })", "foo")
// this is broken on cygwin because canonPath("//./test", false) returns //./test
// FIXME: don't use canonPath
#ifndef __CYGWIN__
,
CASE(R"(./test)", "/test")
#endif
));
CASE(R"({ outPath = "foo"; })", "foo"),
CASE(R"(./test)", "/test")));
#undef CASE
TEST_F(PrimOpTest, substring)

View File

@@ -1,5 +1,4 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/expr/value.hh"
#include "nix/expr/print.hh"
@@ -36,14 +35,14 @@ TEST_F(ValuePrintingTests, tBool)
TEST_F(ValuePrintingTests, tString)
{
Value vString;
vString.mkStringNoCopy("some-string"_sds);
vString.mkStringNoCopy("some-string");
test(vString, "\"some-string\"");
}
TEST_F(ValuePrintingTests, tPath)
{
Value vPath;
vPath.mkStringNoCopy("/foo"_sds);
vPath.mkStringNoCopy("/foo");
test(vPath, "\"/foo\"");
}
@@ -290,10 +289,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
TEST_F(ValuePrintingTests, attrsTypeFirst)
{
Value vType;
vType.mkStringNoCopy("puppy"_sds);
vType.mkStringNoCopy("puppy");
Value vApple;
vApple.mkStringNoCopy("apple"_sds);
vApple.mkStringNoCopy("apple");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.symbols.create("type"), &vType);
@@ -334,7 +333,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
TEST_F(ValuePrintingTests, ansiColorsString)
{
Value v;
v.mkStringNoCopy("puppy"_sds);
v.mkStringNoCopy("puppy");
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}
@@ -342,7 +341,7 @@ TEST_F(ValuePrintingTests, ansiColorsString)
TEST_F(ValuePrintingTests, ansiColorsStringElided)
{
Value v;
v.mkStringNoCopy("puppy"_sds);
v.mkStringNoCopy("puppy");
test(
v,
@@ -390,7 +389,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
TEST_F(ValuePrintingTests, ansiColorsDerivation)
{
Value vDerivation;
vDerivation.mkStringNoCopy("derivation"_sds);
vDerivation.mkStringNoCopy("derivation");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);
@@ -413,7 +412,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!"_sds);
message.mkStringNoCopy("uh oh!");
Value vError;
vError.mkApp(&throw_, &message);
@@ -430,12 +429,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!"_sds);
message.mkStringNoCopy("uh oh!");
Value vError;
vError.mkApp(&throw_, &message);
Value vDerivation;
vDerivation.mkStringNoCopy("derivation"_sds);
vDerivation.mkStringNoCopy("derivation");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);

View File

@@ -1,5 +1,4 @@
#include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h>
@@ -28,17 +27,17 @@ TEST_F(ValueTest, staticString)
{
Value vStr1;
Value vStr2;
vStr1.mkStringNoCopy("foo"_sds);
vStr2.mkStringNoCopy("foo"_sds);
vStr1.mkStringNoCopy("foo");
vStr2.mkStringNoCopy("foo");
auto & sd1 = vStr1.string_data();
auto & sd2 = vStr2.string_data();
auto sd1 = vStr1.string_view();
auto sd2 = vStr2.string_view();
// The strings should be the same
ASSERT_EQ(sd1.view(), sd2.view());
ASSERT_EQ(sd1, sd2);
// The strings should also be backed by the same (static) allocation
ASSERT_EQ(&sd1, &sd2);
ASSERT_EQ(sd1.data(), sd2.data());
}
} // namespace nix

View File

@@ -147,7 +147,7 @@ struct AttrDb
for (auto * elem : *context) {
if (!first)
ctx.push_back(' ');
ctx.append(elem->view());
ctx.append(elem);
first = false;
}
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)

View File

@@ -3,7 +3,6 @@
#include "nix/expr/primops.hh"
#include "nix/expr/print-options.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh"
#include "nix/util/exit.hh"
#include "nix/util/types.hh"
#include "nix/util/util.hh"
@@ -29,8 +28,6 @@
#include "parser-tab.hh"
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <cstring>
@@ -51,23 +48,28 @@ using json = nlohmann::json;
namespace nix {
StringData & StringData::alloc(size_t size)
static char * allocString(size_t size)
{
void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1);
char * t;
t = (char *) GC_MALLOC_ATOMIC(size);
if (!t)
throw std::bad_alloc();
auto res = new (t) StringData(size);
return *res;
return t;
}
const StringData & StringData::make(std::string_view s)
// 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.
static const char * makeImmutableString(std::string_view s)
{
if (s.empty())
return ""_sds;
auto & res = alloc(s.size());
std::memcpy(&res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
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;
}
RootValue allocRootValue(Value * v)
@@ -541,7 +543,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = v2->primOp()->name,
.arity = v2->primOp()->arity,
.args = v2->primOp()->args,
.doc = *doc,
.doc = doc,
};
}
if (v.isLambda()) {
@@ -583,8 +585,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = name,
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
.args = {},
/* NOTE: memory leak when compiled without GC. */
.doc = StringData::make(s.view()),
.doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC
};
}
if (isFunctor(v)) {
@@ -818,34 +819,33 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
void Value::mkString(std::string_view s)
{
mkStringNoCopy(StringData::make(s));
mkStringNoCopy(makeImmutableString(s));
}
Value::StringWithContext::Context *
Value::StringWithContext::Context::fromBuilder(const NixStringContext & context, EvalMemory & mem)
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
{
if (context.empty())
return nullptr;
auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
std::ranges::transform(
context, ctx->elems, [](const NixStringContextElem & elt) { return &StringData::make(elt.to_string()); });
context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); });
return ctx;
}
void Value::mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem)
void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkStringNoCopy(StringData::make(s), Value::StringWithContext::Context::fromBuilder(context, mem));
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
}
void Value::mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem)
void Value::mkStringMove(const char * s, const NixStringContext & context)
{
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context, mem));
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
}
void Value::mkPath(const SourcePath & path)
{
mkPath(&*path.accessor, StringData::make(path.path.abs()));
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
}
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
@@ -881,9 +881,9 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
}
ListBuilder::ListBuilder(EvalMemory & mem, size_t size)
ListBuilder::ListBuilder(size_t size)
: size(size)
, elems(size <= 2 ? inlineElems : (Value **) mem.allocBytes(size * sizeof(Value *)))
, elems(size <= 2 ? inlineElems : (Value **) allocBytes(size * sizeof(Value *)))
{
}
@@ -923,8 +923,7 @@ void EvalState::mkStorePathString(const StorePath & p, Value & v)
store->printStorePath(p),
NixStringContext{
NixStringContextElem::Opaque{.path = p},
},
mem);
});
}
std::string EvalState::mkOutputStringRaw(
@@ -946,7 +945,7 @@ void EvalState::mkOutputString(
std::optional<StorePath> optStaticOutputPath,
const ExperimentalFeatureSettings & xpSettings)
{
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b}, mem);
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b});
}
std::string EvalState::mkSingleDerivedPathStringRaw(const SingleDerivedPath & p)
@@ -981,8 +980,7 @@ void EvalState::mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v
mkSingleDerivedPathStringRaw(p),
NixStringContext{
std::visit([](auto && v) -> NixStringContextElem { return v; }, p),
},
mem);
});
}
Value * Expr::maybeThunk(EvalState & state, Env & env)
@@ -2101,21 +2099,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
.atPos(pos)
.withFrame(env, *this)
.debugThrow();
std::string resultStr;
resultStr.reserve(sSize);
std::string result_str;
result_str.reserve(sSize);
for (const auto & part : strings) {
resultStr += *part;
result_str += *part;
}
v.mkPath(state.rootPath(CanonPath(resultStr)));
v.mkPath(state.rootPath(CanonPath(result_str)));
} else {
auto & resultStr = StringData::alloc(sSize);
auto * tmp = resultStr.data();
char * result_str = allocString(sSize + 1);
char * tmp = result_str;
for (const auto & part : strings) {
std::memcpy(tmp, part->data(), part->size());
memcpy(tmp, part->data(), part->size());
tmp += part->size();
}
*tmp = '\0';
v.mkStringMove(resultStr, context, state.mem);
*tmp = 0;
v.mkStringMove(result_str, context);
}
}
@@ -2290,7 +2288,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
{
if (auto * ctx = v.context())
for (auto * elem : *ctx)
context.insert(NixStringContextElem::parse(elem->view(), xpSettings));
context.insert(NixStringContextElem::parse(elem, xpSettings));
}
std::string_view EvalState::forceString(
@@ -2312,7 +2310,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
error<EvalError>(
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string_view(),
(*v.context()->begin())->view())
*v.context()->begin())
.withTrace(pos, errorCtx)
.debugThrow();
}

View File

@@ -12,7 +12,7 @@ namespace nix {
* Note: Various places expect the allocated memory to be zeroed.
*/
[[gnu::always_inline]]
inline void * EvalMemory::allocBytes(size_t n)
inline void * allocBytes(size_t n)
{
void * p;
#if NIX_USE_BOEHMGC

View File

@@ -108,7 +108,7 @@ struct PrimOp
/**
* Optional free-form documentation about the primop.
*/
const StringData * doc = nullptr;
const char * doc = nullptr;
/**
* Add a trace item, while calling the `<name>` builtin.
@@ -156,7 +156,7 @@ struct 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.
@@ -335,7 +335,6 @@ public:
EvalMemory & operator=(const EvalMemory &) = delete;
EvalMemory & operator=(EvalMemory &&) = delete;
inline void * allocBytes(size_t n);
inline Value * allocValue();
inline Env & allocEnv(size_t size);
@@ -349,7 +348,7 @@ public:
ListBuilder buildList(size_t size)
{
stats.nrListElems += size;
return ListBuilder(*this, size);
return ListBuilder(size);
}
const Statistics & getStats() const &
@@ -837,7 +836,11 @@ public:
std::optional<std::string> name;
size_t arity;
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;
};
/**

View File

@@ -31,8 +31,6 @@ headers = [ config_pub_h ] + files(
'print.hh',
'repl-exit-status.hh',
'search-path.hh',
'string-data.hh',
'string-data-static.hh',
'symbol-table.hh',
'value-to-json.hh',
'value-to-xml.hh',

View File

@@ -3,7 +3,6 @@
#include <map>
#include <span>
#include <memory>
#include <vector>
#include <memory_resource>
#include <algorithm>
@@ -12,7 +11,6 @@
#include "nix/expr/value.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh"
@@ -188,18 +186,22 @@ struct ExprString : Expr
* This is only for strings already allocated in our polymorphic allocator,
* or that live at least that long (e.g. c++ string literals)
*/
ExprString(const StringData & s)
ExprString(const char * s)
{
v.mkStringNoCopy(s);
};
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
{
if (sv.size() == 0) {
v.mkStringNoCopy(""_sds);
auto len = sv.length();
if (len == 0) {
v.mkStringNoCopy("");
return;
}
v.mkStringNoCopy(StringData::make(*alloc.resource(), sv));
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkStringNoCopy(s);
};
Value * maybeThunk(EvalState & state, Env & env) override;
@@ -214,7 +216,11 @@ struct ExprPath : Expr
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
: accessor(accessor)
{
v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv));
auto len = sv.length();
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkPath(&*accessor, s);
}
Value * maybeThunk(EvalState & state, Env & env) override;
@@ -339,7 +345,7 @@ struct ExprOpHasAttr : Expr
Expr * e;
std::span<AttrName> attrPath;
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::span<AttrName> attrPath)
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::vector<AttrName> attrPath)
: e(e)
, attrPath({alloc.allocate_object<AttrName>(attrPath.size()), attrPath.size()})
{
@@ -433,7 +439,7 @@ struct ExprList : Expr
{
std::span<Expr *> elems;
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::span<Expr *> exprs)
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::vector<Expr *> exprs)
: elems({alloc.allocate_object<Expr *>(exprs.size()), exprs.size()})
{
std::ranges::copy(exprs, elems.begin());
@@ -562,7 +568,7 @@ public:
const PosTable & positions,
std::pmr::polymorphic_allocator<char> & alloc,
PosIdx pos,
const FormalsBuilder & formals,
FormalsBuilder formals,
Expr * body)
: ExprLambda(positions, alloc, pos, Symbol(), formals, body) {};
@@ -753,19 +759,7 @@ struct ExprConcatStrings : Expr
std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::span<std::pair<PosIdx, Expr *>> es)
: pos(pos)
, forceString(forceString)
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
{
std::ranges::copy(es, this->es.begin());
};
ExprConcatStrings(
std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::initializer_list<std::pair<PosIdx, Expr *>> es)
const std::vector<std::pair<PosIdx, Expr *>> & es)
: pos(pos)
, forceString(forceString)
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
@@ -845,19 +839,7 @@ public:
add(std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::span<std::pair<PosIdx, Expr *>> es)
requires(std::same_as<C, ExprConcatStrings>)
{
return alloc.new_object<C>(alloc, pos, forceString, es);
}
template<class C>
[[gnu::always_inline]]
C *
add(std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::initializer_list<std::pair<PosIdx, Expr *>> es)
const std::vector<std::pair<PosIdx, Expr *>> & es)
requires(std::same_as<C, ExprConcatStrings>)
{
return alloc.new_object<C>(alloc, pos, forceString, es);

View File

@@ -4,8 +4,6 @@
#include <limits>
#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
namespace nix {
@@ -96,7 +94,7 @@ struct ParserState
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
void validateFormals(FormalsBuilder & formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es);
Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc);
};
@@ -239,10 +237,10 @@ inline void ParserState::validateFormals(FormalsBuilder & formals, PosIdx pos, S
}
inline Expr *
ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es)
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
{
if (es.empty())
return exprs.add<ExprString>(""_sds);
return exprs.add<ExprString>("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -334,7 +332,7 @@ ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std:
// If there is nothing at all, return the empty string directly.
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
if (es2.size() == 0) {
auto * const result = exprs.add<ExprString>(""_sds);
auto * const result = exprs.add<ExprString>("");
return result;
}
@@ -343,7 +341,7 @@ ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std:
auto * const result = (es2)[0].second;
return result;
}
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, es2);
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, std::move(es2));
}
inline PosIdx LexerState::at(const ParserLocation & loc)

View File

@@ -12,7 +12,11 @@ struct RegisterPrimOp
{
typedef std::vector<PrimOp> PrimOps;
static PrimOps & primOps();
static PrimOps & primOps()
{
static PrimOps primOps;
return primOps;
}
/**
* You can register a constant by passing an arity of 0. fun

View File

@@ -110,7 +110,7 @@ struct PrintOptions
* `PrintOptions` for unknown and therefore potentially large values in error messages,
* to avoid printing "too much" output.
*/
static constexpr PrintOptions errorPrintOptions = PrintOptions{
static PrintOptions errorPrintOptions = PrintOptions{
.ansiColors = true,
.maxDepth = 10,
.maxAttrs = 10,

View File

@@ -1,44 +0,0 @@
#pragma once
///@file
#include "nix/expr/string-data.hh"
namespace nix {
template<size_t N>
struct StringData::Static
{
/**
* @note Must be first to make layout compatible with StringData.
*/
const size_t size = N - 1;
char data[N];
consteval Static(const char (&str)[N])
{
static_assert(N > 0);
if (str[size] != '\0')
throw;
std::copy_n(str, N, data);
}
operator const StringData &() const &
{
static_assert(sizeof(decltype(*this)) >= sizeof(StringData));
static_assert(alignof(decltype(*this)) == alignof(StringData));
/* NOTE: This cast is somewhat on the fence of what's legal in C++.
The question boils down to whether flexible array members are
layout compatible with fixed-size arrays. This is a gray area, since
FAMs are not standard anyway.
*/
return *reinterpret_cast<const StringData *>(this);
}
};
template<StringData::Static S>
const StringData & operator""_sds()
{
return S;
}
} // namespace nix

View File

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

View File

@@ -3,7 +3,6 @@
#include <memory_resource>
#include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/util/chunked-vector.hh"
#include "nix/util/error.hh"
@@ -17,6 +16,7 @@ class SymbolValue : protected Value
friend class SymbolStr;
friend class SymbolTable;
uint32_t size_;
uint32_t idx;
SymbolValue() = default;
@@ -24,7 +24,7 @@ class SymbolValue : protected Value
public:
operator std::string_view() const noexcept
{
return string_view();
return {c_str(), size_};
}
};
@@ -96,13 +96,13 @@ class SymbolStr
SymbolValueStore & store;
std::string_view s;
std::size_t hash;
std::pmr::memory_resource & resource;
std::pmr::polymorphic_allocator<char> & alloc;
Key(SymbolValueStore & store, std::string_view s, std::pmr::memory_resource & stringMemory)
Key(SymbolValueStore & store, std::string_view s, std::pmr::polymorphic_allocator<char> & stringAlloc)
: store(store)
, s(s)
, hash(HashType{}(s))
, resource(stringMemory)
, alloc(stringAlloc)
{
}
};
@@ -122,10 +122,14 @@ public:
// for multi-threaded implementations: lock store and allocator here
const auto & [v, idx] = key.store.add(SymbolValue{});
if (size == 0) {
v.mkStringNoCopy(""_sds, nullptr);
v.mkStringNoCopy("", nullptr);
} else {
v.mkStringNoCopy(StringData::make(key.resource, key.s));
auto s = key.alloc.allocate(size + 1);
memcpy(s, key.s.data(), size);
s[size] = '\0';
v.mkStringNoCopy(s, nullptr);
}
v.size_ = size;
v.idx = idx;
this->s = &v;
}
@@ -135,12 +139,6 @@ public:
return *s == s2;
}
[[gnu::always_inline]]
const StringData & string_data() const noexcept
{
return s->string_data();
}
[[gnu::always_inline]]
const char * c_str() const noexcept
{
@@ -157,17 +155,13 @@ public:
[[gnu::always_inline]]
bool empty() const noexcept
{
auto * p = &s->string_data();
// Save a dereference in the sentinel value case
if (p == &""_sds)
return true;
return p->size() == 0;
return s->size_ == 0;
}
[[gnu::always_inline]]
size_t size() const noexcept
{
return s->string_data().size();
return s->size_;
}
[[gnu::always_inline]]
@@ -265,6 +259,7 @@ private:
* During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based.
*/
std::pmr::monotonic_buffer_resource buffer;
std::pmr::polymorphic_allocator<char> stringAlloc{&buffer};
SymbolStr::SymbolValueStore store{16};
/**
@@ -287,7 +282,7 @@ public:
// Most symbols are looked up more than once, so we trade off insertion performance
// for lookup performance.
// FIXME: make this thread-safe.
return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first);
return Symbol(*symbols.insert(SymbolStr::Key{store, s, stringAlloc}).first);
}
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const

View File

@@ -1,16 +1,12 @@
#pragma once
///@file
#include <bit>
#include <cassert>
#include <memory>
#include <span>
#include <string_view>
#include <type_traits>
#include <concepts>
#include "nix/expr/eval-gc.hh"
#include "nix/expr/string-data.hh"
#include "nix/expr/value/context.hh"
#include "nix/util/source-path.hh"
#include "nix/expr/print-options.hh"
@@ -86,7 +82,6 @@ class PosIdx;
struct Pos;
class StorePath;
class EvalState;
class EvalMemory;
class XMLWriter;
class Printer;
@@ -160,7 +155,7 @@ class ListBuilder
Value * inlineElems[2] = {nullptr, nullptr};
public:
Value ** elems;
ListBuilder(EvalMemory & mem, size_t size);
ListBuilder(size_t size);
// NOTE: Can be noexcept because we are just copying integral values and
// raw pointers.
@@ -224,7 +219,7 @@ struct ValueBase
*/
struct StringWithContext
{
const StringData * str;
const char * c_str;
/**
* The type of the context itself.
@@ -239,7 +234,7 @@ struct ValueBase
*/
struct Context
{
using value_type = const StringData *;
using value_type = const char *;
using size_type = std::size_t;
using iterator = const value_type *;
@@ -278,7 +273,7 @@ struct ValueBase
/**
* @return null pointer when context.empty()
*/
static Context * fromBuilder(const NixStringContext & context, EvalMemory & mem);
static Context * fromBuilder(const NixStringContext & context);
};
/**
@@ -290,7 +285,7 @@ struct ValueBase
struct Path
{
SourceAccessor * accessor;
const StringData * path;
const char * path;
};
struct Null
@@ -651,13 +646,13 @@ protected:
void getStorage(StringWithContext & string) const noexcept
{
string.context = untagPointer<decltype(string.context)>(payload[0]);
string.str = std::bit_cast<const StringData *>(payload[1]);
string.c_str = std::bit_cast<const char *>(payload[1]);
}
void getStorage(Path & path) const noexcept
{
path.accessor = untagPointer<decltype(path.accessor)>(payload[0]);
path.path = std::bit_cast<const StringData *>(payload[1]);
path.path = std::bit_cast<const char *>(payload[1]);
}
void setStorage(NixInt integer) noexcept
@@ -702,7 +697,7 @@ protected:
void setStorage(StringWithContext string) noexcept
{
setUntaggablePayload<pdString>(string.context, string.str);
setUntaggablePayload<pdString>(string.context, string.c_str);
}
void setStorage(Path path) noexcept
@@ -1055,22 +1050,22 @@ public:
setStorage(b);
}
void mkStringNoCopy(const StringData & s, const Value::StringWithContext::Context * context = nullptr) noexcept
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
{
setStorage(StringWithContext{.str = &s, .context = context});
setStorage(StringWithContext{.c_str = s, .context = context});
}
void mkString(std::string_view s);
void mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem);
void mkString(std::string_view s, const NixStringContext & context);
void mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem);
void mkStringMove(const char * s, const NixStringContext & context);
void mkPath(const SourcePath & path);
inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept
inline void mkPath(SourceAccessor * accessor, const char * path) noexcept
{
setStorage(Path{.accessor = accessor, .path = &path});
setStorage(Path{.accessor = accessor, .path = path});
}
inline void mkNull() noexcept
@@ -1168,23 +1163,17 @@ public:
SourcePath path() const
{
return SourcePath(
ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), std::string(pathStrView())));
}
const StringData & string_data() const noexcept
{
return *getStorage<StringWithContext>().str;
}
const char * c_str() const noexcept
{
return getStorage<StringWithContext>().str->data();
return SourcePath(ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), pathStr()));
}
std::string_view string_view() const noexcept
{
return string_data().view();
return std::string_view{getStorage<StringWithContext>().c_str};
}
const char * c_str() const noexcept
{
return getStorage<StringWithContext>().c_str;
}
const Value::StringWithContext::Context * context() const noexcept
@@ -1244,12 +1233,12 @@ public:
const char * pathStr() const noexcept
{
return getStorage<Path>().path->c_str();
return getStorage<Path>().path;
}
std::string_view pathStrView() const noexcept
{
return getStorage<Path>().path->view();
return std::string_view{getStorage<Path>().path};
}
SourceAccessor * pathAccessor() const noexcept

View File

@@ -70,6 +70,11 @@ mkMesonLibrary (finalAttrs: {
nix-util
nix-store
nix-fetchers
]
++ finalAttrs.passthru.externalPropagatedBuildInputs;
# Hack for sake of the dev shell
passthru.externalPropagatedBuildInputs = [
boost
nlohmann_json
]

View File

@@ -126,26 +126,26 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%define api.value.type variant
%type <Expr *> start expr expr_function expr_if expr_op
%type <Expr *> expr_select expr_simple expr_app
%type <Expr *> expr_pipe_from expr_pipe_into
%type <nix::Expr *> start expr expr_function expr_if expr_op
%type <nix::Expr *> expr_select expr_simple expr_app
%type <nix::Expr *> expr_pipe_from expr_pipe_into
%type <std::vector<Expr *>> list
%type <ExprAttrs *> binds binds1
%type <FormalsBuilder> formals formal_set
%type <Formal> formal
%type <std::vector<AttrName>> attrpath
%type <std::vector<std::pair<AttrName, PosIdx>>> attrs
%type <std::vector<std::pair<PosIdx, Expr *>>> string_parts_interpolated
%type <std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>> ind_string_parts
%type <Expr *> path_start
%type <std::variant<Expr *, std::string_view>> string_parts string_attr
%type <StringToken> attr
%token <StringToken> ID
%token <StringToken> STR IND_STR
%token <NixInt> INT_LIT
%token <NixFloat> FLOAT_LIT
%token <StringToken> PATH HPATH SPATH PATH_END
%token <StringToken> URI
%type <nix::ExprAttrs *> binds binds1
%type <nix::FormalsBuilder> formals formal_set
%type <nix::Formal> formal
%type <std::vector<nix::AttrName>> attrpath
%type <std::vector<std::pair<nix::AttrName, nix::PosIdx>>> attrs
%type <std::vector<std::pair<nix::PosIdx, nix::Expr *>>> string_parts_interpolated
%type <std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>>> ind_string_parts
%type <nix::Expr *> path_start
%type <std::variant<nix::Expr *, std::string_view>> string_parts string_attr
%type <nix::StringToken> attr
%token <nix::StringToken> ID
%token <nix::StringToken> STR IND_STR
%token <nix::NixInt> INT_LIT
%token <nix::NixFloat> FLOAT_LIT
%token <nix::StringToken> PATH HPATH SPATH PATH_END
%token <nix::StringToken> URI
%token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW
%token PIPE_FROM PIPE_INTO /* <| and |> */
%token DOLLAR_CURLY /* == ${ */
@@ -186,7 +186,7 @@ expr_function
| formal_set ':' expr_function[body]
{
state->validateFormals($formal_set);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -194,7 +194,7 @@ expr_function
{
auto arg = state->symbols.create($ID);
state->validateFormals($formal_set, CUR_POS, arg);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -202,7 +202,7 @@ expr_function
{
auto arg = state->symbols.create($ID);
state->validateFormals($formal_set, CUR_POS, arg);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -251,7 +251,7 @@ expr_op
| expr_op OR expr_op { $$ = state->exprs.add<ExprOpOr>(state->at(@2), $1, $3); }
| expr_op IMPL expr_op { $$ = state->exprs.add<ExprOpImpl>(state->at(@2), $1, $3); }
| expr_op UPDATE expr_op { $$ = state->exprs.add<ExprOpUpdate>(state->at(@2), $1, $3); }
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, $3); }
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, std::move($3)); }
| expr_op '+' expr_op
{ $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, state->at(@2), false, {{state->at(@1), $1}, {state->at(@3), $3}}); }
| expr_op '-' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.sub), {$1, $3}); }
@@ -272,9 +272,9 @@ expr_app
expr_select
: expr_simple '.' attrpath
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, nullptr); }
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), nullptr); }
| expr_simple '.' attrpath OR_KW expr_select
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, $5); $5->warnIfCursedOr(state->symbols, state->positions); }
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), $5); $5->warnIfCursedOr(state->symbols, state->positions); }
| /* Backwards compatibility: because Nixpkgs has a function named or,
allow stuff like map or [...]. This production is problematic (see
https://github.com/NixOS/nix/issues/11118) and will be refactored in the
@@ -304,12 +304,12 @@ expr_simple
$2);
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = state->stripIndentation(CUR_POS, $2);
$$ = state->stripIndentation(CUR_POS, std::move($2));
}
| path_start PATH_END
| path_start string_parts_interpolated PATH_END {
$2.insert($2.begin(), {state->at(@1), $1});
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, $2);
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, std::move($2));
}
| SPATH {
std::string_view path($1.p + 1, $1.l - 2);
@@ -338,12 +338,12 @@ expr_simple
{ $2->pos = CUR_POS; $$ = $2; }
| '{' '}'
{ $$ = state->exprs.add<ExprAttrs>(CUR_POS); }
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, $2); }
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, std::move($2)); }
;
string_parts
: STR { $$ = $1; }
| string_parts_interpolated { $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, $1); }
| string_parts_interpolated { $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, std::move($1)); }
| { $$ = std::string_view(); }
;
@@ -425,7 +425,7 @@ binds1
if (!$accum->inheritFromExprs)
$accum->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
$accum->inheritFromExprs->push_back($expr);
auto from = state->exprs.add<ExprInheritFrom>(state->at(@expr), $accum->inheritFromExprs->size() - 1);
auto from = new nix::ExprInheritFrom(state->at(@expr), $accum->inheritFromExprs->size() - 1);
for (auto & [i, iPos] : $attrs) {
if ($accum->attrs.find(i.symbol) != $accum->attrs.end())
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/globals.hh"
@@ -18,9 +17,9 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
static RegisterPrimOp primop_unsafeDiscardStringContext({
.name = "__unsafeDiscardStringContext",
.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.
)"_sds,
)",
.fun = prim_unsafeDiscardStringContext,
});
@@ -34,7 +33,7 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args,
static RegisterPrimOp primop_hasContext(
{.name = "__hasContext",
.args = {"s"},
.doc = &R"(
.doc = R"(
Return `true` if string *s* has a non-empty context.
The context can be obtained with
[`getContext`](#builtins-getContext).
@@ -51,7 +50,7 @@ static RegisterPrimOp primop_hasContext(
> then throw "package name cannot contain string context"
> else { ${name} = meta; }
> ```
)"_sds,
)",
.fun = prim_hasContext});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -70,13 +69,13 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
}
}
v.mkString(*s, context2, state.mem);
v.mkString(*s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
{.name = "__unsafeDiscardOutputDependency",
.args = {"s"},
.doc = &R"(
.doc = R"(
Create a copy of the given string where every
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
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.
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)"_sds,
)",
.fun = prim_unsafeDiscardOutputDependency});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -138,13 +137,13 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
context.begin()->raw)}),
};
v.mkString(*s, context2, state.mem);
v.mkString(*s, context2);
}
static RegisterPrimOp primop_addDrvOutputDependencies(
{.name = "__addDrvOutputDependencies",
.args = {"s"},
.doc = &R"(
.doc = R"(
Create a copy of the given string where a single
[constant](@docroot@/language/string-context.md#string-context-constant)
string context element is turned into a
@@ -157,7 +156,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies(
The latter is supported so this function is idempotent.
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)"_sds,
)",
.fun = prim_addDrvOutputDependencies});
/* 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(
{.name = "__getContext",
.args = {"s"},
.doc = &R"(
.doc = R"(
Return the string context of *s*.
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" ]; }; }
```
)"_sds,
)",
.fun = prim_getContext});
/* Append the given context to a given string.
@@ -322,7 +321,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** arg
}
}
v.mkString(orig, context, state.mem);
v.mkString(orig, context);
}
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});

View File

@@ -1,5 +1,4 @@
#include "nix/expr/primops.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-open.hh"
#include "nix/store/realisation.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({
.name = "__fetchClosure",
.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.
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.
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.
)"_sds,
)",
.fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure,
});

View File

@@ -81,7 +81,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar
attrs.insert_or_assign("rev", rev->gitRev());
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.store);
auto attrs2 = state.buildBindings(8);
state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath));

View File

@@ -2,7 +2,6 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-api.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/store/filetransfer.hh"
@@ -83,7 +82,7 @@ struct FetchTreeParams
static void fetchTree(
EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
{
fetchers::Input input{};
fetchers::Input input{state.fetchSettings};
NixStringContext context;
std::optional<std::string> type;
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
@@ -195,9 +194,9 @@ static void fetchTree(
}
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
input = lookupInRegistries(state.fetchSettings, state.store, input, fetchers::UseRegistries::Limited).first;
input = lookupInRegistries(state.store, input, fetchers::UseRegistries::Limited).first;
if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) {
if (state.settings.pureEval && !input.isLocked()) {
if (input.getNarHash())
warn(
"Input '%s' is unlocked (e.g. lacks a Git revision) but is checked by NAR hash. "
@@ -220,8 +219,7 @@ static void fetchTree(
throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string());
}
auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, state.store, input, fetchers::UseRegistries::No);
auto cachedInput = state.inputCache->getAccessor(state.store, input, fetchers::UseRegistries::No);
auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor);
@@ -236,7 +234,7 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, V
static RegisterPrimOp primop_fetchTree({
.name = "fetchTree",
.args = {"input"},
.doc = &R"(
.doc = 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)
@@ -319,68 +317,27 @@ static RegisterPrimOp primop_fetchTree({
> }
> ```
- `"tarball"`
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";
> }
> ```
- `"git"`
Fetch a Git tree and copy it to the Nix store.
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
- `allRefs` (Bool, optional)
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`
- `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.
@@ -398,21 +355,62 @@ static RegisterPrimOp primop_fetchTree({
>
> 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.
- `"tarball"`
- `ref` (String, optional)
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)
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
- `url` (String, required)
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
Default: `"HEAD"`
- `rev` (String, optional)
A Git revision; a commit hash.
Default: the tip of `ref`
- `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`
- `lfs` (Bool, optional)
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
- `allRefs` (Bool, optional)
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`
- `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.
- `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.
The following input types are still subject to change:
@@ -458,7 +456,7 @@ static RegisterPrimOp primop_fetchTree({
> ```nix
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
> ```
)"_sds,
)",
.fun = prim_fetchTree,
.experimentalFeature = Xp::FetchTree,
});
@@ -618,7 +616,7 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchurl({
.name = "__fetchurl",
.args = {"arg"},
.doc = &R"(
.doc = R"(
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:
@@ -632,7 +630,7 @@ static RegisterPrimOp primop_fetchurl({
characters that are invalid for the store.
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)"_sds,
)",
.fun = prim_fetchurl,
});
@@ -644,7 +642,7 @@ static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args
static RegisterPrimOp primop_fetchTarball({
.name = "fetchTarball",
.args = {"args"},
.doc = &R"(
.doc = R"(
Download the specified URL, unpack it and return the path of the
unpacked tree. The file must be a tape archive (`.tar`) compressed
with `gzip`, `bzip2` or `xz`. If the tarball consists of a
@@ -682,7 +680,7 @@ static RegisterPrimOp primop_fetchTarball({
```
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)"_sds,
)",
.fun = prim_fetchTarball,
});
@@ -695,7 +693,7 @@ static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchGit({
.name = "fetchGit",
.args = {"args"},
.doc = &R"(
.doc = R"(
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
attribute with the following attributes (all except `url` optional):
@@ -898,7 +896,7 @@ static RegisterPrimOp primop_fetchGit({
given, `fetchGit` uses the current content of the checked-out
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`.
)"_sds,
)",
.fun = prim_fetchGit,
});

View File

@@ -1,6 +1,5 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/string-data-static.hh"
#include "expr-config-private.hh"
@@ -137,7 +136,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
normalizeDatetimeFormat(t);
#endif
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkStringNoCopy("timestamp"_sds);
attrs.alloc("_type").mkStringNoCopy("timestamp");
std::ostringstream s;
s << t;
auto str = s.view();
@@ -170,10 +169,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
}
}
static RegisterPrimOp primop_fromTOML({
.name = "fromTOML",
.args = {"e"},
.doc = &R"(
static RegisterPrimOp primop_fromTOML(
{.name = "fromTOML",
.args = {"e"},
.doc = R"(
Convert a TOML string to a Nix value. For example,
```nix
@@ -186,8 +185,7 @@ static RegisterPrimOp primop_fromTOML({
```
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)"_sds,
.fun = prim_fromTOML,
});
)",
.fun = prim_fromTOML});
} // namespace nix

View File

@@ -1,19 +1,19 @@
#include "nix/fetchers/git-utils.hh"
#include "nix/util/file-system.hh"
#include <gmock/gmock.h>
#include <git2/global.h>
#include <git2/repository.h>
#include <git2/signature.h>
#include <git2/types.h>
#include <git2/object.h>
#include <git2/tag.h>
#include <git2-experimental/global.h>
#include <git2-experimental/repository.h>
#include <git2-experimental/signature.h>
#include <git2-experimental/types.h>
#include <git2-experimental/object.h>
#include <git2-experimental/tag.h>
#include <gtest/gtest.h>
#include "nix/util/fs-sink.hh"
#include "nix/util/serialise.hh"
#include "nix/fetchers/git-lfs-fetch.hh"
#include <git2/blob.h>
#include <git2/tree.h>
#include <git2-experimental/blob.h>
#include <git2-experimental/tree.h>
namespace nix {

View File

@@ -5,7 +5,7 @@
#include "nix/fetchers/fetchers.hh"
#include "nix/fetchers/git-utils.hh"
#include <git2.h>
#include <git2-experimental.h>
#include <gtest/gtest.h>
#include <filesystem>
@@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport)
{"ref", "main"},
});
auto [accessor, i] = input.getAccessor(settings, store);
auto [accessor, i] = input.getAccessor(store);
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
}

View File

@@ -1,61 +0,0 @@
#include "nix/fetchers/fetch-settings.hh"
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include <gtest/gtest.h>
#include <string>
namespace nix {
using fetchers::Attr;
struct InputFromAttrsTestCase
{
fetchers::Attrs attrs;
std::string expectedUrl;
std::string description;
fetchers::Attrs expectedAttrs = attrs;
};
class InputFromAttrsTest : public ::testing::WithParamInterface<InputFromAttrsTestCase>, public ::testing::Test
{};
TEST_P(InputFromAttrsTest, attrsAreCorrectAndRoundTrips)
{
fetchers::Settings fetchSettings;
const auto & testCase = GetParam();
auto input = fetchers::Input::fromAttrs(fetchSettings, fetchers::Attrs(testCase.attrs));
EXPECT_EQ(input.toAttrs(), testCase.expectedAttrs);
EXPECT_EQ(input.toURLString(), testCase.expectedUrl);
auto input2 = fetchers::Input::fromAttrs(fetchSettings, input.toAttrs());
EXPECT_EQ(input, input2);
EXPECT_EQ(input.toAttrs(), input2.toAttrs());
}
INSTANTIATE_TEST_SUITE_P(
InputFromAttrs,
InputFromAttrsTest,
::testing::Values(
// Test for issue #14429.
InputFromAttrsTestCase{
.attrs =
{
{"url", Attr("git+ssh://git@github.com/NixOS/nixpkgs")},
{"type", Attr("git")},
},
.expectedUrl = "git+ssh://git@github.com/NixOS/nixpkgs",
.description = "strips_git_plus_prefix",
.expectedAttrs =
{
{"url", Attr("ssh://git@github.com/NixOS/nixpkgs")},
{"type", Attr("git")},
},
}),
[](const ::testing::TestParamInfo<InputFromAttrsTestCase> & info) { return info.param.description; });
} // namespace nix

View File

@@ -33,7 +33,7 @@ deps_private += rapidcheck
gtest = dependency('gtest', main : true)
deps_private += gtest
libgit2 = dependency('libgit2')
libgit2 = dependency('libgit2-experimental')
deps_private += libgit2
subdir('nix-meson-build-support/common')
@@ -42,7 +42,6 @@ sources = files(
'access-tokens.cc',
'git-utils.cc',
'git.cc',
'input.cc',
'nix_api_fetchers.cc',
'public-key.cc',
)

View File

@@ -1,5 +1,6 @@
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/fetchers/git-utils.hh"
#include <nlohmann/json.hpp>
@@ -111,7 +112,7 @@ StringMap attrsToQuery(const Attrs & attrs)
Hash getRevAttr(const Attrs & attrs, const std::string & name)
{
return Hash::parseAny(getStrAttr(attrs, name), HashAlgorithm::SHA1);
return parseGitHash(getStrAttr(attrs, name));
}
} // namespace nix::fetchers

View File

@@ -89,7 +89,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
// but not all of them. Doing this is to support those other
// operations which are supposed to be robust on
// unknown/uninterpretable inputs.
Input input;
Input input{settings};
input.attrs = attrs;
fixupInput(input);
return input;
@@ -159,9 +159,9 @@ bool Input::isDirect() const
return !scheme || scheme->isDirect(*this);
}
bool Input::isLocked(const Settings & settings) const
bool Input::isLocked() const
{
return scheme && scheme->isLocked(settings, *this);
return scheme && scheme->isLocked(*this);
}
bool Input::isFinal() const
@@ -198,17 +198,17 @@ bool Input::contains(const Input & other) const
}
// FIXME: remove
std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, ref<Store> store) const
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
{
if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try {
auto [accessor, result] = getAccessorUnchecked(settings, store);
auto [accessor, result] = getAccessorUnchecked(store);
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;
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
@@ -297,10 +297,10 @@ 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(ref<Store> store) const
{
try {
auto [accessor, result] = getAccessorUnchecked(settings, store);
auto [accessor, result] = getAccessorUnchecked(store);
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
@@ -313,7 +313,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(ref<Store> store) const
{
// FIXME: cache the accessor
@@ -349,7 +349,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
if (accessor->fingerprint) {
ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive;
auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/");
settings.getCache()->upsert(cacheKey, *store, {}, storePath);
settings->getCache()->upsert(cacheKey, *store, {}, storePath);
}
accessor->setPathDisplay("«" + to_string() + "»");
@@ -360,7 +360,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
}
}
auto [accessor, result] = scheme->getAccessor(settings, store, *this);
auto [accessor, result] = scheme->getAccessor(store, *this);
if (!accessor->fingerprint)
accessor->fingerprint = result.getFingerprint(store);
@@ -377,10 +377,10 @@ Input Input::applyOverrides(std::optional<std::string> ref, std::optional<Hash>
return scheme->applyOverrides(*this, ref, rev);
}
void Input::clone(const Settings & settings, const Path & destDir) const
void Input::clone(const Path & destDir) const
{
assert(scheme);
scheme->clone(settings, *this, destDir);
scheme->clone(*this, destDir);
}
std::optional<std::filesystem::path> Input::getSourcePath() const
@@ -447,6 +447,9 @@ std::optional<Hash> Input::getRev() const
} catch (BadHash & e) {
// Default to sha1 for backwards compatibility with existing
// usages (e.g. `builtins.fetchTree` calls or flake inputs).
//
// Note that means that for SHA-256 git repos, prefixing
// must be used.
hash = Hash::parseAny(*s, HashAlgorithm::SHA1);
}
}
@@ -493,7 +496,7 @@ void InputScheme::putFile(
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 Input & input, const Path & destDir) const
{
throw Error("do not know how to clone input '%s'", input.to_string());
}

View File

@@ -7,10 +7,10 @@
#include "nix/util/hash.hh"
#include "nix/store/ssh.hh"
#include <git2/attr.h>
#include <git2/config.h>
#include <git2/errors.h>
#include <git2/remote.h>
#include <git2-experimental/attr.h>
#include <git2-experimental/config.h>
#include <git2-experimental/errors.h>
#include <git2-experimental/remote.h>
#include <nlohmann/json.hpp>

View File

@@ -13,27 +13,27 @@
#include "nix/util/thread-pool.hh"
#include "nix/util/pool.hh"
#include <git2/attr.h>
#include <git2/blob.h>
#include <git2/branch.h>
#include <git2/commit.h>
#include <git2/config.h>
#include <git2/describe.h>
#include <git2/errors.h>
#include <git2/global.h>
#include <git2/indexer.h>
#include <git2/object.h>
#include <git2/odb.h>
#include <git2/refs.h>
#include <git2/remote.h>
#include <git2/repository.h>
#include <git2/revparse.h>
#include <git2/status.h>
#include <git2/submodule.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/mempack.h>
#include <git2/tag.h>
#include <git2/tree.h>
#include <git2-experimental/attr.h>
#include <git2-experimental/blob.h>
#include <git2-experimental/branch.h>
#include <git2-experimental/commit.h>
#include <git2-experimental/config.h>
#include <git2-experimental/describe.h>
#include <git2-experimental/errors.h>
#include <git2-experimental/global.h>
#include <git2-experimental/indexer.h>
#include <git2-experimental/object.h>
#include <git2-experimental/odb.h>
#include <git2-experimental/refs.h>
#include <git2-experimental/remote.h>
#include <git2-experimental/repository.h>
#include <git2-experimental/revparse.h>
#include <git2-experimental/status.h>
#include <git2-experimental/submodule.h>
#include <git2-experimental/sys/odb_backend.h>
#include <git2-experimental/sys/mempack.h>
#include <git2-experimental/tag.h>
#include <git2-experimental/tree.h>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
@@ -91,10 +91,21 @@ typedef std::unique_ptr<git_indexer, Deleter<git_indexer_free>> Indexer;
Hash toHash(const git_oid & oid)
{
#ifdef GIT_EXPERIMENTAL_SHA256
assert(oid.type == GIT_OID_SHA1);
#endif
Hash hash(HashAlgorithm::SHA1);
HashAlgorithm algo;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (oid.type) {
case GIT_OID_SHA1:
algo = HashAlgorithm::SHA1;
break;
case GIT_OID_SHA256:
algo = HashAlgorithm::SHA256;
break;
default:
unreachable();
}
#pragma GCC diagnostic pop
Hash hash(algo);
memcpy(hash.hash, oid.id, hash.hashSize);
return hash;
}
@@ -111,7 +122,21 @@ static void initLibGit2()
git_oid hashToOID(const Hash & hash)
{
git_oid oid;
if (git_oid_fromstr(&oid, hash.gitRev().c_str()))
git_oid_t t;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (hash.algo) {
case HashAlgorithm::SHA1:
t = GIT_OID_SHA1;
break;
case HashAlgorithm::SHA256:
t = GIT_OID_SHA256;
break;
default:
throw Error("unsupported hash algorithm for Git: %s", printHashAlgo(hash.algo));
}
#pragma GCC diagnostic pop
if (git_oid_fromstr(&oid, hash.gitRev().c_str(), t))
throw Error("cannot convert '%s' to a Git OID", hash.gitRev());
return oid;
}
@@ -304,7 +329,8 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
// (synchronously on the git_packbuilder_write_buf thread)
Indexer indexer;
git_indexer_progress stats;
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), 0, nullptr, nullptr))
git_indexer_options indexer_opts = GIT_INDEXER_OPTIONS_INIT;
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), &indexer_opts))
throw Error("creating git packfile indexer: %s", git_error_last()->message);
// TODO: provide index callback for checkInterrupt() termination
@@ -1328,17 +1354,12 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
return result;
}
namespace fetchers {
ref<GitRepo> Settings::getTarballCache() const
ref<GitRepo> getTarballCache()
{
auto tarballCache(_tarballCache.lock());
if (!*tarballCache)
*tarballCache = GitRepo::openRepo(std::filesystem::path(getCacheDir()) / "tarball-cache", true, true);
return ref<GitRepo>(*tarballCache);
}
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
} // namespace fetchers
return GitRepo::openRepo(repoDir, true, true);
}
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
{
@@ -1383,4 +1404,21 @@ bool isLegalRefName(const std::string & refName)
return false;
}
Hash parseGitHash(std::string_view hashStr)
{
HashAlgorithm algo;
switch (hashStr.size()) {
case 40:
algo = HashAlgorithm::SHA1;
break;
case 64:
algo = HashAlgorithm::SHA256;
break;
default:
throw Error(
"invalid git hash '%s': expected 40 (SHA1) or 64 (SHA256) hex characters, got %d", hashStr, hashStr.size());
}
return Hash::parseNonSRIUnprefixed(hashStr, algo);
}
} // namespace nix

View File

@@ -168,6 +168,8 @@ struct GitInputScheme : InputScheme
return {};
auto url2(url);
if (hasPrefix(url2.scheme, "git+"))
url2.scheme = std::string(url2.scheme, 4);
url2.query.clear();
Attrs attrs;
@@ -229,7 +231,7 @@ struct GitInputScheme : InputScheme
if (auto ref = maybeGetStrAttr(attrs, "ref"); ref && !isLegalRefName(*ref))
throw BadURL("invalid Git branch/tag name '%s'", *ref);
Input input{};
Input input{settings};
input.attrs = attrs;
input.attrs["url"] = fixGitURL(getStrAttr(attrs, "url")).to_string();
getShallowAttr(input);
@@ -278,7 +280,7 @@ struct GitInputScheme : InputScheme
return res;
}
void clone(const Settings & settings, const Input & input, const Path & destDir) const override
void clone(const Input & input, const Path & destDir) const override
{
auto repoInfo = getRepoInfo(input);
@@ -623,7 +625,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromCommit(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const
getAccessorFromCommit(ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
assert(!repoInfo.workdirInfo.isDirty);
@@ -733,10 +735,10 @@ struct GitInputScheme : InputScheme
auto rev = *input.getRev();
input.attrs.insert_or_assign("lastModified", getLastModified(settings, repoInfo, repoDir, rev));
input.attrs.insert_or_assign("lastModified", getLastModified(*input.settings, repoInfo, repoDir, rev));
if (!getShallowAttr(input))
input.attrs.insert_or_assign("revCount", getRevCount(settings, repoInfo, repoDir, rev));
input.attrs.insert_or_assign("revCount", getRevCount(*input.settings, repoInfo, repoDir, rev));
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg());
@@ -779,8 +781,8 @@ struct GitInputScheme : InputScheme
attrs.insert_or_assign("submodules", Explicit<bool>{true});
attrs.insert_or_assign("lfs", Explicit<bool>{smudgeLfs});
attrs.insert_or_assign("allRefs", Explicit<bool>{true});
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
mounts.insert_or_assign(submodule.path, submoduleAccessor);
}
@@ -797,7 +799,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromWorkdir(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const
getAccessorFromWorkdir(ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
auto repoPath = repoInfo.getPath().value();
@@ -829,8 +831,8 @@ struct GitInputScheme : InputScheme
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
// attrs.insert_or_assign("allRefs", Explicit<bool>{ true });
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
/* If the submodule is dirty, mark this repo dirty as
@@ -857,12 +859,12 @@ struct GitInputScheme : InputScheme
input.attrs.insert_or_assign("rev", rev.gitRev());
if (!getShallowAttr(input)) {
input.attrs.insert_or_assign(
"revCount", rev == nullRev ? 0 : getRevCount(settings, repoInfo, repoPath, rev));
"revCount", rev == nullRev ? 0 : getRevCount(*input.settings, repoInfo, repoPath, rev));
}
verifyCommit(input, repo);
} else {
repoInfo.warnDirty(settings);
repoInfo.warnDirty(*input.settings);
if (repoInfo.workdirInfo.headRev) {
input.attrs.insert_or_assign("dirtyRev", repoInfo.workdirInfo.headRev->gitRev() + "-dirty");
@@ -874,14 +876,14 @@ struct GitInputScheme : InputScheme
input.attrs.insert_or_assign(
"lastModified",
repoInfo.workdirInfo.headRev ? getLastModified(settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
: 0);
repoInfo.workdirInfo.headRev
? getLastModified(*input.settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
: 0);
return {accessor, std::move(input)};
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
Input input(_input);
@@ -897,8 +899,8 @@ struct GitInputScheme : InputScheme
}
auto [accessor, final] = input.getRef() || input.getRev() || !repoInfo.getPath()
? getAccessorFromCommit(settings, store, repoInfo, std::move(input))
: getAccessorFromWorkdir(settings, store, repoInfo, std::move(input));
? getAccessorFromCommit(store, repoInfo, std::move(input))
: getAccessorFromWorkdir(store, repoInfo, std::move(input));
return {accessor, std::move(final)};
}
@@ -934,7 +936,7 @@ struct GitInputScheme : InputScheme
}
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
auto rev = input.getRev();
return rev && rev != nullRev;

View File

@@ -48,7 +48,7 @@ struct GitArchiveInputScheme : InputScheme
auto size = path.size();
if (size == 3) {
if (std::regex_match(path[2], revRegex))
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
rev = parseGitHash(path[2]);
else if (isLegalRefName(path[2]))
ref = path[2];
else
@@ -74,7 +74,7 @@ struct GitArchiveInputScheme : InputScheme
if (name == "rev") {
if (rev)
throw BadURL("URL '%s' contains multiple commit hashes", url);
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
rev = parseGitHash(value);
} else if (name == "ref") {
if (!isLegalRefName(value))
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
@@ -92,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme
if (ref && rev)
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev());
Input input{};
Input input{settings};
input.attrs.insert_or_assign("type", std::string{schemeName()});
input.attrs.insert_or_assign("owner", path[0]);
input.attrs.insert_or_assign("repo", path[1]);
@@ -129,7 +129,7 @@ struct GitArchiveInputScheme : InputScheme
getStrAttr(attrs, "owner");
getStrAttr(attrs, "repo");
Input input{};
Input input{settings};
input.attrs = attrs;
return input;
}
@@ -233,9 +233,9 @@ struct GitArchiveInputScheme : InputScheme
std::optional<Hash> treeHash;
};
virtual RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const = 0;
virtual RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
struct TarballInfo
{
@@ -243,7 +243,7 @@ struct GitArchiveInputScheme : InputScheme
time_t lastModified;
};
std::pair<Input, TarballInfo> downloadArchive(const Settings & settings, ref<Store> store, Input input) const
std::pair<Input, TarballInfo> downloadArchive(ref<Store> store, Input input) const
{
if (!maybeGetStrAttr(input.attrs, "ref"))
input.attrs.insert_or_assign("ref", "HEAD");
@@ -252,7 +252,7 @@ struct GitArchiveInputScheme : InputScheme
auto rev = input.getRev();
if (!rev) {
auto refInfo = getRevFromRef(settings, store, input);
auto refInfo = getRevFromRef(store, input);
rev = refInfo.rev;
upstreamTreeHash = refInfo.treeHash;
debug("HEAD revision for '%s' is %s", input.to_string(), refInfo.rev.gitRev());
@@ -261,7 +261,7 @@ struct GitArchiveInputScheme : InputScheme
input.attrs.erase("ref");
input.attrs.insert_or_assign("rev", rev->gitRev());
auto cache = settings.getCache();
auto cache = input.settings->getCache();
Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}};
Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}};
@@ -270,7 +270,7 @@ struct GitArchiveInputScheme : InputScheme
if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) {
auto treeHash = getRevAttr(*treeHashAttrs, "treeHash");
auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified");
if (settings.getTarballCache()->hasObject(treeHash))
if (getTarballCache()->hasObject(treeHash))
return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}};
else
debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev());
@@ -278,7 +278,7 @@ struct GitArchiveInputScheme : InputScheme
}
/* Stream the tarball into the tarball cache. */
auto url = getDownloadUrl(settings, input);
auto url = getDownloadUrl(input);
auto source = sinkToSource([&](Sink & sink) {
FileTransferRequest req(url.url);
@@ -290,7 +290,7 @@ struct GitArchiveInputScheme : InputScheme
*logger, lvlInfo, actUnknown, fmt("unpacking '%s' into the Git cache", input.to_string()));
TarArchive archive{*source};
auto tarballCache = settings.getTarballCache();
auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);
auto tree = parseSink->flush();
@@ -315,29 +315,28 @@ struct GitArchiveInputScheme : InputScheme
return {std::move(input), tarballInfo};
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
auto [input, tarballInfo] = downloadArchive(settings, store, _input);
auto [input, tarballInfo] = downloadArchive(store, _input);
#if 0
input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev());
#endif
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
auto accessor =
settings.getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
return {accessor, input};
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
/* Since we can't verify the integrity of the tarball from the
Git revision alone, we also require a NAR hash for
locking. FIXME: in the future, we may want to require a Git
tree hash instead of a NAR hash. */
return input.getRev().has_value() && (settings.trustTarballsFromGitForges || input.getNarHash().has_value());
return input.getRev().has_value()
&& (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value());
}
std::optional<ExperimentalFeature> experimentalFeature() const override
@@ -387,7 +386,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
return getStrAttr(input.attrs, "repo");
}
RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const override
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
auto host = getHost(input);
auto url = fmt(
@@ -397,22 +396,22 @@ struct GitHubInputScheme : GitArchiveInputScheme
getRepo(input),
*input.getRef());
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
auto json = nlohmann::json::parse(
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
return RefInfo{
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
.treeHash = Hash::parseAny(std::string{json["commit"]["tree"]["sha"]}, HashAlgorithm::SHA1)};
.rev = parseGitHash(std::string{json["sha"]}),
.treeHash = parseGitHash(std::string{json["commit"]["tree"]["sha"]})};
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = getHost(input);
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
// If we have no auth headers then we default to the public archive
// urls so we do not run into rate limits.
@@ -426,12 +425,12 @@ struct GitHubInputScheme : GitArchiveInputScheme
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, const Input & input, const Path & destDir) const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = getHost(input);
Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir);
.clone(destDir);
}
};
@@ -461,7 +460,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
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(nix::ref<Store> store, const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// See rate limiting note below
@@ -472,14 +471,14 @@ struct GitLabInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
*input.getRef());
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
auto json = nlohmann::json::parse(
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
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 = parseGitHash(std::string(json[0]["id"]))};
}
if (json.is_array() && json.size() == 0) {
throw Error("No commits returned by GitLab API -- does the git ref really exist?");
@@ -488,7 +487,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
}
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
// This endpoint has a rate limit threshold that may be
// server-specific and vary based whether the user is
@@ -503,19 +502,19 @@ struct GitLabInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, const Input & input, const Path & destDir) const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// FIXME: get username somewhere
Input::fromURL(
settings,
*input.settings,
fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir);
.clone(destDir);
}
};
@@ -536,7 +535,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
// 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(nix::ref<Store> store, const Input & input) const override
{
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
// and with anonymous access, this method should use it instead.
@@ -547,11 +546,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme
auto base_url =
fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
std::string refUri;
if (ref == "HEAD") {
auto downloadFileResult = downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers);
auto downloadFileResult = downloadFile(store, *input.settings, fmt("%s/HEAD", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto remoteLine = git::parseLsRemoteLine(getLine(contents).first);
@@ -564,7 +563,8 @@ struct SourceHutInputScheme : GitArchiveInputScheme
}
std::regex refRegex(refUri);
auto downloadFileResult = downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers);
auto downloadFileResult =
downloadFile(store, *input.settings, fmt("%s/info/refs", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
std::istringstream is(contents);
@@ -579,10 +579,10 @@ struct SourceHutInputScheme : GitArchiveInputScheme
if (!id)
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
return RefInfo{.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)};
return RefInfo{.rev = parseGitHash(*id)};
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto url =
@@ -592,18 +592,18 @@ struct SourceHutInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, const Input & input, const Path & destDir) const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(
settings,
*input.settings,
fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, destDir);
.clone(destDir);
}
};

View File

@@ -11,12 +11,6 @@
#include <sys/types.h>
namespace nix {
struct GitRepo;
}
namespace nix::fetchers {
struct Cache;
@@ -131,12 +125,8 @@ struct Settings : public Config
ref<Cache> getCache() const;
ref<GitRepo> getTarballCache() const;
private:
mutable Sync<std::shared_ptr<Cache>> _cache;
mutable Sync<std::shared_ptr<GitRepo>> _tarballCache;
};
} // namespace nix::fetchers

View File

@@ -36,6 +36,13 @@ struct Input
{
friend struct InputScheme;
const Settings * settings;
Input(const Settings & settings)
: settings{&settings}
{
}
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;
@@ -80,7 +87,7 @@ public:
* attributes like a Git revision or NAR hash that uniquely
* identify its contents.
*/
bool isLocked(const Settings & settings) const;
bool isLocked() const;
/**
* Only for relative path flakes, i.e. 'path:./foo', returns the
@@ -113,7 +120,7 @@ public:
* Fetch the entire input into the Nix store, returning the
* 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(ref<Store> store) const;
/**
* Check the locking attributes in `result` against
@@ -133,17 +140,17 @@ public:
* input without copying it to the store. Also return a possibly
* unlocked input.
*/
std::pair<ref<SourceAccessor>, Input> getAccessor(const Settings & settings, ref<Store> store) const;
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store) const;
private:
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, ref<Store> store) const;
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(ref<Store> store) const;
public:
Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const;
void clone(const Settings & settings, const Path & destDir) const;
void clone(const Path & destDir) const;
std::optional<std::filesystem::path> getSourcePath() const;
@@ -216,7 +223,7 @@ struct InputScheme
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 Input & input, const Path & destDir) const;
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
@@ -226,8 +233,7 @@ struct InputScheme
std::string_view contents,
std::optional<std::string> commitMsg) const;
virtual std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const = 0;
virtual std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & input) const = 0;
/**
* Is this `InputScheme` part of an experimental feature?
@@ -244,7 +250,7 @@ struct InputScheme
return std::nullopt;
}
virtual bool isLocked(const Settings & settings, const Input & input) const
virtual bool isLocked(const Input & input) const
{
return false;
}

View File

@@ -5,7 +5,7 @@
#include "nix/util/serialise.hh"
#include "nix/util/url.hh"
#include <git2/repository.h>
#include <git2-experimental/repository.h>
#include <nlohmann/json_fwd.hpp>

View File

@@ -120,6 +120,8 @@ struct GitRepo
virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0;
};
ref<GitRepo> getTarballCache();
// A helper to ensure that the `git_*_free` functions get called.
template<auto del>
struct Deleter
@@ -165,4 +167,14 @@ struct Setter
*/
bool isLegalRefName(const std::string & refName);
/**
* Parse a base16-encoded git hash string and determine the hash
* algorithm based on the length (40 chars = SHA1, 64 chars = SHA256).
*
* @note For Nix-native information we should *not* do length tricks,
* but instead always rely on an explicit algorithm. This hack should be
* only for foreign hash literals.
*/
Hash parseGitHash(std::string_view hashStr);
} // namespace nix

View File

@@ -3,7 +3,6 @@
namespace nix::fetchers {
enum class UseRegistries : int;
struct Settings;
struct InputCache
{
@@ -15,8 +14,7 @@ struct InputCache
Attrs extraAttrs;
};
CachedResult
getAccessor(const Settings & settings, ref<Store> store, const Input & originalInput, UseRegistries useRegistries);
CachedResult getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries);
struct CachedInput
{

View File

@@ -59,7 +59,7 @@ Path getUserRegistryPath();
Registries getRegistries(const Settings & settings, ref<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 {
No,
@@ -71,7 +71,6 @@ enum class UseRegistries : int {
* Rewrite a flakeref using the registries. If `filter` is set, only
* use the registries for which the filter function returns true.
*/
std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & input, UseRegistries useRegistries);
std::pair<Input, Attrs> lookupInRegistries(ref<Store> store, const Input & input, UseRegistries useRegistries);
} // namespace nix::fetchers

View File

@@ -23,7 +23,7 @@ struct IndirectInputScheme : InputScheme
if (path.size() == 1) {
} else if (path.size() == 2) {
if (std::regex_match(path[1], revRegex))
rev = Hash::parseAny(path[1], HashAlgorithm::SHA1);
rev = parseGitHash(path[1]);
else if (isLegalRefName(path[1]))
ref = path[1];
else
@@ -34,7 +34,7 @@ struct IndirectInputScheme : InputScheme
ref = path[1];
if (!std::regex_match(path[2], revRegex))
throw BadURL("in flake URL '%s', '%s' is not a commit hash", url, path[2]);
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
rev = parseGitHash(path[2]);
} else
throw BadURL("GitHub URL '%s' is invalid", url);
@@ -44,7 +44,7 @@ struct IndirectInputScheme : InputScheme
// FIXME: forbid query params?
Input input{};
Input input{settings};
input.attrs.insert_or_assign("type", "indirect");
input.attrs.insert_or_assign("id", id);
if (rev)
@@ -76,7 +76,7 @@ struct IndirectInputScheme : InputScheme
if (!std::regex_match(id, flakeRegex))
throw BadURL("'%s' is not a valid flake ID", id);
Input input{};
Input input{settings};
input.attrs = attrs;
return input;
}
@@ -106,8 +106,7 @@ struct IndirectInputScheme : InputScheme
return input;
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & input) const override
{
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
}

View File

@@ -5,23 +5,23 @@
namespace nix::fetchers {
InputCache::CachedResult InputCache::getAccessor(
const Settings & settings, ref<Store> store, const Input & originalInput, UseRegistries useRegistries)
InputCache::CachedResult
InputCache::getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries)
{
auto fetched = lookup(originalInput);
Input resolvedInput = originalInput;
if (!fetched) {
if (originalInput.isDirect()) {
auto [accessor, lockedInput] = originalInput.getAccessor(settings, store);
auto [accessor, lockedInput] = originalInput.getAccessor(store);
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
} else {
if (useRegistries != UseRegistries::No) {
auto [res, extraAttrs] = lookupInRegistries(settings, store, originalInput, useRegistries);
auto [res, extraAttrs] = lookupInRegistries(store, originalInput, useRegistries);
resolvedInput = std::move(res);
fetched = lookup(resolvedInput);
if (!fetched) {
auto [accessor, lockedInput] = resolvedInput.getAccessor(settings, store);
auto [accessor, lockedInput] = resolvedInput.getAccessor(store);
fetched.emplace(
CachedInput{.lockedInput = lockedInput, .accessor = accessor, .extraAttrs = extraAttrs});
}

View File

@@ -89,7 +89,7 @@ struct MercurialInputScheme : InputScheme
throw BadURL("invalid Mercurial branch/tag name '%s'", *ref);
}
Input input{};
Input input{settings};
input.attrs = attrs;
return input;
}
@@ -154,7 +154,7 @@ struct MercurialInputScheme : InputScheme
return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()};
}
StorePath fetchToStore(const Settings & settings, ref<Store> store, Input & input) const
StorePath fetchToStore(ref<Store> store, Input & input) const
{
auto origRev = input.getRev();
@@ -176,10 +176,10 @@ struct MercurialInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked
files. */
if (!settings.allowDirty)
if (!input.settings->allowDirty)
throw Error("Mercurial tree '%s' is unclean", actualUrl);
if (settings.warnDirty)
if (input.settings->warnDirty)
warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({"branch", "-R", actualUrl})));
@@ -240,13 +240,13 @@ struct MercurialInputScheme : InputScheme
Cache::Key refToRevKey{"hgRefToRev", {{"url", actualUrl}, {"ref", *input.getRef()}}};
if (!input.getRev()) {
if (auto res = settings.getCache()->lookupWithTTL(refToRevKey))
if (auto res = input.settings->getCache()->lookupWithTTL(refToRevKey))
input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev());
}
/* If we have a rev, check if we have a cached store path. */
if (auto rev = input.getRev()) {
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), *store))
if (auto res = input.settings->getCache()->lookupStorePath(revInfoKey(*rev), *store))
return makeResult(res->value, res->storePath);
}
@@ -300,7 +300,7 @@ struct MercurialInputScheme : InputScheme
/* Now that we have the rev, check the cache again for a
cached store path. */
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), *store))
if (auto res = input.settings->getCache()->lookupStorePath(revInfoKey(rev), *store))
return makeResult(res->value, res->storePath);
Path tmpDir = createTempDir();
@@ -317,19 +317,18 @@ struct MercurialInputScheme : InputScheme
});
if (!origRev)
settings.getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}});
input.settings->getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}});
settings.getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath);
input.settings->getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath);
return makeResult(infoAttrs, std::move(storePath));
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
Input input(_input);
auto storePath = fetchToStore(settings, store, input);
auto storePath = fetchToStore(store, input);
auto accessor = store->requireStoreObjectAccessor(storePath);
accessor->setPathDisplay("«" + input.to_string() + "»");
@@ -337,7 +336,7 @@ struct MercurialInputScheme : InputScheme
return {accessor, input};
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
return (bool) input.getRev();
}

View File

@@ -28,7 +28,7 @@ subdir('nix-meson-build-support/subprojects')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json
libgit2 = dependency('libgit2', version : '>= 1.9')
libgit2 = dependency('libgit2-experimental', version : '>= 1.9')
deps_private += libgit2
subdir('nix-meson-build-support/common')

View File

@@ -17,7 +17,7 @@ struct PathInputScheme : InputScheme
if (url.authority && url.authority->host.size())
throw Error("path URL '%s' should not have an authority ('%s')", url, *url.authority);
Input input{};
Input input{settings};
input.attrs.insert_or_assign("type", "path");
input.attrs.insert_or_assign("path", renderUrlPathEnsureLegal(url.path));
@@ -60,7 +60,7 @@ struct PathInputScheme : InputScheme
{
getStrAttr(attrs, "path");
Input input{};
Input input{settings};
input.attrs = attrs;
return input;
}
@@ -101,7 +101,7 @@ struct PathInputScheme : InputScheme
return path;
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
return (bool) input.getNarHash();
}
@@ -116,8 +116,7 @@ struct PathInputScheme : InputScheme
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
Input input(_input);
auto path = getStrAttr(input.attrs, "path");
@@ -146,7 +145,7 @@ struct PathInputScheme : InputScheme
auto info = store->queryPathInfo(*storePath);
accessor->fingerprint =
fmt("path:%s", store->queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true));
settings.getCache()->upsert(
input.settings->getCache()->upsert(
makeFetchToStoreCacheKey(
input.getName(), *accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"),
*store,

View File

@@ -131,9 +131,9 @@ std::shared_ptr<Registry> getFlagRegistry(const Settings & settings)
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(*from.settings)->add(from, to, extraAttrs);
}
static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, ref<Store> store)
@@ -172,8 +172,7 @@ Registries getRegistries(const Settings & settings, ref<Store> store)
return registries;
}
std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & _input, UseRegistries useRegistries)
std::pair<Input, Attrs> lookupInRegistries(ref<Store> store, const Input & _input, UseRegistries useRegistries)
{
Attrs extraAttrs;
int n = 0;
@@ -188,7 +187,7 @@ restart:
if (n > 100)
throw Error("cycle detected in flake registry for '%s'", input.to_string());
for (auto & registry : getRegistries(settings, store)) {
for (auto & registry : getRegistries(*input.settings, store)) {
if (useRegistries == UseRegistries::Limited
&& !(registry->type == fetchers::Registry::Flag || registry->type == fetchers::Registry::Global))
continue;

View File

@@ -136,11 +136,11 @@ static DownloadTarballResult downloadTarball_(
.treeHash = treeHash,
.lastModified = (time_t) getIntAttr(infoAttrs, "lastModified"),
.immutableUrl = maybeGetStrAttr(infoAttrs, "immutableUrl"),
.accessor = settings.getTarballCache()->getAccessor(treeHash, false, displayPrefix),
.accessor = getTarballCache()->getAccessor(treeHash, false, displayPrefix),
};
};
if (cached && !settings.getTarballCache()->hasObject(getRevAttr(cached->value, "treeHash")))
if (cached && !getTarballCache()->hasObject(getRevAttr(cached->value, "treeHash")))
cached.reset();
if (cached && !cached->expired)
@@ -179,7 +179,7 @@ static DownloadTarballResult downloadTarball_(
TarArchive{path};
})
: TarArchive{*source};
auto tarballCache = settings.getTarballCache();
auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);
auto tree = parseSink->flush();
@@ -224,7 +224,7 @@ ref<SourceAccessor> downloadTarball(ref<Store> store, const Settings & settings,
auto input = Input::fromAttrs(settings, std::move(attrs));
return input.getAccessor(settings, store).first;
return input.getAccessor(store).first;
}
// An input scheme corresponding to a curl-downloadable resource.
@@ -252,7 +252,7 @@ struct CurlInputScheme : InputScheme
if (!isValidURL(_url, requireTree))
return std::nullopt;
Input input{};
Input input{settings};
auto url = _url;
@@ -302,7 +302,7 @@ struct CurlInputScheme : InputScheme
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
{
Input input{};
Input input{settings};
input.attrs = attrs;
// input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
@@ -319,7 +319,7 @@ struct CurlInputScheme : InputScheme
return url;
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
return (bool) input.getNarHash();
}
@@ -340,8 +340,7 @@ struct FileInputScheme : CurlInputScheme
: (!requireTree && !hasTarballExtension(url)));
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
auto input(_input);
@@ -349,7 +348,7 @@ struct FileInputScheme : CurlInputScheme
the Nix store directly, since there is little deduplication
benefit in using the Git cache for single big files like
tarballs. */
auto file = downloadFile(store, settings, getStrAttr(input.attrs, "url"), input.getName());
auto file = downloadFile(store, *input.settings, getStrAttr(input.attrs, "url"), input.getName());
auto narHash = store->queryPathInfo(file.storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
@@ -378,15 +377,15 @@ struct TarballInputScheme : CurlInputScheme
: (requireTree || hasTarballExtension(url)));
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
auto input(_input);
auto result = downloadTarball_(settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string() + "»");
auto result =
downloadTarball_(*input.settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string() + "»");
if (result.immutableUrl) {
auto immutableInput = Input::fromURL(settings, *result.immutableUrl);
auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl);
// FIXME: would be nice to support arbitrary flakerefs
// here, e.g. git flakes.
if (immutableInput.getType() != "tarball")
@@ -399,7 +398,7 @@ struct TarballInputScheme : CurlInputScheme
input.attrs.insert_or_assign(
"narHash",
settings.getTarballCache()->treeHashToNarHash(settings, result.treeHash).to_string(HashFormat::SRI, true));
getTarballCache()->treeHashToNarHash(*input.settings, result.treeHash).to_string(HashFormat::SRI, true));
return {result.accessor, input};
}

View File

@@ -19,7 +19,6 @@
#include "nix/expr/eval-settings.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/util/configuration.hh"
@@ -39,7 +38,7 @@ PrimOp getFlake(const Settings & settings)
std::string flakeRefS(
state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings))
if (state.settings.pureEval && !flakeRef.input.isLocked())
throw Error(
"cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)",
flakeRefS,
@@ -63,7 +62,7 @@ PrimOp getFlake(const Settings & settings)
return PrimOp{
.name = "__getFlake",
.args = {"args"},
.doc = &R"(
.doc = R"(
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
```nix
@@ -77,7 +76,7 @@ PrimOp getFlake(const Settings & settings)
```nix
(builtins.getFlake "github:edolstra/dwarffs").rev
```
)"_sds,
)",
.fun = prim_getFlake,
.experimentalFeature = Xp::Flakes,
};
@@ -105,7 +104,7 @@ static void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value ** arg
nix::PrimOp parseFlakeRef({
.name = "__parseFlakeRef",
.args = {"flake-ref"},
.doc = &R"(
.doc = R"(
Parse a flake reference, and return its exploded form.
For example:
@@ -119,7 +118,7 @@ nix::PrimOp parseFlakeRef({
```nix
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
```
)"_sds,
)",
.fun = prim_parseFlakeRef,
.experimentalFeature = Xp::Flakes,
});
@@ -163,7 +162,7 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value **
nix::PrimOp flakeRefToString({
.name = "__flakeRefToString",
.args = {"attrs"},
.doc = &R"(
.doc = R"(
Convert a flake reference from attribute set format to URL format.
For example:
@@ -179,7 +178,7 @@ nix::PrimOp flakeRefToString({
```nix
"github:NixOS/nixpkgs/23.05?dir=lib"
```
)"_sds,
)",
.fun = prim_flakeRefToString,
.experimentalFeature = Xp::Flakes,
});

View File

@@ -372,8 +372,7 @@ static Flake getFlake(
const InputAttrPath & lockRootAttrPath)
{
// Fetch a lazy tree first.
auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, state.store, originalRef.input, useRegistries);
auto cachedInput = state.inputCache->getAccessor(state.store, originalRef.input, useRegistries);
auto subdir = fetchers::maybeGetStrAttr(cachedInput.extraAttrs, "dir").value_or(originalRef.subdir);
auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), subdir);
@@ -389,8 +388,7 @@ static Flake getFlake(
debug("refetching input '%s' due to self attribute", newLockedRef);
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
newLockedRef.input.attrs.erase("narHash");
auto cachedInput2 = state.inputCache->getAccessor(
state.fetchSettings, state.store, newLockedRef.input, fetchers::UseRegistries::No);
auto cachedInput2 = state.inputCache->getAccessor(state.store, newLockedRef.input, fetchers::UseRegistries::No);
cachedInput.accessor = cachedInput2.accessor;
lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir);
}
@@ -706,8 +704,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
this input. */
debug("creating new input '%s'", inputAttrPathS);
if (!lockFlags.allowUnlocked && !input.ref->input.isLocked(state.fetchSettings)
&& !input.ref->input.isRelative())
if (!lockFlags.allowUnlocked && !input.ref->input.isLocked() && !input.ref->input.isRelative())
throw Error("cannot update unlocked flake input '%s' in pure mode", inputAttrPathS);
/* Note: in case of an --override-input, we use
@@ -756,7 +753,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
return {*resolvedPath, *input.ref};
} else {
auto cachedInput = state.inputCache->getAccessor(
state.fetchSettings, state.store, input.ref->input, useRegistriesInputs);
state.store, input.ref->input, useRegistriesInputs);
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);

View File

@@ -64,10 +64,9 @@ std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef)
return str;
}
FlakeRef FlakeRef::resolve(
const fetchers::Settings & fetchSettings, ref<Store> store, fetchers::UseRegistries useRegistries) const
FlakeRef FlakeRef::resolve(ref<Store> store, fetchers::UseRegistries useRegistries) const
{
auto [input2, extraAttrs] = lookupInRegistries(fetchSettings, store, input, useRegistries);
auto [input2, extraAttrs] = lookupInRegistries(store, input, useRegistries);
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
}
@@ -288,10 +287,9 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Settings & fetchSettings, const fet
fetchers::maybeGetStrAttr(attrs, "dir").value_or(""));
}
std::pair<ref<SourceAccessor>, FlakeRef>
FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const
std::pair<ref<SourceAccessor>, FlakeRef> FlakeRef::lazyFetch(ref<Store> store) const
{
auto [accessor, lockedInput] = input.getAccessor(fetchSettings, store);
auto [accessor, lockedInput] = input.getAccessor(store);
return {accessor, FlakeRef(std::move(lockedInput), subdir)};
}

View File

@@ -71,15 +71,11 @@ struct FlakeRef
fetchers::Attrs toAttrs() const;
FlakeRef resolve(
const fetchers::Settings & fetchSettings,
ref<Store> store,
fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const;
FlakeRef resolve(ref<Store> store, fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const;
static FlakeRef fromAttrs(const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs);
std::pair<ref<SourceAccessor>, FlakeRef>
lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const;
std::pair<ref<SourceAccessor>, FlakeRef> lazyFetch(ref<Store> store) const;
/**
* Canonicalize a flakeref for the purpose of comparing "old" and

View File

@@ -74,7 +74,7 @@ LockedNode::LockedNode(const fetchers::Settings & fetchSettings, const nlohmann:
, parentInputAttrPath(
json.find("parent") != json.end() ? (std::optional<InputAttrPath>) json["parent"] : std::nullopt)
{
if (!lockedRef.input.isLocked(fetchSettings) && !lockedRef.input.isRelative()) {
if (!lockedRef.input.isLocked() && !lockedRef.input.isRelative()) {
if (lockedRef.input.getNarHash())
warn(
"Lock file entry '%s' is unlocked (e.g. lacks a Git revision) but is checked by NAR hash. "
@@ -282,7 +282,7 @@ std::optional<FlakeRef> LockFile::isUnlocked(const fetchers::Settings & fetchSet
latter case, we can verify the input but we may not be able to
fetch it from anywhere. */
auto isConsideredLocked = [&](const fetchers::Input & input) {
return input.isLocked(fetchSettings) || (fetchSettings.allowDirtyLocks && input.getNarHash());
return input.isLocked() || (fetchSettings.allowDirtyLocks && input.getNarHash());
};
for (auto & i : nodes) {

View File

@@ -467,6 +467,8 @@ public:
std::string getStatus(State & state)
{
auto MiB = 1024.0 * 1024.0;
std::string res;
auto renderActivity =
@@ -514,65 +516,6 @@ public:
return s;
};
auto renderSizeActivity = [&](ActivityType type, const std::string & itemFmt = "%s") {
auto & act = state.activitiesByType[type];
uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed;
for (auto & j : act.its) {
done += j.second->done;
expected += j.second->expected;
running += j.second->running;
failed += j.second->failed;
}
expected = std::max(expected, act.expected);
std::optional<SizeUnit> commonUnit;
std::string s;
if (running || done || expected || failed) {
if (running)
if (expected != 0) {
commonUnit = getCommonSizeUnit({(int64_t) running, (int64_t) done, (int64_t) expected});
s =
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL "/%s",
commonUnit ? renderSizeWithoutUnit(running, *commonUnit) : renderSize(running),
commonUnit ? renderSizeWithoutUnit(done, *commonUnit) : renderSize(done),
commonUnit ? renderSizeWithoutUnit(expected, *commonUnit) : renderSize(expected));
} else {
commonUnit = getCommonSizeUnit({(int64_t) running, (int64_t) done});
s =
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL,
commonUnit ? renderSizeWithoutUnit(running, *commonUnit) : renderSize(running),
commonUnit ? renderSizeWithoutUnit(done, *commonUnit) : renderSize(done));
}
else if (expected != done)
if (expected != 0) {
commonUnit = getCommonSizeUnit({(int64_t) done, (int64_t) expected});
s =
fmt(ANSI_GREEN "%s" ANSI_NORMAL "/%s",
commonUnit ? renderSizeWithoutUnit(done, *commonUnit) : renderSize(done),
commonUnit ? renderSizeWithoutUnit(expected, *commonUnit) : renderSize(expected));
} else {
commonUnit = getSizeUnit(done);
s = fmt(ANSI_GREEN "%s" ANSI_NORMAL, renderSizeWithoutUnit(done, *commonUnit));
}
else {
commonUnit = getSizeUnit(done);
s = fmt(done ? ANSI_GREEN "%s" ANSI_NORMAL : "%s", renderSizeWithoutUnit(done, *commonUnit));
}
if (commonUnit)
s = fmt("%s %siB", s, getSizeUnitSuffix(*commonUnit));
s = fmt(itemFmt, s);
if (failed)
s += fmt(" (" ANSI_RED "%s failed" ANSI_NORMAL ")", renderSize(failed));
}
return s;
};
auto showActivity =
[&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
auto s = renderActivity(type, itemFmt, numberFmt, unit);
@@ -586,7 +529,7 @@ public:
showActivity(actBuilds, "%s built");
auto s1 = renderActivity(actCopyPaths, "%s copied");
auto s2 = renderSizeActivity(actCopyPath);
auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);
if (!s1.empty() || !s2.empty()) {
if (!res.empty())
@@ -602,12 +545,12 @@ public:
}
}
renderSizeActivity(actFileTransfer, "%s DL");
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
{
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
if (s != "") {
s += fmt(", %s / %d inodes freed", renderSize(state.bytesLinked), state.filesLinked);
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
if (!res.empty())
res += ", ";
res += s;

View File

@@ -6,7 +6,6 @@
#include "nix/store/tests/libstore.hh"
#include "nix/util/tests/characterization.hh"
#include "nix/util/tests/json-characterization.hh"
namespace nix {
@@ -17,30 +16,12 @@ class ProtoTest : public CharacterizationTest
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / testStem;
return unitTestData / (std::string{testStem + ".bin"});
}
public:
Path storeDir = "/nix/store";
StoreDirConfig store{storeDir};
/**
* Golden test for `T` JSON reading
*/
template<typename T>
void readJsonTest(PathView testStem, const T & expected)
{
nix::readJsonTest(*this, testStem, expected);
}
/**
* Golden test for `T` JSON write
*/
template<typename T>
void writeJsonTest(PathView testStem, const T & decoded)
{
nix::writeJsonTest(*this, testStem, decoded);
}
};
template<class Proto, const char * protocolDir>
@@ -53,7 +34,7 @@ public:
template<typename T>
void readProtoTest(PathView testStem, typename Proto::Version version, T expected)
{
CharacterizationTest::readTest(std::string{testStem + ".bin"}, [&](const auto & encoded) {
CharacterizationTest::readTest(testStem, [&](const auto & encoded) {
T got = ({
StringSource from{encoded};
Proto::template Serialise<T>::read(
@@ -74,7 +55,7 @@ public:
template<typename T>
void writeProtoTest(PathView testStem, typename Proto::Version version, const T & decoded)
{
CharacterizationTest::writeTest(std::string{testStem + ".bin"}, [&]() {
CharacterizationTest::writeTest(testStem, [&]() {
StringSink to;
Proto::template Serialise<T>::write(
this->store,
@@ -88,25 +69,14 @@ public:
}
};
#define VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME##_read) \
{ \
readProtoTest(STEM, VERSION, VALUE); \
} \
TEST_F(FIXTURE, NAME##_write) \
{ \
writeProtoTest(STEM, VERSION, VALUE); \
}
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME##_json_read) \
{ \
readJsonTest(STEM, VALUE); \
} \
TEST_F(FIXTURE, NAME##_json_write) \
{ \
writeJsonTest(STEM, VALUE); \
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME##_read) \
{ \
readProtoTest(STEM, VERSION, VALUE); \
} \
TEST_F(FIXTURE, NAME##_write) \
{ \
writeProtoTest(STEM, VERSION, VALUE); \
}
} // namespace nix

View File

@@ -3,7 +3,6 @@
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include "nix/util/json-utils.hh"
#include "nix/store/common-protocol.hh"
#include "nix/store/common-protocol-impl.hh"
#include "nix/store/build-result.hh"
@@ -23,7 +22,7 @@ public:
template<typename T>
void readProtoTest(PathView testStem, const T & expected)
{
CharacterizationTest::readTest(std::string{testStem + ".bin"}, [&](const auto & encoded) {
CharacterizationTest::readTest(testStem, [&](const auto & encoded) {
T got = ({
StringSource from{encoded};
CommonProto::Serialise<T>::read(store, CommonProto::ReadConn{.from = from});
@@ -39,7 +38,7 @@ public:
template<typename T>
void writeProtoTest(PathView testStem, const T & decoded)
{
CharacterizationTest::writeTest(std::string{testStem + ".bin"}, [&]() -> std::string {
CharacterizationTest::writeTest(testStem, [&]() -> std::string {
StringSink to;
CommonProto::Serialise<T>::write(store, CommonProto::WriteConn{.to = to}, decoded);
return to.s;
@@ -55,14 +54,6 @@ public:
TEST_F(CommonProtoTest, NAME##_write) \
{ \
writeProtoTest(STEM, VALUE); \
} \
TEST_F(CommonProtoTest, NAME##_json_read) \
{ \
readJsonTest(STEM, VALUE); \
} \
TEST_F(CommonProtoTest, NAME##_json_write) \
{ \
writeJsonTest(STEM, VALUE); \
}
CHARACTERIZATION_TEST(

View File

@@ -1,26 +0,0 @@
[
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "+Xc9Ll6mcPltwaewrk/BAQ56Y3G5T//wzhKUc0zrYu0="
},
"method": "text"
},
{
"hash": {
"algorithm": "sha1",
"format": "base64",
"hash": "gGemBoenViNZM3hiwqns/Fgzqwo="
},
"method": "flat"
},
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
},
"method": "nar"
}
]

View File

@@ -1,4 +0,0 @@
[
"sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!quux"
]

View File

@@ -1,11 +0,0 @@
[
null,
{
"hash": {
"algorithm": "sha1",
"format": "base64",
"hash": "gGemBoenViNZM3hiwqns/Fgzqwo="
},
"method": "flat"
}
]

View File

@@ -1,4 +0,0 @@
[
null,
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar"
]

View File

@@ -1,13 +0,0 @@
[
{
"dependentRealisations": {
"sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!quux": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"
},
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf",
"qwer"
]
}
]

View File

@@ -1,17 +0,0 @@
[
{
"dependentRealisations": {},
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": []
},
{
"dependentRealisations": {},
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf",
"qwer"
]
}
]

View File

@@ -1,22 +0,0 @@
[
[],
[
""
],
[
"",
"bar",
"foo"
],
[
[],
[
""
],
[
"",
"1",
"2"
]
]
]

View File

@@ -1,4 +0,0 @@
[
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar"
]

View File

@@ -1,7 +0,0 @@
[
"",
"hi",
"white rabbit",
"大白兔",
"oh no "
]

View File

@@ -1,22 +0,0 @@
[
[],
[
""
],
[
"",
"foo",
"bar"
],
[
[],
[
""
],
[
"",
"1",
"2"
]
]
]

View File

@@ -69,8 +69,7 @@
"outputChecks": {
"bin": {
"disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"dev"
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
],
"disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
@@ -85,8 +84,7 @@
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"bin"
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
]
}
},

View File

@@ -11,9 +11,9 @@
"__sandboxProfile": "sandcastle",
"allowSubstitutes": "",
"allowedReferences": "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9",
"allowedRequisites": "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z bin",
"allowedRequisites": "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"builder": "/bin/bash",
"disallowedReferences": "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g dev",
"disallowedReferences": "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"disallowedRequisites": "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8",
"exportReferencesGraph": "refs1 /164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9 refs2 /nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"impureEnvVars": "UNICORN",

View File

@@ -23,12 +23,10 @@
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"bin"
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
],
"disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"dev"
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
],
"disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"

View File

@@ -23,8 +23,7 @@
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"dev"
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
],
"disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
@@ -47,8 +46,7 @@
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"bin"
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
],
"disallowedReferences": [],
"disallowedRequisites": [],

View File

@@ -5,9 +5,9 @@
],
"builder": "/bin/bash",
"env": {
"bin": "/nix/store/cnpasdljgkhnwaf78cf3qygcp4qbki1c-advanced-attributes-structured-attrs-bin",
"dev": "/nix/store/ijq6mwpa9jbnpnl33qldfqihrr38kprx-advanced-attributes-structured-attrs-dev",
"out": "/nix/store/h1vh648d3p088kdimy0r8ngpfx7c3nzw-advanced-attributes-structured-attrs"
"bin": "/nix/store/33qms3h55wlaspzba3brlzlrm8m2239g-advanced-attributes-structured-attrs-bin",
"dev": "/nix/store/wyfgwsdi8rs851wmy1xfzdxy7y5vrg5l-advanced-attributes-structured-attrs-dev",
"out": "/nix/store/7cxy4zx1vqc885r4jl2l64pymqbdmhii-advanced-attributes-structured-attrs"
},
"inputs": {
"drvs": {
@@ -33,13 +33,13 @@
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
"path": "cnpasdljgkhnwaf78cf3qygcp4qbki1c-advanced-attributes-structured-attrs-bin"
"path": "33qms3h55wlaspzba3brlzlrm8m2239g-advanced-attributes-structured-attrs-bin"
},
"dev": {
"path": "ijq6mwpa9jbnpnl33qldfqihrr38kprx-advanced-attributes-structured-attrs-dev"
"path": "wyfgwsdi8rs851wmy1xfzdxy7y5vrg5l-advanced-attributes-structured-attrs-dev"
},
"out": {
"path": "h1vh648d3p088kdimy0r8ngpfx7c3nzw-advanced-attributes-structured-attrs"
"path": "7cxy4zx1vqc885r4jl2l64pymqbdmhii-advanced-attributes-structured-attrs"
}
},
"structuredAttrs": {
@@ -66,8 +66,7 @@
"outputChecks": {
"bin": {
"disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar",
"dev"
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
],
"disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
@@ -82,8 +81,7 @@
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev",
"bin"
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
]
}
},

View File

@@ -11,14 +11,14 @@
"__sandboxProfile": "sandcastle",
"allowSubstitutes": "",
"allowedReferences": "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo",
"allowedRequisites": "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev bin",
"allowedRequisites": "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev",
"builder": "/bin/bash",
"disallowedReferences": "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar dev",
"disallowedReferences": "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar",
"disallowedRequisites": "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev",
"exportReferencesGraph": "refs1 /nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo refs2 /nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",
"impureEnvVars": "UNICORN",
"name": "advanced-attributes",
"out": "/nix/store/ymqmybkq5j4nd1xplw6ccdpbjnfi017v-advanced-attributes",
"out": "/nix/store/wyhpwd748pns4k7svh48wdrc8kvjk0ra-advanced-attributes",
"preferLocalBuild": "1",
"requiredSystemFeatures": "rainbow uid-range",
"system": "my-system"
@@ -47,7 +47,7 @@
"name": "advanced-attributes",
"outputs": {
"out": {
"path": "ymqmybkq5j4nd1xplw6ccdpbjnfi017v-advanced-attributes"
"path": "wyhpwd748pns4k7svh48wdrc8kvjk0ra-advanced-attributes"
}
},
"system": "my-system",

View File

@@ -23,12 +23,10 @@
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev",
"bin"
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
],
"disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar",
"dev"
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
],
"disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"

View File

@@ -23,8 +23,7 @@
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar",
"dev"
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
],
"disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
@@ -47,8 +46,7 @@
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev",
"bin"
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
],
"disallowedReferences": [],
"disallowedRequisites": [],

View File

@@ -9,17 +9,9 @@
},
"compression": "xz",
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"downloadHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"downloadSize": 4029176,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",

View File

@@ -7,11 +7,7 @@
},
"method": "nar"
},
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",

Some files were not shown because too many files have changed in this diff Show More