Compare commits

..
Author SHA1 Message Date
Sergei Zimmerman 2ded675e56 treewide: Make exceptions cloneable
This is needed to make it possible to store exceptions in failed values
with each new rethrow getting a fresh copy of the exception object.
2026-02-18 19:45:49 +03:00
274 changed files with 1948 additions and 3787 deletions

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
# required to find all branches
fetch-depth: 0
- name: Create backport PRs
uses: korthout/backport-action@01619ebc9a6e3f6820274221b9956b3e7365000a # v4.1.0
uses: korthout/backport-action@c656f5d5851037b2b38fb5db2691a03fa229e3b2 # v4.0.1
id: backport
with:
# Config README: https://github.com/korthout/backport-action#backport-action
+3 -21
View File
@@ -137,24 +137,6 @@ jobs:
path: out/*
if: ${{ matrix.primary }}
windows_tests:
needs: basic-checks
name: windows unit tests
runs-on: ubuntu-24.04
continue-on-error: true
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
- name: Run Windows unit tests
run: |
nix build --file ci/gha/tests/windows.nix unitTests.nix-util-tests -L
installer_test:
needs: [tests]
strategy:
@@ -192,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@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
if: ${{ !matrix.experimental-installer }}
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
@@ -240,12 +222,12 @@ jobs:
id: installer-tarball-url
run: |
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
- name: Run flake regressions tests
run: MAX_FLAKES=100000 flake-regressions/eval-all.sh
run: MAX_FLAKES=25 flake-regressions/eval-all.sh
profile_build:
needs: tests
-30
View File
@@ -1,30 +0,0 @@
{
nixFlake ? builtins.getFlake ("git+file://" + toString ../../..),
system ? builtins.currentSystem,
pkgs ? nixFlake.inputs.nixpkgs.legacyPackages.${system},
}:
let
packages = nixFlake.packages.${system};
fixOutput =
test:
test.overrideAttrs (prev: {
nativeBuildInputs = prev.nativeBuildInputs or [ ] ++ [ pkgs.colorized-logs ];
env.GTEST_COLOR = "no";
# Wine's console emulation wraps every character in ANSI cursor
# hide/show sequences, making logs unreadable in GitHub Actions.
buildCommand = ''
set -o pipefail
{
${prev.buildCommand}
} 2>&1 | ansi2txt
'';
});
in
{
unitTests = {
"nix-util-tests" = fixOutput packages."nix-util-tests-x86_64-w64-mingw32".passthru.tests.run;
};
}
@@ -1,23 +0,0 @@
---
synopsis: "C API: Errors returned from your primops are not treated as recoverable by default"
prs: [15286, 13930]
---
Nix 2.34 by default remembers the error in the thunk that triggered it.
Previously the following sequence of events worked:
1. Have a thunk that invokes a primop that's defined through the C API
2. The primop returns an error
3. Force the thunk again
4. The primop returns a value
5. The thunk evaluated successfully
**Resolution**
C API consumers that rely on this must change their recoverable error calls:
```diff
-nix_set_err_msg(context, NIX_ERR_*, msg);
+nix_set_err_msg(context, NIX_ERR_RECOVERABLE, msg);
```
@@ -1,40 +0,0 @@
---
synopsis: "Attribute set equality now compares in lexicographic order of attribute names"
prs: [15327]
---
Attribute set equality (`==`) now compares attributes in lexicographic order of attribute names, rather than the internal symbol table order. Comparing attribute sets with mismatching `attrNames` now never evaluates any values, whereas previously that was undefined and depended on the unspecified symbol table order.
Previously, the evaluation order of attribute values during comparison was non-deterministic, meaning expressions like the following could silently succeed or fail depending on evaluation history or unspecified order of evaluation.
For example, depending on the order in which attribute names `a` and `b` have been seen by the evaluator, the following code would either yield `false` or fail to evaluate.
```nix
{ a = throw "oops"; b = 2; } == { a = 2; b = 1; }
```
```
nix-repl> a = 1
nix-repl> b = 2
nix-repl> { a = throw "oops"; b = 2; } == { a = 2; b = 1; }
error:
… while calling the 'throw' builtin
at «string»:1:7:
1| { a = throw "oops"; b = 2; } == { a = 2; b = 1; }
| ^
error: oops
```
```
nix-repl> b = 2
nix-repl> a = 1
nix-repl> { a = throw "oops"; b = 2; } == { a = 2; b = 1; }
false
```
@@ -1,10 +0,0 @@
---
synopsis: Content-Encoding decompression is now handled by libcurl
prs: [15336]
issues: [14324]
---
Transparent decompression of HTTP downloads specifying `Content-Encoding` header now uses libcurl. This adds support for previously advertised, but not supported `deflate` encoding as well as deprecated `x-gzip` alias.
Non-standard `xz`, `bzip2` encodings that were previously advertised are no longer supported, as they do not commonly appear in the wild and should not be sent by compliant servers.
`br`, `zstd`, `gzip` continue to be supported. Distro packaging should ensure that the `libcurl` dependency is linked against required libraries to support these encodings. By default now the build system requires libcurl >= 8.17.0 which is not known to have issues around [pausing and decompression](https://github.com/curl/curl/issues/16280).
-42
View File
@@ -1,42 +0,0 @@
---
synopsis: "New diagnostics infrastructure, with `lint-url-literals` and `lint-short-path-literals` settings"
prs: [15326]
issues: [10048, 10281]
---
A new diagnostics infrastructure has been added for controlling language features that we are considering deprecating.
## [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals)
The `no-url-literals` experimental feature has been stabilized and replaced with a new [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals) setting.
To migrate from the experimental feature, replace:
```
experimental-features = no-url-literals
```
with:
```
lint-url-literals = fatal
```
## [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals)
The [`warn-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-warn-short-path-literals) boolean setting has been deprecated and replaced with [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals).
To migrate, replace:
```
warn-short-path-literals = true
```
with:
```
lint-short-path-literals = warn
```
## Setting values
Both settings accept three values:
- `ignore`: Allow the feature without any diagnostic (default)
- `warn`: Emit a warning when the feature is used
- `fatal`: Treat the feature as a parse error
In the future, we may change what the defaults are.
@@ -1,6 +0,0 @@
---
synopsis: New setting `narinfo-cache-meta-ttl`
prs: [15287]
---
The new setting `narinfo-cache-meta-ttl` controls how long binary cache metadata (i.e. `/nix-cache-info`) is cached locally, in seconds. This was previously hard-coded to 7 days, which is still the default. As a result, you can now use `nix store info --refresh` to check whether a binary cache is still valid.
-21
View File
@@ -1,21 +0,0 @@
---
synopsis: "`nix repl` now supports `inherit` and multiple bindings"
prs: [15082]
---
The `nix repl` now supports `inherit` statements and multiple bindings per line:
```
nix-repl> a = { x = 1; y = 2; }
nix-repl> inherit (a) x y
nix-repl> x + y
3
nix-repl> p = 1; q = 2;
nix-repl> p + q
3
nix-repl> foo.bar.baz = 1;
nix-repl> foo.bar
{ baz = 1; }
```
+2 -3
View File
@@ -188,14 +188,13 @@ All comparison operators are implemented in terms of `<`, and the following equi
## Equality
- [Attribute sets][attribute set] are compared by size first and then by attribute names and their values until a difference is found.
- [Attribute sets][attribute set] are compared first by attribute names and then by items until a difference is found.
- [Lists][list] are compared first by length and then by items until a difference is found.
- Comparison of distinct [functions][function] returns `false`, but identical functions may be subject to [value identity optimization](#value-identity-optimization).
- Numbers are type-compatible, see [arithmetic] operators.
- Floating point numbers only differ up to a limited precision.
The `==` operator is [strict](@docroot@/language/evaluation.md#strictness) in both arguments; when comparing composite types ([attribute sets][attribute set] and [lists][list]), it is partially strict in their contained values: they are evaluated until a difference is found. On `attrNames` mismatches, no contained values are evaluated. Attribute set comparison `a == b` behaves equivalently to `(builtins.attrNames a) == (builtins.attrNames b) && (builtins.attrValues a == builtins.attrValues b)` if [value identity optimization](#value-identity-optimization) is not taken into account.
Equality comparison of contained values happens in canonical order: attributes are compared in lexicographic order of attribute names and list elements are compared in the order in which they appear in the list.
The `==` operator is [strict](@docroot@/language/evaluation.md#strictness) in both arguments; when comparing composite types ([attribute sets][attribute set] and [lists][list]), it is partially strict in their contained values: they are evaluated until a difference is found. <!-- this is woefully underspecified, affecting which expressions evaluate correctly; not just "ordering" or error messages. -->
### Value identity optimization
+1
View File
@@ -358,6 +358,7 @@ dockerTools.buildLayeredImageWithNixDb {
extraCommands = ''
rm -rf nix-support
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
'';
fakeRootCommands = ''
chmod 1777 tmp
Generated
+4 -4
View File
@@ -63,11 +63,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1771903837,
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"lastModified": 1771043024,
"narHash": "sha256-WoiezqWJQ3OHILah+p6rzNXdJceEAmAhyDFZFZ6pZzY=",
"rev": "3aadb7ca9eac2891d52a9dec199d9580a6e2bf44",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.6495.e764fc9a4058/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.5960.3aadb7ca9eac/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
+20 -27
View File
@@ -409,9 +409,7 @@
"nix-cmd" = { };
"nix-nswrapper" = {
linuxOnly = true;
};
"nix-nswrapper" = { };
"nix-cli" = { };
@@ -433,37 +431,32 @@
pkgName:
{
supportsCross ? true,
linuxOnly ? false,
}:
lib.optionalAttrs (linuxOnly -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux) (
{
# These attributes go right into `packages.<system>`.
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
}
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
stdenvName:
{ }:
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${stdenvName}" =
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
}
)
)
{
# These attributes go right into `packages.<system>`.
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
}
// lib.optionalAttrs supportsCross (
flatMapAttrs (lib.genAttrs crossSystems (_: { })) (
crossSystem:
{ }:
lib.optionalAttrs
(linuxOnly -> nixpkgsFor.${system}.cross.${crossSystem}.stdenv.hostPlatform.isLinux)
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
}
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
}
)
)
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
stdenvName:
{ }:
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${stdenvName}" =
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
}
)
)
// lib.optionalAttrs (builtins.elem system linux64BitSystems) {
dockerImage =
-5
View File
@@ -95,11 +95,6 @@
''^tests/functional/lang/eval-fail-bad-string-interpolation-3\.nix$''
''^tests/functional/lang/eval-fail-bad-string-interpolation-4\.nix$''
''^tests/functional/lang/eval-okay-regex-match2\.nix$''
# URL literal tests - nixfmt converts unquoted URLs to strings
''^tests/functional/lang/eval-fail-url-literal\.nix$''
''^tests/functional/lang/eval-okay-url-literal-warn\.nix$''
''^tests/functional/lang/eval-okay-url-literal-default\.nix$''
];
};
clang-format = {
@@ -9,3 +9,9 @@ endif
if 'address' in get_option('b_sanitize')
deps_other += declare_dependency(sources : 'asan-options.cc')
endif
if 'undefined' in get_option('b_sanitize')
add_project_arguments('-DNIX_UBSAN_ENABLED=1', language : 'cpp')
else
add_project_arguments('-DNIX_UBSAN_ENABLED=0', language : 'cpp')
endif
+1 -3
View File
@@ -1,6 +1,6 @@
{
runCommand,
stdenv,
system,
buildPackages,
cacert,
nix,
@@ -9,8 +9,6 @@
let
inherit (stdenv.hostPlatform) system;
installerClosureInfo = buildPackages.closureInfo {
rootPaths = [
nix
+3 -13
View File
@@ -30,19 +30,9 @@ scope: {
NIX_CFLAGS_COMPILE = "-DINITIAL_MARK_STACK_SIZE=1048576";
});
curl =
(pkgs.curl.override {
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
# Make sure we enable all the dependencies for Content-Encoding/Transfer-Encoding decompression.
zstdSupport = true;
brotliSupport = true;
zlibSupport = true;
}).overrideAttrs
{
# TODO: Fix in nixpkgs. Static build with brotli is marked as broken, but it's not the case.
# Remove once https://github.com/NixOS/nixpkgs/pull/494111 lands in the 25.11 channel.
meta.broken = false;
};
curl = pkgs.curl.override {
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
};
libblake3 = pkgs.libblake3.override {
useTBB = !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isStatic);
+3 -23
View File
@@ -115,11 +115,7 @@ rec {
# Binary package for various platforms.
build = forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName}))
pkgName: forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName})
);
shellInputs = removeAttrs (forAllSystems (
@@ -139,10 +135,6 @@ rec {
(
if pkgName == "nix-functional-tests" then
lib.flip builtins.removeAttrs [ "x86_64-w64-mingw32" ]
else if pkgName == "nix-nswrapper" then
lib.filterAttrs (
crossSystem: _do_not_touch: nixpkgsFor.x86_64-linux.cross.${crossSystem}.stdenv.hostPlatform.isLinux
)
else
lib.id
)
@@ -179,13 +171,7 @@ rec {
)
);
in
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-cli);
@@ -205,13 +191,7 @@ rec {
)
);
in
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-perl-bindings);
+2 -2
View File
@@ -407,7 +407,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
struct MixOutLinkBase : virtual Args
{
/** Prefix for any output symlinks. Empty means do not write an output symlink. */
std::filesystem::path outLink;
Path outLink;
MixOutLinkBase(const std::string & defaultOutLink)
: outLink(defaultOutLink)
@@ -435,7 +435,7 @@ struct MixOutLinkByDefault : MixOutLinkBase, virtual Args
addFlag({
.longName = "no-link",
.description = "Do not create symlinks to the build results.",
.handler = {&outLink, std::filesystem::path{}},
.handler = {&outLink, Path("")},
});
}
};
@@ -3,7 +3,6 @@
#include "nix/util/finally.hh"
#include "nix/util/types.hh"
#include <filesystem>
#include <functional>
#include <string>
@@ -37,10 +36,10 @@ public:
class ReadlineLikeInteracter : public virtual ReplInteracter
{
std::filesystem::path historyFile;
std::string historyFile;
public:
ReadlineLikeInteracter(std::filesystem::path historyFile)
: historyFile(std::move(historyFile))
ReadlineLikeInteracter(std::string historyFile)
: historyFile(historyFile)
{
}
+2 -2
View File
@@ -2,7 +2,6 @@
///@file
#include "nix/expr/eval.hh"
#include "nix/util/os-string.hh"
namespace nix {
@@ -28,7 +27,8 @@ struct AbstractNixRepl
* @param programName Name of the command, e.g. `nix` or `nix-env`.
* @param args arguments to the command.
*/
using RunNix = void(const std::string & programName, OsStrings args, const std::optional<std::string> & input);
using RunNix =
void(const std::string & programName, const Strings & args, const std::optional<std::string> & input);
/**
* @param runNix Function to run the nix CLI to support various
+1 -1
View File
@@ -157,7 +157,7 @@ MixFlakeOptions::MixFlakeOptions()
.category = category,
.labels = {"flake-lock-path"},
.handler = {[&](std::string lockFilePath) {
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath).string())};
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))};
}},
.completer = completePath,
});
+3 -3
View File
@@ -118,14 +118,14 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
try {
createDirs(historyFile.parent_path());
createDirs(dirOf(historyFile));
} catch (SystemError & e) {
logWarning(e.info());
}
#if !USE_READLINE
el_hist_size = 1000;
#endif
read_history(historyFile.string().c_str());
read_history(historyFile.c_str());
auto oldRepl = curRepl;
curRepl = repl;
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
@@ -208,7 +208,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
ReadlineLikeInteracter::~ReadlineLikeInteracter()
{
write_history(historyFile.string().c_str());
write_history(historyFile.c_str());
}
}; // namespace nix
+32 -55
View File
@@ -1,7 +1,6 @@
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <memory>
#include "nix/util/error.hh"
#include "nix/cmd/repl-interacter.hh"
@@ -29,8 +28,6 @@
#include "nix/util/ref.hh"
#include "nix/expr/value.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/strings.hh"
namespace nix {
@@ -74,7 +71,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
RunNix * runNixPtr;
void runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input = {});
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
std::unique_ptr<ReplInteracter> interacter;
@@ -101,7 +98,6 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol name, Value & v);
Expr * parseString(std::string s);
ExprAttrs * parseReplBindings(std::string s);
void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace & dt);
@@ -143,7 +139,7 @@ NixRepl::NixRepl(
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
, runNixPtr{runNix}
, interacter(std::make_unique<ReadlineLikeInteracter>(getDataDir() / "repl-history"))
, interacter(make_unique<ReadlineLikeInteracter>((getDataDir() / "repl-history").string()))
{
}
@@ -310,6 +306,21 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
return completions;
}
// FIXME: DRY and match or use the parser
static bool isVarName(std::string_view s)
{
if (s.size() == 0)
return false;
char c = s[0];
if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
return false;
for (auto & i : s)
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
|| i == '\''))
return false;
return true;
}
StorePath NixRepl::getDerivationPath(Value & v)
{
auto packageInfo = getDerivation(*state, v, false);
@@ -497,12 +508,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
// runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI
runProgram2({
.program = editor,
.lookupPath = true,
.args = toOsStrings(std::move(args)),
.isInteractive = true,
});
runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true});
// Reload right after exiting the editor
state->resetFileCache();
@@ -522,7 +528,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
state->callFunction(f, v, result, PosIdx());
StorePath drvPath = getDerivationPath(result);
runNix("nix-shell", toOsStrings({state->store->printStorePath(drvPath)}));
runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
@@ -553,7 +559,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
} else if (command == ":i") {
runNix("nix-env", toOsStrings({"-i", drvPathRaw}));
runNix("nix-env", {"-i", drvPathRaw});
} else if (command == ":log") {
settings.readOnlyMode = true;
Finally roModeReset([&]() { settings.readOnlyMode = false; });
@@ -561,7 +567,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
auto log = fetchBuildLog(state->store, drvPath, drvPathRaw);
logger->writeToStdout(log);
} else {
runNix("nix-shell", toOsStrings({drvPathRaw}));
runNix("nix-shell", {drvPathRaw});
}
}
@@ -662,22 +668,15 @@ ProcessLineResult NixRepl::processLine(std::string line)
throw Error("unknown command '%1%'", command);
else {
// Try parsing as bindings first (handles `x = 1`, `inherit ...`, etc.)
ExprAttrs * bindings = nullptr;
try {
bindings = parseReplBindings(line);
} catch (ParseError &) {
}
if (bindings) {
Env * inheritEnv = bindings->inheritFromExprs ? bindings->buildInheritFromEnv(*state, *env) : nullptr;
for (auto & [symbol, def] : *bindings->attrs) {
Value & v(*state->allocValue());
v.mkThunk(def.chooseByKind(env, env, inheritEnv), def.e);
addVarToScope(symbol, v);
}
size_t p = line.find('=');
std::string name;
if (p != std::string::npos && p < line.size() && line[p + 1] != '='
&& isVarName(name = removeWhitespace(line.substr(0, p)))) {
Expr * e = parseString(line.substr(p + 1));
Value & v(*state->allocValue());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
} else {
// Otherwise evaluate as expression
Value v;
evalString(line, v);
auto suspension = logger->suspend();
@@ -711,7 +710,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
try {
cwd = std::filesystem::current_path();
} catch (std::filesystem::filesystem_error & e) {
throw SystemError(e.code(), "cannot determine current working directory");
throw SysError("cannot determine current working directory");
}
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
@@ -866,28 +865,6 @@ Expr * NixRepl::parseString(std::string s)
}
}
ExprAttrs * NixRepl::parseReplBindings(std::string s)
{
auto basePath = state->rootPath(".");
// Try parsing as bindings
std::exception_ptr bindingsError;
try {
return state->parseReplBindings(s, basePath, staticEnv);
} catch (ParseError &) {
bindingsError = std::current_exception();
}
// Try with semicolon appended (for `inherit foo` shorthand)
// Use original source (s) for error messages, not s + ";"
try {
return state->parseReplBindings(s + ";", s, basePath, staticEnv);
} catch (ParseError &) {
// Semicolon retry failed; rethrow the original bindings error
std::rethrow_exception(bindingsError);
}
}
void NixRepl::evalString(std::string s, Value & v)
{
Expr * e = parseString(s);
@@ -895,10 +872,10 @@ void NixRepl::evalString(std::string s, Value & v)
state->forceValue(v, v.determinePos(noPos));
}
void NixRepl::runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input)
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
{
if (runNixPtr)
(*runNixPtr)(program, std::move(args), input);
(*runNixPtr)(program, args, input);
else
throw Error(
"Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes",
+1 -3
View File
@@ -185,9 +185,7 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
return EvalState{
.fetchSettings = std::move(builder->fetchSettings),
.settings = std::move(builder->settings),
.statePtr = std::make_shared<nix::EvalState>(
builder->lookupPath, builder->store, self->fetchSettings, self->settings),
.state = *self->statePtr,
.state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
};
});
}
+1 -2
View File
@@ -24,8 +24,7 @@ struct EvalState
{
nix::fetchers::Settings fetchSettings;
nix::EvalSettings settings;
std::shared_ptr<nix::EvalState> statePtr;
nix::EvalState & state;
nix::EvalState state;
};
struct BindingsBuilder
+2 -10
View File
@@ -1,5 +1,4 @@
#include "nix/expr/attr-set.hh"
#include "nix/expr/eval-error.hh"
#include "nix/util/configuration.hh"
#include "nix/expr/eval.hh"
#include "nix/store/globals.hh"
@@ -108,13 +107,8 @@ static void nix_c_primop_wrapper(
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
if (ctx.last_err_code != NIX_OK) {
if (ctx.last_err_code == NIX_ERR_RECOVERABLE) {
state.error<nix::RecoverableEvalError>("Recoverable error from custom function: %s", *ctx.last_err)
.atPos(pos)
.debugThrow();
} else {
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
}
/* TODO: Throw different errors depending on the error code */
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
}
if (!vTmp.isValid()) {
@@ -200,8 +194,6 @@ ValueType nix_get_type(nix_c_context * context, const nix_value * value)
switch (v.type()) {
case nThunk:
return NIX_TYPE_THUNK;
case nFailed:
return NIX_TYPE_FAILED;
case nInt:
return NIX_TYPE_INT;
case nFloat:
+1 -2
View File
@@ -100,8 +100,7 @@ typedef enum {
/** @brief External value from C++ plugins or C API
* @see Externals
*/
NIX_TYPE_EXTERNAL,
NIX_TYPE_FAILED,
NIX_TYPE_EXTERNAL
} ValueType;
// forward declarations
+1 -2
View File
@@ -37,8 +37,7 @@ static void BM_EvalDynamicAttrs(benchmark::State & state)
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
auto & st = *stPtr;
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
Expr * expr = st.parseExprFromString(exprStr, st.rootPath(CanonPath::root));
Value v;
+2 -4
View File
@@ -16,8 +16,7 @@ struct GetDerivationsEnv
fetchers::Settings fetchSettings{};
bool readOnlyMode = true;
EvalSettings evalSettings{readOnlyMode};
std::shared_ptr<EvalState> statePtr;
EvalState & state;
EvalState state;
Bindings * autoArgs = nullptr;
Value attrsValue;
@@ -28,8 +27,7 @@ struct GetDerivationsEnv
settings.nixPath = {};
return settings;
}())
, statePtr(std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr))
, state(*statePtr)
, state({}, store, fetchSettings, evalSettings, nullptr)
{
autoArgs = state.buildBindings(0).finish();
-102
View File
@@ -517,106 +517,4 @@ TEST_F(nix_api_expr_test, nix_expr_attrset_update)
assert_ctx_ok();
}
// The following is a test case for retryable thunks. This is a requirement
// for the current way in which NixOps4 evaluates its deployment expressions.
// An alternative strategy could be implemented, but unwinding the stack may
// be a more efficient way to deal with many suspensions/resumptions, compared
// to e.g. using a thread or coroutine stack for each suspended dependency.
// This test models the essential bits of a deployment tool that uses such
// a strategy.
// State for the retryable primop - simulates deployment resource availability
struct DeploymentResourceState
{
bool vm_created = false;
};
static void primop_load_resource_input(
void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
{
assert(context);
assert(state);
auto * resource_state = static_cast<DeploymentResourceState *>(user_data);
// Get the resource input name argument
std::string input_name;
if (nix_get_string(context, args[0], OBSERVE_STRING(input_name)) != NIX_OK)
return;
// Only handle "vm_id" input - throw for anything else
if (input_name != "vm_id") {
std::string error_msg = "unknown resource input: " + input_name;
nix_set_err_msg(context, NIX_ERR_NIX_ERROR, error_msg.c_str());
return;
}
if (resource_state->vm_created) {
// VM has been created, return the ID
nix_init_string(context, ret, "vm-12345");
} else {
// VM not created yet, fail with dependency error
nix_set_err_msg(context, NIX_ERR_RECOVERABLE, "VM not yet created");
}
}
TEST_F(nix_api_expr_test, nix_expr_thunk_re_evaluation_after_deployment)
{
// This test demonstrates NixOps4's requirement: a thunk calling a primop should be
// re-evaluable when deployment resources become available that were not available initially.
DeploymentResourceState resource_state;
PrimOp * primop = nix_alloc_primop(
ctx,
primop_load_resource_input,
1,
"loadResourceInput",
nullptr,
"load a deployment resource input",
&resource_state);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_primop(ctx, primopValue, primop);
assert_ctx_ok();
nix_value * inputName = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_string(ctx, inputName, "vm_id");
assert_ctx_ok();
// Create a single thunk by using nix_init_apply instead of nix_value_call
// This creates a lazy application that can be forced multiple times
nix_value * thunk = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_apply(ctx, thunk, primopValue, inputName);
assert_ctx_ok();
// First force: VM not created yet, should fail
nix_value_force(ctx, state, thunk);
ASSERT_EQ(NIX_ERR_NIX_ERROR, nix_err_code(ctx));
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("VM not yet created"));
// Clear the error context for the next attempt
nix_c_context_free(ctx);
ctx = nix_c_context_create();
// Simulate deployment process: VM gets created
resource_state.vm_created = true;
// Second force of the SAME thunk: this is where the "failed" value issue appears
// With failed value caching, this should fail because the thunk is marked as permanently failed
// Without failed value caching (or with retryable failures), this should succeed
nix_value_force(ctx, state, thunk);
// If we get here without error, the thunk was successfully re-evaluated
assert_ctx_ok();
std::string result;
nix_get_string(ctx, thunk, OBSERVE_STRING(result));
assert_ctx_ok();
ASSERT_STREQ("vm-12345", result.c_str());
}
} // namespace nixC
+1 -2
View File
@@ -27,8 +27,7 @@ static void BM_EvalManyBuiltinsMatchSameRegex(benchmark::State & state)
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
auto & st = *stPtr;
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
Expr * expr = st.parseExprFromString(std::string(exprStr), st.rootPath(CanonPath::root));
Value v;
-16
View File
@@ -188,22 +188,6 @@ TEST_F(ValuePrintingTests, vBlackhole)
test(vBlackhole, "«potential infinite recursion»");
}
TEST_F(ValuePrintingTests, vFailed)
{
Value v;
try {
throw Error("nope");
} catch (...) {
v.mkFailed(std::current_exception(), nullptr);
}
// Historically, a tried and then ignored value (e.g. through tryEval) was
// reverted to the original thunk.
test(v, "«thunk»");
test(v, ANSI_MAGENTA "«thunk»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}
TEST_F(ValuePrintingTests, depthAttrs)
{
Value vOne;
-55
View File
@@ -1,55 +0,0 @@
#include "nix/expr/diagnose.hh"
#include "nix/util/configuration.hh"
#include "nix/util/config-impl.hh"
#include "nix/util/abstract-setting-to-json.hh"
#include <nlohmann/json.hpp>
namespace nix {
template<>
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const
{
if (str == "ignore")
return Diagnose::Ignore;
else if (str == "warn")
return Diagnose::Warn;
else if (str == "fatal")
return Diagnose::Fatal;
else
throw UsageError("option '%s' has invalid value '%s' (expected 'ignore', 'warn', or 'fatal')", name, str);
}
template<>
struct BaseSetting<Diagnose>::trait
{
static constexpr bool appendable = false;
};
template<>
std::string BaseSetting<Diagnose>::to_string() const
{
switch (value) {
case Diagnose::Ignore:
return "ignore";
case Diagnose::Warn:
return "warn";
case Diagnose::Fatal:
return "fatal";
default:
unreachable();
}
}
NLOHMANN_JSON_SERIALIZE_ENUM(
Diagnose,
{
{Diagnose::Ignore, "ignore"},
{Diagnose::Warn, "warn"},
{Diagnose::Fatal, "fatal"},
});
/* Explicit instantiation of templates */
template class BaseSetting<Diagnose>;
} // namespace nix
+1 -1
View File
@@ -70,7 +70,7 @@ struct AttrDb
{
auto state(_state->lock());
auto cacheDir = getCacheDir() / "eval-cache-v6";
auto cacheDir = std::filesystem::path(getCacheDir()) / "eval-cache-v6";
createDirs(cacheDir);
auto dbPath = cacheDir / (fingerprint.to_string(HashFormat::Base16, false) + ".sqlite");
-8
View File
@@ -1,16 +1,9 @@
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/store/store-api.hh"
namespace nix {
InvalidPathError::InvalidPathError(EvalState & state, const StorePath & path)
: CloneableError(state, "path '%s' is not valid", path.to_string())
, path{path}
{
}
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
{
@@ -121,6 +114,5 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<StackOverflowError>;
template class EvalErrorBuilder<InvalidPathError>;
template class EvalErrorBuilder<IFDError>;
template class EvalErrorBuilder<RecoverableEvalError>;
} // namespace nix
+1 -1
View File
@@ -137,7 +137,7 @@ public:
: state(state)
, sampleInterval(period)
, profileFd([&]() {
auto fd = openNewFileForWrite(
AutoCloseFD fd = openNewFileForWrite(
profileFile,
0660,
{
+1 -22
View File
@@ -1,5 +1,4 @@
#include "nix/util/users.hh"
#include "nix/util/logging.hh"
#include "nix/store/globals.hh"
#include "nix/store/profiles.hh"
#include "nix/expr/eval.hh"
@@ -7,26 +6,6 @@
namespace nix {
void DeprecatedWarnSetting::assign(const bool & v)
{
value = v;
warn("'%s' is deprecated, use '%s = %s' instead", name, targetName, v ? "warn" : "ignore");
if (!target.overridden)
target = v ? Diagnose::Warn : Diagnose::Ignore;
}
void DeprecatedWarnSetting::appendOrSet(bool newValue, bool append)
{
assert(!append);
assign(newValue);
}
void DeprecatedWarnSetting::override(const bool & v)
{
overridden = true;
assign(v);
}
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
Strings EvalSettings::parseNixPath(const std::string & s)
@@ -91,7 +70,7 @@ Strings EvalSettings::getDefaultNixPath()
}
};
add(getNixDefExpr() / "channels");
add(std::filesystem::path{getNixDefExpr()} / "channels");
auto profilesDirOpts = settings.getProfileDirsOptions();
add(rootChannelsDir(profilesDirOpts) / "nixpkgs", "nixpkgs");
add(rootChannelsDir(profilesDirOpts));
+33 -180
View File
@@ -1,5 +1,4 @@
#include "nix/expr/eval.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/primops.hh"
#include "nix/expr/print-options.hh"
@@ -32,7 +31,6 @@
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <sstream>
#include <cstring>
@@ -157,8 +155,6 @@ std::string_view showType(ValueType type, bool withArticle)
return WA("a", "float");
case nThunk:
return WA("a", "thunk");
case nFailed:
return WA("an", "error");
}
unreachable();
}
@@ -426,36 +422,29 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
return false;
}
void EvalState::checkURI(const std::string & uri0)
void EvalState::checkURI(const std::string & uri)
{
if (!settings.restrictEval)
return;
if (isAllowedURI(uri0, settings.allowedUris.get()))
if (isAllowedURI(uri, settings.allowedUris.get()))
return;
/* If the URI is a path, then check it against allowedPaths as
well. */
{
std::filesystem::path path(uri0);
if (path.is_absolute()) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(path.string()));
return;
}
if (isAbsolute(uri)) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri));
return;
}
try {
ParsedURL uri = parseURL(uri0);
if (uri.scheme == "file") {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(urlPathToPath(uri.path).string()));
return;
}
} catch (BadURL &) {
if (hasPrefix(uri, "file://")) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri.substr(7)));
return;
}
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri0);
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
}
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
@@ -1998,15 +1987,11 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
UpdateQueue q;
evalForUpdate(state, env, q);
Value vTmp;
vTmp.mkAttrs(&Bindings::emptyBindings);
v.mkAttrs(&Bindings::emptyBindings);
for (auto & rhs : std::views::reverse(q)) {
/* Remember that queue is sorted rightmost attrset first. */
eval(state, /*v=*/vTmp, /*v1=*/vTmp, /*v2=*/rhs);
eval(state, /*v=*/v, /*v1=*/v, /*v2=*/rhs);
}
v = vTmp;
}
void Expr::evalForUpdate(EvalState & state, Env & env, UpdateQueue & q, std::string_view errorCtx)
@@ -2189,54 +2174,6 @@ void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value &
// always force this to be separate, otherwise forceValue may inline it and take
// a massive perf hit
[[gnu::noinline]]
void EvalState::handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos)
{
if (!env)
tryFixupBlackHolePos(v, pos);
auto e = std::current_exception();
Value * recovery = nullptr;
try {
std::rethrow_exception(e);
} catch (const RecoverableEvalError & e) {
recovery = allocValue();
} catch (...) {
}
if (recovery) {
recovery->mkThunk(env, expr);
}
v.mkFailed(e, recovery);
}
[[gnu::noinline]]
void EvalState::handleEvalExceptionForApp(Value & v, const Value & savedApp)
{
auto e = std::current_exception();
Value * recovery = nullptr;
try {
std::rethrow_exception(e);
} catch (const RecoverableEvalError & e) {
recovery = allocValue();
} catch (...) {
}
if (recovery) {
*recovery = savedApp;
}
v.mkFailed(e, recovery);
}
[[gnu::noinline]]
void EvalState::handleEvalFailed(Value & v, const PosIdx pos)
{
assert(v.isFailed());
if (auto recoveryValue = v.failed().recoveryValue) {
v = *recoveryValue;
forceValue(v, pos);
} else {
v.failed().rethrow();
}
}
void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
{
if (!v.isBlackhole())
@@ -2245,8 +2182,7 @@ void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
try {
std::rethrow_exception(e);
} catch (InfiniteRecursionError & e) {
if (!e.hasPos())
e.atPos(positions[pos]);
e.atPos(positions[pos]);
} catch (...) {
}
}
@@ -2794,12 +2730,9 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
return;
case nAttrs: {
const Bindings & attrs1 = *v1.attrs();
const Bindings & attrs2 = *v2.attrs();
if (isDerivation(v1) && isDerivation(v2)) {
auto i = attrs1.get(s.outPath);
auto j = attrs2.get(s.outPath);
auto i = v1.attrs()->get(s.outPath);
auto j = v2.attrs()->get(s.outPath);
if (i && j) {
try {
assertEqValues(*i->value, *j->value, pos, errorCtx);
@@ -2812,7 +2745,7 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
}
}
if (attrs1.size() != attrs2.size()) {
if (v1.attrs()->size() != v2.attrs()->size()) {
error<AssertionError>(
"attribute names of attribute set '%s' differs from attribute set '%s'",
ValuePrinter(*this, v1, errorPrintOptions),
@@ -2820,8 +2753,12 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
.debugThrow();
}
/* This mirrors what EvalState::eqValues does. See the corresponding comment there. */
for (auto i = attrs1.begin(), j = attrs2.begin(); i != attrs1.end(); ++i, ++j) {
// Like normal comparison, we compare the attributes in non-deterministic Symbol index order.
// This function is called when eqValues has found a difference, so to reliably
// report about its result, we should follow in its literal footsteps and not
// try anything fancy that could lead to an error.
Bindings::const_iterator i, j;
for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j) {
if (i->name != j->name) {
// A difference in a sorted list means that one attribute is not contained in the other, but we don't
// know which. Let's find out. Could use <, but this is more clear.
@@ -2843,22 +2780,6 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
}
assert(false);
}
}
/* Comparing in lexicographical order is crucial for determinism. Short-circuiting
the comparison without a canonical ordering of iteration would mean that depending
on the global symbol table order this could either fail or successfully evaluate if
forcing an attribute leads to an error.
This function is called when eqValues has found a difference, so to reliably
report about its result, we should follow in its literal footsteps and not
try anything fancy that could lead to an error. */
boost::container::small_vector<Attr, conservativeStackReservation> sorted1;
v1.attrs()->lexicographicOrder(sorted1, symbols);
boost::container::small_vector<Attr, conservativeStackReservation> sorted2;
v2.attrs()->lexicographicOrder(sorted2, symbols);
for (auto i = sorted1.begin(), j = sorted2.begin(); i != sorted1.end(); ++i, ++j) {
assert(i->name == j->name);
try {
assertEqValues(*i->value, *j->value, pos, errorCtx);
} catch (Error & e) {
@@ -2900,11 +2821,8 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
}
return;
// Cannot be returned by forceValue().
case nThunk:
case nFailed:
unreachable();
case nThunk: // Must not be left by forceValue
assert(false);
default: // Note that we pass compiler flags that should make `default:` unreachable.
// Also note that this probably ran after `eqValues`, which implements
// the same logic more efficiently (without having to unwind stacks),
@@ -2966,46 +2884,23 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return true;
case nAttrs: {
const Bindings & attrs1 = *v1.attrs();
const Bindings & attrs2 = *v2.attrs();
/* If both sets denote a derivation (type = "derivation"),
then compare their outPaths. */
if (isDerivation(v1) && isDerivation(v2)) {
auto i = attrs1.get(s.outPath);
auto j = attrs2.get(s.outPath);
auto i = v1.attrs()->get(s.outPath);
auto j = v2.attrs()->get(s.outPath);
if (i && j)
return eqValues(*i->value, *j->value, pos, errorCtx);
}
if (attrs1.size() != attrs2.size())
if (v1.attrs()->size() != v2.attrs()->size())
return false;
/* Compare in 2 passes: first make sure the attribute keys are equal
without evaluating or recursing into values. Historically, attribute
set comparison has been done in undefined symbol table order. This
approach is compatible with nondeterministic iteration order. */
for (auto i = attrs1.begin(), j = attrs2.begin(); i != attrs1.end(); ++i, ++j) {
/* If we do SoA transformation for Bindings then this could just be a memcmp. */
if (i->name != j->name)
return false;
}
/* Comparing in lexicographical order is crucial for determinism. Short-circuiting
the comparison without a canonical ordering of iteration would mean that depending
on the global symbol table order this could either fail or successfully evaluate if
forcing an attribute leads to an error. */
boost::container::small_vector<Attr, conservativeStackReservation> sorted1;
v1.attrs()->lexicographicOrder(sorted1, symbols);
boost::container::small_vector<Attr, conservativeStackReservation> sorted2;
v2.attrs()->lexicographicOrder(sorted2, symbols);
/* Otherwise, compare the attributes one by one. */
for (auto i = sorted1.begin(), j = sorted2.begin(); i != sorted1.end(); ++i, ++j) {
assert(i->name == j->name);
if (!eqValues(*i->value, *j->value, pos, errorCtx))
Bindings::const_iterator i, j;
for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j)
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
return false;
}
return true;
}
@@ -3021,11 +2916,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
// !!!
return v1.fpoint() == v2.fpoint();
// Cannot be returned by forceValue().
case nThunk:
case nFailed:
unreachable();
case nThunk: // Must not be left by forceValue
assert(false);
default: // Note that we pass compiler flags that should make `default:` unreachable.
error<EvalError>("eqValues: cannot compare %1% with %2%", showType(v1), showType(v2))
.withTrace(pos, errorCtx)
@@ -3246,21 +3138,6 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
}
ExprAttrs *
EvalState::parseReplBindings(std::string s_, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
{
return parseReplBindings(s_, s_, basePath, staticEnv);
}
ExprAttrs * EvalState::parseReplBindings(
std::string s_, std::string errorSource, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
{
auto s = make_ref<std::string>(std::move(errorSource));
// flex requires two NUL terminators for yy_scan_buffer
s_.append("\0\0", 2);
return parseReplBindings(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
}
Expr * EvalState::parseStdin()
{
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
@@ -3402,30 +3279,6 @@ Expr * EvalState::parse(
return result;
}
ExprAttrs * EvalState::parseReplBindings(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv)
{
DocCommentMap tmpDocComments;
DocCommentMap * docComments = &tmpDocComments;
if (auto sourcePath = std::get_if<SourcePath>(&origin)) {
auto [it, _] = positionToDocComment.try_emplace(*sourcePath);
docComments = &it->second;
}
auto bindings = parseReplBindingsFromBuf(
text, length, origin, basePath, mem.exprs, symbols, settings, positions, *docComments, rootFS);
assert(bindings);
bindings->bindVars(*this, staticEnv);
return bindings;
}
DocComment EvalState::getDocCommentForPos(PosIdx pos)
{
auto pos2 = positions[pos];
-23
View File
@@ -414,29 +414,6 @@ public:
void sort();
/**
* Get the attributes in lexicographically sorted order. Needed for deterministic
* iteration order.
*
* @note Unlike the overload returning a vector of const Attr *, this
* copies the attribute values into a @ref out container by value. This is
* to reduce the amount of indirection. Note that the resulting attribute
* values are copies and one must not use a pointer to the Attr object for
* any purpose other than reading its members.
*/
template<typename C>
void lexicographicOrder(C & out, const SymbolTable & symbols) const
{
out.reserve(size());
for (auto attr : *this)
out.push_back(attr);
assert(out.size() == size());
std::sort(out.begin(), out.end(), [&symbols](const Attr & a, const Attr & b) {
std::string_view sa = symbols[a.name], sb = symbols[b.name];
return sa < sb;
});
}
/**
* Returns the attributes in lexicographically sorted order.
*/
-76
View File
@@ -1,76 +0,0 @@
#pragma once
///@file
#include <optional>
#include "nix/util/ansicolor.hh"
#include "nix/util/configuration.hh"
#include "nix/util/error.hh"
#include "nix/util/logging.hh"
namespace nix {
/**
* Diagnostic level for deprecated or non-portable language features.
*/
enum struct Diagnose {
/**
* Ignore the feature without any diagnostic.
*/
Ignore,
/**
* Warn when the feature is used, but allow it.
*/
Warn,
/**
* Treat the feature as a fatal error.
*/
Fatal,
};
template<>
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const;
template<>
std::string BaseSetting<Diagnose>::to_string() const;
/**
* Check a diagnostic setting and either do nothing, log a warning, or throw an error.
*
* The setting name is automatically appended to the error message.
*
* @param setting The diagnostic setting to check
* @param mkError A function that takes a bool (true if fatal, false if warning) and
* returns an optional error to throw (or warn with).
* Only called if level is not `Ignore`.
* If the function returns `std::nullopt`, no diagnostic is emitted.
*
* @throws The error returned by mkError if level is `Fatal` and mkError returns a value
*/
template<typename F>
void diagnose(const Setting<Diagnose> & setting, F && mkError)
{
auto withError = [&](bool fatal, auto && handler) {
auto maybeError = mkError(fatal);
if (!maybeError)
return;
auto & info = maybeError->unsafeInfo();
// Append the setting name to help users find the right setting
info.msg = HintFmt("%s (" ANSI_BOLD "%s" ANSI_NORMAL ")", Uncolored(info.msg.str()), setting.name);
maybeError->recalcWhat();
handler(std::move(*maybeError));
};
switch (setting.get()) {
case Diagnose::Ignore:
return;
case Diagnose::Warn:
withError(false, [](auto && error) { logWarning(error.info()); });
return;
case Diagnose::Fatal:
withError(true, [](auto && error) { throw std::move(error); });
return;
}
}
} // namespace nix
+5 -11
View File
@@ -2,7 +2,6 @@
#include "nix/util/error.hh"
#include "nix/util/pos-idx.hh"
#include "nix/store/path.hh"
namespace nix {
@@ -71,20 +70,15 @@ struct StackOverflowError : public CloneableError<StackOverflowError, EvalBaseEr
MakeError(IFDError, EvalBaseError);
/**
* An evaluation error which should be retried instead of rethrown.
*
* A RecoverableEvalError is not an EvalError, because we shouldn't cache it in
* the eval cache, as it should be retried anyway.
*/
MakeError(RecoverableEvalError, EvalBaseError);
struct InvalidPathError : public CloneableError<InvalidPathError, EvalError>
{
public:
StorePath path;
Path path;
InvalidPathError(EvalState & state, const StorePath & path);
InvalidPathError(EvalState & state, const Path & path)
: CloneableError(state, "path '%s' is not valid", path)
{
}
};
/**
-3
View File
@@ -33,9 +33,6 @@ using gc_allocator = std::allocator<T>;
struct gc
{};
struct gc_cleanup
{};
#endif
namespace nix {
+5 -13
View File
@@ -5,7 +5,6 @@
#include "nix/expr/eval.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval-settings.hh"
#include <exception>
namespace nix {
@@ -92,25 +91,18 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
Expr * expr = v.thunk().expr;
try {
v.mkBlackhole();
// checkInterrupt();
if (env) [[likely]]
expr->eval(*this, *env, v);
else
ExprBlackHole::throwInfiniteRecursionError(*this, v);
} catch (...) {
handleEvalExceptionForThunk(env, expr, v, pos);
v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos);
throw;
}
} else if (v.isApp()) {
Value savedApp = v;
try {
callFunction(*v.app().left, *v.app().right, v, pos);
} catch (...) {
handleEvalExceptionForApp(v, savedApp);
throw;
}
} else if (v.isFailed()) {
handleEvalFailed(v, pos);
}
} else if (v.isApp())
callFunction(*v.app().left, *v.app().right, v, pos);
}
[[gnu::always_inline]]
+8 -89
View File
@@ -1,7 +1,6 @@
#pragma once
///@file
#include "nix/expr/diagnose.hh"
#include "nix/expr/eval-profiler-settings.hh"
#include "nix/util/configuration.hh"
#include "nix/util/source-path.hh"
@@ -11,36 +10,6 @@ namespace nix {
class EvalState;
struct PrimOp;
/**
* A deprecated bool setting that migrates to a `Setting<Diagnose>`.
* When set to true, it emits a deprecation warning and sets the target
* `Setting<Diagnose>` setting to `warn`.
*/
class DeprecatedWarnSetting : public BaseSetting<bool>
{
Setting<Diagnose> & target;
const char * targetName;
public:
DeprecatedWarnSetting(
Config * options,
Setting<Diagnose> & target,
const char * targetName,
const std::string & name,
const std::string & description,
const StringSet & aliases = {})
: BaseSetting<bool>(false, true, name, description, aliases, std::nullopt)
, target(target)
, targetName(targetName)
{
options->addSetting(this);
}
void assign(const bool & v) override;
void appendOrSet(bool newValue, bool append) override;
void override(const bool & v) override;
};
struct EvalSettings : Config
{
/**
@@ -359,70 +328,20 @@ struct EvalSettings : Config
This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment.
)"};
Setting<Diagnose> lintShortPathLiterals{
Setting<bool> warnShortPathLiterals{
this,
Diagnose::Ignore,
"lint-short-path-literals",
false,
"warn-short-path-literals",
R"(
Controls handling of relative path literals that don't start with `./` or `../`.
If set to true, the Nix evaluator will warn when encountering relative path literals
that don't start with `./` or `../`.
- `ignore`: Ignore without warning (default)
- `warn`: Emit a warning suggesting to use `./` prefix
- `fatal`: Treat as a parse error
For example, with this setting set to `warn` or `fatal`, `foo/bar` would
suggest using `./foo/bar` instead.
For example, with this setting enabled, `foo/bar` would emit a warning
suggesting to use `./foo/bar` instead.
This is useful for improving code readability and making path literals
more explicit.
)",
};
DeprecatedWarnSetting warnShortPathLiterals{
this,
lintShortPathLiterals,
"lint-short-path-literals",
"warn-short-path-literals",
R"(
Deprecated. Use [`lint-short-path-literals`](#conf-lint-short-path-literals)` = warn` instead.
)",
};
Setting<Diagnose> lintUrlLiterals{
this,
Diagnose::Ignore,
"lint-url-literals",
R"(
Controls handling of unquoted URLs as part of the Nix language syntax.
The Nix language allows for URL literals, like so:
```
$ nix repl
nix-repl> http://foo
"http://foo"
```
Setting this to `warn` or `fatal` will cause the Nix parser to
warn or throw an error when encountering a URL literal:
```
$ nix repl --lint-url-literals fatal
nix-repl> http://foo
error: URL literal 'http://foo' is deprecated
at «string»:1:1:
1| http://foo
| ^
```
Unquoted URLs are being deprecated and their usage is discouraged.
The reason is that, as opposed to path literals, URLs have no
special properties that distinguish them from regular strings, URLs
containing query parameters have to be quoted anyway, and unquoted URLs
may confuse external tooling.
)",
};
)"};
Setting<unsigned> bindingsUpdateLayerRhsSizeThreshold{
this,
-39
View File
@@ -603,18 +603,6 @@ public:
parseExprFromString(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const SourcePath & basePath);
/**
* Parse REPL bindings from the specified string.
* Returns ExprAttrs with bindings to add to scope.
*/
ExprAttrs *
parseReplBindings(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
ExprAttrs * parseReplBindings(
std::string s,
std::string errorSource,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseStdin();
/**
@@ -664,28 +652,8 @@ public:
*/
inline void forceValue(Value & v, const PosIdx pos);
private:
/**
* Internal support function for forceValue
*
* This code is factored out so that it's not in the heavily inlined hot path.
*/
void handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos);
/**
* Internal support function for forceValue
*
* This code is factored out so that it's not in the heavily inlined hot path.
*/
void handleEvalExceptionForApp(Value & v, const Value & savedApp);
void handleEvalFailed(Value & v, PosIdx pos);
void tryFixupBlackHolePos(Value & v, PosIdx pos);
public:
/**
* Force a value, then recursively force list elements and
* attributes.
@@ -899,13 +867,6 @@ private:
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
ExprAttrs * parseReplBindings(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
/**
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run
* out of system stack.
-1
View File
@@ -11,7 +11,6 @@ headers = [ config_pub_h ] + files(
'attr-path.hh',
'attr-set.hh',
'counter.hh',
'diagnose.hh',
'eval-cache.hh',
'eval-error.hh',
'eval-gc.hh',
+5 -67
View File
@@ -7,7 +7,6 @@
#include <cstring>
#include <memory>
#include <memory_resource>
#include <exception>
#include <span>
#include <string_view>
#include <type_traits>
@@ -43,7 +42,6 @@ enum InternalType {
tBool,
tNull,
tFloat,
tFailed,
tExternal,
tPrimOp,
tAttrs,
@@ -70,7 +68,6 @@ enum InternalType {
*/
typedef enum {
nThunk,
nFailed,
nInt,
nFloat,
nBool,
@@ -427,36 +424,6 @@ struct ValueBase
size_t size;
Value * const * elems;
};
struct Failed : gc_cleanup
{
std::exception_ptr ex;
/**
* Optional value for recovering `RecoverableEvalError`
* Must be set iff `ex` is an instance of `RecoverableEvalError`.
*/
Value * recoveryValue;
Failed(std::exception_ptr ex, Value * recoveryValue)
: ex(ex)
, recoveryValue(recoveryValue)
{
}
[[noreturn]] void rethrow() const
{
try {
std::rethrow_exception(ex);
} catch (BaseError & e) {
/* Rethrow the copy of the exception - not the original one.
Stack tracing mechanisms rely on being able to modify the exceptions
they catch by reference. */
e.throwClone();
} catch (...) {
throw;
}
}
};
};
template<typename T>
@@ -483,7 +450,6 @@ struct PayloadTypeToInternalType
MACRO(PrimOp *, primOp, tPrimOp) \
MACRO(ValueBase::PrimOpApplicationThunk, primOpApp, tPrimOpApp) \
MACRO(ExternalValueBase *, external, tExternal) \
MACRO(ValueBase::Failed *, failed, tFailed) \
MACRO(NixFloat, fpoint, tFloat)
#define NIX_VALUE_PAYLOAD_TYPE(T, FIELD_NAME, DISCRIMINATOR) \
@@ -788,11 +754,6 @@ protected:
path.path = std::bit_cast<const StringData *>(payload[1]);
}
void getStorage(Failed *& failed) const noexcept
{
failed = std::bit_cast<Failed *>(payload[1]);
}
void setStorage(NixInt integer) noexcept
{
setSingleDWordPayload<tInt>(integer.value);
@@ -842,11 +803,6 @@ protected:
{
setUntaggablePayload<pdPath>(path.accessor, path.path);
}
void setStorage(Failed * failed) noexcept
{
setSingleDWordPayload<tFailed>(std::bit_cast<PackedPointer>(failed));
}
};
/**
@@ -1098,12 +1054,12 @@ public:
inline bool isThunk() const
{
return isa<tThunk>();
}
};
inline bool isApp() const
{
return isa<tApp>();
}
};
inline bool isBlackhole() const;
@@ -1111,22 +1067,17 @@ public:
inline bool isLambda() const
{
return isa<tLambda>();
}
};
inline bool isPrimOp() const
{
return isa<tPrimOp>();
}
};
inline bool isPrimOpApp() const
{
return isa<tPrimOpApp>();
}
inline bool isFailed() const
{
return isa<tFailed>();
}
};
/**
* Returns the normal type of a Value. This only returns nThunk if
@@ -1147,7 +1098,6 @@ public:
t[tBool] = nBool;
t[tNull] = nNull;
t[tFloat] = nFloat;
t[tFailed] = nFailed;
t[tExternal] = nExternal;
t[tAttrs] = nAttrs;
t[tPrimOp] = nFunction;
@@ -1285,11 +1235,6 @@ public:
setStorage(n);
}
inline void mkFailed(std::exception_ptr e, Value * recovery) noexcept
{
setStorage(new Value::Failed(e, recovery));
}
bool isList() const noexcept
{
return isa<tListSmall, tListN>();
@@ -1404,13 +1349,6 @@ public:
{
return getStorage<Path>().accessor;
}
Failed & failed() const noexcept
{
auto p = getStorage<Failed *>();
assert(p);
return *p;
}
};
extern ExprBlackHole eBlackHole;
-22
View File
@@ -14,7 +14,6 @@
%x INPATH
%x INPATH_SLASH
%x PATH_START
%x REPL_BINDINGS_MODE
%top {
#include "parser-tab.hh" // YYSTYPE
@@ -114,13 +113,6 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
%%
/* REPL bindings mode: inject REPL_BINDINGS token at start, then switch to normal lexing */
<REPL_BINDINGS_MODE>.|\n {
yyless(0);
yylloc->unstash();
POP_STATE();
return REPL_BINDINGS;
}
if { return IF; }
then { return THEN; }
@@ -347,17 +339,3 @@ or { return OR_KW; }
}
%%
#include <type_traits>
// Verify that the forward declaration in parser.y matches flex's definition
static_assert(std::is_same_v<yyscan_t, void *>);
namespace nix {
void setReplBindingsMode(yyscan_t scanner)
{
yy_push_state(REPL_BINDINGS_MODE, scanner);
}
}
-1
View File
@@ -153,7 +153,6 @@ endforeach
sources = files(
'attr-path.cc',
'attr-set.cc',
'diagnose.cc',
'eval-cache.cc',
'eval-error.cc',
'eval-gc.cc',
+11 -94
View File
@@ -30,7 +30,6 @@
#include "nix/expr/nixexpr.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/diagnose.hh"
#include "nix/expr/parser-state.hh"
#define YY_DECL \
@@ -55,11 +54,6 @@
} \
while (0)
// Forward declaration for flex scanner type.
// lexer-tab.hh is large; avoid including it just for this type.
// Asserted with static_assert in lexer.l.
typedef void * yyscan_t;
namespace nix {
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
@@ -76,28 +70,6 @@ Expr * parseExprFromBuf(
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS);
/**
* Puts the lexer in REPL bindings mode before the first token. This causes
* the parser to accept REPL bindings (attribute definitions).
*/
void setReplBindingsMode(yyscan_t scanner);
/**
* Parse REPL bindings from a buffer.
* Returns ExprAttrs with bindings to add to scope.
*/
ExprAttrs * parseReplBindingsFromBuf(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
Exprs & exprs,
SymbolTable & symbols,
const EvalSettings & settings,
PosTable & positions,
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS);
}
#endif
@@ -203,7 +175,6 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%token IND_STRING_OPEN "start of an indented string"
%token IND_STRING_CLOSE "end of an indented string"
%token ELLIPSIS "'...'"
%token REPL_BINDINGS "start of REPL bindings"
%right IMPL
%left OR
@@ -225,10 +196,6 @@ start: expr {
// This parser does not use yynerrs; suppress the warning.
(void) yynerrs_;
}
| REPL_BINDINGS binds1 {
state->result = $2;
(void) yynerrs_;
};
expr: expr_function;
@@ -370,14 +337,12 @@ expr_simple
state->exprs.add<ExprString>(state->exprs.alloc, path)});
}
| URI {
diagnose(state->settings.lintUrlLiterals, [&](bool fatal) -> std::optional<ParseError> {
return ParseError({
.msg = HintFmt("URL literals are %s. Consider using a string literal \"%s\" instead",
fatal ? "disallowed" : "discouraged",
std::string_view($1.p, $1.l)),
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals)
throw ParseError({
.msg = HintFmt("URL literals are disabled"),
.pos = state->positions[CUR_POS]
});
});
$$ = state->exprs.add<ExprString>(state->exprs.alloc, $1);
}
| '(' expr ')' { $$ = $2; }
@@ -416,17 +381,14 @@ path_start
std::string_view literal({$1.p, $1.l});
/* check for short path literals */
diagnose(state->settings.lintShortPathLiterals, [&](bool) -> std::optional<ParseError> {
if (literal.front() != '/' && literal.front() != '.')
return ParseError({
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'", literal, literal),
.pos = state->positions[CUR_POS]
});
return std::nullopt;
});
if (state->settings.warnShortPathLiterals && literal.front() != '/' && literal.front() != '.') {
logWarning({
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'. (" ANSI_BOLD "warn-short-path-literals" ANSI_NORMAL " = true)", literal, literal),
.pos = state->positions[CUR_POS]
});
}
auto basePath = std::filesystem::path(state->basePath.path.abs());
Path path(absPath(literal, &basePath).string());
Path path(absPath(literal, state->basePath.path.abs()));
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
@@ -615,51 +577,6 @@ Expr * parseExprFromBuf(
return state.result;
}
ExprAttrs * parseReplBindingsFromBuf(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
Exprs & exprs,
SymbolTable & symbols,
const EvalSettings & settings,
PosTable & positions,
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS)
{
yyscan_t scanner;
LexerState lexerState {
.positionToDocComment = docComments,
.positions = positions,
.origin = positions.addOrigin(origin, length),
};
ParserState state {
.lexerState = lexerState,
.exprs = exprs,
.symbols = symbols,
.positions = positions,
.basePath = basePath,
.origin = lexerState.origin,
.rootFS = rootFS,
.settings = settings,
};
yylex_init_extra(&lexerState, &scanner);
Finally _destroy([&] { yylex_destroy(scanner); });
yy_scan_buffer(text, length, scanner);
setReplBindingsMode(scanner);
Parser parser(scanner, &state);
parser.parse();
assert(state.result);
// state.result is Expr *, but the REPL_BINDINGS grammar rule
// always produces an ExprAttrs via the binds1 production.
auto bindings = dynamic_cast<ExprAttrs *>(state.result);
assert(bindings);
return bindings;
}
}
#pragma GCC diagnostic pop // end ignored "-Wswitch-enum"
+1 -1
View File
@@ -12,7 +12,7 @@ SourcePath EvalState::rootPath(CanonPath path)
SourcePath EvalState::rootPath(PathView path)
{
return {rootFS, CanonPath(absPath(path).string())};
return {rootFS, CanonPath(absPath(path))};
}
SourcePath EvalState::storePath(const StorePath & path)
+5 -7
View File
@@ -11,7 +11,6 @@
#include "nix/store/path-references.hh"
#include "nix/store/store-api.hh"
#include "nix/util/util.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/value-to-xml.hh"
@@ -75,7 +74,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
for (auto & c : context) {
auto ensureValid = [&](const StorePath & p) {
if (!store->isValidPath(p))
error<InvalidPathError>(p).debugThrow();
error<InvalidPathError>(store->printStorePath(p)).debugThrow();
};
std::visit(
overloaded{
@@ -504,12 +503,12 @@ void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v)
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
state.error<EvalError>("cannot execute '%1%', since store path '%2%' is not valid", program, e.path.to_string())
state.error<EvalError>("cannot execute '%1%', since path '%2%' is not valid", program, e.path)
.atPos(pos)
.debugThrow();
}
auto output = runProgram(program, true, toOsStrings(std::move(commandArgs)));
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
parsed = state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root));
@@ -563,7 +562,6 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu
v.mkStringNoCopy("float"_sds);
break;
case nThunk:
case nFailed:
unreachable();
}
}
@@ -1934,7 +1932,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, V
directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path.abs()))
path = CanonPath(canonPath(path.abs(), true).string());
path = CanonPath(canonPath(path.abs(), true));
if (!state.store->isInStore(path.abs()))
state.error<EvalError>("path '%1%' is not in the Nix store", path).atPos(pos).debugThrow();
auto path2 = state.store->toStorePath(path.abs()).first;
@@ -2155,7 +2153,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, Va
auto rewrites = state.realiseContext(context);
path = rewriteStrings(std::move(path), rewrites);
} catch (InvalidPathError & e) {
state.error<EvalError>("cannot find '%1%', since path '%2%' is not valid", path, e.path.to_string())
state.error<EvalError>("cannot find '%1%', since path '%2%' is not valid", path, e.path)
.atPos(pos)
.debugThrow();
}
-5
View File
@@ -73,11 +73,6 @@ void printAmbiguous(EvalState & state, Value & v, std::ostream & str, std::set<c
str << "«potential infinite recursion»";
}
break;
case nFailed:
// Historically, a tried and then ignored value (e.g. through tryEval) was
// reverted to the original thunk.
str << "<CODE>";
break;
case nFunction:
if (v.isLambda()) {
str << "<LAMBDA>";
-15
View File
@@ -509,17 +509,6 @@ private:
}
}
void printFailed()
{
if (options.ansiColors)
output << ANSI_MAGENTA;
// Historically, a tried and then ignored value (e.g. through tryEval) was
// reverted to the original thunk.
output << "«thunk»";
if (options.ansiColors)
output << ANSI_NORMAL;
}
void printExternal(Value & v)
{
v.external()->print(output);
@@ -601,10 +590,6 @@ private:
printFunction(v);
break;
case nFailed:
printFailed();
break;
case nThunk:
printThunk(v);
break;
-1
View File
@@ -98,7 +98,6 @@ json printValueAsJSON(
break;
case nThunk:
case nFailed:
case nFunction:
state.error<TypeError>("cannot convert %1% to JSON", showType(v)).atPos(v.determinePos(pos)).debugThrow();
}
-4
View File
@@ -171,11 +171,7 @@ static void printValueAsXML(
break;
case nThunk:
// Historically, a tried and then ignored value (e.g. through tryEval) was
// reverted to the original thunk.
case nFailed:
doc.writeEmptyElement("unevaluated");
break;
}
}
+2 -2
View File
@@ -44,8 +44,8 @@ struct CacheImpl : Cache
{
auto state(_state.lock());
auto dbPath = getCacheDir() / "fetcher-cache-v4.sqlite";
createDirs(dbPath.parent_path());
auto dbPath = (getCacheDir() / "fetcher-cache-v4.sqlite").string();
createDirs(dirOf(dbPath));
state->db = SQLite(dbPath, {.useWAL = nix::settings.useSQLiteWAL});
state->db.isCache();
+1 -4
View File
@@ -161,7 +161,7 @@ bool Input::isFinal() const
return maybeGetBoolAttr(attrs, "__final").value_or(false);
}
std::optional<std::filesystem::path> Input::isRelative() const
std::optional<std::string> Input::isRelative() const
{
assert(scheme);
return scheme->isRelative(*this);
@@ -236,9 +236,6 @@ void Input::checkLocks(Input specified, Input & result)
if (auto prevNarHash = specified.getNarHash())
specified.attrs.insert_or_assign("narHash", prevNarHash->to_string(HashFormat::SRI, true));
if (auto narHash = result.getNarHash())
result.attrs.insert_or_assign("narHash", narHash->to_string(HashFormat::SRI, true));
for (auto & field : specified.attrs) {
auto field2 = result.attrs.find(field.first);
if (field2 != result.attrs.end() && field.second != field2->second)
+9 -14
View File
@@ -1,11 +1,9 @@
#include "nix/fetchers/git-lfs-fetch.hh"
#include "nix/fetchers/git-utils.hh"
#include "nix/store/filetransfer.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/url.hh"
#include "nix/util/users.hh"
#include "nix/util/util.hh"
#include "nix/util/hash.hh"
#include "nix/store/ssh.hh"
@@ -61,27 +59,24 @@ static LfsApiInfo getLfsApi(const ParsedURL & url)
auto args = getNixSshOpts();
if (url.authority->port)
args.push_back(string_to_os_string(fmt("-p%d", *url.authority->port)));
args.push_back(fmt("-p%d", *url.authority->port));
std::ostringstream hostnameAndUser;
if (url.authority->user)
hostnameAndUser << *url.authority->user << "@";
hostnameAndUser << url.authority->host;
args.push_back(string_to_os_string(std::move(hostnameAndUser).str()));
args.push_back(std::move(hostnameAndUser).str());
args.push_back(OS_STR("--"));
args.push_back(OS_STR("git-lfs-authenticate"));
args.push_back("--");
args.push_back("git-lfs-authenticate");
// FIXME %2F encode slashes? Does this command take/accept percent encoding?
args.push_back(string_to_os_string(url.renderPath(/*encode=*/false)));
args.push_back(OS_STR("download"));
args.push_back(url.renderPath(/*encode=*/false));
args.push_back("download");
auto [status, output] = runProgram({.program = "ssh", .args = args});
if (output.empty())
throw Error(
"git-lfs-authenticate: no output (cmd: 'ssh %s')",
concatMapStringsSep(
" ", args, [](const OsString & s) { return escapeShellArgAlways(os_string_to_string(s)); }));
throw Error("git-lfs-authenticate: no output (cmd: 'ssh %s')", concatStringsSep(" ", args));
auto queryResp = nlohmann::json::parse(output);
auto headerIt = queryResp.find("header");
@@ -273,10 +268,10 @@ void Fetch::fetch(
return;
}
auto cacheDir = getCacheDir() / "git-lfs";
std::filesystem::path cacheDir = getCacheDir() / "git-lfs";
std::string key = hashString(HashAlgorithm::SHA256, pointerFilePath.rel()).to_string(HashFormat::Base16, false)
+ "/" + pointer->oid;
auto cachePath = cacheDir / key;
std::filesystem::path cachePath = cacheDir / key;
if (pathExists(cachePath)) {
debug("using cache entry %s -> %s", key, PathFmt(cachePath));
sink(readFile(cachePath));
+18 -32
View File
@@ -4,7 +4,6 @@
#include "nix/fetchers/fetch-settings.hh"
#include "nix/util/base-n.hh"
#include "nix/util/finally.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/signals.hh"
#include "nix/util/users.hh"
@@ -248,8 +247,7 @@ static void initRepoAtomically(std::filesystem::path & path, GitRepo::Options op
|| e.code() == std::errc::directory_not_empty) {
return;
} else
throw SystemError(
e.code(), "moving temporary git repository from %s to %s", PathFmt(tmpDir), PathFmt(path));
throw SysError("moving temporary git repository from %s to %s", PathFmt(tmpDir), PathFmt(path));
}
// we successfully moved the repository, so the temporary directory no longer exists.
delTmpDir.cancel();
@@ -638,24 +636,12 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
// then use code that was removed in this commit (see blame)
auto dir = this->path;
OsStrings gitArgs = {
OS_STR("-C"),
dir.native(),
OS_STR("--git-dir"),
OS_STR("."),
OS_STR("fetch"),
OS_STR("--progress"),
OS_STR("--force"),
};
if (shallow) {
gitArgs.push_back(OS_STR("--depth"));
gitArgs.push_back(OS_STR("1"));
}
gitArgs.push_back(OS_STR("--"));
gitArgs.push_back(string_to_os_string(url));
gitArgs.push_back(string_to_os_string(refspec));
Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--progress", "--force"};
if (shallow)
append(gitArgs, {"--depth", "1"});
append(gitArgs, {std::string("--"), url, refspec});
auto status = runProgram({.program = "git", .args = gitArgs, .isInteractive = true}).first;
auto status = runProgram(RunOptions{.program = "git", .args = gitArgs, .isInteractive = true}).first;
if (status > 0)
throw Error("Failed to fetch git repository '%s'", url);
@@ -695,18 +681,18 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
writeFile(allowedSignersFile, allowedSigners);
// Run verification command
auto [status, output] = runProgram({
.program = "git",
.args{
OS_STR("-c"),
OS_STR("gpg.ssh.allowedSignersFile=") + allowedSignersFile.native(),
OS_STR("-C"),
path.native(),
OS_STR("verify-commit"),
string_to_os_string(rev.gitRev()),
},
.mergeStderrToStdout = true,
});
auto [status, output] = runProgram(
RunOptions{
.program = "git",
.args =
{"-c",
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
"-C",
path.string(),
"verify-commit",
rev.gitRev()},
.mergeStderrToStdout = true,
});
/* Evaluate result through status code and checking if public
key fingerprints appear on stderr. This is necessary
+37 -58
View File
@@ -7,7 +7,6 @@
#include "nix/store/store-api.hh"
#include "nix/util/url-parts.hh"
#include "nix/store/pathlocks.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/git.hh"
#include "nix/fetchers/git-utils.hh"
@@ -45,9 +44,8 @@ static bool isCacheFileWithinTtl(const Settings & settings, time_t now, const Po
std::filesystem::path getCachePath(std::string_view key, bool shallow)
{
auto name =
hashString(HashAlgorithm::SHA256, key).to_string(HashFormat::Nix32, false) + (shallow ? "-shallow" : "");
return getCacheDir() / "gitv3" / std::move(name);
return getCacheDir() / "gitv3"
/ (hashString(HashAlgorithm::SHA256, key).to_string(HashFormat::Nix32, false) + (shallow ? "-shallow" : ""));
}
// Returns the name of the HEAD branch.
@@ -63,7 +61,7 @@ std::optional<std::string> readHead(const std::filesystem::path & path)
RunOptions{
.program = "git",
// FIXME: use 'HEAD' to avoid returning all refs
.args = {OS_STR("ls-remote"), OS_STR("--symref"), path.native()},
.args = {"ls-remote", "--symref", path.string()},
.isInteractive = true,
});
if (status != 0)
@@ -90,19 +88,7 @@ bool storeCachedHead(const std::string & actualUrl, bool shallow, const std::str
{
std::filesystem::path cacheDir = getCachePath(actualUrl, shallow);
try {
runProgram(
"git",
true,
{
OS_STR("-C"),
cacheDir.native(),
OS_STR("--git-dir"),
OS_STR("."),
OS_STR("symbolic-ref"),
OS_STR("--"),
OS_STR("HEAD"),
string_to_os_string(headRef),
});
runProgram("git", true, {"-C", cacheDir.string(), "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef});
} catch (ExecError & e) {
if (
#ifndef WIN32 // TODO abstract over exit status handling on Windows
@@ -452,19 +438,19 @@ struct GitInputScheme : InputScheme
{
auto repoInfo = getRepoInfo(input);
OsStrings args = {OS_STR("clone")};
Strings args = {"clone"};
args.push_back(string_to_os_string(repoInfo.locationToArg()));
args.push_back(repoInfo.locationToArg());
if (auto ref = input.getRef()) {
args.push_back(OS_STR("--branch"));
args.push_back(string_to_os_string(*ref));
args.push_back("--branch");
args.push_back(*ref);
}
if (input.getRev())
throw UnimplementedError("cloning a specific revision is not implemented");
args.push_back(destDir.native());
args.push_back(destDir.string());
runProgram("git", true, args, {}, true);
}
@@ -491,15 +477,14 @@ struct GitInputScheme : InputScheme
auto result = runProgram(
RunOptions{
.program = "git",
.args{
OS_STR("-C"),
repoPath->native(),
OS_STR("--git-dir"),
string_to_os_string(repoInfo.gitDir),
OS_STR("check-ignore"),
OS_STR("--quiet"),
string_to_os_string(std::string(path.rel())),
},
.args =
{"-C",
repoPath->string(),
"--git-dir",
repoInfo.gitDir,
"check-ignore",
"--quiet",
std::string(path.rel())},
});
auto exitCode =
#ifndef WIN32 // TODO abstract over exit status handling on Windows
@@ -514,16 +499,14 @@ struct GitInputScheme : InputScheme
runProgram(
"git",
true,
{
OS_STR("-C"),
repoPath->native(),
OS_STR("--git-dir"),
string_to_os_string(repoInfo.gitDir),
OS_STR("add"),
OS_STR("--intent-to-add"),
OS_STR("--"),
string_to_os_string(std::string(path.rel())),
});
{"-C",
repoPath->string(),
"--git-dir",
repoInfo.gitDir,
"add",
"--intent-to-add",
"--",
std::string(path.rel())});
if (commitMsg) {
// Pause the logger to allow for user input (such as a gpg passphrase) in `git commit`
@@ -531,16 +514,14 @@ struct GitInputScheme : InputScheme
runProgram(
"git",
true,
{
OS_STR("-C"),
repoPath->native(),
OS_STR("--git-dir"),
string_to_os_string(repoInfo.gitDir),
OS_STR("commit"),
string_to_os_string(std::string(path.rel())),
OS_STR("-F"),
OS_STR("-"),
},
{"-C",
repoPath->string(),
"--git-dir",
repoInfo.gitDir,
"commit",
std::string(path.rel()),
"-F",
"-"},
*commitMsg);
}
}
@@ -645,12 +626,10 @@ struct GitInputScheme : InputScheme
//
// See: https://discourse.nixos.org/t/57783 and #9708
//
auto maybeUrlFsPathForFileUrl =
url.scheme == "file" ? std::make_optional(urlPathToPath(url.path)) : std::nullopt;
if (maybeUrlFsPathForFileUrl && !forceHttp && !isBareRepository(maybeUrlFsPathForFileUrl->string())) {
auto & path = *maybeUrlFsPathForFileUrl;
if (url.scheme == "file" && !forceHttp && !isBareRepository(renderUrlPathEnsureLegal(url.path))) {
auto path = renderUrlPathEnsureLegal(url.path);
if (!path.is_absolute()) {
if (!isAbsolute(path)) {
warn(
"Fetching Git repository '%s', which uses a path relative to the current directory. "
"This is not supported and will stop working in a future release. "
@@ -660,7 +639,7 @@ struct GitInputScheme : InputScheme
repoInfo.location = std::filesystem::absolute(path);
} else {
if (maybeUrlFsPathForFileUrl)
if (url.scheme == "file")
/* Query parameters are meaningless for file://, but
Git interprets them as part of the file name. So get
rid of them. */
@@ -86,7 +86,7 @@ public:
* Only for relative path flakes, i.e. 'path:./foo', returns the
* relative path, i.e. './foo'.
*/
std::optional<std::filesystem::path> isRelative() const;
std::optional<std::string> isRelative() const;
/**
* Return whether this is a "final" input, meaning that fetching
@@ -263,7 +263,7 @@ struct InputScheme
return false;
}
virtual std::optional<std::filesystem::path> isRelative(const Input & input) const
virtual std::optional<std::string> isRelative(const Input & input) const
{
return std::nullopt;
}
@@ -6,7 +6,6 @@
#include "nix/store/path.hh"
#include "nix/util/ref.hh"
#include "nix/util/types.hh"
#include "nix/util/url.hh"
namespace nix {
class Store;
@@ -28,7 +27,7 @@ struct DownloadFileResult
DownloadFileResult downloadFile(
Store & store,
const Settings & settings,
const VerbatimURL & url,
const std::string & url,
const std::string & name,
const Headers & headers = {});
+53 -102
View File
@@ -1,9 +1,5 @@
#include "nix/fetchers/fetchers.hh"
#include "nix/util/file-system.hh"
#include "nix/util/fmt.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/util.hh"
#include "nix/util/environment-variables.hh"
#include "nix/util/users.hh"
#include "nix/fetchers/cache.hh"
@@ -14,25 +10,24 @@
#include "nix/fetchers/fetch-settings.hh"
#include <sys/time.h>
#include <variant>
using namespace std::string_literals;
namespace nix::fetchers {
static RunOptions hgOptions(OsStrings args)
static RunOptions hgOptions(const Strings & args)
{
auto env = getEnvOs();
// Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc.
env[OS_STR("HGPLAIN")] = OS_STR("");
return {.program = "hg", .lookupPath = true, .args = std::move(args), .environment = env};
return {.program = "hg", .lookupPath = true, .args = args, .environment = env};
}
// runProgram wrapper that uses hgOptions instead of stock RunOptions.
static std::string runHg(OsStrings args, const std::optional<std::string> & input = {})
static std::string runHg(const Strings & args, const std::optional<std::string> & input = {})
{
RunOptions opts = hgOptions(std::move(args));
RunOptions opts = hgOptions(args);
opts.input = input;
auto res = runProgram(std::move(opts));
@@ -150,7 +145,7 @@ struct MercurialInputScheme : InputScheme
{
auto url = parseURL(getStrAttr(input.attrs, "url"));
if (url.scheme == "file" && !input.getRef() && !input.getRev())
return urlPathToPath(url.path);
return renderUrlPathEnsureLegal(url.path);
return {};
}
@@ -160,36 +155,29 @@ struct MercurialInputScheme : InputScheme
std::string_view contents,
std::optional<std::string> commitMsg) const override
{
std::visit(
overloaded{
[&](const std::filesystem::path & repoPath) {
auto absPath = repoPath / path.rel();
auto [isLocal, repoPath] = getActualUrl(input);
if (!isLocal)
throw Error(
"cannot commit '%s' to Mercurial repository '%s' because it's not a working tree",
path,
input.to_string());
writeFile(absPath, contents);
auto absPath = CanonPath(repoPath) / path;
// FIXME: shut up if file is already tracked.
runHg({OS_STR("add"), absPath.native()});
writeFile(absPath.abs(), contents);
if (commitMsg)
runHg({OS_STR("commit"), absPath.native(), OS_STR("-m"), string_to_os_string(*commitMsg)});
},
[&](const std::string &) {
throw Error(
"cannot commit '%s' to Mercurial repository '%s' because it's not a working tree",
path,
input.to_string());
},
},
getActualUrl(input));
// FIXME: shut up if file is already tracked.
runHg({"add", absPath.abs()});
if (commitMsg)
runHg({"commit", absPath.abs(), "-m", *commitMsg});
}
std::variant<std::filesystem::path, std::string> getActualUrl(const Input & input) const
std::pair<bool, std::string> getActualUrl(const Input & input) const
{
auto url = parseURL(getStrAttr(input.attrs, "url"));
if (url.scheme == "file")
return urlPathToPath(url.path);
else
return url.to_string();
bool isLocal = url.scheme == "file";
return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()};
}
StorePath fetchToStore(const Settings & settings, Store & store, Input & input) const
@@ -198,52 +186,35 @@ struct MercurialInputScheme : InputScheme
auto name = input.getName();
auto actualUrl_ = getActualUrl(input);
auto [isLocal, actualUrl_] = getActualUrl(input);
auto actualUrl = actualUrl_; // work around clang bug
// FIXME: return lastModified.
// FIXME: don't clone local repositories.
if (auto * localPathP = std::get_if<std::filesystem::path>(&actualUrl_)) {
auto & localPath = *localPathP;
auto unlocked = !input.getRef() && !input.getRev();
auto isValidLocalRepo = pathExists(localPath / ".hg");
// short circuiting to not bother checking if locked / no repo is important.
bool dirty = unlocked && isValidLocalRepo
&& runHg({
OS_STR("status"),
OS_STR("-R"),
localPath.native(),
OS_STR("--modified"),
OS_STR("--added"),
OS_STR("--removed"),
}) != "";
if (dirty) {
if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {
bool clean = runHg({"status", "-R", actualUrl, "--modified", "--added", "--removed"}) == "";
if (!clean) {
/* This is an unclean working tree. So copy all tracked
files. */
if (!settings.allowDirty)
throw Error("Mercurial tree '%s' is unclean", PathFmt{localPath});
throw Error("Mercurial tree '%s' is unclean", actualUrl);
if (settings.warnDirty)
warn("Mercurial tree '%s' is unclean", PathFmt{localPath});
warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({OS_STR("branch"), OS_STR("-R"), localPath.native()})));
input.attrs.insert_or_assign("ref", chomp(runHg({"branch", "-R", actualUrl})));
auto files = tokenizeString<StringSet>(
runHg({
OS_STR("status"),
OS_STR("-R"),
localPath.native(),
OS_STR("--clean"),
OS_STR("--modified"),
OS_STR("--added"),
OS_STR("--no-status"),
OS_STR("--print0"),
}),
runHg({"status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0"}),
"\0"s);
auto actualPath = absPath(localPath);
std::filesystem::path actualPath(absPath(actualUrl));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath.string()));
@@ -260,23 +231,18 @@ struct MercurialInputScheme : InputScheme
return files.count(file);
};
return store.addToStore(
auto storePath = store.addToStore(
input.getName(),
{getFSSourceAccessor(), CanonPath(actualPath.string())},
ContentAddressMethod::Raw::NixArchive,
HashAlgorithm::SHA256,
{},
filter);
return storePath;
}
}
auto [actualUrl, actualUrlOs] = std::visit(
overloaded{
[&](const std::filesystem::path & p) { return std::make_pair(p.string(), p.native()); },
[&](const std::string & s) { return std::make_pair(s, string_to_os_string(s)); },
},
actualUrl_);
if (!input.getRef())
input.attrs.insert_or_assign("ref", "default");
@@ -316,48 +282,40 @@ struct MercurialInputScheme : InputScheme
/* If this is a commit hash that we already have, we don't
have to pull again. */
if (!(input.getRev() && pathExists(cacheDir)
&& runProgram(hgOptions({
OS_STR("log"),
OS_STR("-R"),
cacheDir.native(),
OS_STR("-r"),
string_to_os_string(input.getRev()->gitRev()),
OS_STR("--template"),
OS_STR("1"),
}))
&& runProgram(
hgOptions({"log", "-R", cacheDir.string(), "-r", input.getRev()->gitRev(), "--template", "1"}))
.second
== "1")) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
if (pathExists(cacheDir)) {
try {
runHg({OS_STR("pull"), OS_STR("-R"), cacheDir.native(), OS_STR("--"), actualUrlOs});
runHg({"pull", "-R", cacheDir.string(), "--", actualUrl});
} catch (ExecError & e) {
auto transJournal = cacheDir / ".hg" / "store" / "journal";
/* hg throws "abandoned transaction" error only if this file exists */
if (pathExists(transJournal)) {
runHg({OS_STR("recover"), OS_STR("-R"), cacheDir.native()});
runHg({OS_STR("pull"), OS_STR("-R"), cacheDir.native(), OS_STR("--"), actualUrlOs});
runHg({"recover", "-R", cacheDir.string()});
runHg({"pull", "-R", cacheDir.string(), "--", actualUrl});
} else {
throw ExecError(e.status, "'hg pull' %s", statusToString(e.status));
}
}
} else {
createDirs(cacheDir.parent_path());
runHg({OS_STR("clone"), OS_STR("--noupdate"), OS_STR("--"), actualUrlOs, cacheDir.native()});
createDirs(dirOf(cacheDir.string()));
runHg({"clone", "--noupdate", "--", actualUrl, cacheDir.string()});
}
}
/* Fetch the remote rev or ref. */
auto tokens = tokenizeString<std::vector<std::string>>(runHg({
OS_STR("log"),
OS_STR("-R"),
cacheDir.native(),
OS_STR("-r"),
string_to_os_string(input.getRev() ? input.getRev()->gitRev() : *input.getRef()),
OS_STR("--template"),
OS_STR("{node} {rev} {branch}"),
}));
auto tokens = tokenizeString<std::vector<std::string>>(runHg(
{"log",
"-R",
cacheDir.string(),
"-r",
input.getRev() ? input.getRev()->gitRev() : *input.getRef(),
"--template",
"{node} {rev} {branch}"}));
assert(tokens.size() == 3);
auto rev = Hash::parseAny(tokens[0], HashAlgorithm::SHA1);
@@ -373,14 +331,7 @@ struct MercurialInputScheme : InputScheme
std::filesystem::path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runHg({
OS_STR("archive"),
OS_STR("-R"),
cacheDir.native(),
OS_STR("-r"),
string_to_os_string(rev.gitRev()),
tmpDir.native(),
});
runHg({"archive", "-R", cacheDir.string(), "-r", rev.gitRev(), tmpDir.string()});
deletePath(tmpDir / ".hg_archival.txt");
+6 -6
View File
@@ -19,7 +19,7 @@ struct PathInputScheme : InputScheme
Input input{};
input.attrs.insert_or_assign("type", "path");
input.attrs.insert_or_assign("path", urlPathToPath(url.path).string());
input.attrs.insert_or_assign("path", renderUrlPathEnsureLegal(url.path));
for (auto & [name, value] : url.query)
if (name == "rev" || name == "narHash")
@@ -114,10 +114,10 @@ struct PathInputScheme : InputScheme
writeFile(getAbsPath(input) / path.rel(), contents);
}
std::optional<std::filesystem::path> isRelative(const Input & input) const override
std::optional<std::string> isRelative(const Input & input) const override
{
std::filesystem::path path = getStrAttr(input.attrs, "path");
if (path.is_absolute())
auto path = getStrAttr(input.attrs, "path");
if (isAbsolute(path))
return std::nullopt;
else
return path;
@@ -130,9 +130,9 @@ struct PathInputScheme : InputScheme
std::filesystem::path getAbsPath(const Input & input) const
{
std::filesystem::path path = getStrAttr(input.attrs, "path");
auto path = getStrAttr(input.attrs, "path");
if (path.is_absolute())
if (isAbsolute(path))
return canonPath(path);
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
+2 -3
View File
@@ -147,14 +147,13 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, St
return Registry::read(
settings,
[&] -> SourcePath {
std::filesystem::path fsPath{path};
if (!fsPath.is_absolute()) {
if (!isAbsolute(path)) {
auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
if (auto store2 = dynamic_cast<LocalFSStore *>(&store))
store2->addPermRoot(storePath, (getCacheDir() / "flake-registry.json").string());
return {store.requireStoreObjectAccessor(storePath)};
} else {
return SourcePath{getFSSourceAccessor(), CanonPath{fsPath.string()}}.resolveSymlinks();
return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks();
}
}(),
Registry::Global);
+5 -5
View File
@@ -15,7 +15,7 @@ namespace nix::fetchers {
DownloadFileResult downloadFile(
Store & store,
const Settings & settings,
const VerbatimURL & url,
const std::string & url,
const std::string & name,
const Headers & headers)
{
@@ -24,7 +24,7 @@ DownloadFileResult downloadFile(
Cache::Key key{
"file",
{{
{"url", url.to_string()},
{"url", url},
{"name", name},
}}};
@@ -42,7 +42,7 @@ DownloadFileResult downloadFile(
if (cached && !cached->expired)
return useCached();
FileTransferRequest request(url);
FileTransferRequest request(VerbatimURL{url});
request.headers = headers;
if (cached)
request.expectedETag = getStrAttr(cached->value, "etag");
@@ -113,7 +113,7 @@ static DownloadTarballResult downloadTarball_(
// Namely lets catch when the url is a local file path, but
// it is not in fact a tarball.
if (url.scheme == "file") {
std::filesystem::path localPath = urlPathToPath(url.path);
std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path);
if (!localPath.is_absolute()) {
throw Error(
"tarball '%s' must use an absolute path. "
@@ -179,7 +179,7 @@ static DownloadTarballResult downloadTarball_(
auto [fdTemp, path] = createTempFile("nix-zipfile");
cleanupTemp.cancel();
cleanupTemp = {path};
debug("downloading '%s' into %s...", url, PathFmt(path));
debug("downloading '%s' into '%s'...", url, path);
{
FdSink sink(fdTemp.get());
source->drainInto(sink);
+1 -1
View File
@@ -13,7 +13,7 @@ struct nix_flake_settings
struct nix_flake_reference_parse_flags
{
std::optional<std::filesystem::path> baseDirectory;
std::optional<nix::Path> baseDirectory;
};
struct nix_flake_reference
+1 -1
View File
@@ -31,7 +31,7 @@ namespace nix::flake {
// setting name -> setting value -> allow or ignore.
typedef std::map<std::string, std::map<std::string, bool>> TrustedList;
static std::filesystem::path trustedListPath()
std::filesystem::path trustedListPath()
{
return getDataDir() / "trusted-settings.json";
}
+1 -1
View File
@@ -596,7 +596,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
if (auto relativePath = input.ref->input.isRelative()) {
return SourcePath{
overriddenSourcePath.accessor,
CanonPath(relativePath->string(), overriddenSourcePath.path.parent().value())};
CanonPath(*relativePath, overriddenSourcePath.path.parent().value())};
} else
return std::nullopt;
};
+30 -32
View File
@@ -112,7 +112,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
auto succeeds = std::regex_match(url, match, pathFlakeRegex);
if (!succeeds)
throw Error("invalid flakeref '%s'", url);
std::filesystem::path path = match[1].str();
auto path = match[1].str();
auto query = decodeQuery(match[3].str(), /*lenient=*/true);
auto fragment = percentDecode(match[5].str());
@@ -121,62 +121,60 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
path = absPath(path, get(baseDir), true);
path = absPath(path, baseDir->string(), true);
if (isFlake) {
if (!S_ISDIR(lstat(path).st_mode)) {
if (path.filename() == "flake.nix") {
if (baseNameOf(path) == "flake.nix") {
// Be gentle with people who accidentally write `/foo/bar/flake.nix` instead of `/foo/bar`
auto parentPath = path.parent_path();
warn(
"Path %s should point at the directory containing the 'flake.nix' file, not the file itself. "
"Pretending that you meant %s",
PathFmt(path),
PathFmt(parentPath));
path = parentPath;
"Path '%s' should point at the directory containing the 'flake.nix' file, not the file itself. "
"Pretending that you meant '%s'",
path,
dirOf(path));
path = dirOf(path);
} else {
throw BadURL("path %s is not a flake (because it's not a directory)", PathFmt(path));
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
}
}
if (!allowMissing && !pathExists(path / "flake.nix")) {
notice("path %s does not contain a 'flake.nix', searching up", PathFmt(path));
if (!allowMissing && !pathExists(path + "/flake.nix")) {
notice("path '%s' does not contain a 'flake.nix', searching up", path);
// Save device to detect filesystem boundary
dev_t device = lstat(path).st_dev;
bool found = false;
while (path != "/") {
if (pathExists(path / "flake.nix")) {
if (pathExists(path + "/flake.nix")) {
found = true;
break;
} else if (pathExists(path / ".git"))
} else if (pathExists(path + "/.git"))
throw Error(
"path %s is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)",
PathFmt(path));
"path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)",
path);
else {
if (lstat(path).st_dev != device)
throw Error(
"unable to find a flake before encountering filesystem boundary at %s", PathFmt(path));
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
}
path = path.parent_path();
path = dirOf(path);
}
if (!found)
throw BadURL("could not find a flake.nix file");
}
if (!allowMissing && !pathExists(path / "flake.nix"))
throw BadURL("path %s is not a flake (because it doesn't contain a 'flake.nix' file)", PathFmt(path));
if (!allowMissing && !pathExists(path + "/flake.nix"))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
auto flakeRoot = path;
std::string subdir;
while (flakeRoot != "/") {
if (pathExists(flakeRoot / ".git")) {
if (pathExists(flakeRoot + "/.git")) {
auto parsedURL = ParsedURL{
.scheme = "git+file",
.authority = ParsedURL::Authority{},
.path = pathToUrlPath(flakeRoot),
.path = splitString<std::vector<std::string>>(flakeRoot, "/"),
.query = query,
.fragment = fragment,
};
@@ -187,19 +185,19 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
parsedURL.query.insert_or_assign("dir", subdir);
}
if (pathExists(flakeRoot / ".git" / "shallow"))
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return fromParsedURL(fetchSettings, std::move(parsedURL), isFlake);
}
subdir = flakeRoot.filename().string() + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = flakeRoot.parent_path();
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
}
}
} else {
if (!preserveRelativePaths && !path.is_absolute())
if (!preserveRelativePaths && !isAbsolute(path))
throw BadURL("flake reference '%s' is not an absolute path", url);
}
@@ -207,8 +205,8 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
fetchSettings,
{
.scheme = "path",
.authority = path.is_absolute() ? std::optional{ParsedURL::Authority{}} : std::nullopt,
.path = pathToUrlPath(path),
.authority = isAbsolute(path) ? std::optional{ParsedURL::Authority{}} : std::nullopt,
.path = splitString<std::vector<std::string>>(path, "/"),
.query = query,
.fragment = fragment,
},
@@ -252,9 +250,9 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
auto parsed = parseURL(url, /*lenient=*/true);
if (baseDir && (parsed.scheme == "path" || parsed.scheme == "git+file")) {
/* Here we know that the path must not contain encoded '/' or NUL bytes. */
auto path = urlPathToPath(parsed.path);
if (!path.is_absolute())
parsed.path = pathToUrlPath(absPath(path, get(baseDir)));
auto path = renderUrlPathEnsureLegal(parsed.path);
if (!isAbsolute(path))
parsed.path = splitString<std::vector<std::string>>(absPath(path, baseDir->string()), "/");
}
return fromParsedURL(fetchSettings, std::move(parsed), isFlake);
} catch (BadURL &) {
+1 -1
View File
@@ -73,7 +73,7 @@ std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler(defaultSt
void defaultStackOverflowHandler(siginfo_t * info, void * ctx)
{
char msg[] = "error: stack overflow (possible infinite recursion)\n";
[[gnu::unused]] auto res = ::write(2, msg, strlen(msg));
[[gnu::unused]] auto res = write(2, msg, strlen(msg));
_exit(1); // maybe abort instead?
}
+1 -1
View File
@@ -116,7 +116,7 @@ nix_err nix_store_real_path(
context->last_err_code = NIX_OK;
try {
auto store2 = store->ptr.dynamic_pointer_cast<nix::LocalFSStore>();
auto res = store2 ? store2->toRealPath(path->path).string() : store->ptr->printStorePath(path->path);
auto res = store2 ? store2->toRealPath(path->path) : store->ptr->printStorePath(path->path);
return call_nix_get_string_callback(res, callback, user_data);
}
NIXC_CATCH_ERRS
+1 -2
View File
@@ -1,5 +1,4 @@
#include "nix/store/tests/https-store.hh"
#include "nix/util/os-string.hh"
#include <thread>
@@ -22,7 +21,7 @@ ref<TestHttpBinaryCacheStore> TestHttpBinaryCacheStoreConfig::openTestStore(ref<
void HttpsBinaryCacheStoreTest::openssl(Strings args)
{
runProgram("openssl", /*lookupPath=*/true, toOsStrings(std::move(args)));
runProgram("openssl", /*lookupPath=*/true, args);
}
void HttpsBinaryCacheStoreTest::SetUp()
@@ -50,7 +50,8 @@ protected:
#else
// resolve any symlinks in i.e. on macOS /tmp -> /private/tmp
// because this is not allowed for a nix store.
auto tmpl = nix::absPath(nix::defaultTempDir() / "tests_nix-store.XXXXXX", nullptr, true);
auto tmpl =
nix::absPath(std::filesystem::path(nix::defaultTempDir()) / "tests_nix-store.XXXXXX", std::nullopt, true);
nixDir = mkdtemp((char *) tmpl.c_str());
#endif
@@ -1 +0,0 @@
C:\foo\bar\baz?trusted=true
+6 -2
View File
@@ -13,6 +13,10 @@ using testing::Eq;
using testing::Field;
using testing::SizeIs;
namespace nix::fs {
using namespace std::filesystem;
}
using namespace nix;
TEST(machines, getMachinesWithEmptyBuilders)
@@ -27,7 +31,7 @@ TEST(machines, getMachinesUriOnly)
ASSERT_THAT(actual, SizeIs(1));
EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS")));
EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq(std::filesystem::path{})));
EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0)));
EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1)));
EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1)));
EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, SizeIs(0)));
@@ -49,7 +53,7 @@ TEST(machines, getMachinesDefaults)
ASSERT_THAT(actual, SizeIs(1));
EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS")));
EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq(std::filesystem::path{})));
EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0)));
EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1)));
EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1)));
EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, SizeIs(0)));
+2 -2
View File
@@ -26,7 +26,7 @@ TEST(NarInfoDiskCacheImpl, create_and_read)
{
auto cache = NarInfoDiskCache::getTest(
settings.getNarInfoDiskCacheSettings(), {.useWAL = settings.useSQLiteWAL}, dbPath);
settings.getNarInfoDiskCacheSettings(), {.useWAL = settings.useSQLiteWAL}, dbPath.string());
// Set up "background noise" and check that different caches receive different ids
{
@@ -76,7 +76,7 @@ TEST(NarInfoDiskCacheImpl, create_and_read)
// We can't clear the in-memory cache, so we use a new cache object. This is
// more realistic anyway.
auto cache2 = NarInfoDiskCache::getTest(
settings.getNarInfoDiskCacheSettings(), {.useWAL = settings.useSQLiteWAL}, dbPath);
settings.getNarInfoDiskCacheSettings(), {.useWAL = settings.useSQLiteWAL}, dbPath.string());
{
auto r = cache2->upToDateCacheExists("http://foo");
+1 -19
View File
@@ -85,20 +85,6 @@ static StoreReference localExample_2{
},
};
#ifdef _WIN32
static StoreReference localExample_windows{
.variant =
StoreReference::Specified{
.scheme = "local",
.authority = "/C:/foo/bar/baz",
},
.params =
{
{"trusted", "true"},
},
};
#endif
static StoreReference localExample_3{
.variant =
StoreReference::Specified{
@@ -122,11 +108,7 @@ URI_TEST_READ(local_3_no_percent, localExample_3)
URI_TEST_READ(local_shorthand_1, localExample_1)
#ifndef _WIN32
URI_TEST_READ(local_shorthand_path_unix, localExample_2)
#else
URI_TEST_READ(local_shorthand_path_windows, localExample_windows)
#endif
URI_TEST_READ(local_shorthand_2, localExample_2)
URI_TEST(
local_shorthand_3,
+3 -3
View File
@@ -27,12 +27,12 @@ namespace nix {
BinaryCacheStore::BinaryCacheStore(Config & config)
: config{config}
{
if (!config.secretKeyFile.get().empty())
signers.push_back(std::make_unique<LocalSigner>(SecretKey{readFile(config.secretKeyFile.get())}));
if (config.secretKeyFile != "")
signers.push_back(std::make_unique<LocalSigner>(SecretKey{readFile(config.secretKeyFile)}));
if (config.secretKeyFiles != "") {
std::stringstream ss(config.secretKeyFiles);
std::string keyPath;
Path keyPath;
while (std::getline(ss, keyPath, ',')) {
signers.push_back(std::make_unique<LocalSigner>(SecretKey{readFile(keyPath)}));
}
+9 -12
View File
@@ -387,11 +387,8 @@ Goal::Co DerivationBuildingGoal::tryToBuild(StorePathSet inputPaths)
for (auto & i : drv->outputsAndOptPaths(worker.store)) {
if (i.second.second)
lockFiles.insert(localStore->toRealPath(*i.second.second));
else {
auto lockPath = localStore->toRealPath(drvPath);
lockPath += "." + i.first;
lockFiles.insert(std::move(lockPath));
}
else
lockFiles.insert(localStore->toRealPath(drvPath) + "." + i.first);
}
}
@@ -1064,7 +1061,7 @@ static void runPostBuildHook(
LogSink sink(act);
runProgram2({
.program = workerSettings.postBuildHook.get(),
.program = workerSettings.postBuildHook,
.environment = hookEnvironment,
.standardOut = &sink,
.mergeStderrToStdout = true,
@@ -1181,15 +1178,15 @@ LogFile::LogFile(Store & store, const StorePath & drvPath, const LogFileSettings
auto baseName = std::string(baseNameOf(store.printStorePath(drvPath)));
std::filesystem::path logDir;
Path logDir;
if (auto localStore = dynamic_cast<LocalStore *>(&store))
logDir = localStore->config->logDir.get();
logDir = localStore->config->logDir;
else
logDir = logSettings.nixLogDir;
auto dir = logDir / LocalFSStore::drvsLogDir / baseName.substr(0, 2);
logDir = logSettings.nixLogDir.string();
Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, baseName.substr(0, 2));
createDirs(dir);
auto logFileName = dir / (baseName.substr(2) + (logSettings.compressLog ? ".bz2" : ""));
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2), logSettings.compressLog ? ".bz2" : "");
fd = openNewFileForWrite(
logFileName,
@@ -1199,7 +1196,7 @@ LogFile::LogFile(Store & store, const StorePath & drvPath, const LogFileSettings
.followSymlinksOnTruncate = true, /* FIXME: Probably shouldn't follow symlinks. */
});
if (!fd)
throw SysError("creating log file %1%", PathFmt(logFileName));
throw SysError("creating log file '%1%'", logFileName);
fileSink = std::make_shared<FdSink>(fd.get());
-7
View File
@@ -20,9 +20,6 @@ Worker::Worker(Store & store, Store & evalStore)
: act(*logger, actRealise)
, actDerivations(*logger, actBuilds)
, actSubstitutions(*logger, actCopyPaths)
#ifdef _WIN32
, ioport{CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)}
#endif
, store(store)
, evalStore(evalStore)
, settings(nix::settings.getWorkerSettings())
@@ -30,10 +27,6 @@ Worker::Worker(Store & store, Store & evalStore)
return nix::settings.getWorkerSettings().useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>{};
}}
{
#ifdef _WIN32
if (!ioport)
throw windows::WinError("CreateIoCompletionPort");
#endif
nrLocalBuilds = 0;
nrSubstitutions = 0;
lastWokenUp = steady_time_point::min();
+12 -11
View File
@@ -20,15 +20,14 @@ namespace {
struct State
{
std::map<std::filesystem::path, int> priorities;
std::map<Path, int> priorities;
unsigned long symlinks = 0;
};
} // namespace
/* For each activated package, create symlinks */
static void
createLinks(State & state, const std::filesystem::path & srcDir, const std::filesystem::path & dstDir, int priority)
static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority)
{
DirectoryIterator srcFiles;
@@ -36,7 +35,7 @@ createLinks(State & state, const std::filesystem::path & srcDir, const std::file
srcFiles = DirectoryIterator{srcDir};
} catch (SystemError & e) {
if (e.is(std::errc::not_a_directory)) {
warn("not including %s in the user environment because it's not a directory", PathFmt(srcDir));
warn("not including '%s' in the user environment because it's not a directory", srcDir);
return;
}
throw;
@@ -79,8 +78,9 @@ createLinks(State & state, const std::filesystem::path & srcDir, const std::file
} else if (S_ISLNK(dstSt.st_mode)) {
auto target = canonPath(dstFile, true);
if (!S_ISDIR(lstat(target).st_mode))
throw Error("collision between %1% and non-directory %2%", PathFmt(srcFile), PathFmt(target));
unlink(dstFile);
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
if (unlink(dstFile.c_str()) == -1)
throw SysError("unlinking '%1%'", dstFile);
if (mkdir(
dstFile.c_str()
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
@@ -107,7 +107,8 @@ createLinks(State & state, const std::filesystem::path & srcDir, const std::file
throw BuildEnvFileConflictError(readLink(dstFile), srcFile, priority);
if (prevPriority < priority)
continue;
unlink(dstFile);
if (unlink(dstFile.c_str()) == -1)
throw SysError("unlinking '%1%'", dstFile);
} else if (S_ISDIR(dstSt.st_mode))
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
}
@@ -119,20 +120,20 @@ createLinks(State & state, const std::filesystem::path & srcDir, const std::file
}
}
void buildProfile(const std::filesystem::path & out, Packages && pkgs)
void buildProfile(const Path & out, Packages && pkgs)
{
State state;
PathSet done, postponed;
auto addPkg = [&](const std::filesystem::path & pkgDir, int priority) {
if (!done.insert(pkgDir.string()).second)
auto addPkg = [&](const Path & pkgDir, int priority) {
if (!done.insert(pkgDir).second)
return;
createLinks(state, pkgDir, out, priority);
try {
for (const auto & p : tokenizeString<std::vector<std::string>>(
readFile(pkgDir / "nix-support" / "propagated-user-env-packages"), " \n"))
readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n"))
if (!done.count(p))
postponed.insert(p);
} catch (SystemError & e) {
+1 -1
View File
@@ -15,7 +15,7 @@ static void builtinFetchurl(const BuiltinBuilderContext & ctx)
pass a pointer to the data. */
if (ctx.netrcData != "") {
fileTransferSettings.netrcFile = "netrc";
writeFile(fileTransferSettings.netrcFile.get(), ctx.netrcData, 0600);
writeFile(fileTransferSettings.netrcFile, ctx.netrcData, 0600);
}
fileTransferSettings.caFile = "ca-certificates.crt";
+2 -4
View File
@@ -27,8 +27,6 @@ static void builtinUnpackChannel(const BuiltinBuilderContext & ctx)
size_t fileCount;
std::string fileName;
auto entries = DirectoryIterator{out};
if (entries == DirectoryIterator{})
throw Error("channel tarball '%s' is empty", src);
fileName = entries->path().string();
fileCount = std::distance(entries.begin(), entries.end());
@@ -38,8 +36,8 @@ static void builtinUnpackChannel(const BuiltinBuilderContext & ctx)
auto target = out / channelName;
try {
std::filesystem::rename(fileName, target);
} catch (std::filesystem::filesystem_error & e) {
throw SystemError(e.code(), "failed to rename %1% to %2%", fileName, target.string());
} catch (std::filesystem::filesystem_error &) {
throw SysError("failed to rename %1% to %2%", fileName, target.string());
}
}
+3 -3
View File
@@ -680,17 +680,17 @@ static void performOp(
"you are not privileged to create perm roots\n\n"
"hint: you can just do this client-side without special privileges, and probably want to do that instead.");
auto storePath = WorkerProto::Serialise<StorePath>::read(*store, rconn);
std::filesystem::path gcRoot = absPath(readString(conn.from));
Path gcRoot = absPath(readString(conn.from));
logger->startWork();
auto & localFSStore = require<LocalFSStore>(*store);
localFSStore.addPermRoot(storePath, gcRoot);
logger->stopWork();
conn.to << gcRoot.string();
conn.to << gcRoot;
break;
}
case WorkerProto::Op::AddIndirectRoot: {
std::filesystem::path path = absPath(readString(conn.from));
Path path = absPath(readString(conn.from));
logger->startWork();
auto & indirectRootStore = require<IndirectRootStore>(*store);
+45 -55
View File
@@ -2,6 +2,7 @@
#include "nix/store/globals.hh"
#include "nix/util/config-global.hh"
#include "nix/store/store-api.hh"
#include "nix/util/compression.hh"
#include "nix/util/finally.hh"
#include "nix/util/callback.hh"
#include "nix/util/signals.hh"
@@ -87,10 +88,14 @@ struct curlFileTransfer : public FileTransfer
FileTransferRequest request;
FileTransferResult result;
std::unique_ptr<Activity> _act;
bool done = false; // whether either the success or failure function has been called
Callback<FileTransferResult> callback;
CURL * req = 0;
// buffer to accompany the `req` above
char errbuf[CURL_ERROR_SIZE];
bool active = false; // whether the handle has been added to the multi object
bool paused = false; // whether the request has been paused previously
bool enqueued = false; // whether the request has been added the incoming queue
std::string statusMsg;
unsigned int attempt = 0;
@@ -101,35 +106,9 @@ struct curlFileTransfer : public FileTransfer
curlSList requestHeaders;
/**
* Whether either the success or failure function has been called.
*/
bool done:1 = false;
std::string encoding;
/**
* Whether the handle has been added to the multi object.
*/
bool active:1 = false;
/**
* Whether the request has been paused previously.
*/
bool paused:1 = false;
/**
* Whether the request has been added the incoming queue.
*/
bool enqueued:1 = false;
/**
* Whether we can use range downloads for retries.
*/
bool acceptRanges:1 = false;
/**
* Whether the response has a non-trivial (not "identity") Content-Encoding.
*/
bool hasContentEncoding:1 = false;
bool acceptRanges = false;
curl_off_t writtenToSink = 0;
@@ -190,6 +169,15 @@ struct curlFileTransfer : public FileTransfer
{
result.urls.push_back(request.uri.to_string());
/* Don't set Accept-Encoding for S3 requests that use AWS SigV4 signing.
curl's SigV4 implementation signs all headers including Accept-Encoding,
but some S3-compatible services (like GCS) modify this header in transit,
causing signature verification to fail.
See https://github.com/NixOS/nix/issues/15019 */
#if NIX_WITH_AWS_AUTH
if (!request.awsSigV4Provider)
#endif
appendHeaders("Accept-Encoding: zstd, br, gzip, deflate, bzip2, xz");
if (!request.expectedETag.empty())
appendHeaders("If-None-Match: " + request.expectedETag);
if (!request.mimeType.empty())
@@ -237,6 +225,7 @@ struct curlFileTransfer : public FileTransfer
}
LambdaSink finalSink;
std::shared_ptr<FinishSink> decompressionSink;
std::optional<StringSink> errorSink;
std::exception_ptr callbackException;
@@ -246,15 +235,18 @@ struct curlFileTransfer : public FileTransfer
size_t realSize = size * nmemb;
result.bodySize += realSize;
if (!errorSink && !successfulStatuses.count(getHTTPStatus())) {
// In this case we want to construct a TeeSink, to keep
// the response around (which we figure won't be big
// like an actual download should be) to improve error
// messages.
errorSink = StringSink{};
if (!decompressionSink) {
decompressionSink = makeDecompressionSink(encoding, finalSink);
if (!successfulStatuses.count(getHTTPStatus())) {
// In this case we want to construct a TeeSink, to keep
// the response around (which we figure won't be big
// like an actual download should be) to improve error
// messages.
errorSink = StringSink{};
}
}
finalSink({static_cast<const char *>(contents), realSize});
(*decompressionSink)({(char *) contents, realSize});
if (paused) {
/* The callback has signaled that the transfer needs to be
paused. Already consumed data won't be returned twice unlike
@@ -296,7 +288,7 @@ struct curlFileTransfer : public FileTransfer
result.bodySize = 0;
statusMsg = trim(match.str(1));
acceptRanges = false;
hasContentEncoding = false;
encoding = "";
appendCurrentUrl();
} else {
@@ -319,10 +311,8 @@ struct curlFileTransfer : public FileTransfer
}
}
else if (name == "content-encoding") {
auto value = toLower(trim(line.substr(i + 1)));
hasContentEncoding = !value.empty() && value != "identity";
}
else if (name == "content-encoding")
encoding = trim(line.substr(i + 1));
else if (name == "accept-ranges" && toLower(trim(line.substr(i + 1))) == "bytes")
acceptRanges = true;
@@ -340,10 +330,14 @@ struct curlFileTransfer : public FileTransfer
}
return realSize;
} catch (...) {
#if LIBCURL_VERSION_NUM >= 0x075700
/* https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html:
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. */
callbackException = std::current_exception();
return CURL_WRITEFUNC_ERROR;
#else
return realSize;
#endif
}
static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp)
@@ -481,16 +475,6 @@ struct curlFileTransfer : public FileTransfer
}
curl_easy_setopt(req, CURLOPT_URL, request.uri.to_string().c_str());
/* Enable transparent decompression for downloads.
Skip for uploads (Accept-Encoding is meaningless when sending data)
and when resuming from an offset (byte ranges don't work with
compressed content). */
if (writtenToSink == 0 && !request.data)
/* Empty string means to enable all supported (that libcurl has
been linked to support) encodings. */
curl_easy_setopt(req, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(req, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(req, CURLOPT_MAXREDIRS, 10);
curl_easy_setopt(req, CURLOPT_NOSIGNAL, 1);
@@ -637,6 +621,14 @@ struct curlFileTransfer : public FileTransfer
appendCurrentUrl();
if (decompressionSink) {
try {
decompressionSink->finish();
} catch (...) {
callbackException = std::current_exception();
}
}
if (code == CURLE_WRITE_ERROR && result.etag == request.expectedETag) {
code = CURLE_OK;
httpStatus = 304;
@@ -654,9 +646,7 @@ struct curlFileTransfer : public FileTransfer
if (httpStatus == 304 && result.etag == "")
result.etag = request.expectedETag;
curl_off_t dlSize = 0;
curl_easy_getinfo(req, CURLINFO_SIZE_DOWNLOAD_T, &dlSize);
act().progress(dlSize, dlSize);
act().progress(result.bodySize, result.bodySize);
done = true;
callback(std::move(result));
}
@@ -705,7 +695,6 @@ struct curlFileTransfer : public FileTransfer
case CURLE_TOO_MANY_REDIRECTS:
case CURLE_WRITE_ERROR:
case CURLE_UNSUPPORTED_PROTOCOL:
case CURLE_BAD_CONTENT_ENCODING:
err = Misc;
break;
default: // Shut up warnings
@@ -749,7 +738,7 @@ struct curlFileTransfer : public FileTransfer
sink, we can only retry if the server supports
ranged requests. */
if (err == Transient && attempt < fileTransfer.settings.tries
&& (!this->request.dataCallback || writtenToSink == 0 || (acceptRanges && !hasContentEncoding))) {
&& (!this->request.dataCallback || writtenToSink == 0 || (acceptRanges && encoding.empty()))) {
int ms = retryTimeMs
* std::pow(
2.0f, attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(fileTransfer.mt19937));
@@ -757,6 +746,7 @@ struct curlFileTransfer : public FileTransfer
warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms);
else
warn("%s; retrying in %d ms", exc.what(), ms);
decompressionSink.reset();
errorSink.reset();
embargo = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
try {
+52 -53
View File
@@ -35,13 +35,13 @@
namespace nix {
static std::string gcSocketPath = "gc-socket/socket";
static std::string gcSocketPath = "/gc-socket/socket";
static std::string gcRootsDir = "gcroots";
void LocalStore::addIndirectRoot(const std::filesystem::path & path)
void LocalStore::addIndirectRoot(const Path & path)
{
std::string hash = hashString(HashAlgorithm::SHA1, path.string()).to_string(HashFormat::Nix32, false);
auto realRoot = canonPath(config->stateDir.get() / gcRootsDir / "auto" / hash);
std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false);
Path realRoot = canonPath(fmt("%1%/%2%/auto/%3%", config->stateDir, gcRootsDir, hash));
makeSymlink(realRoot, path);
}
@@ -57,11 +57,11 @@ void LocalStore::createTempRootsFile()
if (pathExists(fnTempRoots))
/* It *must* be stale, since there can be no two
processes with the same pid. */
tryUnlink(fnTempRoots);
unlink(fnTempRoots.c_str());
*fdTempRoots = openLockFile(fnTempRoots, true);
debug("acquiring write lock on %s", PathFmt(fnTempRoots));
debug("acquiring write lock on '%s'", fnTempRoots);
lockFile(fdTempRoots->get(), ltWrite, true);
/* Check whether the garbage collector didn't get in our
@@ -105,8 +105,8 @@ restart:
auto fdRootsSocket(_fdRootsSocket.lock());
if (!*fdRootsSocket) {
auto socketPath = config->stateDir.get() / gcSocketPath;
debug("connecting to '%s'", PathFmt(socketPath));
auto socketPath = config->stateDir.get() + gcSocketPath;
debug("connecting to '%s'", socketPath);
*fdRootsSocket = createUnixDomainSocket();
try {
nix::connect(toSocket(fdRootsSocket->get()), socketPath);
@@ -166,13 +166,13 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
// those to keep the directory alive.
continue;
}
auto path = i.path();
Path path = i.path().string();
pid_t pid = std::stoi(name);
debug("reading temporary root file %1%", PathFmt(path));
debug("reading temporary root file '%1%'", path);
AutoCloseFD fd(toDescriptor(open(
path.string().c_str(),
path.c_str(),
#ifndef _WIN32
O_CLOEXEC |
#endif
@@ -182,15 +182,15 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
/* It's okay if the file has disappeared. */
if (errno == ENOENT)
continue;
throw SysError("opening temporary roots file %1%", PathFmt(path));
throw SysError("opening temporary roots file '%1%'", path);
}
/* Try to acquire a write lock without blocking. This can
only succeed if the owning process has died. In that case
we don't care about its temporary roots. */
if (lockFile(fd.get(), ltWrite, false)) {
printInfo("removing stale temporary roots file %1%", PathFmt(path));
tryUnlink(path);
printInfo("removing stale temporary roots file '%1%'", path);
unlink(path.c_str());
writeFull(fd.get(), "d");
continue;
}
@@ -202,7 +202,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
std::string::size_type pos = 0, end;
while ((end = contents.find((char) 0, pos)) != std::string::npos) {
auto root = std::string_view(contents).substr(pos, end - pos);
Path root(contents, pos, end - pos);
debug("got temporary root '%s'", root);
tempRoots[parseStorePath(root)].emplace(censor ? censored : fmt("{temp:%d}", pid));
pos = end + 1;
@@ -210,15 +210,15 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
}
}
void LocalStore::findRoots(const std::filesystem::path & path, std::filesystem::file_type type, Roots & roots)
void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, Roots & roots)
{
auto foundRoot = [&](const std::filesystem::path & path, const std::filesystem::path & target) {
auto foundRoot = [&](const Path & path, const Path & target) {
try {
auto storePath = toStorePath(target.string()).first;
auto storePath = toStorePath(target).first;
if (isValidPath(storePath))
roots[std::move(storePath)].emplace(path.string());
roots[std::move(storePath)].emplace(path);
else
printInfo("skipping invalid root from %1% to %2%", PathFmt(path), PathFmt(target));
printInfo("skipping invalid root from '%1%' to '%2%'", path, target);
} catch (BadStorePath &) {
}
};
@@ -231,38 +231,37 @@ void LocalStore::findRoots(const std::filesystem::path & path, std::filesystem::
if (type == std::filesystem::file_type::directory) {
for (auto & i : DirectoryIterator{path}) {
checkInterrupt();
findRoots(i.path(), i.symlink_status().type(), roots);
findRoots(i.path().string(), i.symlink_status().type(), roots);
}
}
else if (type == std::filesystem::file_type::symlink) {
auto target = readLink(path);
if (isInStore(target.string()))
Path target = readLink(path);
if (isInStore(target))
foundRoot(path, target);
/* Handle indirect roots. */
else {
auto parentPath = path.parent_path();
target = absPath(target, &parentPath);
target = absPath(target, dirOf(path));
if (!pathExists(target)) {
if (isInDir(path, config->stateDir.get() / gcRootsDir / "auto")) {
printInfo("removing stale link from %1% to %2%", PathFmt(path), PathFmt(target));
tryUnlink(path);
if (isInDir(path, std::filesystem::path{config->stateDir.get()} / gcRootsDir / "auto")) {
printInfo("removing stale link from '%1%' to '%2%'", path, target);
unlink(path.c_str());
}
} else {
if (!std::filesystem::is_symlink(target))
return;
auto target2 = readLink(target);
if (isInStore(target2.string()))
Path target2 = readLink(target);
if (isInStore(target2))
foundRoot(target, target2);
}
}
}
else if (type == std::filesystem::file_type::regular) {
auto storePath = maybeParseStorePath(storeDir + "/" + std::string(baseNameOf(path.string())));
auto storePath = maybeParseStorePath(storeDir + "/" + std::string(baseNameOf(path)));
if (storePath && isValidPath(*storePath))
roots[std::move(*storePath)].emplace(path.string());
roots[std::move(*storePath)].emplace(path);
}
}
@@ -271,16 +270,16 @@ void LocalStore::findRoots(const std::filesystem::path & path, std::filesystem::
/* We only ignore permanent failures. */
if (e.code() == std::errc::permission_denied || e.code() == std::errc::no_such_file_or_directory
|| e.code() == std::errc::not_a_directory)
printInfo("cannot read potential root %1%", PathFmt(path));
printInfo("cannot read potential root '%1%'", path);
else
throw SystemError(e.code(), "finding GC roots in %1%", PathFmt(path));
throw;
}
catch (SystemError & e) {
/* We only ignore permanent failures. */
if (e.is(std::errc::permission_denied) || e.is(std::errc::no_such_file_or_directory)
|| e.is(std::errc::not_a_directory))
printInfo("cannot read potential root %1%", PathFmt(path));
printInfo("cannot read potential root '%1%'", path);
else
throw;
}
@@ -289,8 +288,8 @@ void LocalStore::findRoots(const std::filesystem::path & path, std::filesystem::
void LocalStore::findRootsNoTemp(Roots & roots, bool censor)
{
/* Process direct roots in {gcroots,profiles}. */
findRoots(config->stateDir.get() / gcRootsDir, std::filesystem::file_type::unknown, roots);
findRoots(config->stateDir.get() / "profiles", std::filesystem::file_type::unknown, roots);
findRoots(config->stateDir + "/" + gcRootsDir, std::filesystem::file_type::unknown, roots);
findRoots(config->stateDir + "/profiles", std::filesystem::file_type::unknown, roots);
/* Add additional roots returned by different platforms-specific
heuristics. This is typically used to add running programs to
@@ -399,8 +398,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
readFile(*p);
/* Start the server for receiving new roots. */
auto socketPath = config->stateDir.get() / gcSocketPath;
createDirs(socketPath.parent_path());
auto socketPath = config->stateDir.get() + gcSocketPath;
createDirs(dirOf(socketPath));
auto fdServer = createUnixDomainSocket(socketPath, 0666);
// TODO nonblocking socket on windows?
@@ -408,7 +407,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
throw UnimplementedError("External GC client not implemented yet");
#else
if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) | O_NONBLOCK) == -1)
throw SysError("making socket %s non-blocking", PathFmt(socketPath));
throw SysError("making socket '%1%' non-blocking", socketPath);
Pipe shutdownPipe;
shutdownPipe.create();
@@ -539,17 +538,16 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Helper function that deletes a path from the store and throws
GCLimitReached if we've deleted enough garbage. */
auto deleteFromStore = [&](std::string_view baseName, bool isKnownPath) {
assert(!std::filesystem::path(baseName).is_absolute());
Path path = storeDir + "/" + std::string(baseName);
auto realPath = config->realStoreDir.get() / std::string(baseName);
Path realPath = config->realStoreDir + "/" + std::string(baseName);
/* There may be temp directories in the store that are still in use
by another process. We need to be sure that we can acquire an
exclusive lock before deleting them. */
if (baseName.find("tmp-", 0) == 0) {
auto tmpDirFd = openDirectory(realPath);
AutoCloseFD tmpDirFd = openDirectory(realPath);
if (!tmpDirFd || !lockFile(tmpDirFd.get(), ltWrite, false)) {
debug("skipping locked tempdir %s", PathFmt(realPath));
debug("skipping locked tempdir '%s'", realPath);
return;
}
}
@@ -714,14 +712,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
printInfo("determining live/dead paths...");
try {
AutoCloseDir dir(opendir(config->realStoreDir.get().string().c_str()));
AutoCloseDir dir(opendir(config->realStoreDir.get().c_str()));
if (!dir)
throw SysError("opening directory %1%", PathFmt(config->realStoreDir.get()));
throw SysError("opening directory '%1%'", config->realStoreDir);
/* Read the store and delete all paths that are invalid or
unreachable. We don't use readDirectory() here so that
GCing can start faster. */
auto linksName = linksDir.filename();
auto linksName = baseNameOf(linksDir);
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) {
checkInterrupt();
@@ -758,9 +756,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
printInfo("deleting unused links...");
AutoCloseDir dir(opendir(linksDir.string().c_str()));
AutoCloseDir dir(opendir(linksDir.c_str()));
if (!dir)
throw SysError("opening directory %1%", PathFmt(linksDir));
throw SysError("opening directory '%1%'", linksDir);
int64_t actualSize = 0, unsharedSize = 0;
@@ -770,7 +768,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
std::string name = dirent->d_name;
if (name == "." || name == "..")
continue;
auto path = linksDir / name;
Path path = linksDir + "/" + name;
auto st = lstat(path);
@@ -780,9 +778,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
continue;
}
printMsg(lvlTalkative, "deleting unused link %1%", PathFmt(path));
printMsg(lvlTalkative, "deleting unused link '%1%'", path);
unlink(path);
if (unlink(path.c_str()) == -1)
throw SysError("deleting '%1%'", path);
/* Do not account for deleted file here. Rely on deletePath()
accounting. */
@@ -819,7 +818,7 @@ void LocalStore::autoGC(bool sync)
struct statvfs st;
if (statvfs(config->realStoreDir.get().c_str(), &st))
throw SysError("getting filesystem info about '%s'", PathFmt(config->realStoreDir.get()));
throw SysError("getting filesystem info about '%s'", config->realStoreDir);
return (uint64_t) st.f_bavail * st.f_frsize;
};
@@ -39,8 +39,7 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
fetch debug info on demand
)"};
Setting<std::filesystem::path> secretKeyFile{
this, "", "secret-key", "Path to the secret key used to sign the binary cache."};
Setting<Path> secretKeyFile{this, "", "secret-key", "Path to the secret key used to sign the binary cache."};
Setting<std::string> secretKeyFiles{
this, "", "secret-keys", "List of comma-separated paths to the secret keys used to sign the binary cache."};
@@ -2,7 +2,6 @@
///@file
#include "nix/store/store-api.hh"
#include "nix/util/fmt.hh"
namespace nix {
@@ -11,11 +10,11 @@ namespace nix {
*/
struct Package
{
std::filesystem::path path;
Path path;
bool active;
int priority;
Package(const std::filesystem::path & path, bool active, int priority)
Package(const Path & path, bool active, int priority)
: path{path}
, active{active}
, priority{priority}
@@ -26,18 +25,18 @@ struct Package
class BuildEnvFileConflictError final : public CloneableError<BuildEnvFileConflictError, Error>
{
public:
const std::filesystem::path fileA;
const std::filesystem::path fileB;
const Path fileA;
const Path fileB;
int priority;
BuildEnvFileConflictError(const std::filesystem::path fileA, const std::filesystem::path fileB, int priority)
BuildEnvFileConflictError(const Path fileA, const Path fileB, int priority)
: CloneableError(
"Unable to build profile. There is a conflict for the following files:\n"
"\n"
" %1%\n"
" %2%",
PathFmt(fileA),
PathFmt(fileB))
fileA,
fileB)
, fileA(fileA)
, fileB(fileB)
, priority(priority)
@@ -47,6 +46,6 @@ public:
typedef std::vector<Package> Packages;
void buildProfile(const std::filesystem::path & out, Packages && pkgs);
void buildProfile(const Path & out, Packages && pkgs);
} // namespace nix
@@ -15,7 +15,7 @@ struct CommonSSHStoreConfig : virtual StoreConfig
CommonSSHStoreConfig(std::string_view scheme, const ParsedURL::Authority & authority, const Params & params);
CommonSSHStoreConfig(std::string_view scheme, std::string_view authority, const Params & params);
Setting<std::filesystem::path> sshKey{
Setting<Path> sshKey{
this, "", "ssh-key", "Path to the SSH private key used to authenticate to the remote machine."};
Setting<std::string> sshPublicHostKey{
-11
View File
@@ -86,17 +86,6 @@ struct NarInfoDiskCacheSettings : public virtual Config
would prevent trying to pull the path again and failing with a hash
mismatch if the build isn't reproducible.
)"};
Setting<unsigned int> ttlMeta{
this,
7 * 24 * 3600,
"narinfo-cache-meta-ttl",
R"(
The TTL in seconds for caching binary cache metadata (i.e.
`/nix-cache-info`). This determines how long information about a
binary cache (such as its store directory, priority, and whether it
wants mass queries) is considered valid before being refreshed.
)"};
};
class Settings : public virtual Config,
@@ -55,7 +55,7 @@ struct IndirectRootStore : public virtual LocalFSStore
* The implementation of this method is concrete, but it delegates
* to `addIndirectRoot()` which is abstract.
*/
std::filesystem::path addPermRoot(const StorePath & storePath, const std::filesystem::path & gcRoot) override final;
Path addPermRoot(const StorePath & storePath, const Path & gcRoot) override final;
/**
* Add an indirect root, which is a weak reference to the
@@ -66,10 +66,10 @@ struct IndirectRootStore : public virtual LocalFSStore
*
* The form this weak-reference takes is implementation-specific.
*/
virtual void addIndirectRoot(const std::filesystem::path & path) = 0;
virtual void addIndirectRoot(const Path & path) = 0;
protected:
void makeSymlink(const std::filesystem::path & link, const std::filesystem::path & target);
void makeSymlink(const Path & link, const Path & target);
};
} // namespace nix
@@ -14,7 +14,7 @@ struct LocalBinaryCacheStoreConfig : std::enable_shared_from_this<LocalBinaryCac
*/
LocalBinaryCacheStoreConfig(std::string_view scheme, PathView binaryCacheDir, const Params & params);
std::filesystem::path binaryCacheDir;
Path binaryCacheDir;
static const std::string name()
{
@@ -10,8 +10,7 @@ namespace nix {
struct LocalFSStoreConfig : virtual StoreConfig
{
private:
static Setting<std::optional<std::filesystem::path>>
makeRootDirSetting(LocalFSStoreConfig & self, std::optional<std::filesystem::path> defaultValue)
static OptionalPathSetting makeRootDirSetting(LocalFSStoreConfig & self, std::optional<Path> defaultValue)
{
return {
&self,
@@ -33,7 +32,7 @@ public:
*/
LocalFSStoreConfig(PathView path, const Params & params);
Setting<std::optional<std::filesystem::path>> rootDir = makeRootDirSetting(*this, std::nullopt);
OptionalPathSetting rootDir = makeRootDirSetting(*this, std::nullopt);
private:
@@ -41,36 +40,30 @@ private:
* An indirection so that we don't need to refer to global settings
* in headers.
*/
static std::filesystem::path getDefaultStateDir();
static Path getDefaultStateDir();
/**
* An indirection so that we don't need to refer to global settings
* in headers.
*/
static std::filesystem::path getDefaultLogDir();
static Path getDefaultLogDir();
public:
Setting<std::filesystem::path> stateDir{
PathSetting stateDir{
this,
rootDir.get() ? *rootDir.get() / "nix" / "var" / "nix" : getDefaultStateDir(),
rootDir.get() ? *rootDir.get() + "/nix/var/nix" : getDefaultStateDir(),
"state",
"Directory where Nix stores state.",
};
"Directory where Nix stores state."};
Setting<std::filesystem::path> logDir{
PathSetting logDir{
this,
rootDir.get() ? *rootDir.get() / "nix" / "var" / "log" / "nix" : getDefaultLogDir(),
rootDir.get() ? *rootDir.get() + "/nix/var/log/nix" : getDefaultLogDir(),
"log",
"directory where Nix stores log files.",
};
"directory where Nix stores log files."};
Setting<std::filesystem::path> realStoreDir{
this,
rootDir.get() ? *rootDir.get() / "nix" / "store" : std::filesystem::path{storeDir},
"real",
"Physical path of the Nix store.",
};
PathSetting realStoreDir{
this, rootDir.get() ? *rootDir.get() + "/nix/store" : storeDir, "real", "Physical path of the Nix store."};
};
struct alignas(8) /* Work around ASAN failures on i686-linux. */
@@ -84,7 +77,7 @@ struct alignas(8) /* Work around ASAN failures on i686-linux. */
inline static std::string operationName = "Local Filesystem Store";
const static std::filesystem::path drvsLogDir;
const static std::string drvsLogDir;
LocalFSStore(const Config & params);
@@ -100,21 +93,27 @@ struct alignas(8) /* Work around ASAN failures on i686-linux. */
* @param gcRoot The location of the symlink.
*
* @param storePath The store object being rooted. The symlink will
* point to `toRealPath(storePath)`.
* point to `toRealPath(store.printStorePath(storePath))`.
*
* How the permanent GC root corresponding to this symlink is
* managed is implementation-specific.
*/
virtual std::filesystem::path addPermRoot(const StorePath & storePath, const std::filesystem::path & gcRoot) = 0;
virtual Path addPermRoot(const StorePath & storePath, const Path & gcRoot) = 0;
virtual std::filesystem::path getRealStoreDir()
virtual Path getRealStoreDir()
{
return config.realStoreDir;
}
std::filesystem::path toRealPath(const StorePath & storePath)
Path toRealPath(const StorePath & storePath)
{
return getRealStoreDir() / storePath.to_string();
return toRealPath(printStorePath(storePath));
}
Path toRealPath(const Path & storePath)
{
assert(isInStore(storePath));
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
}
std::optional<std::string> getBuildLogExact(const StorePath & path) override;
@@ -31,7 +31,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig
Must be used as OverlayFS lower layer for this store's store dir.
)"};
const Setting<std::filesystem::path> upperLayer{
PathSetting upperLayer{
(StoreConfig *) this,
"",
"upper-layer",
@@ -53,7 +53,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig
default, but can be disabled if needed.
)"};
const Setting<std::filesystem::path> remountHook{
PathSetting remountHook{
(StoreConfig *) this,
"",
"remount-hook",
@@ -99,7 +99,7 @@ protected:
* at that file path. It might be stored in the lower layer instead,
* or it might not be part of this store at all.
*/
std::filesystem::path toUpperPath(const StorePath & path) const;
Path toUpperPath(const StorePath & path) const;
friend struct LocalOverlayStore;
};
@@ -184,7 +184,7 @@ private:
* Check which layers the store object exists in to try to avoid
* needing to remount.
*/
void deleteStorePath(const std::filesystem::path & path, uint64_t & bytesFreed, bool isKnownPath) override;
void deleteStorePath(const Path & path, uint64_t & bytesFreed, bool isKnownPath) override;
/**
* Deduplicate by removing store objects from the upper layer that
+14 -18
View File
@@ -228,12 +228,12 @@ private:
public:
const std::filesystem::path dbDir;
const std::filesystem::path linksDir;
const std::filesystem::path reservedPath;
const std::filesystem::path schemaPath;
const std::filesystem::path tempRootsDir;
const std::filesystem::path fnTempRoots;
const Path dbDir;
const Path linksDir;
const Path reservedPath;
const Path schemaPath;
const Path tempRootsDir;
const Path fnTempRoots;
private:
@@ -244,7 +244,7 @@ public:
/**
* Hack for build-remote.cc.
*/
PathSetNG locksHeld;
PathSet locksHeld;
/**
* Initialise the local store, upgrading the schema if
@@ -319,7 +319,7 @@ public:
* The weak reference merely is a symlink to `path' from
* /nix/var/nix/gcroots/auto/<hash of `path'>.
*/
void addIndirectRoot(const std::filesystem::path & path) override;
void addIndirectRoot(const Path & path) override;
private:
@@ -353,7 +353,7 @@ public:
* @param isKnownPath true if this is a known store path, false if it's
* garbage/unknown content found in the store directory
*/
virtual void deleteStorePath(const std::filesystem::path & path, uint64_t & bytesFreed, bool isKnownPath);
virtual void deleteStorePath(const Path & path, uint64_t & bytesFreed, bool isKnownPath);
/**
* Optimise the disk space usage of the Nix store by hard-linking
@@ -367,7 +367,7 @@ public:
* Optimise a single store path. Optionally, test the encountered
* symlinks for corruption.
*/
void optimisePath(const std::filesystem::path & path, RepairFlag repair);
void optimisePath(const Path & path, RepairFlag repair);
bool verifyStore(bool checkContents, RepairFlag repair) override;
@@ -481,7 +481,7 @@ private:
PathSet queryValidPathsOld();
ValidPathInfo queryPathInfoOld(const Path & path);
void findRoots(const std::filesystem::path & path, std::filesystem::file_type type, Roots & roots);
void findRoots(const Path & path, std::filesystem::file_type type, Roots & roots);
void findRootsNoTemp(Roots & roots, bool censor);
@@ -492,13 +492,9 @@ private:
typedef boost::unordered_flat_set<ino_t> InodeHash;
InodeHash loadInodeHash();
Strings readDirectoryIgnoringInodes(const std::filesystem::path & path, const InodeHash & inodeHash);
void optimisePath_(
Activity * act,
OptimiseStats & stats,
const std::filesystem::path & path,
InodeHash & inodeHash,
RepairFlag repair);
Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash);
void
optimisePath_(Activity * act, OptimiseStats & stats, const Path & path, InodeHash & inodeHash, RepairFlag repair);
// Internal versions that are not wrapped in retry_sqlite.
bool isValidPath_(State & state, const StorePath & path);
+1 -1
View File
@@ -17,7 +17,7 @@ struct Machine
const StoreReference storeUri;
const StringSet systemTypes;
const std::filesystem::path sshKey;
const std::string sshKey;
const unsigned int maxJobs;
const float speedFactor;
const StringSet supportedFeatures;
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more