Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ded675e56 |
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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; }
|
||||
```
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
runCommand,
|
||||
stdenv,
|
||||
system,
|
||||
buildPackages,
|
||||
cacert,
|
||||
nix,
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
let
|
||||
|
||||
inherit (stdenv.hostPlatform) system;
|
||||
|
||||
installerClosureInfo = buildPackages.closureInfo {
|
||||
rootPaths = [
|
||||
nix
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
: state(state)
|
||||
, sampleInterval(period)
|
||||
, profileFd([&]() {
|
||||
auto fd = openNewFileForWrite(
|
||||
AutoCloseFD fd = openNewFileForWrite(
|
||||
profileFile,
|
||||
0660,
|
||||
{
|
||||
|
||||
@@ -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
@@ -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];
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,9 +33,6 @@ using gc_allocator = std::allocator<T>;
|
||||
struct gc
|
||||
{};
|
||||
|
||||
struct gc_cleanup
|
||||
{};
|
||||
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 &) {
|
||||
|
||||
@@ -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?
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
File renamed without changes.
@@ -1 +0,0 @@
|
||||
C:\foo\bar\baz?trusted=true
|
||||
@@ -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)));
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)}));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in new issue
Block a user