Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9697754418 | ||
|
|
f38ae92a38 | ||
|
|
0f518f44e2 | ||
|
|
afb839a0c9 | ||
|
|
37c4b10c44 | ||
|
|
b3599166ad | ||
|
|
47a237f7ec | ||
|
|
86a1121d16 | ||
|
|
c39150e6bb | ||
|
|
4c072c7c5f | ||
|
|
9cf92c012d | ||
|
|
d826427f02 | ||
|
|
314f044c2b | ||
|
|
1f8b85786e | ||
|
|
2890840b96 | ||
|
|
61e21b2557 | ||
|
|
706cee5c49 | ||
|
|
32d6e58069 | ||
|
|
886a418d23 | ||
|
|
11f35afa6f | ||
|
|
3392020710 | ||
|
|
e257ff10fd | ||
|
|
3c52344300 | ||
|
|
7abbce500b | ||
|
|
0d85875c3a | ||
|
|
7d361f1a82 | ||
|
|
af066af7f3 | ||
|
|
78513b1fc8 | ||
|
|
8a8715af89 | ||
|
|
18ed6c3bdf | ||
|
|
38571c50e6 | ||
|
|
84727bebb4 | ||
|
|
9f242fae76 | ||
|
|
caded0d55e | ||
|
|
c79b5dca2a | ||
|
|
cc55da201b | ||
|
|
93cc063344 | ||
|
|
c26599b143 | ||
|
|
0278c03de5 | ||
|
|
2a8f579c53 | ||
|
|
ab5ff86917 | ||
|
|
06952cf7c4 | ||
|
|
da2f165128 | ||
|
|
1b8662b85c | ||
|
|
be2b87ed4d | ||
|
|
b06a392114 | ||
|
|
dd05106d1c | ||
|
|
df2723b972 | ||
|
|
d9367da027 | ||
|
|
f9701da8f6 | ||
|
|
50c401b4c1 | ||
|
|
36a8d151e1 | ||
|
|
529a01ade2 | ||
|
|
b072c069b7 | ||
|
|
4ad3446311 | ||
|
|
875b76d0c7 | ||
|
|
15380b4c6e | ||
|
|
1758a6ef25 | ||
|
|
95a87f2c2a | ||
|
|
45f6e3521a | ||
|
|
af515baf6e | ||
|
|
8be7030299 | ||
|
|
6432bf9197 | ||
|
|
0a4737f519 | ||
|
|
06764118ea | ||
|
|
b667b4cded | ||
|
|
71e0114708 | ||
|
|
2a84123631 | ||
|
|
bf19eebb9b | ||
|
|
1cf0fa0633 | ||
|
|
03f852b2c6 | ||
|
|
3e43f4aeff | ||
|
|
0c19e6b56c | ||
|
|
7c882a5075 | ||
|
|
34fb7a7e9d | ||
|
|
a9b813cc3b | ||
|
|
f2e11ddce1 | ||
|
|
030c8aa833 | ||
|
|
992d99592f | ||
|
|
6b11c2cd70 | ||
|
|
73cdaf44cf | ||
|
|
4dabde0485 | ||
|
|
1958152d14 | ||
|
|
6ef9d8efba |
@@ -0,0 +1,51 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: DontAlign
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BitFieldColonSpacing: None
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: MultiLine
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: true
|
||||
BreakAfterAttributes: Always
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ColumnLimit: 100
|
||||
EmptyLineAfterAccessModifier: Leave
|
||||
EmptyLineBeforeAccessModifier: Leave
|
||||
FixNamespaceComments: false
|
||||
IndentWidth: 4
|
||||
InsertBraces: true
|
||||
InsertTrailingCommas: Wrapped
|
||||
LambdaBodyIndentation: Signature
|
||||
PackConstructorInitializers: CurrentLine
|
||||
PointerAlignment: Middle
|
||||
SortIncludes: Never
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
|
||||
@@ -7,30 +7,26 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
## Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
If you have a problem with a specific package or NixOS,
|
||||
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
|
||||
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
|
||||
|
||||
**Steps To Reproduce**
|
||||
## Steps To Reproduce
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
## Expected behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**`nix-env --version` output**
|
||||
## `nix-env --version` output
|
||||
|
||||
**Additional context**
|
||||
## Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
**Priorities**
|
||||
|
||||
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||
|
||||
@@ -7,18 +7,18 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
## Is your feature request related to a problem? Please describe.
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
## Describe the solution you'd like
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
## Describe alternatives you've considered
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
## Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
**Priorities**
|
||||
|
||||
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||
|
||||
@@ -30,7 +30,3 @@ assignees: ''
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Priorities
|
||||
|
||||
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||
|
||||
@@ -25,7 +25,3 @@ assignees: ''
|
||||
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
|
||||
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
|
||||
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
|
||||
|
||||
## Priorities
|
||||
|
||||
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||
|
||||
@@ -9,7 +9,3 @@
|
||||
<!-- Invasive change: Discuss alternative designs or approaches you considered. -->
|
||||
|
||||
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
||||
|
||||
# Priorities
|
||||
|
||||
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||
|
||||
@@ -41,6 +41,7 @@ makefiles += \
|
||||
tests/functional/ca/local.mk \
|
||||
tests/functional/dyn-drv/local.mk \
|
||||
tests/functional/test-libstoreconsumer/local.mk \
|
||||
tests/functional/repl_characterization/local.mk \
|
||||
tests/functional/plugins/local.mk
|
||||
else
|
||||
makefiles += \
|
||||
@@ -60,7 +61,7 @@ endif
|
||||
OPTIMIZE = 1
|
||||
|
||||
ifeq ($(OPTIMIZE), 1)
|
||||
GLOBAL_CXXFLAGS += -O3 $(CXXLTO)
|
||||
GLOBAL_CXXFLAGS += -O2 $(CXXLTO)
|
||||
GLOBAL_LDFLAGS += $(CXXLTO)
|
||||
else
|
||||
GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
synopsis: Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set
|
||||
prs: 9914
|
||||
---
|
||||
|
||||
If the `debugger-on-trace` option is set and `--debugger` is given,
|
||||
`builtins.trace` calls will behave similarly to `builtins.break` and will enter
|
||||
the debug REPL. This is useful for determining where warnings are being emitted
|
||||
from.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
synopsis: Duplicate attribute reports are more accurate
|
||||
# prs: cl 557
|
||||
---
|
||||
|
||||
Duplicate attribute errors are now more accurate, showing the path at which an error was detected rather than the full, possibly longer, path that caused the error.
|
||||
Error reports are now
|
||||
```ShellSession
|
||||
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
||||
error: attribute 'a.b' already defined at «string»:1:3
|
||||
at «string»:1:12:
|
||||
1| { a.b = 1; a.b.c.d = 1;
|
||||
| ^
|
||||
```
|
||||
instead of
|
||||
```ShellSession
|
||||
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
||||
error: attribute 'a.b.c.d' already defined at «string»:1:3
|
||||
at «string»:1:12:
|
||||
1| { a.b = 1; a.b.c.d = 1;
|
||||
| ^
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
synopsis: consistent order of lambda formals in printed expressions
|
||||
prs: 9874
|
||||
---
|
||||
|
||||
Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order.
|
||||
This makes printed formals independent of the context they appear in.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
synopsis: fix duplicate attribute error positions for `inherit`
|
||||
prs: 9874
|
||||
---
|
||||
|
||||
When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
synopsis: "`inherit (x) ...` evaluates `x` only once"
|
||||
prs: 9847
|
||||
---
|
||||
|
||||
`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
|
||||
This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
|
||||
@@ -48,50 +48,6 @@
|
||||
})
|
||||
stdenvs);
|
||||
|
||||
baseFiles =
|
||||
# .gitignore has already been processed, so any changes in it are irrelevant
|
||||
# at this point. It is not represented verbatim for test purposes because
|
||||
# that would interfere with repo semantics.
|
||||
fileset.fileFilter (f: f.name != ".gitignore") ./.;
|
||||
|
||||
configureFiles = fileset.unions [
|
||||
./.version
|
||||
./configure.ac
|
||||
./m4
|
||||
# TODO: do we really need README.md? It doesn't seem used in the build.
|
||||
./README.md
|
||||
];
|
||||
|
||||
topLevelBuildFiles = fileset.unions [
|
||||
./local.mk
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./mk
|
||||
];
|
||||
|
||||
functionalTestFiles = fileset.unions [
|
||||
./tests/functional
|
||||
./tests/unit
|
||||
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
|
||||
];
|
||||
|
||||
nixSrc = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersection baseFiles (fileset.unions [
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./doc
|
||||
./misc
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./unit-test-data
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
functionalTestFiles
|
||||
]);
|
||||
};
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
nixpkgsFor = forAllSystems
|
||||
(system: let
|
||||
@@ -118,205 +74,45 @@
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
commonDeps =
|
||||
{ pkgs
|
||||
, isStatic ? pkgs.stdenv.hostPlatform.isStatic
|
||||
}:
|
||||
with pkgs; rec {
|
||||
# Use "busybox-sandbox-shell" if present,
|
||||
# if not (legacy) fallback and hope it's sufficient.
|
||||
sh = pkgs.busybox-sandbox-shell or (busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
testNixVersions = pkgs: client: daemon: let
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
pname =
|
||||
"nix-tests"
|
||||
+ lib.optionalString
|
||||
(lib.versionAtLeast daemon.version "2.4pre20211005" &&
|
||||
lib.versionAtLeast client.version "2.4pre20211005")
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
'';
|
||||
});
|
||||
|
||||
configureFlags =
|
||||
lib.optionals stdenv.isLinux [
|
||||
"--with-boost=${boost}/lib"
|
||||
"--with-sandbox-shell=${sh}/bin/busybox"
|
||||
]
|
||||
++ lib.optionals (stdenv.isLinux && !(isStatic && stdenv.system == "aarch64-linux")) [
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
];
|
||||
|
||||
testConfigureFlags = [
|
||||
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
|
||||
];
|
||||
|
||||
internalApiDocsConfigureFlags = [
|
||||
"--enable-internal-api-docs"
|
||||
];
|
||||
|
||||
changelog-d = pkgs.buildPackages.callPackage ./misc/changelog-d.nix { };
|
||||
|
||||
nativeBuildDeps =
|
||||
[
|
||||
buildPackages.bison
|
||||
buildPackages.flex
|
||||
(lib.getBin buildPackages.lowdown)
|
||||
buildPackages.mdbook
|
||||
buildPackages.mdbook-linkcheck
|
||||
buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
buildPackages.pkg-config
|
||||
|
||||
# Tests
|
||||
buildPackages.git
|
||||
buildPackages.mercurial # FIXME: remove? only needed for tests
|
||||
buildPackages.jq # Also for custom mdBook preprocessor.
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)]
|
||||
# Official releases don't have rl-next, so we don't need to compile a changelog
|
||||
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
|
||||
;
|
||||
|
||||
buildDeps =
|
||||
[ curl
|
||||
bzip2 xz brotli editline
|
||||
openssl sqlite
|
||||
libarchive
|
||||
boost
|
||||
lowdown
|
||||
libsodium
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||
|
||||
checkDeps = [
|
||||
gtest
|
||||
rapidcheck
|
||||
];
|
||||
|
||||
internalApiDocsDeps = [
|
||||
buildPackages.doxygen
|
||||
];
|
||||
|
||||
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
|
||||
(aws-sdk-cpp.override {
|
||||
apis = ["s3" "transfer"];
|
||||
customMemoryManagement = false;
|
||||
});
|
||||
|
||||
propagatedDeps =
|
||||
[ ((boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs(o: {
|
||||
patches = (o.patches or []) ++ [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
|
||||
# https://github.com/ivmai/bdwgc/pull/586
|
||||
./boehmgc-traceable_allocator-public.diff
|
||||
];
|
||||
})
|
||||
)
|
||||
nlohmann_json
|
||||
];
|
||||
};
|
||||
|
||||
installScriptFor = systems:
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
runCommand "installer-script"
|
||||
{ buildInputs = [ nix ];
|
||||
}
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
|
||||
# Converts /nix/store/50p3qk8k...-nix-2.4pre20201102_550e11f/bin/nix to 50p3qk8k.../bin/nix.
|
||||
tarballPath() {
|
||||
# Remove the store prefix
|
||||
local path=''${1#${builtins.storeDir}/}
|
||||
# Get the path relative to the derivation root
|
||||
local rest=''${path#*/}
|
||||
# Get the derivation hash
|
||||
local drvHash=''${path%%-*}
|
||||
echo "$drvHash/$rest"
|
||||
}
|
||||
|
||||
substitute ${./scripts/install.in} $out/install \
|
||||
${pkgs.lib.concatMapStrings
|
||||
(system: let
|
||||
tarball = if builtins.elem system crossSystems then self.hydraJobs.binaryTarballCross.x86_64-linux.${system} else self.hydraJobs.binaryTarball.${system};
|
||||
in '' \
|
||||
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${tarball}/*.tar.xz) \
|
||||
--replace '@tarballPath_${system}@' $(tarballPath ${tarball}/*.tar.xz) \
|
||||
''
|
||||
)
|
||||
systems
|
||||
} --replace '@nixVersion@' ${version}
|
||||
|
||||
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
testNixVersions = pkgs: client: daemon: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation {
|
||||
inherit fileset;
|
||||
};
|
||||
in nix.overrideAttrs (prevAttrs: {
|
||||
NIX_DAEMON_PACKAGE = daemon;
|
||||
NIX_CLIENT_PACKAGE = client;
|
||||
name =
|
||||
"nix-tests"
|
||||
+ optionalString
|
||||
(versionAtLeast daemon.version "2.4pre20211005" &&
|
||||
versionAtLeast client.version "2.4pre20211005")
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
inherit version;
|
||||
|
||||
src = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersection baseFiles (fileset.unions [
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
functionalTestFiles
|
||||
]);
|
||||
};
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ awsDeps ++ checkDeps;
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
configureFlags =
|
||||
testConfigureFlags # otherwise configure fails
|
||||
++ [ "--disable-build" ];
|
||||
dontBuild = true;
|
||||
doInstallCheck = true;
|
||||
|
||||
configureFlags = prevAttrs.configureFlags ++ [
|
||||
# We don't need the actual build here.
|
||||
"--disable-build"
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
'';
|
||||
|
||||
installCheckPhase = (optionalString pkgs.stdenv.hostPlatform.isDarwin ''
|
||||
installCheckPhase = lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'') + ''
|
||||
'' + ''
|
||||
mkdir -p src/nix-channel
|
||||
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
|
||||
'';
|
||||
};
|
||||
});
|
||||
|
||||
binaryTarball = nix: pkgs:
|
||||
let
|
||||
inherit (pkgs) buildPackages;
|
||||
inherit (pkgs) cacert;
|
||||
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
|
||||
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix ]; };
|
||||
in
|
||||
|
||||
buildPackages.runCommand "nix-binary-tarball-${version}"
|
||||
@@ -325,45 +121,7 @@
|
||||
}
|
||||
''
|
||||
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
|
||||
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
|
||||
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
if type -p shellcheck; then
|
||||
# SC1090: Don't worry about not being able to find
|
||||
# $nix/etc/profile.d/nix.sh
|
||||
shellcheck --exclude SC1090 $TMPDIR/install
|
||||
shellcheck $TMPDIR/create-darwin-volume.sh
|
||||
shellcheck $TMPDIR/install-darwin-multi-user.sh
|
||||
shellcheck $TMPDIR/install-systemd-multi-user.sh
|
||||
|
||||
# SC1091: Don't panic about not being able to source
|
||||
# /etc/profile
|
||||
# SC2002: Ignore "useless cat" "error", when loading
|
||||
# .reginfo, as the cat is a much cleaner
|
||||
# implementation, even though it is "useless"
|
||||
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
|
||||
# root's home directory
|
||||
shellcheck --external-sources \
|
||||
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
|
||||
fi
|
||||
|
||||
chmod +x $TMPDIR/install
|
||||
chmod +x $TMPDIR/create-darwin-volume.sh
|
||||
chmod +x $TMPDIR/install-darwin-multi-user.sh
|
||||
chmod +x $TMPDIR/install-systemd-multi-user.sh
|
||||
chmod +x $TMPDIR/install-multi-user
|
||||
dir=nix-${version}-${pkgs.system}
|
||||
fn=$out/$dir.tar.xz
|
||||
mkdir -p $out/nix-support
|
||||
@@ -373,123 +131,67 @@
|
||||
--mtime='1970-01-01' \
|
||||
--absolute-names \
|
||||
--hard-dereference \
|
||||
--transform "s,$TMPDIR/install,$dir/install," \
|
||||
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
|
||||
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
||||
--transform "s,$NIX_STORE,$dir/store,S" \
|
||||
$TMPDIR/install \
|
||||
$TMPDIR/create-darwin-volume.sh \
|
||||
$TMPDIR/install-darwin-multi-user.sh \
|
||||
$TMPDIR/install-systemd-multi-user.sh \
|
||||
$TMPDIR/install-multi-user \
|
||||
$TMPDIR/reginfo \
|
||||
$(cat ${installerClosureInfo}/store-paths)
|
||||
'';
|
||||
|
||||
overlayFor = getStdenv: final: prev:
|
||||
let currentStdenv = getStdenv final; in
|
||||
{
|
||||
nixStable = prev.nix;
|
||||
|
||||
nix =
|
||||
with final;
|
||||
with commonDeps {
|
||||
let
|
||||
currentStdenv = getStdenv final;
|
||||
comDeps = with final; commonDeps {
|
||||
inherit pkgs;
|
||||
inherit (currentStdenv.hostPlatform) isStatic;
|
||||
};
|
||||
let
|
||||
canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
|
||||
in currentStdenv.mkDerivation (finalAttrs: {
|
||||
name = "nix-${version}";
|
||||
inherit version;
|
||||
in {
|
||||
nixStable = prev.nix;
|
||||
|
||||
src = nixSrc;
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
changelog-d = final.buildPackages.callPackage ./misc/changelog-d.nix { };
|
||||
boehmgc-nix = (final.boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs (o: {
|
||||
patches = (o.patches or [ ]) ++ [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps
|
||||
# There have been issues building these dependencies
|
||||
++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps
|
||||
++ lib.optionals finalAttrs.doCheck checkDeps;
|
||||
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
disallowedReferences = [ boost ];
|
||||
|
||||
preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
|
||||
''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
# https://github.com/NixOS/nixpkgs/issues/45462
|
||||
mkdir -p $out/lib
|
||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||
rm -f $out/lib/*.a
|
||||
${lib.optionalString currentStdenv.hostPlatform.isLinux ''
|
||||
chmod u+w $out/lib/*.so.*
|
||||
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
''}
|
||||
${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
|
||||
for LIB in $out/lib/*.dylib; do
|
||||
chmod u+w $LIB
|
||||
install_name_tool -id $LIB $LIB
|
||||
install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
|
||||
done
|
||||
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
|
||||
''}
|
||||
'';
|
||||
|
||||
configureFlags = configureFlags ++
|
||||
[ "--sysconfdir=/etc" ] ++
|
||||
lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
|
||||
[ (lib.enableFeature finalAttrs.doCheck "tests") ] ++
|
||||
lib.optionals finalAttrs.doCheck testConfigureFlags ++
|
||||
lib.optional (!canRunInstalled) "--disable-doc-gen";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
|
||||
|
||||
doCheck = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $doc/nix-support
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
${lib.optionalString currentStdenv.hostPlatform.isStatic ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||
''}
|
||||
${lib.optionalString currentStdenv.isDarwin ''
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
''}
|
||||
'';
|
||||
|
||||
doInstallCheck = finalAttrs.doCheck;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'';
|
||||
|
||||
separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
passthru.perl-bindings = final.callPackage ./perl {
|
||||
inherit fileset;
|
||||
stdenv = currentStdenv;
|
||||
};
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
# https://github.com/ivmai/bdwgc/pull/586
|
||||
./boehmgc-traceable_allocator-public.diff
|
||||
];
|
||||
});
|
||||
|
||||
default-busybox-sandbox-shell = final.busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
'';
|
||||
};
|
||||
|
||||
nix = final.callPackage ./package.nix {
|
||||
inherit versionSuffix fileset;
|
||||
stdenv = currentStdenv;
|
||||
boehmgc = final.boehmgc-nix;
|
||||
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
@@ -502,43 +204,41 @@
|
||||
# Binary package for various platforms.
|
||||
build = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
|
||||
mesonBuild = forAllSystems (system: self.packages.${system}.nix.override {
|
||||
buildWithMeson = true;
|
||||
});
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
|
||||
|
||||
# Binary tarball for various platforms, containing a Nix store
|
||||
# with the closure of 'nix' package, and the second half of
|
||||
# the installation script.
|
||||
# with the closure of 'nix' package.
|
||||
binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native);
|
||||
|
||||
# docker image with Nix inside
|
||||
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||
|
||||
# API docs for Nix's unstable internal C++ interfaces.
|
||||
internal-api-docs =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
with commonDeps { inherit pkgs; };
|
||||
internal-api-docs = let
|
||||
nixpkgs = nixpkgsFor.x86_64-linux.native;
|
||||
inherit (nixpkgs) pkgs;
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "nix-internal-api-docs";
|
||||
inherit version;
|
||||
|
||||
src = nixSrc;
|
||||
|
||||
configureFlags = testConfigureFlags ++ internalApiDocsConfigureFlags;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ propagatedDeps
|
||||
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
|
||||
|
||||
dontBuild = true;
|
||||
|
||||
installTargets = [ "internal-api-html" ];
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit versionSuffix fileset officialRelease buildUnreleasedNotes;
|
||||
inherit (pkgs) changelog-d;
|
||||
internalApiDocs = true;
|
||||
boehmgc = pkgs.boehmgc-nix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
|
||||
};
|
||||
in
|
||||
nix.overrideAttrs (prev: {
|
||||
# This Hydra job is just for the internal API docs.
|
||||
# We don't need the build artifacts here.
|
||||
dontBuild = true;
|
||||
doCheck = false;
|
||||
doInstallCheck = false;
|
||||
});
|
||||
|
||||
# System tests.
|
||||
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
|
||||
@@ -552,7 +252,7 @@
|
||||
type -p nix-env
|
||||
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
|
||||
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
|
||||
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
|
||||
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
|
||||
mkdir $out
|
||||
'';
|
||||
|
||||
@@ -564,31 +264,18 @@
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
installTests = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}.native; in
|
||||
pkgs.runCommand "install-tests" {
|
||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||
againstCurrentUnstable =
|
||||
# FIXME: temporarily disable this on macOS because of #3605.
|
||||
if system == "x86_64-linux"
|
||||
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
|
||||
else null;
|
||||
# Disabled because the latest stable version doesn't handle
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||
} "touch $out");
|
||||
};
|
||||
|
||||
checks = forAllSystems (system: {
|
||||
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
|
||||
mesonBuild = self.hydraJobs.mesonBuild.${system};
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
installTests = self.hydraJobs.installTests.${system};
|
||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||
rl-next =
|
||||
let pkgs = nixpkgsFor.${system}.native;
|
||||
in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } ''
|
||||
LANG=C.UTF-8 ${(commonDeps { inherit pkgs; }).changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
|
||||
LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
|
||||
'';
|
||||
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||
@@ -629,36 +316,31 @@
|
||||
devShells = let
|
||||
makeShell = pkgs: stdenv:
|
||||
let
|
||||
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit stdenv versionSuffix fileset;
|
||||
boehmgc = pkgs.boehmgc-nix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
|
||||
forDevShell = true;
|
||||
};
|
||||
in
|
||||
with commonDeps { inherit pkgs; };
|
||||
stdenv.mkDerivation {
|
||||
name = "nix";
|
||||
nix.overrideAttrs (prev: {
|
||||
nativeBuildInputs = prev.nativeBuildInputs
|
||||
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
|
||||
++ lib.optional
|
||||
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
|
||||
pkgs.buildPackages.clang-tools
|
||||
++ [
|
||||
# FIXME(Qyriad): remove once the migration to Meson is complete.
|
||||
pkgs.buildPackages.meson
|
||||
pkgs.buildPackages.ninja
|
||||
];
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
src = null;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps
|
||||
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
|
||||
++ lib.optional
|
||||
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
|
||||
pkgs.buildPackages.clang-tools
|
||||
# We want changelog-d in the shell even if the current build doesn't need it
|
||||
++ lib.optional (officialRelease || ! buildUnreleasedNotes) changelog-d
|
||||
;
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
strictDeps = false;
|
||||
|
||||
buildInputs = buildDeps ++ propagatedDeps
|
||||
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
|
||||
|
||||
configureFlags = configureFlags
|
||||
++ testConfigureFlags ++ internalApiDocsConfigureFlags
|
||||
++ lib.optional (!canRunInstalled) "--disable-doc-gen";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
shellHook =
|
||||
''
|
||||
shellHook = ''
|
||||
PATH=$prefix/bin:$PATH
|
||||
unset PYTHONPATH
|
||||
export MANPATH=$out/share/man:$MANPATH
|
||||
@@ -666,7 +348,10 @@
|
||||
# Make bash completion work.
|
||||
XDG_DATA_DIRS+=:$out/share
|
||||
'';
|
||||
};
|
||||
} // lib.optionalAttrs (stdenv.isLinux && pkgs.glibcLocales != null) {
|
||||
# Required to make non-NixOS Linux not complain about missing locale files during configure in a dev shell
|
||||
LOCALE_ARCHIVE = "${lib.getLib pkgs.glibcLocales}/lib/locale/locale-archive";
|
||||
});
|
||||
in
|
||||
forAllSystems (system:
|
||||
let
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import requests
|
||||
import textwrap
|
||||
import dataclasses
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
|
||||
API_BASE = 'https://git.lix.systems/api/v1'
|
||||
API_KEY = os.environ['FORGEJO_API_KEY']
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
fmt = logging.Formatter('{asctime} {levelname} {name}: {message}',
|
||||
datefmt='%b %d %H:%M:%S',
|
||||
style='{')
|
||||
|
||||
if not any(isinstance(h, logging.StreamHandler) for h in log.handlers):
|
||||
hand = logging.StreamHandler()
|
||||
hand.setFormatter(fmt)
|
||||
log.addHandler(hand)
|
||||
|
||||
# These are erring in the direction of re-triage, rather than necessarily
|
||||
# mapping all metadata of the issue
|
||||
LABEL_MAPPING = {
|
||||
'lix-import': 153, # 'imported',
|
||||
'contributor-experience': 148, # 'devx',
|
||||
'bug': 150, # 'bug',
|
||||
'UX': 149, # 'ux',
|
||||
'error-messages': 149, # 'ux',
|
||||
'lix-stability': 146, # 'stability',
|
||||
'performance': 147, # 'performance',
|
||||
'tests': 121, # 'tests',
|
||||
}
|
||||
|
||||
def api(method, endpoint: str, resp_json=True, **kwargs):
|
||||
log.info('http %s %s', method, endpoint)
|
||||
if not endpoint.startswith('https'):
|
||||
endpoint = API_BASE + endpoint
|
||||
resp = requests.request(method,
|
||||
endpoint,
|
||||
headers={'Authorization': f'Bearer {API_KEY}'},
|
||||
**kwargs)
|
||||
resp.raise_for_status()
|
||||
if resp_json:
|
||||
return resp.json()
|
||||
else:
|
||||
return resp
|
||||
|
||||
def paginate(method: str, url: str):
|
||||
while True:
|
||||
resp = api(method, url, resp_json=False)
|
||||
yield from resp.json()
|
||||
next_one = resp.links.get('next')
|
||||
if not next_one:
|
||||
return
|
||||
url = next_one.get('url')
|
||||
if not url:
|
||||
return
|
||||
|
||||
class DataClassUnpack:
|
||||
"""Taken from: https://stackoverflow.com/a/72164665"""
|
||||
classFieldCache = {}
|
||||
|
||||
@classmethod
|
||||
def instantiate(cls, classToInstantiate, argDict):
|
||||
if classToInstantiate not in cls.classFieldCache:
|
||||
cls.classFieldCache[classToInstantiate] = {
|
||||
f.name
|
||||
for f in getattr(classToInstantiate, dataclasses._FIELDS).values() if f._field_type is not dataclasses._FIELD_CLASSVAR # type: ignore
|
||||
}
|
||||
|
||||
fieldSet = cls.classFieldCache[classToInstantiate]
|
||||
filteredArgDict = {k: v for k, v in argDict.items() if k in fieldSet}
|
||||
return classToInstantiate(**filteredArgDict)
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Label:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Issue:
|
||||
number: int
|
||||
url: str
|
||||
html_url: str
|
||||
title: str
|
||||
body: str
|
||||
labels: dataclasses.InitVar[list[dict]]
|
||||
labels_clean: list[Label] = dataclasses.field(init=False)
|
||||
|
||||
def __post_init__(self, labels):
|
||||
self.labels_clean = [DataClassUnpack.instantiate(Label, l) for l in labels]
|
||||
|
||||
def issues_to_import():
|
||||
yield from paginate('GET', '/repos/nixos/nix/issues?state=open&labels=lix-import')
|
||||
|
||||
def issues_already_imported():
|
||||
yield from paginate('GET', '/repos/lix-project/lix/issues?state=open&labels=imported')
|
||||
|
||||
|
||||
UPSTREAM_ISSUE_RE = re.compile(r'^Upstream-Issue: https://git\.lix\.systems/NixOS/nix/issues/(\d+)$', re.MULTILINE)
|
||||
|
||||
def make_already_imported():
|
||||
d = {}
|
||||
for issue in issues_already_imported():
|
||||
iss = DataClassUnpack.instantiate(Issue, issue)
|
||||
print(iss)
|
||||
match = UPSTREAM_ISSUE_RE.search(iss.body)
|
||||
if match:
|
||||
d[int(match.group(1))] = iss
|
||||
|
||||
return d
|
||||
|
||||
def new_issue(title, body, labels):
|
||||
api('POST', '/repos/lix-project/lix/issues', resp_json=True, json={
|
||||
'labels': labels,
|
||||
'body': body,
|
||||
'title': title,
|
||||
})
|
||||
|
||||
already_imported = make_already_imported()
|
||||
|
||||
def import_issue(iss: Issue):
|
||||
if iss.number in already_imported:
|
||||
log.info('Skipping already imported %d', iss.number)
|
||||
return
|
||||
new_body = textwrap.dedent('''
|
||||
Upstream-Issue: {iss}
|
||||
|
||||
{original_body}
|
||||
''').format(iss=iss.html_url, original_body=iss.body)
|
||||
|
||||
new_labels = [LABEL_MAPPING[l.name] for l in iss.labels_clean if l.name in LABEL_MAPPING]
|
||||
|
||||
new_title = '[Nix#{num}] {title}'.format(num=iss.number, title=iss.title)
|
||||
|
||||
log.info('%s', f'create issue with: {new_labels} {new_title} {new_body}')
|
||||
new_issue(new_title, new_body, new_labels)
|
||||
|
||||
def go():
|
||||
print('Have you turned off the forgejo mailer or limited the queue workers to 0 (assuming that works)? Enter "We have" if so:')
|
||||
answer = input('> ')
|
||||
if answer != 'We have':
|
||||
return
|
||||
|
||||
log.info('Importing issues!')
|
||||
for issue in issues_to_import():
|
||||
import_issue(DataClassUnpack.instantiate(Issue, issue))
|
||||
|
||||
if __name__ == '__main__':
|
||||
go()
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
#
|
||||
# OUTLINE:
|
||||
#
|
||||
# The top-level meson.build file (this file) handles general logic for build options,
|
||||
# generation of config.h (which is put in the build directory, not the source root
|
||||
# like the previous, autoconf-based build system did), the mechanism for header
|
||||
# generation, and the few global C++ compiler arguments that are added to all targets in Lix.
|
||||
#
|
||||
# src/meson.build coordinates each of Lix's subcomponents (the lib dirs in ./src),
|
||||
# which each have their own meson.build. Lix's components depend on each other,
|
||||
# so each of `src/lib{util,store,fetchers,expr,main,cmd}/meson.build` rely on variables
|
||||
# set in earlier `meson.build` files. Each of these also defines the install targets for
|
||||
# their headers.
|
||||
#
|
||||
# src/meson.build also collects the miscellaneous source files that are in further subdirectories
|
||||
# that become part of the final Nix command (things like `src/nix-build/*.cc`).
|
||||
#
|
||||
# Finally, src/nix/meson.build defines the Nix command itself, relying on all prior meson files.
|
||||
|
||||
project('lix', 'cpp',
|
||||
version : run_command('bash', '-c', 'echo -n $(cat ./.version)$VERSION_SUFFIX', check : true).stdout().strip(),
|
||||
default_options : [
|
||||
'cpp_std=c++20',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
],
|
||||
)
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
prefix = get_option('prefix')
|
||||
# For each of these paths, assume that it is relative to the prefix unless
|
||||
# it is already an absolute path (which is the default for store-dir, state-dir, and log-dir).
|
||||
path_opts = [
|
||||
# Meson built-ins.
|
||||
'datadir',
|
||||
'sysconfdir',
|
||||
'bindir',
|
||||
'mandir',
|
||||
'libdir',
|
||||
'includedir',
|
||||
# Homecooked Lix directories.
|
||||
'store-dir',
|
||||
'state-dir',
|
||||
'log-dir',
|
||||
]
|
||||
foreach optname : path_opts
|
||||
varname = optname.replace('-', '_')
|
||||
path = get_option(optname)
|
||||
if fs.is_absolute(path)
|
||||
set_variable(varname, path)
|
||||
else
|
||||
set_variable(varname, prefix / path)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
host_system = host_machine.cpu_family() + '-' + host_machine.system()
|
||||
message('canonical Nix system name:', host_system)
|
||||
|
||||
deps = [ ]
|
||||
configdata = { }
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
boehm = dependency('bdw-gc', required : get_option('gc'))
|
||||
if boehm.found()
|
||||
deps += boehm
|
||||
endif
|
||||
configdata += {
|
||||
'HAVE_BOEHMGC': boehm.found().to_int(),
|
||||
}
|
||||
|
||||
boost = dependency('boost', required : true, modules : ['context', 'coroutine', 'container'])
|
||||
deps += boost
|
||||
|
||||
cpuid = dependency('libcpuid', 'cpuid', required : get_option('cpuid'))
|
||||
configdata += {
|
||||
'HAVE_LIBCPUID': cpuid.found().to_int(),
|
||||
}
|
||||
deps += cpuid
|
||||
|
||||
seccomp = dependency('libseccomp', 'seccomp', required : get_option('seccomp-sandboxing'))
|
||||
configdata += {
|
||||
'HAVE_SECCOMP': seccomp.found().to_int(),
|
||||
}
|
||||
|
||||
libarchive = dependency('libarchive', required : true)
|
||||
deps += libarchive
|
||||
|
||||
brotli = [
|
||||
dependency('libbrotlicommon', required : true),
|
||||
dependency('libbrotlidec', required : true),
|
||||
dependency('libbrotlienc', required : true),
|
||||
]
|
||||
deps += brotli
|
||||
|
||||
openssl = dependency('libcrypto', 'openssl', required : true)
|
||||
deps += openssl
|
||||
|
||||
aws_sdk = dependency('aws-cpp-sdk-core', required : true)
|
||||
if aws_sdk.found()
|
||||
# The AWS pkg-config adds -std=c++11.
|
||||
aws_sdk = aws_sdk.partial_dependency(
|
||||
compile_args : false,
|
||||
includes : true,
|
||||
link_args : true,
|
||||
links : true,
|
||||
sources : true,
|
||||
)
|
||||
deps += aws_sdk
|
||||
s = aws_sdk.version().split('.')
|
||||
configdata += {
|
||||
'AWS_VERSION_MAJOR': s[0].to_int(),
|
||||
'AWS_VERSION_MINOR': s[1].to_int(),
|
||||
'AWS_VERSION_PATCH': s[2].to_int(),
|
||||
}
|
||||
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : true).partial_dependency(
|
||||
compile_args : false,
|
||||
includes : true,
|
||||
link_args : true,
|
||||
links : true,
|
||||
sources : true,
|
||||
)
|
||||
endif
|
||||
|
||||
aws_s3 = dependency('aws-cpp-sdk-s3', required : true)
|
||||
if aws_s3.found()
|
||||
# The AWS pkg-config adds -std=c++11.
|
||||
aws_s3 = aws_s3.partial_dependency(
|
||||
compile_args : false,
|
||||
includes : true,
|
||||
link_args : true,
|
||||
links : true,
|
||||
sources : true,
|
||||
)
|
||||
deps += aws_s3
|
||||
endif
|
||||
|
||||
configdata += {
|
||||
'ENABLE_S3': aws_s3.found().to_int(),
|
||||
}
|
||||
|
||||
sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19', required : true)
|
||||
deps += sqlite
|
||||
|
||||
sodium = dependency('libsodium', 'sodium', required : true)
|
||||
deps += sodium
|
||||
|
||||
curl = dependency('libcurl', 'curl', required : true)
|
||||
deps += curl
|
||||
|
||||
editline = dependency('libeditline', 'editline', version : '>=1.14', required : true)
|
||||
deps += editline
|
||||
|
||||
lowdown = dependency('lowdown', version : '>=0.9.0', required : true)
|
||||
deps += lowdown
|
||||
|
||||
rapidcheck = dependency('rapidcheck', required : false)
|
||||
deps += rapidcheck
|
||||
|
||||
gtest = dependency('gtest', required : false)
|
||||
deps += gtest
|
||||
|
||||
#
|
||||
# Build-time tools
|
||||
#
|
||||
bash = find_program('bash')
|
||||
|
||||
# Used to workaround https://github.com/mesonbuild/meson/issues/2320 in src/nix/meson.build.
|
||||
installcmd = find_program('install')
|
||||
|
||||
sandbox_shell = get_option('sandbox-shell')
|
||||
# Consider it required if we're on Linux and the user explicitly specified a non-default value.
|
||||
sandbox_shell_required = sandbox_shell != 'busybox' and host_machine.system() == 'linux'
|
||||
# NOTE(Qyriad): package.nix puts busybox in buildInputs for Linux.
|
||||
# Most builds should not require setting this.
|
||||
busybox = find_program(sandbox_shell, required : sandbox_shell_required, native : false)
|
||||
if not busybox.found() and host_machine.system() == 'linux' and sandbox_shell_required
|
||||
warning('busybox not found and other sandbox shell was specified')
|
||||
warning('a sandbox shell is recommended on Linux -- configure with -Dsandbox-shell=/path/to/shell to set')
|
||||
endif
|
||||
# FIXME(Qyriad): the autoconf system checks that busybox has the "standalone" feature, indicating
|
||||
# that busybox sh won't run busybox applets as builtins (which would break our sandbox).
|
||||
|
||||
lsof = find_program('lsof')
|
||||
bison = find_program('bison')
|
||||
flex = find_program('flex')
|
||||
|
||||
# This is how Nix does generated headers...
|
||||
# FIXME(Qyriad): do we really need to use the shell for this?
|
||||
gen_header = generator(
|
||||
bash,
|
||||
arguments : [
|
||||
'-c',
|
||||
'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'',
|
||||
],
|
||||
capture : true,
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
)
|
||||
|
||||
#
|
||||
# Configuration
|
||||
#
|
||||
|
||||
run_command('ln', '-s',
|
||||
meson.project_build_root() / '__nothing_link_target',
|
||||
meson.project_build_root() / '__nothing_symlink',
|
||||
check : true,
|
||||
)
|
||||
can_link_symlink = run_command('ln',
|
||||
meson.project_build_root() / '__nothing_symlink',
|
||||
meson.project_build_root() / '__nothing_hardlink',
|
||||
check : false,
|
||||
).returncode() == 0
|
||||
run_command('rm', '-f',
|
||||
meson.project_build_root() / '__nothing_symlink',
|
||||
meson.project_build_root() / '__nothing_hardlink',
|
||||
check : true,
|
||||
)
|
||||
summary('can hardlink to symlink', can_link_symlink, bool_yn : true)
|
||||
configdata += { 'CAN_LINK_SYMLINK': can_link_symlink.to_int() }
|
||||
|
||||
|
||||
# Check for each of these functions, and create a define like `#define HAVE_LCHOWN 1`.
|
||||
check_funcs = [
|
||||
'lchown',
|
||||
'lutimes',
|
||||
'pipe2',
|
||||
'posix_fallocate',
|
||||
'statvfs',
|
||||
'strsignal',
|
||||
'sysconf',
|
||||
]
|
||||
foreach funcspec : check_funcs
|
||||
define_name = 'HAVE_' + funcspec.underscorify().to_upper()
|
||||
define_value = cxx.has_function(funcspec).to_int()
|
||||
configdata += {
|
||||
define_name: define_value,
|
||||
}
|
||||
endforeach
|
||||
|
||||
# extern "C++" functions can't be checked with the normal mechanism (much less member functions),
|
||||
# so we have to do this one ourselves.
|
||||
has_pubsetbuf = cxx.compiles('''
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
static char buf[1024];
|
||||
decltype(cerr.rdbuf()->pubsetbuf(buf, sizeof(buf))) _;
|
||||
''')
|
||||
summary('have std::basic_streambuf::pubsetbuf', has_pubsetbuf, bool_yn : true)
|
||||
configdata += {
|
||||
'HAVE_PUBSETBUF': has_pubsetbuf.to_int(),
|
||||
}
|
||||
|
||||
config_h = configure_file(
|
||||
configuration : {
|
||||
'PACKAGE_NAME': '"' + meson.project_name() + '"',
|
||||
'PACKAGE_VERSION': '"' + meson.project_version() + '"',
|
||||
'PACKAGE_TARNAME': '"' + meson.project_name() + '"',
|
||||
'PACKAGE_STRING': '"' + meson.project_name() + ' ' + meson.project_version() + '"',
|
||||
'HAVE_STRUCT_DIRENT_D_TYPE': 1, # FIXME: actually check this for solaris
|
||||
'SYSTEM': '"' + host_system + '"',
|
||||
} + configdata,
|
||||
output : 'config.h',
|
||||
)
|
||||
|
||||
install_headers(config_h, subdir : 'nix')
|
||||
|
||||
add_project_arguments(
|
||||
# TODO(Qyriad): Yes this is how the autoconf+Make system did it.
|
||||
# It would be nice for our headers to be idempotent instead.
|
||||
'-include', 'config.h',
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wimplicit-fallthrough',
|
||||
'-Werror=switch',
|
||||
'-Werror=switch-enum',
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
add_project_link_arguments(
|
||||
'-pthread',
|
||||
# FIXME(Qyriad): autoconf did this only if not Darwin, Solaris, or FreeBSD
|
||||
# (...so only if Linux?)
|
||||
'-Wl,--no-copy-dt-needed-entries',
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
subdir('src')
|
||||
@@ -0,0 +1,32 @@
|
||||
# vim: filetype=meson
|
||||
option('gc', type : 'feature',
|
||||
description : 'enable garbage collection in the Nix expression evaluator (requires Boehm GC)',
|
||||
)
|
||||
# TODO(Qyriad): is this feature maintained?
|
||||
option('embedded-sandbox-shell', type : 'feature',
|
||||
description : 'include the sandbox shell in the Nix bindary',
|
||||
)
|
||||
|
||||
option('cpuid', type : 'feature',
|
||||
description : 'determine microarchitecture levels with libcpuid (only relevant on x86_64)',
|
||||
)
|
||||
|
||||
option('seccomp-sandboxing', type : 'feature',
|
||||
description : 'build support for seccomp sandboxing (recommended unless your arch doesn\'t support libseccomp, only relevant on Linux)',
|
||||
)
|
||||
|
||||
option('sandbox-shell', type : 'string', value : 'busybox',
|
||||
description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)',
|
||||
)
|
||||
|
||||
option('store-dir', type : 'string', value : '/nix/store',
|
||||
description : 'path of the Nix store',
|
||||
)
|
||||
|
||||
option('state-dir', type : 'string', value : '/nix/var/nix',
|
||||
description : 'path to store state in for Nix',
|
||||
)
|
||||
|
||||
option('log-dir', type : 'string', value : '/nix/var/log',
|
||||
description : 'path to store logs in for Nix',
|
||||
)
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Meson will call this with an absolute path to Bash.
|
||||
# The shebang is just for convenience.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "cleanup-install: removing Meson-placed C++ sources from dest includedir"
|
||||
|
||||
if [[ "${1/--help/}" != "$1" ]]; then
|
||||
echo "cleanup-install: this script should only be called from the Meson build system"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure the includedir was passed as the first argument
|
||||
# (set -u will make this fail otherwise).
|
||||
includedir="$1"
|
||||
# And then ensure that first argument is a directory that exists.
|
||||
if ! [[ -d "$1" ]]; then
|
||||
echo "cleanup-install: this script should only be called from the Meson build system"
|
||||
echo "argv[1] (${1@Q}) is not a directory"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# If DESTDIR environment variable is set, prepend it to the include dir.
|
||||
# Unfortunately, we cannot do this on the Meson side. We do have an environment variable
|
||||
# `MESON_INSTALL_DESTDIR_PREFIX`, but that will not refer to the include directory if
|
||||
# includedir has been set separately, which Lix's split-output derivation does.
|
||||
# We also cannot simply do an inline bash conditional like "${DESTDIR:=}" or similar,
|
||||
# because we need to specifically *join* DESTDIR and includedir with a slash, and *not*
|
||||
# have a slash if DESTDIR isn't set at all, since $includedir could be a relative directory.
|
||||
# Finally, DESTDIR is only available to us as an environment variable in these install scripts,
|
||||
# not in Meson logic.
|
||||
# Therefore, our best option is to have Meson pass this script the configured includedir,
|
||||
# and perform this dance with it and $DESTDIR.
|
||||
if [[ -n "${DESTDIR:-}" ]]; then
|
||||
includedir="$DESTDIR/$includedir"
|
||||
fi
|
||||
|
||||
# Intentionally not using -f.
|
||||
# If these files don't exist then our assumptions have been violated and we should fail.
|
||||
rm -v "$includedir/nix/parser-tab.cc" "$includedir/nix/lexer-tab.cc"
|
||||
@@ -1,5 +1,5 @@
|
||||
%.gen.hh: %
|
||||
@echo 'R"foo(' >> $@.tmp
|
||||
@echo 'R"__NIX_STR(' >> $@.tmp
|
||||
$(trace-gen) cat $< >> $@.tmp
|
||||
@echo ')foo"' >> $@.tmp
|
||||
@echo ')__NIX_STR"' >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
+4
-1
@@ -6,6 +6,9 @@ programs-list :=
|
||||
# - $(1)_NAME: the name of the program (e.g. ‘foo’); defaults to
|
||||
# $(1).
|
||||
#
|
||||
# - $(1)_ENV: environment variables to set when running the program
|
||||
# from the Makefile using the $(1)_RUN target.
|
||||
#
|
||||
# - $(1)_DIR: the directory where the (non-installed) program will be
|
||||
# placed.
|
||||
#
|
||||
@@ -87,6 +90,6 @@ define build-program
|
||||
# Phony target to run this program (typically as a dependency of 'check').
|
||||
.PHONY: $(1)_RUN
|
||||
$(1)_RUN: $$($(1)_PATH)
|
||||
$(trace-test) $$(UNIT_TEST_ENV) $$($(1)_PATH)
|
||||
$(trace-test) $$($(1)_ENV) $$($(1)_PATH)
|
||||
|
||||
endef
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
stdenv,
|
||||
autoconf-archive,
|
||||
autoreconfHook,
|
||||
aws-sdk-cpp,
|
||||
boehmgc,
|
||||
nlohmann_json,
|
||||
bison,
|
||||
changelog-d,
|
||||
boost,
|
||||
brotli,
|
||||
bzip2,
|
||||
curl,
|
||||
doxygen,
|
||||
editline,
|
||||
fileset,
|
||||
flex,
|
||||
git,
|
||||
gtest,
|
||||
jq,
|
||||
libarchive,
|
||||
libcpuid,
|
||||
libseccomp,
|
||||
libsodium,
|
||||
lsof,
|
||||
lowdown,
|
||||
mdbook,
|
||||
mdbook-linkcheck,
|
||||
mercurial,
|
||||
meson,
|
||||
ninja,
|
||||
openssl,
|
||||
pkg-config,
|
||||
rapidcheck,
|
||||
sqlite,
|
||||
util-linuxMinimal ? utillinuxMinimal,
|
||||
utillinuxMinimal ? null,
|
||||
xz,
|
||||
|
||||
busybox-sandbox-shell,
|
||||
|
||||
pname ? "nix",
|
||||
versionSuffix ? "",
|
||||
officialRelease ? true,
|
||||
# Set to true to build the release notes for the next release.
|
||||
buildUnreleasedNotes ? false,
|
||||
internalApiDocs ? false,
|
||||
# Avoid setting things that would interfere with a functioning devShell
|
||||
forDevShell ? false,
|
||||
|
||||
# FIXME(Qyriad): build Lix using Meson instead of autoconf and make.
|
||||
# This flag will be removed when the migration to Meson is complete.
|
||||
buildWithMeson ? false,
|
||||
|
||||
# Not a real argument, just the only way to approximate let-binding some
|
||||
# stuff for argument defaults.
|
||||
__forDefaults ? {
|
||||
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
|
||||
},
|
||||
}: let
|
||||
inherit (__forDefaults) canRunInstalled;
|
||||
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
|
||||
aws-sdk-cpp-nix = aws-sdk-cpp.override {
|
||||
apis = [ "s3" "transfer" ];
|
||||
customMemoryManagement = false;
|
||||
};
|
||||
|
||||
testConfigureFlags = [
|
||||
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
|
||||
];
|
||||
|
||||
# The internal API docs need these for the build, but if we're not building
|
||||
# Nix itself, then these don't need to be propagated.
|
||||
maybePropagatedInputs = [
|
||||
boehmgc
|
||||
nlohmann_json
|
||||
];
|
||||
|
||||
# .gitignore has already been processed, so any changes in it are irrelevant
|
||||
# at this point. It is not represented verbatim for test purposes because
|
||||
# that would interfere with repo semantics.
|
||||
baseFiles = fileset.fileFilter (f: f.name != ".gitignore") ./.;
|
||||
|
||||
configureFiles = fileset.unions [
|
||||
./.version
|
||||
./configure.ac
|
||||
./m4
|
||||
# TODO: do we really need README.md? It doesn't seem used in the build.
|
||||
./README.md
|
||||
];
|
||||
|
||||
topLevelBuildFiles = fileset.unions ([
|
||||
./local.mk
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./mk
|
||||
] ++ lib.optionals buildWithMeson [
|
||||
./meson.build
|
||||
./meson.options
|
||||
./meson/cleanup-install.bash
|
||||
]);
|
||||
|
||||
functionalTestFiles = fileset.unions [
|
||||
./tests/functional
|
||||
./tests/unit
|
||||
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
|
||||
];
|
||||
|
||||
in stdenv.mkDerivation (finalAttrs: {
|
||||
inherit pname version;
|
||||
|
||||
src = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersection baseFiles (fileset.unions ([
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
functionalTestFiles
|
||||
] ++ lib.optionals (!finalAttrs.dontBuild || internalApiDocs) [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./doc
|
||||
./misc
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
]));
|
||||
};
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
outputs = [ "out" ]
|
||||
++ lib.optionals (!finalAttrs.dontBuild) [ "dev" "doc" ];
|
||||
|
||||
dontBuild = false;
|
||||
|
||||
# FIXME(Qyriad): see if this is still needed once the migration to Meson is completed.
|
||||
mesonFlags = lib.optionals (buildWithMeson && stdenv.hostPlatform.isLinux) [
|
||||
"-Dsandbox-shell=${lib.getBin busybox-sandbox-shell}/bin/busybox"
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
bison
|
||||
flex
|
||||
] ++ [
|
||||
(lib.getBin lowdown)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
autoconf-archive
|
||||
] ++ lib.optional (!buildWithMeson) autoreconfHook ++ [
|
||||
pkg-config
|
||||
|
||||
# Tests
|
||||
git
|
||||
mercurial
|
||||
jq
|
||||
lsof
|
||||
] ++ lib.optional stdenv.hostPlatform.isLinux util-linuxMinimal
|
||||
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
|
||||
++ lib.optional internalApiDocs doxygen
|
||||
++ lib.optionals buildWithMeson [
|
||||
meson
|
||||
ninja
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
curl
|
||||
bzip2
|
||||
xz
|
||||
brotli
|
||||
editline
|
||||
openssl
|
||||
sqlite
|
||||
libarchive
|
||||
boost
|
||||
lowdown
|
||||
libsodium
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ libseccomp busybox-sandbox-shell ]
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
|
||||
# There have been issues building these dependencies
|
||||
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) aws-sdk-cpp-nix
|
||||
++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs
|
||||
;
|
||||
|
||||
checkInputs = [
|
||||
gtest
|
||||
rapidcheck
|
||||
];
|
||||
|
||||
propagatedBuildInputs = lib.optionals (!finalAttrs.dontBuild) maybePropagatedInputs;
|
||||
|
||||
disallowedReferences = [
|
||||
boost
|
||||
];
|
||||
|
||||
# Needed for Meson to find Boost.
|
||||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
env = lib.optionalAttrs (buildWithMeson || forDevShell) {
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
};
|
||||
|
||||
preConfigure = lib.optionalString (!finalAttrs.dontBuild && !stdenv.hostPlatform.isStatic) ''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
# https://github.com/NixOS/nixpkgs/issues/45462
|
||||
mkdir -p $out/lib
|
||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||
rm -f $out/lib/*.a
|
||||
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isLinux) ''
|
||||
chmod u+w $out/lib/*.so.*
|
||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isDarwin) ''
|
||||
for LIB in $out/lib/*.dylib; do
|
||||
chmod u+w $LIB
|
||||
install_name_tool -id $LIB $LIB
|
||||
install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
|
||||
done
|
||||
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
|
||||
'' + ''
|
||||
# Workaround https://github.com/NixOS/nixpkgs/issues/294890.
|
||||
if [[ -n "''${doCheck:-}" ]]; then
|
||||
appendToVar configureFlags "--enable-tests"
|
||||
else
|
||||
appendToVar configureFlags "--disable-tests"
|
||||
fi
|
||||
'';
|
||||
|
||||
configureFlags = lib.optionals stdenv.isLinux [
|
||||
"--with-boost=${boost}/lib"
|
||||
"--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
|
||||
] ++ lib.optionals (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) [
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
]
|
||||
++ lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell"
|
||||
++ lib.optionals (finalAttrs.doCheck || internalApiDocs) testConfigureFlags
|
||||
++ lib.optional (!canRunInstalled) "--disable-doc-gen"
|
||||
++ [ (lib.enableFeature internalApiDocs "internal-api-docs") ]
|
||||
++ lib.optional (!forDevShell) "--sysconfdir=/etc";
|
||||
|
||||
installTargets = lib.optional internalApiDocs "internal-api-html";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
|
||||
|
||||
doCheck = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
postInstall = lib.optionalString (!finalAttrs.dontBuild) ''
|
||||
mkdir -p $doc/nix-support
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
'' + lib.optionalString stdenv.hostPlatform.isStatic ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||
'' + lib.optionalString stdenv.isDarwin ''
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
'' + lib.optionalString internalApiDocs ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> "$out/nix-support/hydra-build-products"
|
||||
'';
|
||||
|
||||
doInstallCheck = finalAttrs.doCheck;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'';
|
||||
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic && !finalAttrs.dontBuild;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
|
||||
passthru.perl-bindings = pkgs.callPackage ./perl {
|
||||
inherit fileset stdenv;
|
||||
};
|
||||
})
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
((NEW_NIX_FIRST_BUILD_UID=301))
|
||||
|
||||
id_available(){
|
||||
dscl . list /Users UniqueID | grep -E '\b'$1'\b' >/dev/null
|
||||
}
|
||||
|
||||
change_nixbld_names_and_ids(){
|
||||
local name uid next_id
|
||||
((next_id=NEW_NIX_FIRST_BUILD_UID))
|
||||
echo "Attempting to migrate nixbld users."
|
||||
echo "Each user should change from nixbld# to _nixbld#"
|
||||
echo "and their IDs relocated to $next_id+"
|
||||
while read -r name uid; do
|
||||
echo " Checking $name (uid: $uid)"
|
||||
# iterate for a clean ID
|
||||
while id_available "$next_id"; do
|
||||
((next_id++))
|
||||
if ((next_id >= 400)); then
|
||||
echo "We've hit UID 400 without placing all of your users :("
|
||||
echo "You should use the commands in this script as a starting"
|
||||
echo "point to review your UID-space and manually move the"
|
||||
echo "remaining users (or delete them, if you don't need them)."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $name == _* ]]; then
|
||||
echo " It looks like $name has already been renamed--skipping."
|
||||
else
|
||||
# first 3 are cleanup, it's OK if they aren't here
|
||||
sudo dscl . delete /Users/$name dsAttrTypeNative:_writers_passwd &>/dev/null || true
|
||||
sudo dscl . change /Users/$name NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true
|
||||
# remove existing user from group
|
||||
sudo dseditgroup -o edit -t user -d $name nixbld || true
|
||||
sudo dscl . change /Users/$name UniqueID $uid $next_id
|
||||
sudo dscl . change /Users/$name RecordName $name _$name
|
||||
# add renamed user to group
|
||||
sudo dseditgroup -o edit -t user -a _$name nixbld
|
||||
echo " $name migrated to _$name (uid: $next_id)"
|
||||
fi
|
||||
done < <(dscl . list /Users UniqueID | grep nixbld | sort -n -k2)
|
||||
}
|
||||
|
||||
change_nixbld_names_and_ids
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
# set -x
|
||||
|
||||
|
||||
# mapfile BUILDS_FOR_LATEST_EVAL < <(
|
||||
# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||
# jq -r '.evals[0].builds[] | @sh')
|
||||
BUILDS_FOR_LATEST_EVAL=$(
|
||||
curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||
jq -r '.evals[0].builds[]')
|
||||
|
||||
someBuildFailed=0
|
||||
|
||||
for buildId in $BUILDS_FOR_LATEST_EVAL; do
|
||||
buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
|
||||
|
||||
finished=$(echo "$buildInfo" | jq -r '.finished')
|
||||
|
||||
if [[ $finished = 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
|
||||
|
||||
if [[ $buildStatus != 0 ]]; then
|
||||
someBuildFailed=1
|
||||
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
|
||||
fi
|
||||
done
|
||||
|
||||
exit "$someBuildFailed"
|
||||
@@ -1,875 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# I'm a little agnostic on the choices, but supporting a wide
|
||||
# slate of uses for now, including:
|
||||
# - import-only: `. create-darwin-volume.sh no-main[ ...]`
|
||||
# - legacy: `./create-darwin-volume.sh` or `. create-darwin-volume.sh`
|
||||
# (both will run main())
|
||||
# - external alt-routine: `./create-darwin-volume.sh no-main func[ ...]`
|
||||
if [ "${1-}" = "no-main" ]; then
|
||||
shift
|
||||
readonly _CREATE_VOLUME_NO_MAIN=1
|
||||
else
|
||||
readonly _CREATE_VOLUME_NO_MAIN=0
|
||||
# declare some things we expect to inherit from install-multi-user
|
||||
# I don't love this (because it's a bit of a kludge).
|
||||
#
|
||||
# CAUTION: (Dec 19 2020)
|
||||
# This is a stopgap. It doesn't cover the full slate of
|
||||
# identifiers we inherit--just those necessary to:
|
||||
# - avoid breaking direct invocations of this script (here/now)
|
||||
# - avoid hard-to-reverse structural changes before the call to rm
|
||||
# single-user support is verified
|
||||
#
|
||||
# In the near-mid term, I (personally) think we should:
|
||||
# - decide to deprecate the direct call and add a notice
|
||||
# - fold all of this into install-darwin-multi-user.sh
|
||||
# - intentionally remove the old direct-invocation form (kill the
|
||||
# routine, replace this script w/ deprecation notice and a note
|
||||
# on the remove-after date)
|
||||
#
|
||||
readonly NIX_ROOT="${NIX_ROOT:-/nix}"
|
||||
|
||||
_sudo() {
|
||||
shift # throw away the 'explanation'
|
||||
/usr/bin/sudo "$@"
|
||||
}
|
||||
failure() {
|
||||
if [ "$*" = "" ]; then
|
||||
cat
|
||||
else
|
||||
echo "$@"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
task() {
|
||||
echo "$@"
|
||||
}
|
||||
fi
|
||||
|
||||
# usually "disk1"
|
||||
root_disk_identifier() {
|
||||
# For performance (~10ms vs 280ms) I'm parsing 'diskX' from stat output
|
||||
# (~diskXsY)--but I'm retaining the more-semantic approach since
|
||||
# it documents intent better.
|
||||
# /usr/sbin/diskutil info -plist / | xmllint --xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" -
|
||||
#
|
||||
local special_device
|
||||
special_device="$(/usr/bin/stat -f "%Sd" /)"
|
||||
echo "${special_device%s[0-9]*}"
|
||||
}
|
||||
|
||||
# make it easy to play w/ 'Case-sensitive APFS'
|
||||
readonly NIX_VOLUME_FS="${NIX_VOLUME_FS:-APFS}"
|
||||
readonly NIX_VOLUME_LABEL="${NIX_VOLUME_LABEL:-Nix Store}"
|
||||
# Strongly assuming we'll make a volume on the device / is on
|
||||
# But you can override NIX_VOLUME_USE_DISK to create it on some other device
|
||||
readonly NIX_VOLUME_USE_DISK="${NIX_VOLUME_USE_DISK:-$(root_disk_identifier)}"
|
||||
NIX_VOLUME_USE_SPECIAL="${NIX_VOLUME_USE_SPECIAL:-}"
|
||||
NIX_VOLUME_USE_UUID="${NIX_VOLUME_USE_UUID:-}"
|
||||
readonly NIX_VOLUME_MOUNTD_DEST="${NIX_VOLUME_MOUNTD_DEST:-/Library/LaunchDaemons/org.nixos.darwin-store.plist}"
|
||||
|
||||
if /usr/bin/fdesetup isactive >/dev/null; then
|
||||
test_filevault_in_use() { return 0; }
|
||||
# no readonly; we may modify if user refuses from cure_volume
|
||||
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-1}"
|
||||
else
|
||||
test_filevault_in_use() { return 1; }
|
||||
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-0}"
|
||||
fi
|
||||
|
||||
should_encrypt_volume() {
|
||||
test_filevault_in_use && (( NIX_VOLUME_DO_ENCRYPT == 1 ))
|
||||
}
|
||||
|
||||
substep() {
|
||||
printf " %s\n" "" "- $1" "" "${@:2}"
|
||||
}
|
||||
|
||||
|
||||
volumes_labeled() {
|
||||
local label="$1"
|
||||
xsltproc --novalid --stringparam label "$label" - <(/usr/sbin/ioreg -ra -c "AppleAPFSVolume") <<'EOF'
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
<xsl:output method="text"/>
|
||||
<xsl:template match="/">
|
||||
<xsl:apply-templates select="/plist/array/dict/key[text()='IORegistryEntryName']/following-sibling::*[1][text()=$label]/.."/>
|
||||
</xsl:template>
|
||||
<xsl:template match="dict">
|
||||
<xsl:apply-templates match="string" select="key[text()='BSD Name']/following-sibling::*[1]"/>
|
||||
<xsl:text>=</xsl:text>
|
||||
<xsl:apply-templates match="string" select="key[text()='UUID']/following-sibling::*[1]"/>
|
||||
<xsl:text>
</xsl:text>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
EOF
|
||||
# I cut label out of the extracted values, but here it is for reference:
|
||||
# <xsl:apply-templates match="string" select="key[text()='IORegistryEntryName']/following-sibling::*[1]"/>
|
||||
# <xsl:text>=</xsl:text>
|
||||
}
|
||||
|
||||
right_disk() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
[[ "$volume_special" == "$NIX_VOLUME_USE_DISK"s* ]]
|
||||
}
|
||||
|
||||
right_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# if set, it must match; otherwise ensure it's on the right disk
|
||||
if [ -z "$NIX_VOLUME_USE_SPECIAL" ]; then
|
||||
if right_disk "$volume_special"; then
|
||||
NIX_VOLUME_USE_SPECIAL="$volume_special" # latch on
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
[ "$volume_special" = "$NIX_VOLUME_USE_SPECIAL" ]
|
||||
fi
|
||||
}
|
||||
|
||||
right_uuid() {
|
||||
local volume_uuid="$1"
|
||||
# if set, it must match; otherwise allow
|
||||
if [ -z "$NIX_VOLUME_USE_UUID" ]; then
|
||||
NIX_VOLUME_USE_UUID="$volume_uuid" # latch on
|
||||
return 0
|
||||
else
|
||||
[ "$volume_uuid" = "$NIX_VOLUME_USE_UUID" ]
|
||||
fi
|
||||
}
|
||||
|
||||
cure_volumes() {
|
||||
local found volume special uuid
|
||||
# loop just in case they have more than one volume
|
||||
# (nothing stops you from doing this)
|
||||
for volume in $(volumes_labeled "$NIX_VOLUME_LABEL"); do
|
||||
# CAUTION: this could (maybe) be a more normal read
|
||||
# loop like:
|
||||
# while IFS== read -r special uuid; do
|
||||
# # ...
|
||||
# done <<<"$(volumes_labeled "$NIX_VOLUME_LABEL")"
|
||||
#
|
||||
# I did it with for to skirt a problem with the obvious
|
||||
# pattern replacing stdin and causing user prompts
|
||||
# inside (which also use read and access stdin) to skip
|
||||
#
|
||||
# If there's an existing encrypted volume we can't find
|
||||
# in keychain, the user never gets prompted to delete
|
||||
# the volume, and the install fails.
|
||||
#
|
||||
# If you change this, a human needs to test a very
|
||||
# specific scenario: you already have an encrypted
|
||||
# Nix Store volume, and have deleted its credential
|
||||
# from keychain. Ensure the script asks you if it can
|
||||
# delete the volume, and then prompts for your sudo
|
||||
# password to confirm.
|
||||
#
|
||||
# shellcheck disable=SC1097
|
||||
IFS== read -r special uuid <<< "$volume"
|
||||
# take the first one that's on the right disk
|
||||
if [ -z "${found:-}" ]; then
|
||||
if right_volume "$special" && right_uuid "$uuid"; then
|
||||
cure_volume "$special" "$uuid"
|
||||
found="${special} (${uuid})"
|
||||
else
|
||||
warning <<EOF
|
||||
Ignoring ${special} (${uuid}) because I am looking for:
|
||||
disk=${NIX_VOLUME_USE_DISK} special=${NIX_VOLUME_USE_SPECIAL:-${NIX_VOLUME_USE_DISK}sX} uuid=${NIX_VOLUME_USE_UUID:-any}
|
||||
EOF
|
||||
# TODO: give chance to delete if ! headless?
|
||||
fi
|
||||
else
|
||||
warning <<EOF
|
||||
Ignoring ${special} (${uuid}), already found target: $found
|
||||
EOF
|
||||
# TODO reminder? I feel like I want one
|
||||
# idiom that reminds some warnings, or warns
|
||||
# some reminders?
|
||||
# TODO: if ! headless, chance to delete?
|
||||
fi
|
||||
done
|
||||
if [ -z "${found:-}" ]; then
|
||||
readonly NIX_VOLUME_USE_SPECIAL NIX_VOLUME_USE_UUID
|
||||
fi
|
||||
}
|
||||
|
||||
volume_encrypted() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# Trying to match the first line of output; known first lines:
|
||||
# No cryptographic users for <special>
|
||||
# Cryptographic user for <special> (1 found)
|
||||
# Cryptographic users for <special> (2 found)
|
||||
/usr/sbin/diskutil apfs listCryptoUsers -plist "$volume_special" | /usr/bin/grep -q APFSCryptoUserUUID
|
||||
}
|
||||
|
||||
test_fstab() {
|
||||
/usr/bin/grep -q "$NIX_ROOT apfs rw" /etc/fstab 2>/dev/null
|
||||
}
|
||||
|
||||
test_nix_root_is_symlink() {
|
||||
[ -L "$NIX_ROOT" ]
|
||||
}
|
||||
|
||||
test_synthetic_conf_either(){
|
||||
/usr/bin/grep -qE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_synthetic_conf_mountable() {
|
||||
/usr/bin/grep -q "^${NIX_ROOT:1}$" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_synthetic_conf_symlinked() {
|
||||
/usr/bin/grep -qE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_nix_volume_mountd_installed() {
|
||||
test -e "$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
# current volume password
|
||||
test_keychain_by_uuid() {
|
||||
local volume_uuid="$1"
|
||||
# Note: doesn't need sudo just to check; doesn't output pw
|
||||
security find-generic-password -s "$volume_uuid" &>/dev/null
|
||||
}
|
||||
|
||||
get_volume_pass() {
|
||||
local volume_uuid="$1"
|
||||
_sudo \
|
||||
"to confirm keychain has a password that unlocks this volume" \
|
||||
security find-generic-password -s "$volume_uuid" -w
|
||||
}
|
||||
|
||||
verify_volume_pass() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
_sudo "to confirm the password actually unlocks the volume" \
|
||||
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
|
||||
}
|
||||
|
||||
volume_pass_works() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
get_volume_pass "$volume_uuid" | verify_volume_pass "$volume_special" "$volume_uuid"
|
||||
}
|
||||
|
||||
# Create the paths defined in synthetic.conf, saving us a reboot.
|
||||
create_synthetic_objects() {
|
||||
# Big Sur takes away the -B flag we were using and replaces it
|
||||
# with a -t flag that appears to do the same thing (but they
|
||||
# don't behave exactly the same way in terms of return values).
|
||||
# This feels a little dirty, but as far as I can tell the
|
||||
# simplest way to get the right one is to just throw away stderr
|
||||
# and call both... :]
|
||||
{
|
||||
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true # Big Sur
|
||||
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true # Catalina
|
||||
} >/dev/null 2>&1
|
||||
}
|
||||
|
||||
test_nix() {
|
||||
test -d "$NIX_ROOT"
|
||||
}
|
||||
|
||||
test_voldaemon() {
|
||||
test -f "$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
generate_mount_command() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid mountpoint cmd=()
|
||||
printf -v volume_uuid "%q" "$2"
|
||||
printf -v mountpoint "%q" "$NIX_ROOT"
|
||||
|
||||
case "$cmd_type" in
|
||||
encrypted)
|
||||
cmd=(/bin/sh -c "/usr/bin/security find-generic-password -s '$volume_uuid' -w | /usr/sbin/diskutil apfs unlockVolume '$volume_uuid' -mountpoint '$mountpoint' -stdinpassphrase");;
|
||||
unencrypted)
|
||||
cmd=(/usr/sbin/diskutil mount -mountPoint "$mountpoint" "$volume_uuid");;
|
||||
*)
|
||||
failure "Invalid first arg $cmd_type to generate_mount_command";;
|
||||
esac
|
||||
|
||||
printf " <string>%s</string>\n" "${cmd[@]}"
|
||||
}
|
||||
|
||||
generate_mount_daemon() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid="$2"
|
||||
cat <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>Label</key>
|
||||
<string>org.nixos.darwin-store</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
$(generate_mount_command "$cmd_type" "$volume_uuid")
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
}
|
||||
|
||||
_eat_bootout_err() {
|
||||
/usr/bin/grep -v "Boot-out failed: 36: Operation now in progress"
|
||||
}
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
uninstall_launch_daemon_directions() {
|
||||
local daemon_label="$1" # i.e., org.nixos.blah-blah
|
||||
local daemon_plist="$2" # abspath
|
||||
substep "Uninstall LaunchDaemon $daemon_label" \
|
||||
" sudo launchctl bootout system/$daemon_label" \
|
||||
" sudo rm $daemon_plist"
|
||||
}
|
||||
|
||||
uninstall_launch_daemon_prompt() {
|
||||
local daemon_label="$1" # i.e., org.nixos.blah-blah
|
||||
local daemon_plist="$2" # abspath
|
||||
local reason_for_daemon="$3"
|
||||
cat <<EOF
|
||||
|
||||
The installer adds a LaunchDaemon to $reason_for_daemon: $daemon_label
|
||||
EOF
|
||||
if ui_confirm "Can I remove it?"; then
|
||||
_sudo "to terminate the daemon" \
|
||||
launchctl bootout "system/$daemon_label" 2> >(_eat_bootout_err >&2) || true
|
||||
# this can "fail" with a message like:
|
||||
# Boot-out failed: 36: Operation now in progress
|
||||
_sudo "to remove the daemon definition" rm "$daemon_plist"
|
||||
fi
|
||||
}
|
||||
|
||||
nix_volume_mountd_uninstall_directions() {
|
||||
uninstall_launch_daemon_directions "org.nixos.darwin-store" \
|
||||
"$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
nix_volume_mountd_uninstall_prompt() {
|
||||
uninstall_launch_daemon_prompt "org.nixos.darwin-store" \
|
||||
"$NIX_VOLUME_MOUNTD_DEST" \
|
||||
"mount your Nix volume"
|
||||
}
|
||||
|
||||
# TODO: move nix_daemon to install-darwin-multi-user if/when uninstall_launch_daemon_prompt moves up to install-multi-user
|
||||
nix_daemon_uninstall_prompt() {
|
||||
uninstall_launch_daemon_prompt "org.nixos.nix-daemon" \
|
||||
"$NIX_DAEMON_DEST" \
|
||||
"run the nix-daemon"
|
||||
}
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
nix_daemon_uninstall_directions() {
|
||||
uninstall_launch_daemon_directions "org.nixos.nix-daemon" \
|
||||
"$NIX_DAEMON_DEST"
|
||||
}
|
||||
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
synthetic_conf_uninstall_directions() {
|
||||
# :1 to strip leading slash
|
||||
substep "Remove ${NIX_ROOT:1} from /etc/synthetic.conf" \
|
||||
" If nix is the only entry: sudo rm /etc/synthetic.conf" \
|
||||
" Otherwise: sudo /usr/bin/sed -i '' -e '/^${NIX_ROOT:1}$/d' /etc/synthetic.conf"
|
||||
}
|
||||
|
||||
synthetic_conf_uninstall_prompt() {
|
||||
cat <<EOF
|
||||
|
||||
During install, I add '${NIX_ROOT:1}' to /etc/synthetic.conf, which instructs
|
||||
macOS to create an empty root directory for mounting the Nix volume.
|
||||
EOF
|
||||
# make the edit to a copy
|
||||
/usr/bin/grep -vE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf > "$SCRATCH/synthetic.conf.edit"
|
||||
|
||||
if test_synthetic_conf_symlinked; then
|
||||
warning <<EOF
|
||||
|
||||
/etc/synthetic.conf already contains a line instructing your system
|
||||
to make '${NIX_ROOT}' as a symlink:
|
||||
$(/usr/bin/grep -nE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf)
|
||||
|
||||
This may mean your system has/had a non-standard Nix install.
|
||||
|
||||
The volume-creation process in this installer is *not* compatible
|
||||
with a symlinked store, so I'll have to remove this instruction to
|
||||
continue.
|
||||
|
||||
If you want/need to keep this instruction, answer 'n' to abort.
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# ask to rm if this left the file empty aside from comments, else edit
|
||||
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/synthetic.conf.edit") &>/dev/null; then
|
||||
if confirm_rm "/etc/synthetic.conf"; then
|
||||
if test_nix_root_is_symlink; then
|
||||
failure >&2 <<EOF
|
||||
I removed /etc/synthetic.conf, but $NIX_ROOT is already a symlink
|
||||
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
|
||||
Once you've rebooted, run the installer again.
|
||||
EOF
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if confirm_edit "$SCRATCH/synthetic.conf.edit" "/etc/synthetic.conf"; then
|
||||
if test_nix_root_is_symlink; then
|
||||
failure >&2 <<EOF
|
||||
I edited Nix out of /etc/synthetic.conf, but $NIX_ROOT is already a symlink
|
||||
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
|
||||
Once you've rebooted, run the installer again.
|
||||
EOF
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# fallback instructions
|
||||
echo "Manually remove nix from /etc/synthetic.conf"
|
||||
return 1
|
||||
}
|
||||
|
||||
add_nix_vol_fstab_line() {
|
||||
local uuid="$1"
|
||||
# shellcheck disable=SC1003,SC2026
|
||||
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
|
||||
shift
|
||||
|
||||
# wrap `ex` to work around problems w/ vim features breaking exit codes
|
||||
# - plugins (see github.com/NixOS/nix/issues/5468): -u NONE
|
||||
# - swap file: -n
|
||||
#
|
||||
# the first draft used `--noplugin`, but github.com/NixOS/nix/issues/6462
|
||||
# suggests we need the less-semantic `-u NONE`
|
||||
#
|
||||
# we'd prefer EDITOR="/usr/bin/ex -u NONE" but vifs doesn't word-split
|
||||
# the EDITOR env.
|
||||
#
|
||||
# TODO: at some point we should switch to `--clean`, but it wasn't added
|
||||
# until https://github.com/vim/vim/releases/tag/v8.0.1554 while the macOS
|
||||
# minver 10.12.6 seems to have released with vim 7.4
|
||||
cat > "$SCRATCH/ex_cleanroom_wrapper" <<EOF
|
||||
#!/bin/sh
|
||||
/usr/bin/ex -u NONE -n "\$@"
|
||||
EOF
|
||||
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
|
||||
|
||||
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
|
||||
:a
|
||||
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
# TODO: preserving my notes on suid,owners above until resolved
|
||||
# There *may* be some issue regarding volume ownership, see nix#3156
|
||||
#
|
||||
# It seems like the cheapest fix is adding "suid,owners" to fstab, but:
|
||||
# - We don't have much info on this condition yet
|
||||
# - I'm not certain if these cause other problems?
|
||||
# - There's a "chown" component some people claim to need to fix this
|
||||
# that I don't understand yet
|
||||
# (Note however that I've had to add a chown step to handle
|
||||
# single->multi-user reinstalls, which may cover this)
|
||||
#
|
||||
# I'm not sure if it's safe to approach this way?
|
||||
#
|
||||
# I think I think the most-proper way to test for it is:
|
||||
# diskutil info -plist "$NIX_VOLUME_LABEL" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1][name()='true']" -; echo $?
|
||||
#
|
||||
# There's also `sudo /usr/sbin/vsdbutil -c /path` (which is much faster, but is also
|
||||
# deprecated and needs minor parsing).
|
||||
#
|
||||
# If no one finds a problem with doing so, I think the simplest approach
|
||||
# is to just eagerly set this. I found a few imperative approaches:
|
||||
# (diskutil enableOwnership, ~100ms), a cheap one (/usr/sbin/vsdbutil -a, ~40-50ms),
|
||||
# a very cheap one (append the internal format to /var/db/volinfo.database).
|
||||
#
|
||||
# But vsdbutil's deprecation notice suggests using fstab, so I want to
|
||||
# give that a whirl first.
|
||||
#
|
||||
# TODO: when this is workable, poke infinisil about reproducing the issue
|
||||
# and confirming this fix?
|
||||
}
|
||||
|
||||
delete_nix_vol_fstab_line() {
|
||||
# TODO: I'm scaffolding this to handle the new nix volumes
|
||||
# but it might be nice to generalize a smidge further to
|
||||
# go ahead and set up a pattern for curing "old" things
|
||||
# we no longer do?
|
||||
EDITOR="/usr/bin/patch" _sudo "to cut nix from fstab" "$@" < <(/usr/bin/diff /etc/fstab <(/usr/bin/grep -v "$NIX_ROOT apfs rw" /etc/fstab))
|
||||
# leaving some parts out of the grep; people may fiddle this a little?
|
||||
}
|
||||
|
||||
# TODO: hope to remove with --uninstall
|
||||
fstab_uninstall_directions() {
|
||||
substep "Remove ${NIX_ROOT} from /etc/fstab" \
|
||||
" If nix is the only entry: sudo rm /etc/fstab" \
|
||||
" Otherwise, run 'sudo /usr/sbin/vifs' to remove the nix line"
|
||||
}
|
||||
|
||||
fstab_uninstall_prompt() {
|
||||
cat <<EOF
|
||||
During install, I add '${NIX_ROOT}' to /etc/fstab so that macOS knows what
|
||||
mount options to use for the Nix volume.
|
||||
EOF
|
||||
cp /etc/fstab "$SCRATCH/fstab.edit"
|
||||
# technically doesn't need the _sudo path, but throwing away the
|
||||
# output is probably better than mostly-duplicating the code...
|
||||
delete_nix_vol_fstab_line patch "$SCRATCH/fstab.edit" &>/dev/null
|
||||
|
||||
# if the patch test edit, minus comment lines, is equal to empty (:)
|
||||
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/fstab.edit") &>/dev/null; then
|
||||
# this edit would leave it empty; propose deleting it
|
||||
if confirm_rm "/etc/fstab"; then
|
||||
return 0
|
||||
else
|
||||
echo "Remove nix from /etc/fstab (or remove the file)"
|
||||
fi
|
||||
else
|
||||
echo "I might be able to help you make this edit. Here's the diff:"
|
||||
if ! _diff "/etc/fstab" "$SCRATCH/fstab.edit" && ui_confirm "Does the change above look right?"; then
|
||||
delete_nix_vol_fstab_line /usr/sbin/vifs
|
||||
else
|
||||
echo "Remove nix from /etc/fstab (or remove the file)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
remove_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
_sudo "to unmount the Nix volume" \
|
||||
/usr/sbin/diskutil unmount force "$volume_special" || true # might not be mounted
|
||||
_sudo "to delete the Nix volume" \
|
||||
/usr/sbin/diskutil apfs deleteVolume "$volume_special"
|
||||
}
|
||||
|
||||
# aspiration: robust enough to both fix problems
|
||||
# *and* update older darwin volumes
|
||||
cure_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
header "Found existing Nix volume"
|
||||
row " special" "$volume_special"
|
||||
row " uuid" "$volume_uuid"
|
||||
|
||||
if volume_encrypted "$volume_special"; then
|
||||
row "encrypted" "yes"
|
||||
if volume_pass_works "$volume_special" "$volume_uuid"; then
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
ok "Found a working decryption password in keychain :)"
|
||||
echo ""
|
||||
else
|
||||
# - this is a volume we made, and
|
||||
# - the user encrypted it on their own
|
||||
# - something deleted the credential
|
||||
# - this is an old or BYO volume and the pw
|
||||
# just isn't somewhere we can find it.
|
||||
#
|
||||
# We're going to explain why we're freaking out
|
||||
# and prompt them to either delete the volume
|
||||
# (requiring a sudo auth), or abort to fix
|
||||
warning <<EOF
|
||||
|
||||
This volume is encrypted, but I don't see a password to decrypt it.
|
||||
The quick fix is to let me delete this volume and make you a new one.
|
||||
If that's okay, enter your (sudo) password to continue. If not, you
|
||||
can ensure the decryption password is in your system keychain with a
|
||||
"Where" (service) field set to this volume's UUID:
|
||||
$volume_uuid
|
||||
EOF
|
||||
if password_confirm "delete this volume"; then
|
||||
remove_volume "$volume_special"
|
||||
else
|
||||
# TODO: this is a good design case for a warn-and
|
||||
# remind idiom...
|
||||
failure <<EOF
|
||||
Your Nix volume is encrypted, but I couldn't find its password. Either:
|
||||
- Delete or rename the volume out of the way
|
||||
- Ensure its decryption password is in the system keychain with a
|
||||
"Where" (service) field set to this volume's UUID:
|
||||
$volume_uuid
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
elif test_filevault_in_use; then
|
||||
row "encrypted" "no"
|
||||
warning <<EOF
|
||||
FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted.
|
||||
EOF
|
||||
# if we're interactive, give them a chance to
|
||||
# encrypt the volume. If not, /shrug
|
||||
if ! headless && (( NIX_VOLUME_DO_ENCRYPT == 1 )); then
|
||||
if ui_confirm "Should I encrypt it and add the decryption key to your keychain?"; then
|
||||
encrypt_volume "$volume_uuid" "$NIX_VOLUME_LABEL"
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
else
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
reminder "FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
row "encrypted" "no"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_volume_artifacts() {
|
||||
if test_synthetic_conf_either; then
|
||||
# NIX_ROOT is in synthetic.conf
|
||||
if synthetic_conf_uninstall_prompt; then
|
||||
# TODO: moot until we tackle uninstall, but when we're
|
||||
# actually uninstalling, we should issue:
|
||||
# reminder "macOS will clean up the empty mount-point directory at $NIX_ROOT on reboot."
|
||||
:
|
||||
fi
|
||||
fi
|
||||
if test_fstab; then
|
||||
fstab_uninstall_prompt
|
||||
fi
|
||||
|
||||
if test_nix_volume_mountd_installed; then
|
||||
nix_volume_mountd_uninstall_prompt
|
||||
fi
|
||||
}
|
||||
|
||||
setup_synthetic_conf() {
|
||||
if test_nix_root_is_symlink; then
|
||||
if ! test_synthetic_conf_symlinked; then
|
||||
failure >&2 <<EOF
|
||||
error: $NIX_ROOT is a symlink (-> $(readlink "$NIX_ROOT")).
|
||||
Please remove it. If nix is in /etc/synthetic.conf, remove it and reboot.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
if ! test_synthetic_conf_mountable; then
|
||||
task "Configuring /etc/synthetic.conf to make a mount-point at $NIX_ROOT" >&2
|
||||
# technically /etc/synthetic.d/nix is supported in Big Sur+
|
||||
# but handling both takes even more code...
|
||||
# See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
|
||||
_sudo "to add Nix to /etc/synthetic.conf" \
|
||||
/usr/bin/ex -u NONE -n /etc/synthetic.conf <<EOF
|
||||
:a
|
||||
${NIX_ROOT:1}
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
if ! test_synthetic_conf_mountable; then
|
||||
failure "error: failed to configure synthetic.conf" >&2
|
||||
fi
|
||||
create_synthetic_objects
|
||||
if ! test_nix; then
|
||||
failure >&2 <<EOF
|
||||
error: failed to bootstrap $NIX_ROOT
|
||||
If you enabled FileVault after booting, this is likely a known issue
|
||||
with macOS that you'll have to reboot to fix. If you didn't enable FV,
|
||||
though, please open an issue describing how the system that you see
|
||||
this error on was set up.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
setup_fstab() {
|
||||
local volume_uuid="$1"
|
||||
# fstab used to be responsible for mounting the volume. Now the last
|
||||
# step adds a LaunchDaemon responsible for mounting. This is technically
|
||||
# redundant for mounting, but diskutil appears to pick up mount options
|
||||
# from fstab (and diskutil's support for specifying them directly is not
|
||||
# consistent across versions/subcommands).
|
||||
if ! test_fstab; then
|
||||
task "Configuring /etc/fstab to specify volume mount options" >&2
|
||||
add_nix_vol_fstab_line "$volume_uuid" /usr/sbin/vifs
|
||||
fi
|
||||
}
|
||||
|
||||
encrypt_volume() {
|
||||
local volume_uuid="$1"
|
||||
local volume_label="$2"
|
||||
local password
|
||||
|
||||
task "Encrypt the Nix volume" >&2
|
||||
|
||||
# Note: mount/unmount are late additions to support the right order
|
||||
# of operations for creating the volume and then baking its uuid into
|
||||
# other artifacts; not as well-trod wrt to potential errors, race
|
||||
# conditions, etc.
|
||||
|
||||
_sudo "to mount your Nix volume for encrypting" \
|
||||
/usr/sbin/diskutil mount "$volume_label"
|
||||
|
||||
password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)"
|
||||
_sudo "to add your Nix volume's password to Keychain" \
|
||||
/usr/bin/security -i <<EOF
|
||||
add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain"
|
||||
EOF
|
||||
builtin printf "%s" "$password" | _sudo "to actually encrypt your Nix volume" \
|
||||
/usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase
|
||||
|
||||
_sudo "to unmount the encrypted volume" \
|
||||
/usr/sbin/diskutil unmount force "$volume_label"
|
||||
}
|
||||
|
||||
create_volume() {
|
||||
# Notes:
|
||||
# 1) using `-nomount` instead of `-mountpoint "$NIX_ROOT"` to get
|
||||
# its UUID and set mount opts in fstab before first mount
|
||||
#
|
||||
# 2) system is in some sense less secure than user keychain... (it's
|
||||
# possible to read the password for decrypting the keychain) but
|
||||
# the user keychain appears to be available too late. As far as I
|
||||
# can tell, the file with this password (/var/db/SystemKey) is
|
||||
# inside the FileVault envelope. If that isn't true, it may make
|
||||
# sense to store the password inside the envelope?
|
||||
#
|
||||
# 3) At some point it would be ideal to have a small binary to serve
|
||||
# as the daemon itself, and for it to replace /usr/bin/security here.
|
||||
#
|
||||
# 4) *UserAgent exemptions should let the system seamlessly supply the
|
||||
# password if noauto is removed from fstab entry. This is intentional;
|
||||
# the user will hopefully look for help if the volume stops mounting,
|
||||
# rather than failing over into subtle race-condition problems.
|
||||
#
|
||||
# 5) If we ever get users griping about not having space to do
|
||||
# anything useful with Nix, it is possibly to specify
|
||||
# `-reserve 10g` or something, which will fail w/o that much
|
||||
#
|
||||
# 6) getting special w/ awk may be fragile, but doing it to:
|
||||
# - save time over running slow diskutil commands
|
||||
# - skirt risk we grab wrong volume if multiple match
|
||||
_sudo "to create a new APFS volume '$NIX_VOLUME_LABEL' on $NIX_VOLUME_USE_DISK" \
|
||||
/usr/sbin/diskutil apfs addVolume "$NIX_VOLUME_USE_DISK" "$NIX_VOLUME_FS" "$NIX_VOLUME_LABEL" -nomount | /usr/bin/awk '/Created new APFS Volume/ {print $5}'
|
||||
}
|
||||
|
||||
volume_uuid_from_special() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# For reasons I won't pretend to fathom, this returns 253 when it works
|
||||
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -k "$volume_special" || true
|
||||
}
|
||||
|
||||
# this sometimes clears immediately, and AFAIK clears
|
||||
# within about 1s. diskutil info on an unmounted path
|
||||
# fails in around 50-100ms and a match takes about
|
||||
# 250-300ms. I suspect it's usually ~250-750ms
|
||||
await_volume() {
|
||||
# caution: this could, in theory, get stuck
|
||||
until /usr/sbin/diskutil info "$NIX_ROOT" &>/dev/null; do
|
||||
:
|
||||
done
|
||||
}
|
||||
|
||||
setup_volume() {
|
||||
local use_special use_uuid profile_packages
|
||||
task "Creating a Nix volume" >&2
|
||||
|
||||
use_special="${NIX_VOLUME_USE_SPECIAL:-$(create_volume)}"
|
||||
|
||||
_sudo "to ensure the Nix volume is not mounted" \
|
||||
/usr/sbin/diskutil unmount force "$use_special" || true # might not be mounted
|
||||
|
||||
use_uuid=${NIX_VOLUME_USE_UUID:-$(volume_uuid_from_special "$use_special")}
|
||||
|
||||
setup_fstab "$use_uuid"
|
||||
|
||||
if should_encrypt_volume; then
|
||||
encrypt_volume "$use_uuid" "$NIX_VOLUME_LABEL"
|
||||
setup_volume_daemon "encrypted" "$use_uuid"
|
||||
# TODO: might be able to save ~60ms by caching or setting
|
||||
# this somewhere rather than re-checking here.
|
||||
elif volume_encrypted "$use_special"; then
|
||||
setup_volume_daemon "encrypted" "$use_uuid"
|
||||
else
|
||||
setup_volume_daemon "unencrypted" "$use_uuid"
|
||||
fi
|
||||
|
||||
await_volume
|
||||
|
||||
if [ "$(/usr/sbin/diskutil info -plist "$NIX_ROOT" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
_sudo "to set enableOwnership (enabling users to own files)" \
|
||||
/usr/sbin/diskutil enableOwnership "$NIX_ROOT"
|
||||
fi
|
||||
|
||||
# TODO: below is a vague kludge for now; I just don't know
|
||||
# what if any safe action there is to take here. Also, the
|
||||
# reminder isn't very helpful.
|
||||
# I'm less sure where this belongs, but it also wants mounted, pre-install
|
||||
if type -p nix-env; then
|
||||
profile_packages="$(nix-env --query --installed)"
|
||||
# TODO: can probably do below faster w/ read
|
||||
# intentionally unquoted string to eat whitespace in wc output
|
||||
# shellcheck disable=SC2046,SC2059
|
||||
if ! [ $(printf "$profile_packages" | /usr/bin/wc -l) = "0" ]; then
|
||||
reminder <<EOF
|
||||
Nix now supports only multi-user installs on Darwin/macOS, and your user's
|
||||
Nix profile has some packages in it. These packages may obscure those in the
|
||||
default profile, including the Nix this installer will add. You should
|
||||
review these packages:
|
||||
$profile_packages
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
setup_volume_daemon() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid="$2"
|
||||
if ! test_voldaemon; then
|
||||
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
|
||||
# See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
|
||||
_sudo "to install the Nix volume mounter" /usr/bin/ex -u NONE -n "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
||||
:a
|
||||
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
|
||||
# TODO: should probably alert the user if this is disabled?
|
||||
_sudo "to launch the Nix volume mounter" \
|
||||
launchctl bootstrap system "$NIX_VOLUME_MOUNTD_DEST" || true
|
||||
# TODO: confirm whether kickstart is necessesary?
|
||||
# I feel a little superstitous, but it can guard
|
||||
# against multiple problems (doesn't start, old
|
||||
# version still running for some reason...)
|
||||
_sudo "to launch the Nix volume mounter" \
|
||||
launchctl kickstart -k system/org.nixos.darwin-store
|
||||
fi
|
||||
}
|
||||
|
||||
setup_darwin_volume() {
|
||||
setup_synthetic_conf
|
||||
setup_volume
|
||||
}
|
||||
|
||||
if [ "$_CREATE_VOLUME_NO_MAIN" = 1 ]; then
|
||||
if [ -n "$*" ]; then
|
||||
"$@" # expose functions in case we want multiple routines?
|
||||
fi
|
||||
else
|
||||
# no reason to pay for bash to process this
|
||||
main() {
|
||||
{
|
||||
echo ""
|
||||
echo " ------------------------------------------------------------------ "
|
||||
echo " | This installer will create a volume for the nix store and |"
|
||||
echo " | configure it to mount at $NIX_ROOT. Follow these steps to uninstall. |"
|
||||
echo " ------------------------------------------------------------------ "
|
||||
echo ""
|
||||
echo " 1. Remove the entry from fstab using 'sudo /usr/sbin/vifs'"
|
||||
echo " 2. Run 'sudo launchctl bootout system/org.nixos.darwin-store'"
|
||||
echo " 3. Remove $NIX_VOLUME_MOUNTD_DEST"
|
||||
echo " 4. Destroy the data volume using '/usr/sbin/diskutil apfs deleteVolume'"
|
||||
echo " 5. Remove the 'nix' line from /etc/synthetic.conf (or the file)"
|
||||
echo ""
|
||||
} >&2
|
||||
|
||||
setup_darwin_volume
|
||||
}
|
||||
|
||||
main "$@"
|
||||
fi
|
||||
@@ -1,226 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
|
||||
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
# create by default; set 0 to DIY, use a symlink, etc.
|
||||
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
|
||||
|
||||
# caution: may update times on / if not run as normal non-root user
|
||||
read_only_root() {
|
||||
# this touch command ~should~ always produce an error
|
||||
# as of this change I confirmed /usr/bin/touch emits:
|
||||
# "touch: /: Operation not permitted" Monterey
|
||||
# "touch: /: Read-only file system" Catalina+ and Big Sur
|
||||
# "touch: /: Permission denied" Mojave
|
||||
# (not matching prefix for compat w/ coreutils touch in case using
|
||||
# an explicit path causes problems; its prefix differs)
|
||||
case "$(/usr/bin/touch / 2>&1)" in
|
||||
*"Read-only file system") # Catalina, Big Sur
|
||||
return 0
|
||||
;;
|
||||
*"Operation not permitted") # Monterey
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Avoiding the slow semantic way to get this information (~330ms vs ~8ms)
|
||||
# unless using touch causes problems. Just in case, that approach is:
|
||||
# diskutil info -plist / | <find the Writable or WritableVolume keys>, i.e.
|
||||
# diskutil info -plist / | xmllint --xpath "name(/plist/dict/key[text()='Writable']/following-sibling::*[1])" -
|
||||
}
|
||||
|
||||
if read_only_root && [ "$NIX_VOLUME_CREATE" = 1 ]; then
|
||||
should_create_volume() { return 0; }
|
||||
else
|
||||
should_create_volume() { return 1; }
|
||||
fi
|
||||
|
||||
# shellcheck source=./create-darwin-volume.sh
|
||||
. "$EXTRACTED_NIX_PATH/create-darwin-volume.sh" "no-main"
|
||||
|
||||
dsclattr() {
|
||||
/usr/bin/dscl . -read "$1" \
|
||||
| /usr/bin/awk "/$2/ { print \$2 }"
|
||||
}
|
||||
|
||||
test_nix_daemon_installed() {
|
||||
test -e "$NIX_DAEMON_DEST"
|
||||
}
|
||||
|
||||
poly_cure_artifacts() {
|
||||
if should_create_volume; then
|
||||
task "Fixing any leftover Nix volume state"
|
||||
cat <<EOF
|
||||
Before I try to install, I'll check for any existing Nix volume config
|
||||
and ask for your permission to remove it (so that the installer can
|
||||
start fresh). I'll also ask for permission to fix any issues I spot.
|
||||
EOF
|
||||
cure_volumes
|
||||
remove_volume_artifacts
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_installed_check() {
|
||||
if should_create_volume; then
|
||||
test_nix_daemon_installed || test_nix_volume_mountd_installed
|
||||
else
|
||||
test_nix_daemon_installed
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_uninstall_directions() {
|
||||
echo "$1. Remove macOS-specific components:"
|
||||
if should_create_volume && test_nix_volume_mountd_installed; then
|
||||
nix_volume_mountd_uninstall_directions
|
||||
fi
|
||||
if test_nix_daemon_installed; then
|
||||
nix_daemon_uninstall_directions
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_setup_note() {
|
||||
if should_create_volume; then
|
||||
echo " - create a Nix volume and a LaunchDaemon to mount it"
|
||||
fi
|
||||
echo " - create a LaunchDaemon (at $NIX_DAEMON_DEST) for nix-daemon"
|
||||
echo ""
|
||||
}
|
||||
|
||||
poly_extra_try_me_commands() {
|
||||
:
|
||||
}
|
||||
|
||||
poly_configure_nix_daemon_service() {
|
||||
task "Setting up the nix-daemon LaunchDaemon"
|
||||
_sudo "to set up the nix-daemon as a LaunchDaemon" \
|
||||
/usr/bin/install -m "u=rw,go=r" "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
|
||||
|
||||
_sudo "to load the LaunchDaemon plist for nix-daemon" \
|
||||
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
_sudo "to start the nix-daemon" \
|
||||
launchctl kickstart -k system/org.nixos.nix-daemon
|
||||
}
|
||||
|
||||
poly_group_exists() {
|
||||
/usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_group_id_get() {
|
||||
dsclattr "/Groups/$1" "PrimaryGroupID"
|
||||
}
|
||||
|
||||
poly_create_build_group() {
|
||||
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
|
||||
/usr/sbin/dseditgroup -o create \
|
||||
-r "Nix build group for nix-daemon" \
|
||||
-i "$NIX_BUILD_GROUP_ID" \
|
||||
"$NIX_BUILD_GROUP_NAME" >&2
|
||||
}
|
||||
|
||||
poly_user_exists() {
|
||||
/usr/bin/dscl . -read "/Users/$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_user_id_get() {
|
||||
dsclattr "/Users/$1" "UniqueID"
|
||||
}
|
||||
|
||||
poly_user_hidden_get() {
|
||||
dsclattr "/Users/$1" "IsHidden"
|
||||
}
|
||||
|
||||
poly_user_hidden_set() {
|
||||
_sudo "in order to make $1 a hidden user" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
|
||||
}
|
||||
|
||||
poly_user_home_get() {
|
||||
dsclattr "/Users/$1" "NFSHomeDirectory"
|
||||
}
|
||||
|
||||
poly_user_home_set() {
|
||||
# This can trigger a permission prompt now:
|
||||
# "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings.
|
||||
_sudo "in order to give $1 a safe home directory" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
|
||||
}
|
||||
|
||||
poly_user_note_get() {
|
||||
dsclattr "/Users/$1" "RealName"
|
||||
}
|
||||
|
||||
poly_user_note_set() {
|
||||
_sudo "in order to give $username a useful note" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
|
||||
}
|
||||
|
||||
poly_user_shell_get() {
|
||||
dsclattr "/Users/$1" "UserShell"
|
||||
}
|
||||
|
||||
poly_user_shell_set() {
|
||||
_sudo "in order to give $1 a safe shell" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
|
||||
}
|
||||
|
||||
poly_user_in_group_check() {
|
||||
username=$1
|
||||
group=$2
|
||||
/usr/sbin/dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_user_in_group_set() {
|
||||
username=$1
|
||||
group=$2
|
||||
|
||||
_sudo "Add $username to the $group group"\
|
||||
/usr/sbin/dseditgroup -o edit -t user \
|
||||
-a "$username" "$group"
|
||||
}
|
||||
|
||||
poly_user_primary_group_get() {
|
||||
dsclattr "/Users/$1" "PrimaryGroupID"
|
||||
}
|
||||
|
||||
poly_user_primary_group_set() {
|
||||
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "PrimaryGroupID" "$2"
|
||||
}
|
||||
|
||||
poly_create_build_user() {
|
||||
username=$1
|
||||
uid=$2
|
||||
builder_num=$3
|
||||
|
||||
_sudo "Creating the Nix build user (#$builder_num), $username" \
|
||||
/usr/bin/dscl . create "/Users/$username" \
|
||||
UniqueID "${uid}"
|
||||
}
|
||||
|
||||
poly_prepare_to_install() {
|
||||
if should_create_volume; then
|
||||
header "Preparing a Nix volume"
|
||||
# intentional indent below to match task indent
|
||||
cat <<EOF
|
||||
Nix traditionally stores its data in the root directory $NIX_ROOT, but
|
||||
macOS now (starting in 10.15 Catalina) has a read-only root directory.
|
||||
To support Nix, I will create a volume and configure macOS to mount it
|
||||
at $NIX_ROOT.
|
||||
EOF
|
||||
setup_darwin_volume
|
||||
fi
|
||||
|
||||
if [ "$(/usr/sbin/diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
failure "This script needs a /nix volume with global permissions! This may require running sudo /usr/sbin/diskutil enableOwnership /nix."
|
||||
fi
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,284 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
umask 0022
|
||||
|
||||
dest="/nix"
|
||||
self="$(dirname "$0")"
|
||||
nix="@nix@"
|
||||
cacert="@cacert@"
|
||||
|
||||
|
||||
if ! [ -e "$self/.reginfo" ]; then
|
||||
echo "$0: incomplete installer (.reginfo is missing)" >&2
|
||||
fi
|
||||
|
||||
if [ -z "$USER" ] && ! USER=$(id -u -n); then
|
||||
echo "$0: \$USER is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$HOME" ]; then
|
||||
echo "$0: \$HOME is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# macOS support for 10.12.6 or higher
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
IFS='.' read -r macos_major macos_minor macos_patch << EOF
|
||||
$(sw_vers -productVersion)
|
||||
EOF
|
||||
if [ "$macos_major" -lt 10 ] || { [ "$macos_major" -eq 10 ] && [ "$macos_minor" -lt 12 ]; } || { [ "$macos_minor" -eq 12 ] && [ "$macos_patch" -lt 6 ]; }; then
|
||||
# patch may not be present; command substitution for simplicity
|
||||
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.12.6 or higher"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine if we could use the multi-user installer or not
|
||||
if [ "$(uname -s)" = "Linux" ]; then
|
||||
echo "Note: a multi-user installation is possible. See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation" >&2
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
"Darwin")
|
||||
INSTALL_MODE=daemon;;
|
||||
*)
|
||||
INSTALL_MODE=no-daemon;;
|
||||
esac
|
||||
|
||||
# space-separated string
|
||||
ACTIONS=
|
||||
|
||||
# handle the command line flags
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
--daemon)
|
||||
INSTALL_MODE=daemon
|
||||
ACTIONS="${ACTIONS}install "
|
||||
;;
|
||||
--no-daemon)
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
printf '\e[1;31mError: --no-daemon installs are no-longer supported on Darwin/macOS!\e[0m\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_MODE=no-daemon
|
||||
# intentional tail space
|
||||
ACTIONS="${ACTIONS}install "
|
||||
;;
|
||||
# --uninstall)
|
||||
# # intentional tail space
|
||||
# ACTIONS="${ACTIONS}uninstall "
|
||||
# ;;
|
||||
--yes)
|
||||
export NIX_INSTALLER_YES=1;;
|
||||
--no-channel-add)
|
||||
export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
|
||||
--daemon-user-count)
|
||||
export NIX_USER_COUNT=$2
|
||||
shift;;
|
||||
--no-modify-profile)
|
||||
NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
|
||||
--darwin-use-unencrypted-nix-store-volume)
|
||||
{
|
||||
echo "Warning: the flag --darwin-use-unencrypted-nix-store-volume"
|
||||
echo " is no longer needed and will be removed in the future."
|
||||
echo ""
|
||||
} >&2;;
|
||||
--nix-extra-conf-file)
|
||||
# shellcheck disable=SC2155
|
||||
export NIX_EXTRA_CONF="$(cat "$2")"
|
||||
shift;;
|
||||
*)
|
||||
{
|
||||
echo "Nix Installer [--daemon|--no-daemon] [--daemon-user-count INT] [--yes] [--no-channel-add] [--no-modify-profile] [--nix-extra-conf-file FILE]"
|
||||
|
||||
echo "Choose installation method."
|
||||
echo ""
|
||||
echo " --daemon: Installs and configures a background daemon that manages the store,"
|
||||
echo " providing multi-user support and better isolation for local builds."
|
||||
echo " Both for security and reproducibility, this method is recommended if"
|
||||
echo " supported on your platform."
|
||||
echo " See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation"
|
||||
echo ""
|
||||
echo " --no-daemon: Simple, single-user installation that does not require root and is"
|
||||
echo " trivial to uninstall."
|
||||
echo " (default)"
|
||||
echo ""
|
||||
echo " --yes: Run the script non-interactively, accepting all prompts."
|
||||
echo ""
|
||||
echo " --no-channel-add: Don't add any channels. nixpkgs-unstable is installed by default."
|
||||
echo ""
|
||||
echo " --no-modify-profile: Don't modify the user profile to automatically load nix."
|
||||
echo ""
|
||||
echo " --daemon-user-count: Number of build users to create. Defaults to 32."
|
||||
echo ""
|
||||
echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix/nix.conf"
|
||||
echo ""
|
||||
if [ -n "${INVOKED_FROM_INSTALL_IN:-}" ]; then
|
||||
echo " --tarball-url-prefix URL: Base URL to download the Nix tarball from."
|
||||
fi
|
||||
} >&2
|
||||
|
||||
exit;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "$INSTALL_MODE" = "daemon" ]; then
|
||||
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
|
||||
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
printf '\e[1;31mwarning: installing Nix as root is not supported by this script!\e[0m\n'
|
||||
fi
|
||||
|
||||
echo "performing a single-user installation of Nix..." >&2
|
||||
|
||||
if ! [ -e "$dest" ]; then
|
||||
cmd="mkdir -m 0755 $dest && chown $USER $dest"
|
||||
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
|
||||
if ! sudo sh -c "$cmd"; then
|
||||
echo "$0: please manually run '$cmd' as root to create $dest" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [ -w "$dest" ]; then
|
||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/manual/nix/stable/installation/multi-user.html. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The auto-chroot code in openFromNonUri() checks for the
|
||||
# non-existence of /nix/var/nix, so we need to create it here.
|
||||
mkdir -p "$dest/store" "$dest/var/nix"
|
||||
|
||||
printf "copying Nix to %s..." "${dest}/store" >&2
|
||||
# Insert a newline if no progress is shown.
|
||||
if [ ! -t 0 ]; then
|
||||
echo ""
|
||||
fi
|
||||
|
||||
for i in $(cd "$self/store" >/dev/null && echo ./*); do
|
||||
if [ -t 0 ]; then
|
||||
printf "." >&2
|
||||
fi
|
||||
i_tmp="$dest/store/$i.$$"
|
||||
if [ -e "$i_tmp" ]; then
|
||||
rm -rf "$i_tmp"
|
||||
fi
|
||||
if ! [ -e "$dest/store/$i" ]; then
|
||||
cp -RPp "$self/store/$i" "$i_tmp"
|
||||
chmod -R a-w "$i_tmp"
|
||||
chmod +w "$i_tmp"
|
||||
mv "$i_tmp" "$dest/store/$i"
|
||||
chmod -w "$dest/store/$i"
|
||||
fi
|
||||
done
|
||||
echo "" >&2
|
||||
|
||||
if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then
|
||||
echo "$0: unable to register valid paths" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=./nix-profile.sh.in
|
||||
. "$nix/etc/profile.d/nix.sh"
|
||||
|
||||
NIX_LINK="$HOME/.nix-profile"
|
||||
|
||||
if ! "$nix/bin/nix-env" -i "$nix"; then
|
||||
echo "$0: unable to install Nix into your default profile" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install an SSL certificate bundle.
|
||||
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
||||
"$nix/bin/nix-env" -i "$cacert"
|
||||
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
|
||||
fi
|
||||
|
||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||
if [ -z "$NIX_INSTALLER_NO_CHANNEL_ADD" ]; then
|
||||
if ! "$nix/bin/nix-channel" --list | grep -q "^nixpkgs "; then
|
||||
"$nix/bin/nix-channel" --add https://nixos.org/channels/nixpkgs-unstable
|
||||
fi
|
||||
if [ -z "$_NIX_INSTALLER_TEST" ]; then
|
||||
if ! "$nix/bin/nix-channel" --update nixpkgs; then
|
||||
echo "Fetching the nixpkgs channel failed. (Are you offline?)"
|
||||
echo "To try again later, run \"nix-channel --update nixpkgs\"."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
added=
|
||||
p=
|
||||
p_sh=$NIX_LINK/etc/profile.d/nix.sh
|
||||
p_fish=$NIX_LINK/etc/profile.d/nix.fish
|
||||
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
||||
# Make the shell source nix.sh during login.
|
||||
for i in .bash_profile .bash_login .profile; do
|
||||
fn="$HOME/$i"
|
||||
if [ -w "$fn" ]; then
|
||||
if ! grep -q "$p_sh" "$fn"; then
|
||||
echo "modifying $fn..." >&2
|
||||
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
|
||||
fi
|
||||
added=1
|
||||
p=${p_sh}
|
||||
break
|
||||
fi
|
||||
done
|
||||
for i in .zshenv .zshrc; do
|
||||
fn="$HOME/$i"
|
||||
if [ -w "$fn" ]; then
|
||||
if ! grep -q "$p_sh" "$fn"; then
|
||||
echo "modifying $fn..." >&2
|
||||
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
|
||||
fi
|
||||
added=1
|
||||
p=${p_sh}
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "$HOME/.config/fish" ]; then
|
||||
fishdir=$HOME/.config/fish/conf.d
|
||||
if [ ! -d "$fishdir" ]; then
|
||||
mkdir -p "$fishdir"
|
||||
fi
|
||||
|
||||
fn="$fishdir/nix.fish"
|
||||
echo "placing $fn..." >&2
|
||||
printf '\nif test -e %s; . %s; end # added by Nix installer\n' "$p_fish" "$p_fish" > "$fn"
|
||||
added=1
|
||||
p=${p_fish}
|
||||
fi
|
||||
else
|
||||
p=${p_sh}
|
||||
fi
|
||||
|
||||
if [ -z "$added" ]; then
|
||||
cat >&2 <<EOF
|
||||
|
||||
Installation finished! To ensure that the necessary environment
|
||||
variables are set, please add the line
|
||||
|
||||
. $p
|
||||
|
||||
to your shell profile (e.g. ~/.profile).
|
||||
EOF
|
||||
else
|
||||
cat >&2 <<EOF
|
||||
|
||||
Installation finished! To ensure that the necessary environment
|
||||
variables are set, either log in again, or type
|
||||
|
||||
. $p
|
||||
|
||||
in your shell.
|
||||
EOF
|
||||
fi
|
||||
@@ -1,222 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
|
||||
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
|
||||
readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
|
||||
|
||||
readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket
|
||||
readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket
|
||||
|
||||
readonly TMPFILES_SRC=/lib/tmpfiles.d/nix-daemon.conf
|
||||
readonly TMPFILES_DEST=/etc/tmpfiles.d/nix-daemon.conf
|
||||
|
||||
# Path for the systemd override unit file to contain the proxy settings
|
||||
readonly SERVICE_OVERRIDE=${SERVICE_DEST}.d/override.conf
|
||||
|
||||
create_systemd_override() {
|
||||
header "Configuring proxy for the nix-daemon service"
|
||||
_sudo "create directory for systemd unit override" mkdir -p "$(dirname "$SERVICE_OVERRIDE")"
|
||||
cat <<EOF | _sudo "create systemd unit override" tee "$SERVICE_OVERRIDE"
|
||||
[Service]
|
||||
$1
|
||||
EOF
|
||||
}
|
||||
|
||||
escape_systemd_env() {
|
||||
temp_var="${1//\'/\\\'}"
|
||||
echo "${temp_var//\%/%%}"
|
||||
}
|
||||
|
||||
# Gather all non-empty proxy environment variables into a string
|
||||
create_systemd_proxy_env() {
|
||||
vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY"
|
||||
for v in $vars; do
|
||||
if [ "x${!v:-}" != "x" ]; then
|
||||
echo "Environment=${v}=$(escape_systemd_env ${!v})"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
handle_network_proxy() {
|
||||
# Create a systemd unit override with proxy environment variables
|
||||
# if any proxy environment variables are not empty.
|
||||
PROXY_ENV_STRING=$(create_systemd_proxy_env)
|
||||
if [ -n "${PROXY_ENV_STRING}" ]; then
|
||||
create_systemd_override "${PROXY_ENV_STRING}"
|
||||
fi
|
||||
}
|
||||
|
||||
poly_cure_artifacts() {
|
||||
:
|
||||
}
|
||||
|
||||
poly_service_installed_check() {
|
||||
[ "$(systemctl is-enabled nix-daemon.service)" = "linked" ] \
|
||||
|| [ "$(systemctl is-enabled nix-daemon.socket)" = "enabled" ]
|
||||
}
|
||||
|
||||
poly_service_uninstall_directions() {
|
||||
cat <<EOF
|
||||
$1. Delete the systemd service and socket units
|
||||
|
||||
sudo systemctl stop nix-daemon.socket
|
||||
sudo systemctl stop nix-daemon.service
|
||||
sudo systemctl disable nix-daemon.socket
|
||||
sudo systemctl disable nix-daemon.service
|
||||
sudo systemctl daemon-reload
|
||||
EOF
|
||||
}
|
||||
|
||||
poly_service_setup_note() {
|
||||
cat <<EOF
|
||||
- load and start a service (at $SERVICE_DEST
|
||||
and $SOCKET_DEST) for nix-daemon
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
poly_extra_try_me_commands() {
|
||||
if [ -e /run/systemd/system ]; then
|
||||
:
|
||||
else
|
||||
cat <<EOF
|
||||
$ sudo nix-daemon
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
poly_configure_nix_daemon_service() {
|
||||
if [ -e /run/systemd/system ]; then
|
||||
task "Setting up the nix-daemon systemd service"
|
||||
|
||||
_sudo "to create the nix-daemon tmpfiles config" \
|
||||
ln -sfn "/nix/var/nix/profiles/default$TMPFILES_SRC" "$TMPFILES_DEST"
|
||||
|
||||
_sudo "to run systemd-tmpfiles once to pick that path up" \
|
||||
systemd-tmpfiles --create --prefix=/nix/var/nix
|
||||
|
||||
_sudo "to set up the nix-daemon service" \
|
||||
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
|
||||
|
||||
_sudo "to set up the nix-daemon socket service" \
|
||||
systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC"
|
||||
|
||||
handle_network_proxy
|
||||
|
||||
_sudo "to load the systemd unit for nix-daemon" \
|
||||
systemctl daemon-reload
|
||||
|
||||
_sudo "to start the nix-daemon.socket" \
|
||||
systemctl start nix-daemon.socket
|
||||
|
||||
_sudo "to start the nix-daemon.service" \
|
||||
systemctl restart nix-daemon.service
|
||||
else
|
||||
reminder "I don't support your init system yet; you may want to add nix-daemon manually."
|
||||
fi
|
||||
}
|
||||
|
||||
poly_group_exists() {
|
||||
getent group "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_group_id_get() {
|
||||
getent group "$1" | cut -d: -f3
|
||||
}
|
||||
|
||||
poly_create_build_group() {
|
||||
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
|
||||
groupadd -g "$NIX_BUILD_GROUP_ID" --system \
|
||||
"$NIX_BUILD_GROUP_NAME" >&2
|
||||
}
|
||||
|
||||
poly_user_exists() {
|
||||
getent passwd "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_user_id_get() {
|
||||
getent passwd "$1" | cut -d: -f3
|
||||
}
|
||||
|
||||
poly_user_hidden_get() {
|
||||
echo "1"
|
||||
}
|
||||
|
||||
poly_user_hidden_set() {
|
||||
true
|
||||
}
|
||||
|
||||
poly_user_home_get() {
|
||||
getent passwd "$1" | cut -d: -f6
|
||||
}
|
||||
|
||||
poly_user_home_set() {
|
||||
_sudo "in order to give $1 a safe home directory" \
|
||||
usermod --home "$2" "$1"
|
||||
}
|
||||
|
||||
poly_user_note_get() {
|
||||
getent passwd "$1" | cut -d: -f5
|
||||
}
|
||||
|
||||
poly_user_note_set() {
|
||||
_sudo "in order to give $1 a useful comment" \
|
||||
usermod --comment "$2" "$1"
|
||||
}
|
||||
|
||||
poly_user_shell_get() {
|
||||
getent passwd "$1" | cut -d: -f7
|
||||
}
|
||||
|
||||
poly_user_shell_set() {
|
||||
_sudo "in order to prevent $1 from logging in" \
|
||||
usermod --shell "$2" "$1"
|
||||
}
|
||||
|
||||
poly_user_in_group_check() {
|
||||
groups "$1" | grep -q "$2" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_user_in_group_set() {
|
||||
_sudo "Add $1 to the $2 group"\
|
||||
usermod --append --groups "$2" "$1"
|
||||
}
|
||||
|
||||
poly_user_primary_group_get() {
|
||||
getent passwd "$1" | cut -d: -f4
|
||||
}
|
||||
|
||||
poly_user_primary_group_set() {
|
||||
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
|
||||
usermod --gid "$2" "$1"
|
||||
|
||||
}
|
||||
|
||||
poly_create_build_user() {
|
||||
username=$1
|
||||
uid=$2
|
||||
builder_num=$3
|
||||
|
||||
_sudo "Creating the Nix build user, $username" \
|
||||
useradd \
|
||||
--home-dir /var/empty \
|
||||
--comment "Nix build user $builder_num" \
|
||||
--gid "$NIX_BUILD_GROUP_ID" \
|
||||
--groups "$NIX_BUILD_GROUP_NAME" \
|
||||
--no-user-group \
|
||||
--system \
|
||||
--shell /sbin/nologin \
|
||||
--uid "$uid" \
|
||||
--password "!" \
|
||||
"$username"
|
||||
}
|
||||
|
||||
poly_prepare_to_install() {
|
||||
:
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script installs the Nix package manager on your system by
|
||||
# downloading a binary distribution and running its installer script
|
||||
# (which in turn creates and populates /nix).
|
||||
|
||||
{ # Prevent execution if this script was only partially downloaded
|
||||
oops() {
|
||||
echo "$0:" "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
umask 0022
|
||||
|
||||
tmpDir="$(mktemp -d -t nix-binary-tarball-unpack.XXXXXXXXXX || \
|
||||
oops "Can't create temporary directory for downloading the Nix binary tarball")"
|
||||
cleanup() {
|
||||
rm -rf "$tmpDir"
|
||||
}
|
||||
trap cleanup EXIT INT QUIT TERM
|
||||
|
||||
require_util() {
|
||||
command -v "$1" > /dev/null 2>&1 ||
|
||||
oops "you do not have '$1' installed, which I need to $2"
|
||||
}
|
||||
|
||||
case "$(uname -s).$(uname -m)" in
|
||||
Linux.x86_64)
|
||||
hash=@tarballHash_x86_64-linux@
|
||||
path=@tarballPath_x86_64-linux@
|
||||
system=x86_64-linux
|
||||
;;
|
||||
Linux.i?86)
|
||||
hash=@tarballHash_i686-linux@
|
||||
path=@tarballPath_i686-linux@
|
||||
system=i686-linux
|
||||
;;
|
||||
Linux.aarch64)
|
||||
hash=@tarballHash_aarch64-linux@
|
||||
path=@tarballPath_aarch64-linux@
|
||||
system=aarch64-linux
|
||||
;;
|
||||
Linux.armv6l)
|
||||
hash=@tarballHash_armv6l-linux@
|
||||
path=@tarballPath_armv6l-linux@
|
||||
system=armv6l-linux
|
||||
;;
|
||||
Linux.armv7l)
|
||||
hash=@tarballHash_armv7l-linux@
|
||||
path=@tarballPath_armv7l-linux@
|
||||
system=armv7l-linux
|
||||
;;
|
||||
Darwin.x86_64)
|
||||
hash=@tarballHash_x86_64-darwin@
|
||||
path=@tarballPath_x86_64-darwin@
|
||||
system=x86_64-darwin
|
||||
;;
|
||||
Darwin.arm64|Darwin.aarch64)
|
||||
hash=@tarballHash_aarch64-darwin@
|
||||
path=@tarballPath_aarch64-darwin@
|
||||
system=aarch64-darwin
|
||||
;;
|
||||
*) oops "sorry, there is no binary distribution of Nix for your platform";;
|
||||
esac
|
||||
|
||||
# Use this command-line option to fetch the tarballs using nar-serve or Cachix
|
||||
if [ "${1:-}" = "--tarball-url-prefix" ]; then
|
||||
if [ -z "${2:-}" ]; then
|
||||
oops "missing argument for --tarball-url-prefix"
|
||||
fi
|
||||
url=${2}/${path}
|
||||
shift 2
|
||||
else
|
||||
url=https://releases.nixos.org/nix/nix-@nixVersion@/nix-@nixVersion@-$system.tar.xz
|
||||
fi
|
||||
|
||||
tarball=$tmpDir/nix-@nixVersion@-$system.tar.xz
|
||||
|
||||
require_util tar "unpack the binary tarball"
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
require_util xz "unpack the binary tarball"
|
||||
fi
|
||||
|
||||
if command -v curl > /dev/null 2>&1; then
|
||||
fetch() { curl --fail -L "$1" -o "$2"; }
|
||||
elif command -v wget > /dev/null 2>&1; then
|
||||
fetch() { wget "$1" -O "$2"; }
|
||||
else
|
||||
oops "you don't have wget or curl installed, which I need to download the binary tarball"
|
||||
fi
|
||||
|
||||
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
|
||||
fetch "$url" "$tarball" || oops "failed to download '$url'"
|
||||
|
||||
if command -v sha256sum > /dev/null 2>&1; then
|
||||
hash2="$(sha256sum -b "$tarball" | cut -c1-64)"
|
||||
elif command -v shasum > /dev/null 2>&1; then
|
||||
hash2="$(shasum -a 256 -b "$tarball" | cut -c1-64)"
|
||||
elif command -v openssl > /dev/null 2>&1; then
|
||||
hash2="$(openssl dgst -r -sha256 "$tarball" | cut -c1-64)"
|
||||
else
|
||||
oops "cannot verify the SHA-256 hash of '$url'; you need one of 'shasum', 'sha256sum', or 'openssl'"
|
||||
fi
|
||||
|
||||
if [ "$hash" != "$hash2" ]; then
|
||||
oops "SHA-256 hash mismatch in '$url'; expected $hash, got $hash2"
|
||||
fi
|
||||
|
||||
unpack=$tmpDir/unpack
|
||||
mkdir -p "$unpack"
|
||||
tar -xJf "$tarball" -C "$unpack" || oops "failed to unpack '$url'"
|
||||
|
||||
script=$(echo "$unpack"/*/install)
|
||||
|
||||
[ -e "$script" ] || oops "installation script is missing from the binary tarball!"
|
||||
export INVOKED_FROM_INSTALL_IN=1
|
||||
"$script" "$@"
|
||||
|
||||
} # End of wrapping
|
||||
@@ -7,7 +7,5 @@ profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.fish, $(profiledir)/nix.fish, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile-daemon.fish, $(profiledir)/nix-daemon.fish, 0644))
|
||||
|
||||
clean-files += $(nix_noinst_scripts)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
function add_path --argument-names new_path
|
||||
if type -q fish_add_path
|
||||
# fish 3.2.0 or newer
|
||||
fish_add_path --prepend --global $new_path
|
||||
else
|
||||
# older versions of fish
|
||||
if not contains $new_path $fish_user_paths
|
||||
set --global fish_user_paths $new_path $fish_user_paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Only execute this file once per shell.
|
||||
if test -n "$__ETC_PROFILE_NIX_SOURCED"
|
||||
exit
|
||||
end
|
||||
|
||||
set __ETC_PROFILE_NIX_SOURCED 1
|
||||
|
||||
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if test -z "$XDG_DATA_DIRS"
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
|
||||
end
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if test -n "$NIX_SSH_CERT_FILE"
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
|
||||
else if test -e /etc/ssl/ca-bundle.pem # openSUSE Tumbleweed
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/ca-bundle.pem
|
||||
else if test -e /etc/ssl/certs/ca-bundle.crt # Old NixOS
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-bundle.crt
|
||||
else if test -e /etc/pki/tls/certs/ca-bundle.crt # Fedora, CentOS
|
||||
set --export NIX_SSL_CERT_FILE /etc/pki/tls/certs/ca-bundle.crt
|
||||
else if test -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" # fall back to cacert in Nix profile
|
||||
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
|
||||
else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile
|
||||
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt"
|
||||
else
|
||||
# Fall back to what is in the nix profiles, favouring whatever is defined last.
|
||||
for i in $NIX_PROFILES
|
||||
if test -e "$i/etc/ssl/certs/ca-bundle.crt"
|
||||
set --export NIX_SSL_CERT_FILE "$i/etc/ssl/certs/ca-bundle.crt"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add_path "@localstatedir@/nix/profiles/default/bin"
|
||||
add_path "$HOME/.nix-profile/bin"
|
||||
|
||||
functions -e add_path
|
||||
@@ -1,72 +0,0 @@
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
||||
__ETC_PROFILE_NIX_SOURCED=1
|
||||
|
||||
NIX_LINK=$HOME/.nix-profile
|
||||
if [ -n "${XDG_STATE_HOME-}" ]; then
|
||||
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||
else
|
||||
NIX_LINK_NEW=$HOME/.local/state/nix/profile
|
||||
fi
|
||||
if [ -e "$NIX_LINK_NEW" ]; then
|
||||
NIX_LINK="$NIX_LINK_NEW"
|
||||
else
|
||||
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||
warning="\033[1;35mwarning:\033[0m"
|
||||
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||
else
|
||||
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||
# 1. Update to newer Nix;
|
||||
# 2. Remove .nix-profile;
|
||||
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||
# 4. Roll back to older Nix.
|
||||
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if [ -z "${XDG_DATA_DIRS-}" ]; then
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
fi
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
|
||||
elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
|
||||
elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
|
||||
export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
|
||||
else
|
||||
# Fall back to what is in the nix profiles, favouring whatever is defined last.
|
||||
check_nix_profiles() {
|
||||
if [ -n "$ZSH_VERSION" ]; then
|
||||
# Zsh by default doesn't split words in unquoted parameter expansion.
|
||||
# Set local_options for these options to be reverted at the end of the function
|
||||
# and shwordsplit to force splitting words in $NIX_PROFILES below.
|
||||
setopt local_options shwordsplit
|
||||
fi
|
||||
for i in $NIX_PROFILES; do
|
||||
if [ -e "$i/etc/ssl/certs/ca-bundle.crt" ]; then
|
||||
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
|
||||
fi
|
||||
done
|
||||
}
|
||||
check_nix_profiles
|
||||
unset -f check_nix_profiles
|
||||
fi
|
||||
|
||||
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
||||
unset NIX_LINK
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
|
||||
installerHash=$(echo "$script" | cut -b12-43 -)
|
||||
|
||||
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
|
||||
|
||||
echo "::set-output name=installerURL::$installerURL"
|
||||
@@ -126,7 +126,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||
mkdir(currentLoad.c_str(), 0777);
|
||||
|
||||
while (true) {
|
||||
bestSlotLock = -1;
|
||||
bestSlotLock.reset();
|
||||
AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true);
|
||||
lockFile(lock.get(), ltWrite, true);
|
||||
|
||||
@@ -229,7 +229,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||
futimens(bestSlotLock.get(), NULL);
|
||||
#endif
|
||||
|
||||
lock = -1;
|
||||
lock.reset();
|
||||
|
||||
try {
|
||||
|
||||
@@ -282,7 +282,7 @@ connected:
|
||||
copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
||||
}
|
||||
|
||||
uploadLock = -1;
|
||||
uploadLock.reset();
|
||||
|
||||
auto drv = store->readDerivation(*drvPath);
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s)
|
||||
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
|
||||
}
|
||||
|
||||
else if (hasPrefix(s, "flake:")) {
|
||||
else if (s.starts_with("flake:")) {
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
|
||||
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
|
||||
|
||||
@@ -159,7 +159,7 @@ void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
|
||||
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
|
||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||
for (auto & input : flake.inputs)
|
||||
if (hasPrefix(input.first, prefix))
|
||||
if (input.first.starts_with(prefix))
|
||||
completions->add(input.first);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +320,7 @@ void completeFlakeRefWithFragment(
|
||||
auto attrPath = parseAttrPath(*evalState, attrPathS);
|
||||
|
||||
std::string lastAttr;
|
||||
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
|
||||
if (!attrPath.empty() && !attrPathS.ends_with(".")) {
|
||||
lastAttr = evalState->symbols[attrPath.back()];
|
||||
attrPath.pop_back();
|
||||
}
|
||||
@@ -329,7 +329,7 @@ void completeFlakeRefWithFragment(
|
||||
if (!attr) continue;
|
||||
|
||||
for (auto & attr2 : (*attr)->getAttrs()) {
|
||||
if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
|
||||
if (std::string_view(evalState->symbols[attr2]).starts_with(lastAttr)) {
|
||||
auto attrPath2 = (*attr)->getAttrPath(attr2);
|
||||
/* Strip the attrpath prefix. */
|
||||
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
||||
@@ -367,12 +367,12 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
for (auto & registry : fetchers::getRegistries(store)) {
|
||||
for (auto & entry : registry->entries) {
|
||||
auto from = entry.from.to_string();
|
||||
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
|
||||
if (!prefix.starts_with("flake:") && from.starts_with("flake:")) {
|
||||
std::string from2(from, 6);
|
||||
if (hasPrefix(from2, prefix))
|
||||
if (from2.starts_with(prefix))
|
||||
completions->add(from2);
|
||||
} else {
|
||||
if (hasPrefix(from, prefix))
|
||||
if (from.starts_with(prefix))
|
||||
completions->add(from);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
libcmd_sources = files(
|
||||
'built-path.cc',
|
||||
'command-installable-value.cc',
|
||||
'command.cc',
|
||||
'common-eval-args.cc',
|
||||
'editor-for.cc',
|
||||
'installable-attr-path.cc',
|
||||
'installable-derived-path.cc',
|
||||
'installable-flake.cc',
|
||||
'installable-value.cc',
|
||||
'installables.cc',
|
||||
'legacy.cc',
|
||||
'markdown.cc',
|
||||
'repl.cc',
|
||||
'repl-interacter.cc',
|
||||
)
|
||||
|
||||
libcmd_headers = files(
|
||||
'built-path.hh',
|
||||
'command-installable-value.hh',
|
||||
'command.hh',
|
||||
'common-eval-args.hh',
|
||||
'editor-for.hh',
|
||||
'installable-attr-path.hh',
|
||||
'installable-derived-path.hh',
|
||||
'installable-flake.hh',
|
||||
'installable-value.hh',
|
||||
'installables.hh',
|
||||
'legacy.hh',
|
||||
'markdown.hh',
|
||||
'repl-interacter.hh',
|
||||
'repl.hh',
|
||||
)
|
||||
|
||||
libcmd = library(
|
||||
'nixcmd',
|
||||
libcmd_sources,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixexpr,
|
||||
liblixfetchers,
|
||||
liblixmain,
|
||||
boehm,
|
||||
editline,
|
||||
lowdown,
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(libcmd_headers, subdir : 'nix', preserve_path : true)
|
||||
|
||||
liblixcmd = declare_dependency(
|
||||
include_directories : '.',
|
||||
link_with : libcmd,
|
||||
)
|
||||
@@ -0,0 +1,208 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef READLINE
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#else
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
// This results in linker errors due to to name-mangling of editline C symbols.
|
||||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "repl-interacter.hh"
|
||||
#include "util.hh"
|
||||
#include "repl.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace {
|
||||
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
|
||||
volatile sig_atomic_t g_signal_received = 0;
|
||||
|
||||
void sigintHandler(int signo)
|
||||
{
|
||||
g_signal_received = signo;
|
||||
}
|
||||
};
|
||||
|
||||
static detail::ReplCompleterMixin * curRepl; // ugly
|
||||
|
||||
static char * completionCallback(char * s, int * match)
|
||||
{
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
if (possible.size() == 1) {
|
||||
*match = 1;
|
||||
auto * res = strdup(possible.begin()->c_str() + strlen(s));
|
||||
if (!res)
|
||||
throw Error("allocation failure");
|
||||
return res;
|
||||
} else if (possible.size() > 1) {
|
||||
auto checkAllHaveSameAt = [&](size_t pos) {
|
||||
auto & first = *possible.begin();
|
||||
for (auto & p : possible) {
|
||||
if (p.size() <= pos || p[pos] != first[pos])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
size_t start = strlen(s);
|
||||
size_t len = 0;
|
||||
while (checkAllHaveSameAt(start + len))
|
||||
++len;
|
||||
if (len > 0) {
|
||||
*match = 1;
|
||||
auto * res = strdup(std::string(*possible.begin(), start, len).c_str());
|
||||
if (!res)
|
||||
throw Error("allocation failure");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*match = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int listPossibleCallback(char * s, char *** avp)
|
||||
{
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
|
||||
if (possible.size() > (INT_MAX / sizeof(char *)))
|
||||
throw Error("too many completions");
|
||||
|
||||
int ac = 0;
|
||||
char ** vp = nullptr;
|
||||
|
||||
auto check = [&](auto * p) {
|
||||
if (!p) {
|
||||
if (vp) {
|
||||
while (--ac >= 0)
|
||||
free(vp[ac]);
|
||||
free(vp);
|
||||
}
|
||||
throw Error("allocation failure");
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
vp = check((char **) malloc(possible.size() * sizeof(char *)));
|
||||
|
||||
for (auto & p : possible)
|
||||
vp[ac++] = check(strdup(p.c_str()));
|
||||
|
||||
*avp = vp;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
|
||||
{
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef READLINE
|
||||
el_hist_size = 1000;
|
||||
#endif
|
||||
read_history(historyFile.c_str());
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
#ifndef READLINE
|
||||
rl_set_complete_func(completionCallback);
|
||||
rl_set_list_possib_func(listPossibleCallback);
|
||||
#endif
|
||||
return restoreRepl;
|
||||
}
|
||||
|
||||
static constexpr const char * promptForType(ReplPromptType promptType)
|
||||
{
|
||||
switch (promptType) {
|
||||
case ReplPromptType::ReplPrompt:
|
||||
return "nix-repl> ";
|
||||
case ReplPromptType::ContinuationPrompt:
|
||||
return " ";
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
auto setupSignals = [&]() {
|
||||
act.sa_handler = sigintHandler;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGINT, &act, &old))
|
||||
throw SysError("installing handler for SIGINT");
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
|
||||
throw SysError("unblocking SIGINT");
|
||||
};
|
||||
auto restoreSignals = [&]() {
|
||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||
throw SysError("restoring signals");
|
||||
|
||||
if (sigaction(SIGINT, &old, 0))
|
||||
throw SysError("restoring handler for SIGINT");
|
||||
};
|
||||
|
||||
setupSignals();
|
||||
char * s = readline(promptForType(promptType));
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
input += s;
|
||||
input += '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
||||
{
|
||||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
AutomationInteracter::Guard AutomationInteracter::init(detail::ReplCompleterMixin *)
|
||||
{
|
||||
return Guard([] {});
|
||||
}
|
||||
|
||||
// ASCII ENQ character
|
||||
constexpr const char * automationPrompt = "\x05";
|
||||
|
||||
bool AutomationInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
std::cout << std::unitbuf;
|
||||
std::cout << automationPrompt;
|
||||
if (!std::getline(std::cin, input)) {
|
||||
// reset failure bits on EOF
|
||||
std::cin.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
/// @file
|
||||
|
||||
#include "finally.hh"
|
||||
#include "types.hh"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace detail {
|
||||
/** Provides the completion hooks for the repl, without exposing its complete
|
||||
* internals. */
|
||||
struct ReplCompleterMixin {
|
||||
virtual StringSet completePrefix(const std::string & prefix) = 0;
|
||||
};
|
||||
};
|
||||
|
||||
enum class ReplPromptType {
|
||||
ReplPrompt,
|
||||
ContinuationPrompt,
|
||||
};
|
||||
|
||||
class ReplInteracter
|
||||
{
|
||||
public:
|
||||
using Guard = Finally<std::function<void()>>;
|
||||
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
|
||||
/** Returns a boolean of whether the interacter got EOF */
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
|
||||
virtual ~ReplInteracter(){};
|
||||
};
|
||||
|
||||
class ReadlineLikeInteracter : public virtual ReplInteracter
|
||||
{
|
||||
std::string historyFile;
|
||||
public:
|
||||
ReadlineLikeInteracter(std::string historyFile)
|
||||
: historyFile(historyFile)
|
||||
{
|
||||
}
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~ReadlineLikeInteracter() override;
|
||||
};
|
||||
|
||||
class AutomationInteracter : public virtual ReplInteracter
|
||||
{
|
||||
public:
|
||||
AutomationInteracter() = default;
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~AutomationInteracter() override = default;
|
||||
};
|
||||
|
||||
};
|
||||
+67
-182
@@ -3,22 +3,8 @@
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef READLINE
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#else
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
// This results in linker errors due to to name-mangling of editline C symbols.
|
||||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "box_ptr.hh"
|
||||
#include "repl-interacter.hh"
|
||||
#include "repl.hh"
|
||||
|
||||
#include "ansicolor.hh"
|
||||
@@ -28,6 +14,7 @@ extern "C" {
|
||||
#include "eval-inline.hh"
|
||||
#include "eval-settings.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "signals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "log-store.hh"
|
||||
#include "common-eval-args.hh"
|
||||
@@ -40,6 +27,7 @@ extern "C" {
|
||||
#include "finally.hh"
|
||||
#include "markdown.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "signals.hh"
|
||||
#include "print.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -49,8 +37,30 @@ extern "C" {
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Returned by `NixRepl::processLine`.
|
||||
*/
|
||||
enum class ProcessLineResult {
|
||||
/**
|
||||
* The user exited with `:quit`. The REPL should exit. The surrounding
|
||||
* program or evaluation (e.g., if the REPL was acting as the debugger)
|
||||
* should also exit.
|
||||
*/
|
||||
Quit,
|
||||
/**
|
||||
* The user exited with `:continue`. The REPL should exit, but the program
|
||||
* should continue running.
|
||||
*/
|
||||
Continue,
|
||||
/**
|
||||
* The user did not exit. The REPL should request another line of input.
|
||||
*/
|
||||
PromptAgain,
|
||||
};
|
||||
|
||||
struct NixRepl
|
||||
: AbstractNixRepl
|
||||
, detail::ReplCompleterMixin
|
||||
#if HAVE_BOEHMGC
|
||||
, gc
|
||||
#endif
|
||||
@@ -66,19 +76,18 @@ struct NixRepl
|
||||
int displ;
|
||||
StringSet varNames;
|
||||
|
||||
const Path historyFile;
|
||||
box_ptr<ReplInteracter> interacter;
|
||||
|
||||
NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues);
|
||||
virtual ~NixRepl();
|
||||
virtual ~NixRepl() = default;
|
||||
|
||||
void mainLoop() override;
|
||||
ReplExitStatus mainLoop() override;
|
||||
void initEnv() override;
|
||||
|
||||
StringSet completePrefix(const std::string & prefix);
|
||||
bool getLine(std::string & input, const std::string & prompt);
|
||||
virtual StringSet completePrefix(const std::string & prefix) override;
|
||||
StorePath getDerivationPath(Value & v);
|
||||
bool processLine(std::string line);
|
||||
ProcessLineResult processLine(std::string line);
|
||||
|
||||
void loadFile(const Path & path);
|
||||
void loadFlake(const std::string & flakeRef);
|
||||
@@ -98,7 +107,8 @@ struct NixRepl
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth
|
||||
.maxDepth = maxDepth,
|
||||
.prettyIndent = 2
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -111,6 +121,12 @@ std::string removeWhitespace(std::string s)
|
||||
return s;
|
||||
}
|
||||
|
||||
static box_ptr<ReplInteracter> makeInteracter() {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ReplAutomation))
|
||||
return make_box_ptr<AutomationInteracter>();
|
||||
else
|
||||
return make_box_ptr<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history");
|
||||
}
|
||||
|
||||
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues)
|
||||
@@ -118,16 +134,10 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalS
|
||||
, debugTraceIndex(0)
|
||||
, getValues(getValues)
|
||||
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
|
||||
, historyFile(getDataDir() + "/nix/repl-history")
|
||||
, interacter(makeInteracter())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NixRepl::~NixRepl()
|
||||
{
|
||||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
void runNix(Path program, const Strings & args,
|
||||
const std::optional<std::string> & input = {})
|
||||
{
|
||||
@@ -144,79 +154,6 @@ void runNix(Path program, const Strings & args,
|
||||
return;
|
||||
}
|
||||
|
||||
static NixRepl * curRepl; // ugly
|
||||
|
||||
static char * completionCallback(char * s, int *match) {
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
if (possible.size() == 1) {
|
||||
*match = 1;
|
||||
auto *res = strdup(possible.begin()->c_str() + strlen(s));
|
||||
if (!res) throw Error("allocation failure");
|
||||
return res;
|
||||
} else if (possible.size() > 1) {
|
||||
auto checkAllHaveSameAt = [&](size_t pos) {
|
||||
auto &first = *possible.begin();
|
||||
for (auto &p : possible) {
|
||||
if (p.size() <= pos || p[pos] != first[pos])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
size_t start = strlen(s);
|
||||
size_t len = 0;
|
||||
while (checkAllHaveSameAt(start + len)) ++len;
|
||||
if (len > 0) {
|
||||
*match = 1;
|
||||
auto *res = strdup(std::string(*possible.begin(), start, len).c_str());
|
||||
if (!res) throw Error("allocation failure");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*match = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int listPossibleCallback(char *s, char ***avp) {
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
|
||||
if (possible.size() > (INT_MAX / sizeof(char*)))
|
||||
throw Error("too many completions");
|
||||
|
||||
int ac = 0;
|
||||
char **vp = nullptr;
|
||||
|
||||
auto check = [&](auto *p) {
|
||||
if (!p) {
|
||||
if (vp) {
|
||||
while (--ac >= 0)
|
||||
free(vp[ac]);
|
||||
free(vp);
|
||||
}
|
||||
throw Error("allocation failure");
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
vp = check((char **)malloc(possible.size() * sizeof(char*)));
|
||||
|
||||
for (auto & p : possible)
|
||||
vp[ac++] = check(strdup(p.c_str()));
|
||||
|
||||
*avp = vp;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
|
||||
volatile sig_atomic_t g_signal_received = 0;
|
||||
|
||||
void sigintHandler(int signo) {
|
||||
g_signal_received = signo;
|
||||
}
|
||||
}
|
||||
|
||||
static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
|
||||
{
|
||||
if (dt.isError)
|
||||
@@ -242,7 +179,7 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
||||
|
||||
static bool isFirstRepl = true;
|
||||
|
||||
void NixRepl::mainLoop()
|
||||
ReplExitStatus NixRepl::mainLoop()
|
||||
{
|
||||
if (isFirstRepl) {
|
||||
std::string_view debuggerNotice = "";
|
||||
@@ -256,24 +193,7 @@ void NixRepl::mainLoop()
|
||||
|
||||
loadFiles();
|
||||
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef READLINE
|
||||
el_hist_size = 1000;
|
||||
#endif
|
||||
read_history(historyFile.c_str());
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = this;
|
||||
Finally restoreRepl([&] { curRepl = oldRepl; });
|
||||
#ifndef READLINE
|
||||
rl_set_complete_func(completionCallback);
|
||||
rl_set_list_possib_func(listPossibleCallback);
|
||||
#endif
|
||||
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
||||
|
||||
std::string input;
|
||||
|
||||
@@ -282,16 +202,26 @@ void NixRepl::mainLoop()
|
||||
logger->pause();
|
||||
// When continuing input from previous lines, don't print a prompt, just align to the same
|
||||
// number of chars as the prompt.
|
||||
if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
|
||||
// ctrl-D should exit the debugger.
|
||||
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
||||
// Ctrl-D should exit the debugger.
|
||||
state->debugStop = false;
|
||||
state->debugQuit = true;
|
||||
logger->cout("");
|
||||
break;
|
||||
// TODO: Should Ctrl-D exit just the current debugger session or
|
||||
// the entire program?
|
||||
return ReplExitStatus::QuitAll;
|
||||
}
|
||||
logger->resume();
|
||||
try {
|
||||
if (!removeWhitespace(input).empty() && !processLine(input)) return;
|
||||
switch (processLine(input)) {
|
||||
case ProcessLineResult::Quit:
|
||||
return ReplExitStatus::QuitAll;
|
||||
case ProcessLineResult::Continue:
|
||||
return ReplExitStatus::Continue;
|
||||
case ProcessLineResult::PromptAgain:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} catch (ParseError & e) {
|
||||
if (e.msg().find("unexpected end of file") != std::string::npos) {
|
||||
// For parse errors on incomplete input, we continue waiting for the next line of
|
||||
@@ -321,51 +251,6 @@ void NixRepl::mainLoop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NixRepl::getLine(std::string & input, const std::string & prompt)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
auto setupSignals = [&]() {
|
||||
act.sa_handler = sigintHandler;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGINT, &act, &old))
|
||||
throw SysError("installing handler for SIGINT");
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
|
||||
throw SysError("unblocking SIGINT");
|
||||
};
|
||||
auto restoreSignals = [&]() {
|
||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||
throw SysError("restoring signals");
|
||||
|
||||
if (sigaction(SIGINT, &old, 0))
|
||||
throw SysError("restoring handler for SIGINT");
|
||||
};
|
||||
|
||||
setupSignals();
|
||||
char * s = readline(prompt.c_str());
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
input += s;
|
||||
input += '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
{
|
||||
StringSet completions;
|
||||
@@ -387,7 +272,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
auto dir = std::string(cur, 0, slash);
|
||||
auto prefix2 = std::string(cur, slash + 1);
|
||||
for (auto & entry : readDirectory(dir == "" ? "/" : dir)) {
|
||||
if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2))
|
||||
if (entry.name[0] != '.' && entry.name.starts_with(prefix2))
|
||||
completions.insert(prev + dir + "/" + entry.name);
|
||||
}
|
||||
} catch (Error &) {
|
||||
@@ -478,10 +363,11 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
|
||||
}
|
||||
}
|
||||
|
||||
bool NixRepl::processLine(std::string line)
|
||||
ProcessLineResult NixRepl::processLine(std::string line)
|
||||
{
|
||||
line = trim(line);
|
||||
if (line == "") return true;
|
||||
if (line.empty())
|
||||
return ProcessLineResult::PromptAgain;
|
||||
|
||||
_isInterrupted = false;
|
||||
|
||||
@@ -576,13 +462,13 @@ bool NixRepl::processLine(std::string line)
|
||||
else if (state->debugRepl && (command == ":s" || command == ":step")) {
|
||||
// set flag to stop at next DebugTrace; exit repl.
|
||||
state->debugStop = true;
|
||||
return false;
|
||||
return ProcessLineResult::Continue;
|
||||
}
|
||||
|
||||
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
|
||||
// set flag to run to next breakpoint or end of program; exit repl.
|
||||
state->debugStop = false;
|
||||
return false;
|
||||
return ProcessLineResult::Continue;
|
||||
}
|
||||
|
||||
else if (command == ":a" || command == ":add") {
|
||||
@@ -725,8 +611,7 @@ bool NixRepl::processLine(std::string line)
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
state->debugStop = false;
|
||||
state->debugQuit = true;
|
||||
return false;
|
||||
return ProcessLineResult::Quit;
|
||||
}
|
||||
|
||||
else if (command == ":doc") {
|
||||
@@ -787,7 +672,7 @@ bool NixRepl::processLine(std::string line)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ProcessLineResult::PromptAgain;
|
||||
}
|
||||
|
||||
void NixRepl::loadFile(const Path & path)
|
||||
@@ -918,7 +803,7 @@ std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
}
|
||||
|
||||
|
||||
void AbstractNixRepl::runSimple(
|
||||
ReplExitStatus AbstractNixRepl::runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv)
|
||||
{
|
||||
@@ -940,7 +825,7 @@ void AbstractNixRepl::runSimple(
|
||||
for (auto & [name, value] : extraEnv)
|
||||
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
||||
|
||||
repl->mainLoop();
|
||||
return repl->mainLoop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-7
@@ -3,11 +3,6 @@
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#define GC_INCLUDE_NEW
|
||||
#include <gc/gc_cpp.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct AbstractNixRepl
|
||||
@@ -28,13 +23,13 @@ struct AbstractNixRepl
|
||||
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues);
|
||||
|
||||
static void runSimple(
|
||||
static ReplExitStatus runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv);
|
||||
|
||||
virtual void initEnv() = 0;
|
||||
|
||||
virtual void mainLoop() = 0;
|
||||
virtual ReplExitStatus mainLoop() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,15 +28,7 @@ template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
|
||||
{
|
||||
error.err.traces.push_front(
|
||||
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = false});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrameTrace(PosIdx pos, const std::string_view text)
|
||||
{
|
||||
error.err.traces.push_front(
|
||||
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = true});
|
||||
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text))});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -63,9 +55,9 @@ EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint, bool frame)
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint)
|
||||
{
|
||||
error.addTrace(error.state.positions[pos], hint, frame);
|
||||
error.addTrace(error.state.positions[pos], hint);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint, bool frame = false);
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint);
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
|
||||
|
||||
@@ -29,7 +29,7 @@ static Strings parseNixPath(const std::string & s)
|
||||
|
||||
if (*p == ':') {
|
||||
auto prefix = std::string(start2, s.end());
|
||||
if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) {
|
||||
if (EvalSettings::isPseudoUrl(prefix) || prefix.starts_with("flake:")) {
|
||||
++p;
|
||||
while (p != s.end() && *p != ':') ++p;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ bool EvalSettings::isPseudoUrl(std::string_view s)
|
||||
|
||||
std::string EvalSettings::resolvePseudoUrl(std::string_view url)
|
||||
{
|
||||
if (hasPrefix(url, "channel:"))
|
||||
if (url.starts_with("channel:"))
|
||||
return "https://nixos.org/channels/" + std::string(url.substr(8)) + "/nixexprs.tar.xz";
|
||||
else
|
||||
return std::string(url);
|
||||
|
||||
@@ -114,6 +114,16 @@ struct EvalSettings : Config
|
||||
|
||||
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth",
|
||||
"The maximum function call depth to allow before erroring."};
|
||||
|
||||
Setting<bool> builtinsTraceDebugger{this, false, "debugger-on-trace",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given,
|
||||
[`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will
|
||||
enter the debugger like
|
||||
[`builtins.break`](@docroot@/language/builtins.md#builtins-break).
|
||||
|
||||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
};
|
||||
|
||||
extern EvalSettings evalSettings;
|
||||
|
||||
+93
-43
@@ -3,6 +3,7 @@
|
||||
#include "hash.hh"
|
||||
#include "primops.hh"
|
||||
#include "print-options.hh"
|
||||
#include "shared.hh"
|
||||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
#include "store-api.hh"
|
||||
@@ -391,7 +392,6 @@ EvalState::EvalState(
|
||||
, buildStore(buildStore ? buildStore : store)
|
||||
, debugRepl(nullptr)
|
||||
, debugStop(false)
|
||||
, debugQuit(false)
|
||||
, trylevel(0)
|
||||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -484,7 +484,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
|
||||
*/
|
||||
Path abspath = canonPath(path_.path.abs());
|
||||
|
||||
if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
|
||||
if (abspath.starts_with(corepkgsPrefix)) return CanonPath(abspath);
|
||||
|
||||
for (auto & i : *allowedPaths) {
|
||||
if (isDirOrInDir(abspath, i)) {
|
||||
@@ -527,18 +527,18 @@ void EvalState::checkURI(const std::string & uri)
|
||||
if (uri == prefix ||
|
||||
(uri.size() > prefix.size()
|
||||
&& prefix.size() > 0
|
||||
&& hasPrefix(uri, prefix)
|
||||
&& uri.starts_with(prefix)
|
||||
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
|
||||
return;
|
||||
|
||||
/* If the URI is a path, then check it against allowedPaths as
|
||||
well. */
|
||||
if (hasPrefix(uri, "/")) {
|
||||
if (uri.starts_with("/")) {
|
||||
checkSourcePath(CanonPath(uri));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPrefix(uri, "file://")) {
|
||||
if (uri.starts_with("file://")) {
|
||||
checkSourcePath(CanonPath(std::string(uri, 7)));
|
||||
return;
|
||||
}
|
||||
@@ -642,7 +642,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||
}
|
||||
|
||||
auto envName = symbols.create(primOp.name);
|
||||
if (hasPrefix(primOp.name, "__"))
|
||||
if (primOp.name.starts_with("__"))
|
||||
primOp.name = primOp.name.substr(2);
|
||||
|
||||
Value * v = allocValue();
|
||||
@@ -677,12 +677,21 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||
}
|
||||
|
||||
|
||||
static std::set<std::string_view> sortedBindingNames(const SymbolTable & st, const StaticEnv & se)
|
||||
{
|
||||
std::set<std::string_view> bindings;
|
||||
for (auto [symbol, displ] : se.vars)
|
||||
bindings.emplace(st[symbol]);
|
||||
return bindings;
|
||||
}
|
||||
|
||||
|
||||
// just for the current level of StaticEnv, not the whole chain.
|
||||
void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
|
||||
{
|
||||
std::cout << ANSI_MAGENTA;
|
||||
for (auto & i : se.vars)
|
||||
std::cout << st[i.first] << " ";
|
||||
for (auto & i : sortedBindingNames(st, se))
|
||||
std::cout << i << " ";
|
||||
std::cout << ANSI_NORMAL;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -691,13 +700,14 @@ void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
|
||||
void printWithBindings(const SymbolTable & st, const Env & env)
|
||||
{
|
||||
if (!env.values[0]->isThunk()) {
|
||||
std::set<std::string_view> bindings;
|
||||
for (const auto & attr : *env.values[0]->attrs)
|
||||
bindings.emplace(st[attr.name]);
|
||||
|
||||
std::cout << "with: ";
|
||||
std::cout << ANSI_MAGENTA;
|
||||
Bindings::iterator j = env.values[0]->attrs->begin();
|
||||
while (j != env.values[0]->attrs->end()) {
|
||||
std::cout << st[j->name] << " ";
|
||||
++j;
|
||||
}
|
||||
for (auto & i : bindings)
|
||||
std::cout << i << " ";
|
||||
std::cout << ANSI_NORMAL;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -718,9 +728,9 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env &
|
||||
std::cout << ANSI_MAGENTA;
|
||||
// for the top level, don't print the double underscore ones;
|
||||
// they are in builtins.
|
||||
for (auto & i : se.vars)
|
||||
if (!hasPrefix(st[i.first], "__"))
|
||||
std::cout << st[i.first] << " ";
|
||||
for (auto & i : sortedBindingNames(st, se))
|
||||
if (!i.starts_with("__"))
|
||||
std::cout << i << " ";
|
||||
std::cout << ANSI_NORMAL;
|
||||
std::cout << std::endl;
|
||||
if (se.isWith)
|
||||
@@ -798,18 +808,30 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
||||
auto se = getStaticEnv(expr);
|
||||
if (se) {
|
||||
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
|
||||
(debugRepl)(ref<EvalState>(shared_from_this()), *vm);
|
||||
auto exitStatus = (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
|
||||
switch (exitStatus) {
|
||||
case ReplExitStatus::QuitAll:
|
||||
if (error)
|
||||
throw *error;
|
||||
throw Exit(0);
|
||||
case ReplExitStatus::Continue:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||
template<typename... Args>
|
||||
void EvalState::addErrorTrace(Error & e, const Args & ... formatArgs) const
|
||||
{
|
||||
e.addTrace(nullptr, s, s2);
|
||||
e.addTrace(nullptr, HintFmt(formatArgs...));
|
||||
}
|
||||
|
||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
|
||||
template<typename... Args>
|
||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const
|
||||
{
|
||||
e.addTrace(positions[pos], HintFmt(s, s2), frame);
|
||||
e.addTrace(positions[pos], HintFmt(formatArgs...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
@@ -928,12 +950,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||
|
||||
void EvalState::mkPos(Value & v, PosIdx p)
|
||||
{
|
||||
auto pos = positions[p];
|
||||
if (auto path = std::get_if<SourcePath>(&pos.origin)) {
|
||||
auto origin = positions.originOf(p);
|
||||
if (auto path = std::get_if<SourcePath>(&origin)) {
|
||||
auto attrs = buildBindings(3);
|
||||
attrs.alloc(sFile).mkString(path->path.abs());
|
||||
attrs.alloc(sLine).mkInt(pos.line);
|
||||
attrs.alloc(sColumn).mkInt(pos.column);
|
||||
makePositionThunks(*this, p, attrs.alloc(sLine), attrs.alloc(sColumn));
|
||||
v.mkAttrs(attrs);
|
||||
} else
|
||||
v.mkNull();
|
||||
@@ -1205,6 +1226,18 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
|
||||
}
|
||||
|
||||
|
||||
Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
|
||||
{
|
||||
Env & inheritEnv = state.allocEnv(inheritFromExprs->size());
|
||||
inheritEnv.up = &up;
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto from : *inheritFromExprs)
|
||||
inheritEnv.values[displ++] = from->maybeThunk(state, up);
|
||||
|
||||
return &inheritEnv;
|
||||
}
|
||||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||
@@ -1216,6 +1249,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
Env & env2(state.allocEnv(attrs.size()));
|
||||
env2.up = &env;
|
||||
dynamicEnv = &env2;
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
|
||||
|
||||
AttrDefs::iterator overrides = attrs.find(state.sOverrides);
|
||||
bool hasOverrides = overrides != attrs.end();
|
||||
@@ -1226,11 +1260,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides && !i.second.inherited) {
|
||||
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i.second.e);
|
||||
mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), i.second.e);
|
||||
} else
|
||||
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||
}
|
||||
@@ -1262,9 +1296,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
for (auto & i : attrs)
|
||||
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
|
||||
else {
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
|
||||
for (auto & i : attrs) {
|
||||
v.attrs->push_back(Attr(
|
||||
i.first,
|
||||
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
|
||||
i.second.pos));
|
||||
}
|
||||
}
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
for (auto & i : dynamicAttrs) {
|
||||
@@ -1296,12 +1336,17 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
Env & env2(state.allocEnv(attrs->attrs.size()));
|
||||
env2.up = &env;
|
||||
|
||||
Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
|
||||
|
||||
/* The recursive attributes are evaluated in the new environment,
|
||||
while the inherited attributes are evaluated in the original
|
||||
environment. */
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||
for (auto & i : attrs->attrs) {
|
||||
env2.values[displ++] = i.second.e->maybeThunk(
|
||||
state,
|
||||
*i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
}
|
||||
|
||||
auto dts = state.debugRepl
|
||||
? makeDebugTraceStacker(
|
||||
@@ -1596,9 +1641,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||
"while calling %s",
|
||||
lambda.name
|
||||
? concatStrings("'", symbols[lambda.name], "'")
|
||||
: "anonymous lambda",
|
||||
true);
|
||||
if (pos) addErrorTrace(e, pos, "from call site%s", "", true);
|
||||
: "anonymous lambda");
|
||||
if (pos) addErrorTrace(e, pos, "from call site");
|
||||
}
|
||||
throw;
|
||||
}
|
||||
@@ -2710,9 +2754,12 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta
|
||||
|
||||
Expr * EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
auto s = make_ref<std::string>(std::move(s_));
|
||||
s->append("\0\0", 2);
|
||||
return parse(s->data(), s->size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||
// NOTE this method (and parseStdin) must take care to *fully copy* their input
|
||||
// into their respective Pos::Origin until the parser stops overwriting its input
|
||||
// data.
|
||||
auto s = make_ref<std::string>(s_);
|
||||
s_.append("\0\0", 2);
|
||||
return parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -2724,12 +2771,15 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath
|
||||
|
||||
Expr * EvalState::parseStdin()
|
||||
{
|
||||
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
|
||||
// input into their respective Pos::Origin until the parser stops overwriting its
|
||||
// input data.
|
||||
//Activity act(*logger, lvlTalkative, "parsing standard input");
|
||||
auto buffer = drainFD(0);
|
||||
// drainFD should have left some extra space for terminators
|
||||
auto s = make_ref<std::string>(buffer);
|
||||
buffer.append("\0\0", 2);
|
||||
auto s = make_ref<std::string>(std::move(buffer));
|
||||
return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
|
||||
return parse(buffer.data(), buffer.size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -2755,7 +2805,7 @@ SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_
|
||||
if (pathExists(res)) return CanonPath(canonPath(res));
|
||||
}
|
||||
|
||||
if (hasPrefix(path, "nix/"))
|
||||
if (path.starts_with("nix/"))
|
||||
return CanonPath(concatStrings(corepkgsPrefix, path.substr(4)));
|
||||
|
||||
error<ThrownError>(
|
||||
@@ -2788,7 +2838,7 @@ std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Pa
|
||||
}
|
||||
}
|
||||
|
||||
else if (hasPrefix(value, "flake:")) {
|
||||
else if (value.starts_with("flake:")) {
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false);
|
||||
debug("fetching flake search path element '%s''", value);
|
||||
|
||||
+6
-5
@@ -11,6 +11,7 @@
|
||||
#include "experimental-features.hh"
|
||||
#include "input-accessor.hh"
|
||||
#include "search-path.hh"
|
||||
#include "repl-exit-status.hh"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -207,9 +208,8 @@ public:
|
||||
/**
|
||||
* Debugger
|
||||
*/
|
||||
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
bool debugStop;
|
||||
bool debugQuit;
|
||||
int trylevel;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
@@ -434,10 +434,12 @@ public:
|
||||
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
||||
void addErrorTrace(Error & e, const Args & ... formatArgs) const;
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -753,7 +755,6 @@ struct DebugTraceStacker {
|
||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||
~DebugTraceStacker()
|
||||
{
|
||||
// assert(evalState.debugTraces.front() == trace);
|
||||
evalState.debugTraces.pop_front();
|
||||
}
|
||||
EvalState & evalState;
|
||||
|
||||
@@ -35,7 +35,7 @@ void ConfigFile::apply()
|
||||
|
||||
for (auto & [name, value] : settings) {
|
||||
|
||||
auto baseName = hasPrefix(name, "extra-") ? std::string(name, 6) : name;
|
||||
auto baseName = name.starts_with("extra-") ? std::string(name, 6) : name;
|
||||
|
||||
// FIXME: Move into libutil/config.cc.
|
||||
std::string valueS;
|
||||
|
||||
@@ -227,11 +227,10 @@ static Flake getFlake(
|
||||
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
||||
};
|
||||
|
||||
// NOTE evalFile forces vInfo to be an attrset because mustBeTrivial is true.
|
||||
Value vInfo;
|
||||
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
|
||||
|
||||
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
flake.description = description->value->string.s;
|
||||
|
||||
@@ -186,7 +186,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!hasPrefix(path, "/"))
|
||||
if (!path.starts_with("/"))
|
||||
throw BadURL("flake reference '%s' is not an absolute path", url);
|
||||
auto query = decodeQuery(match[2]);
|
||||
path = canonPath(path + "/" + getOr(query, "dir", ""));
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
#define GC_INCLUDE_NEW
|
||||
#include <gc/gc.h>
|
||||
#include <gc/gc_cpp.h>
|
||||
#include <gc/gc_allocator.h>
|
||||
@@ -39,4 +40,4 @@ using SmallValueVector = SmallVector<Value *, nItems>;
|
||||
template <size_t nItems>
|
||||
using SmallTemporaryValueVector = SmallVector<Value, nItems>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -296,22 +296,16 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
|
||||
typedef std::set<Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in `done').
|
||||
The result boolean indicates whether it makes sense
|
||||
/* The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const std::string & attrPath, DrvInfos & drvs, Done & done,
|
||||
const std::string & attrPath, DrvInfos & drvs,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (!done.insert(v.attrs).second) return false;
|
||||
|
||||
DrvInfo drv(state, attrPath, v.attrs);
|
||||
|
||||
drv.queryName();
|
||||
@@ -330,9 +324,8 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
getDerivation(state, v, "", drvs, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return {};
|
||||
return std::move(drvs.front());
|
||||
}
|
||||
@@ -347,6 +340,9 @@ static std::string addToPath(const std::string & s1, const std::string & s2)
|
||||
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs'. If it evaluates to a different
|
||||
kind of set recurse (unless it's already in `done'). */
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const std::string & pathPrefix, Bindings & autoArgs,
|
||||
DrvInfos & drvs, Done & done,
|
||||
@@ -356,10 +352,14 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
/* Process the expression. */
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, ignoreAssertionFailures)) ;
|
||||
|
||||
else if (v.type() == nAttrs) {
|
||||
|
||||
/* Dont consider sets we've already seen, e.g. y in
|
||||
`rec { x.d = derivation {...}; y = x; }`. */
|
||||
if (!done.insert(v.attrs).second) return;
|
||||
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
@@ -376,7 +376,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
|
||||
if (combineChannels)
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
@@ -390,9 +390,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
}
|
||||
|
||||
else if (v.type() == nList) {
|
||||
// NOTE we can't really deduplicate here because small lists don't have stable addresses
|
||||
// and can cause spurious duplicate detections due to v being on the stack.
|
||||
for (auto [n, elem] : enumerate(v.listItems())) {
|
||||
std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
|
||||
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
if (getDerivation(state, *elem, pathPrefix2, drvs, ignoreAssertionFailures))
|
||||
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-20
@@ -39,33 +39,16 @@ namespace nix {
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
loc->first_line = loc->last_line = 1;
|
||||
loc->first_column = loc->last_column = 1;
|
||||
loc->first_line = loc->last_line = 0;
|
||||
loc->first_column = loc->last_column = 0;
|
||||
}
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
loc->stash();
|
||||
|
||||
loc->first_line = loc->last_line;
|
||||
loc->first_column = loc->last_column;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
switch (*s++) {
|
||||
case '\r':
|
||||
if (*s == '\n') { /* cr/lf */
|
||||
i++;
|
||||
s++;
|
||||
}
|
||||
/* fall through */
|
||||
case '\n':
|
||||
++loc->last_line;
|
||||
loc->last_column = 1;
|
||||
break;
|
||||
default:
|
||||
++loc->last_column;
|
||||
}
|
||||
}
|
||||
loc->last_column += len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
parser_tab = custom_target(
|
||||
input : 'parser.y',
|
||||
output : [
|
||||
'parser-tab.cc',
|
||||
'parser-tab.hh',
|
||||
],
|
||||
command : [
|
||||
'bison',
|
||||
'-v',
|
||||
'-o',
|
||||
'@OUTPUT0@',
|
||||
'@INPUT@',
|
||||
'-d',
|
||||
],
|
||||
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
|
||||
# an install script below which removes parser-tab.cc.
|
||||
install : true,
|
||||
install_dir : includedir / 'nix',
|
||||
)
|
||||
|
||||
lexer_tab = custom_target(
|
||||
input : [
|
||||
'lexer.l',
|
||||
parser_tab,
|
||||
],
|
||||
output : [
|
||||
'lexer-tab.cc',
|
||||
'lexer-tab.hh',
|
||||
],
|
||||
command : [
|
||||
'flex',
|
||||
'--outfile',
|
||||
'@OUTPUT0@',
|
||||
'--header-file=' + '@OUTPUT1@',
|
||||
'@INPUT0@',
|
||||
],
|
||||
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
|
||||
# an install script below which removes lexer-tab.cc.
|
||||
install : true,
|
||||
install_dir : includedir / 'nix',
|
||||
)
|
||||
|
||||
# TODO(Qyriad): When the parser and lexer are rewritten this should be removed.
|
||||
# NOTE(Qyriad): We do this this way instead of an inline bash or rm command
|
||||
# due to subtleties in Meson. Check the comments in cleanup-install.bash for details.
|
||||
meson.add_install_script(
|
||||
bash,
|
||||
meson.project_source_root() / 'meson/cleanup-install.bash',
|
||||
'@0@'.format(includedir),
|
||||
)
|
||||
|
||||
imported_drv_to_derivation_gen = gen_header.process('imported-drv-to-derivation.nix')
|
||||
fetchurl_gen = gen_header.process('fetchurl.nix')
|
||||
derivation_gen = gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir())
|
||||
call_flake_gen = gen_header.process('flake/call-flake.nix')
|
||||
|
||||
libexpr_sources = files(
|
||||
'attr-path.cc',
|
||||
'attr-set.cc',
|
||||
'eval-cache.cc',
|
||||
'eval-error.cc',
|
||||
'eval-settings.cc',
|
||||
'eval.cc',
|
||||
'function-trace.cc',
|
||||
'get-drvs.cc',
|
||||
'json-to-value.cc',
|
||||
'nixexpr.cc',
|
||||
'paths.cc',
|
||||
'primops.cc',
|
||||
'print-ambiguous.cc',
|
||||
'print.cc',
|
||||
'search-path.cc',
|
||||
'value-to-json.cc',
|
||||
'value-to-xml.cc',
|
||||
'flake/config.cc',
|
||||
'flake/flake.cc',
|
||||
'flake/flakeref.cc',
|
||||
'flake/lockfile.cc',
|
||||
'primops/context.cc',
|
||||
'primops/fetchClosure.cc',
|
||||
'primops/fetchMercurial.cc',
|
||||
'primops/fetchTree.cc',
|
||||
'primops/fromTOML.cc',
|
||||
'value/context.cc',
|
||||
)
|
||||
|
||||
libexpr_headers = files(
|
||||
'attr-path.hh',
|
||||
'attr-set.hh',
|
||||
'eval-cache.hh',
|
||||
'eval-error.hh',
|
||||
'eval-inline.hh',
|
||||
'eval-settings.hh',
|
||||
'eval.hh',
|
||||
'flake/flake.hh',
|
||||
'flake/flakeref.hh',
|
||||
'flake/lockfile.hh',
|
||||
'function-trace.hh',
|
||||
'gc-small-vector.hh',
|
||||
'get-drvs.hh',
|
||||
'json-to-value.hh',
|
||||
'nixexpr.hh',
|
||||
'parser-state.hh',
|
||||
'pos-idx.hh',
|
||||
'pos-table.hh',
|
||||
'primops.hh',
|
||||
'print-ambiguous.hh',
|
||||
'print-options.hh',
|
||||
'print.hh',
|
||||
'repl-exit-status.hh',
|
||||
'search-path.hh',
|
||||
'symbol-table.hh',
|
||||
'value/context.hh',
|
||||
'value-to-json.hh',
|
||||
'value-to-xml.hh',
|
||||
'value.hh',
|
||||
)
|
||||
|
||||
libexpr = library(
|
||||
'nixexpr',
|
||||
libexpr_sources,
|
||||
parser_tab,
|
||||
lexer_tab,
|
||||
imported_drv_to_derivation_gen,
|
||||
fetchurl_gen,
|
||||
derivation_gen,
|
||||
call_flake_gen,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixfetchers,
|
||||
boehm,
|
||||
boost,
|
||||
],
|
||||
# for shared.hh
|
||||
include_directories : '../libmain',
|
||||
cpp_args : [
|
||||
# FIXME(Qyriad): can we please fix this. toml11 pls.
|
||||
# Technically this only applies to fromTOML.cc, but, well
|
||||
# https://github.com/mesonbuild/meson/issues/1367
|
||||
'-Wno-error=switch-enum',
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(
|
||||
libexpr_headers,
|
||||
subdir : 'nix',
|
||||
preserve_path : true
|
||||
)
|
||||
|
||||
liblixexpr = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libexpr,
|
||||
)
|
||||
+125
-28
@@ -68,10 +68,8 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
|
||||
}
|
||||
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
typedef const decltype(attrs)::value_type * Attr;
|
||||
std::vector<Attr> sorted;
|
||||
for (auto & i : attrs) sorted.push_back(&i);
|
||||
@@ -79,10 +77,37 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||
return sa < sb;
|
||||
});
|
||||
std::vector<Symbol> inherits;
|
||||
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
|
||||
for (auto & i : sorted) {
|
||||
if (i->second.inherited)
|
||||
str << "inherit " << symbols[i->first] << " " << "; ";
|
||||
else {
|
||||
switch (i->second.kind) {
|
||||
case AttrDef::Kind::Plain:
|
||||
break;
|
||||
case AttrDef::Kind::Inherited:
|
||||
inherits.push_back(i->first);
|
||||
break;
|
||||
case AttrDef::Kind::InheritedFrom: {
|
||||
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
|
||||
inheritsFrom[&from].push_back(i->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!inherits.empty()) {
|
||||
str << "inherit";
|
||||
for (auto sym : inherits) str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (const auto & [from, syms] : inheritsFrom) {
|
||||
str << "inherit (";
|
||||
(*inheritFromExprs)[from->displ]->show(symbols, str);
|
||||
str << ")";
|
||||
for (auto sym : syms) str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (auto & i : sorted) {
|
||||
if (i->second.kind == AttrDef::Kind::Plain) {
|
||||
str << symbols[i->first] << " = ";
|
||||
i->second.e->show(symbols, str);
|
||||
str << "; ";
|
||||
@@ -95,6 +120,13 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
i.valueExpr->show(symbols, str);
|
||||
str << "; ";
|
||||
}
|
||||
}
|
||||
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
showBindings(symbols, str);
|
||||
str << "}";
|
||||
}
|
||||
|
||||
@@ -115,7 +147,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
if (hasFormals()) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
for (auto & i : formals->formals) {
|
||||
// the natural Symbol ordering is by creation time, which can lead to the
|
||||
// same expression being printed in two different ways depending on its
|
||||
// context. always use lexicographic ordering to avoid this.
|
||||
for (auto & i : formals->lexicographicOrder(symbols)) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << symbols[i.name];
|
||||
if (i.def) {
|
||||
@@ -150,15 +185,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << "(let ";
|
||||
for (auto & i : attrs->attrs)
|
||||
if (i.second.inherited) {
|
||||
str << "inherit " << symbols[i.first] << "; ";
|
||||
}
|
||||
else {
|
||||
str << symbols[i.first] << " = ";
|
||||
i.second.e->show(symbols, str);
|
||||
str << "; ";
|
||||
}
|
||||
attrs->showBindings(symbols, str);
|
||||
str << "in ";
|
||||
body->show(symbols, str);
|
||||
str << ")";
|
||||
@@ -303,6 +330,12 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
@@ -326,22 +359,47 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
|
||||
i.expr->bindVars(es, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (!inheritFromExprs)
|
||||
return nullptr;
|
||||
|
||||
// the inherit (from) source values are inserted into an env of its own, which
|
||||
// does not introduce any variable names.
|
||||
// analysis must see an empty env, or an env that contains only entries with
|
||||
// otherwise unused names to not interfere with regular names. the parser
|
||||
// has already filled all exprs that access this env with appropriate level
|
||||
// and displacement, and nothing else is allowed to access it. ideally we'd
|
||||
// not even *have* an expr that grabs anything from this env since it's fully
|
||||
// invisible, but the evaluator does not allow for this yet.
|
||||
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
|
||||
for (auto from : *inheritFromExprs)
|
||||
from->bindVars(es, env);
|
||||
|
||||
return inner;
|
||||
}
|
||||
|
||||
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
if (recursive) {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), recursive ? attrs.size() : 0);
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
|
||||
// No need to sort newEnv since attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, newEnv);
|
||||
@@ -349,8 +407,10 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto inheritFromEnv = bindInheritSources(es, env);
|
||||
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(es, env);
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, env);
|
||||
@@ -407,19 +467,23 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
|
||||
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
|
||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs->attrs)
|
||||
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, newEnv));
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
@@ -517,6 +581,39 @@ std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||
|
||||
|
||||
|
||||
/* Position table. */
|
||||
|
||||
Pos PosTable::operator[](PosIdx p) const
|
||||
{
|
||||
auto origin = resolve(p);
|
||||
if (!origin)
|
||||
return {};
|
||||
|
||||
const auto offset = origin->offsetOf(p);
|
||||
|
||||
Pos result{0, 0, origin->origin};
|
||||
auto lines = this->lines.lock();
|
||||
auto linesForInput = (*lines)[origin->offset];
|
||||
|
||||
if (linesForInput.empty()) {
|
||||
auto source = result.getSource().value_or("");
|
||||
const char * begin = source.data();
|
||||
for (Pos::LinesIterator it(source), end; it != end; it++)
|
||||
linesForInput.push_back(it->data() - begin);
|
||||
if (linesForInput.empty())
|
||||
linesForInput.push_back(0);
|
||||
}
|
||||
// as above: the first line starts at byte 0 and is always present
|
||||
auto lineStartOffset = std::prev(
|
||||
std::upper_bound(linesForInput.begin(), linesForInput.end(), offset));
|
||||
|
||||
result.line = 1 + (lineStartOffset - linesForInput.begin());
|
||||
result.column = 1 + (offset - *lineStartOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Symbol table. */
|
||||
|
||||
size_t SymbolTable::totalSize() const
|
||||
|
||||
+49
-4
@@ -7,7 +7,6 @@
|
||||
#include "value.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "error.hh"
|
||||
#include "chunked-vector.hh"
|
||||
#include "position.hh"
|
||||
#include "eval-error.hh"
|
||||
#include "pos-idx.hh"
|
||||
@@ -129,6 +128,23 @@ struct ExprVar : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
/**
|
||||
* A pseudo-expression for the purpose of evaluating the `from` expression in `inherit (from)` syntax.
|
||||
* Unlike normal variable references, the displacement is set during parsing, and always refers to
|
||||
* `ExprAttrs::inheritFromExprs` (by itself or in `ExprLet`), whose values are put into their own `Env`.
|
||||
*/
|
||||
struct ExprInheritFrom : ExprVar
|
||||
{
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
|
||||
{
|
||||
this->level = 0;
|
||||
this->displ = displ;
|
||||
this->fromWith = nullptr;
|
||||
}
|
||||
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
};
|
||||
|
||||
struct ExprSelect : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
@@ -154,16 +170,40 @@ struct ExprAttrs : Expr
|
||||
bool recursive;
|
||||
PosIdx pos;
|
||||
struct AttrDef {
|
||||
bool inherited;
|
||||
enum class Kind {
|
||||
/** `attr = expr;` */
|
||||
Plain,
|
||||
/** `inherit attr1 attrn;` */
|
||||
Inherited,
|
||||
/** `inherit (expr) attr1 attrn;` */
|
||||
InheritedFrom,
|
||||
};
|
||||
|
||||
Kind kind;
|
||||
Expr * e;
|
||||
PosIdx pos;
|
||||
Displacement displ; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
|
||||
: inherited(inherited), e(e), pos(pos) { };
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
|
||||
template<typename T>
|
||||
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
|
||||
{
|
||||
switch (kind) {
|
||||
case Kind::Plain:
|
||||
return plain;
|
||||
case Kind::Inherited:
|
||||
return inherited;
|
||||
default:
|
||||
case Kind::InheritedFrom:
|
||||
return inheritedFrom;
|
||||
}
|
||||
}
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
||||
struct DynamicAttrDef {
|
||||
Expr * nameExpr, * valueExpr;
|
||||
PosIdx pos;
|
||||
@@ -176,6 +216,11 @@ struct ExprAttrs : Expr
|
||||
ExprAttrs() : recursive(false) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
};
|
||||
|
||||
struct ExprList : Expr
|
||||
|
||||
+16
-10
@@ -24,20 +24,15 @@ struct ParserLocation
|
||||
int last_line, last_column;
|
||||
|
||||
// backup to recover from yyless(0)
|
||||
int stashed_first_line, stashed_first_column;
|
||||
int stashed_last_line, stashed_last_column;
|
||||
int stashed_first_column, stashed_last_column;
|
||||
|
||||
void stash() {
|
||||
stashed_first_line = first_line;
|
||||
stashed_first_column = first_column;
|
||||
stashed_last_line = last_line;
|
||||
stashed_last_column = last_column;
|
||||
}
|
||||
|
||||
void unstash() {
|
||||
first_line = stashed_first_line;
|
||||
first_column = stashed_first_column;
|
||||
last_line = stashed_last_line;
|
||||
last_column = stashed_last_column;
|
||||
}
|
||||
};
|
||||
@@ -88,12 +83,12 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
|
||||
if (i->symbol) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
if (!j->second.inherited) {
|
||||
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
|
||||
if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
|
||||
attrs = attrs2;
|
||||
} else
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
|
||||
} else {
|
||||
ExprAttrs * nested = new ExprAttrs;
|
||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||
@@ -117,13 +112,24 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
|
||||
auto ae = dynamic_cast<ExprAttrs *>(e);
|
||||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (jAttrs && ae) {
|
||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
for (auto & ad : ae->attrs) {
|
||||
auto j2 = jAttrs->attrs.find(ad.first);
|
||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
||||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
||||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
||||
from.displ += jAttrs->inheritFromExprs->size();
|
||||
}
|
||||
}
|
||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
||||
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
|
||||
}
|
||||
} else {
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
@@ -264,7 +270,7 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||
|
||||
inline PosIdx ParserState::at(const ParserLocation & loc)
|
||||
{
|
||||
return positions.add(origin, loc.first_line, loc.first_column);
|
||||
return positions.add(origin, loc.first_column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+28
-13
@@ -62,6 +62,10 @@ using namespace nix;
|
||||
|
||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
|
||||
{
|
||||
if (std::string_view(error).starts_with("syntax error, unexpected end of file")) {
|
||||
loc->first_column = loc->last_column;
|
||||
loc->first_line = loc->last_line;
|
||||
}
|
||||
throw ParseError({
|
||||
.msg = HintFmt(error),
|
||||
.pos = state->positions[state->at(*loc)]
|
||||
@@ -85,6 +89,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
|
||||
nix::StringToken uri;
|
||||
nix::StringToken str;
|
||||
std::vector<nix::AttrName> * attrNames;
|
||||
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
|
||||
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
|
||||
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
|
||||
}
|
||||
@@ -95,7 +100,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
|
||||
%type <attrs> binds
|
||||
%type <formals> formals
|
||||
%type <formal> formal
|
||||
%type <attrNames> attrs attrpath
|
||||
%type <attrNames> attrpath
|
||||
%type <inheritAttrs> attrs
|
||||
%type <string_parts> string_parts_interpolated
|
||||
%type <ind_string_parts> ind_string_parts
|
||||
%type <e> path_start string_parts string_attr
|
||||
@@ -307,21 +313,30 @@ binds
|
||||
: binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; }
|
||||
| binds INHERIT attrs ';'
|
||||
{ $$ = $1;
|
||||
for (auto & i : *$3) {
|
||||
for (auto & [i, iPos] : *$3) {
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
state->dupAttr(i.symbol, state->at(@3), $$->attrs[i.symbol].pos);
|
||||
auto pos = state->at(@3);
|
||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
|
||||
$$->attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
||||
}
|
||||
delete $3;
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
{ $$ = $1;
|
||||
/* !!! Should ensure sharing of the expression in $4. */
|
||||
for (auto & i : *$6) {
|
||||
if (!$$->inheritFromExprs)
|
||||
$$->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
$$->inheritFromExprs->push_back($4);
|
||||
auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
|
||||
for (auto & [i, iPos] : *$6) {
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos);
|
||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), state->at(@6)));
|
||||
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
|
||||
$$->attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(
|
||||
new ExprSelect(iPos, from, i.symbol),
|
||||
iPos,
|
||||
ExprAttrs::AttrDef::Kind::InheritedFrom));
|
||||
}
|
||||
delete $6;
|
||||
}
|
||||
@@ -329,12 +344,12 @@ binds
|
||||
;
|
||||
|
||||
attrs
|
||||
: attrs attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($2))); }
|
||||
: attrs attr { $$ = $1; $1->emplace_back(AttrName(state->symbols.create($2)), state->at(@2)); }
|
||||
| attrs string_attr
|
||||
{ $$ = $1;
|
||||
ExprString * str = dynamic_cast<ExprString *>($2);
|
||||
if (str) {
|
||||
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
|
||||
delete str;
|
||||
} else
|
||||
throw ParseError({
|
||||
@@ -342,7 +357,7 @@ attrs
|
||||
.pos = state->positions[state->at(@2)]
|
||||
});
|
||||
}
|
||||
| { $$ = new AttrPath; }
|
||||
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
|
||||
;
|
||||
|
||||
attrpath
|
||||
@@ -420,7 +435,7 @@ Expr * parseExprFromBuf(
|
||||
.symbols = symbols,
|
||||
.positions = positions,
|
||||
.basePath = basePath,
|
||||
.origin = {origin},
|
||||
.origin = positions.addOrigin(origin, length),
|
||||
.s = astSymbols,
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace nix {
|
||||
|
||||
class PosIdx
|
||||
{
|
||||
friend struct LazyPosAcessors;
|
||||
friend class PosTable;
|
||||
|
||||
private:
|
||||
|
||||
+47
-44
@@ -7,6 +7,7 @@
|
||||
#include "chunked-vector.hh"
|
||||
#include "pos-idx.hh"
|
||||
#include "position.hh"
|
||||
#include "sync.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -17,66 +18,68 @@ public:
|
||||
{
|
||||
friend PosTable;
|
||||
private:
|
||||
// must always be invalid by default, add() replaces this with the actual value.
|
||||
// subsequent add() calls use this index as a token to quickly check whether the
|
||||
// current origins.back() can be reused or not.
|
||||
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t offset;
|
||||
|
||||
// Used for searching in PosTable::[].
|
||||
explicit Origin(uint32_t idx)
|
||||
: idx(idx)
|
||||
, origin{std::monostate()}
|
||||
{
|
||||
}
|
||||
Origin(Pos::Origin origin, uint32_t offset, size_t size):
|
||||
offset(offset), origin(origin), size(size)
|
||||
{}
|
||||
|
||||
public:
|
||||
const Pos::Origin origin;
|
||||
const size_t size;
|
||||
|
||||
Origin(Pos::Origin origin)
|
||||
: origin(origin)
|
||||
uint32_t offsetOf(PosIdx p) const
|
||||
{
|
||||
return p.id - 1 - offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct Offset
|
||||
{
|
||||
uint32_t line, column;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Origin> origins;
|
||||
ChunkedVector<Offset, 8192> offsets;
|
||||
using Lines = std::vector<uint32_t>;
|
||||
|
||||
public:
|
||||
PosTable()
|
||||
: offsets(1024)
|
||||
{
|
||||
origins.reserve(1024);
|
||||
}
|
||||
std::map<uint32_t, Origin> origins;
|
||||
mutable Sync<std::map<uint32_t, Lines>> lines;
|
||||
|
||||
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
|
||||
const Origin * resolve(PosIdx p) const
|
||||
{
|
||||
const auto idx = offsets.add({line, column}).second;
|
||||
if (origins.empty() || origins.back().idx != origin.idx) {
|
||||
origin.idx = idx;
|
||||
origins.push_back(origin);
|
||||
}
|
||||
return PosIdx(idx + 1);
|
||||
}
|
||||
if (p.id == 0)
|
||||
return nullptr;
|
||||
|
||||
Pos operator[](PosIdx p) const
|
||||
{
|
||||
if (p.id == 0 || p.id > offsets.size())
|
||||
return {};
|
||||
const auto idx = p.id - 1;
|
||||
/* we want the last key <= idx, so we'll take prev(first key > idx).
|
||||
this is guaranteed to never rewind origin.begin because the first
|
||||
key is always 0. */
|
||||
const auto pastOrigin = std::upper_bound(
|
||||
origins.begin(), origins.end(), Origin(idx), [](const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||
const auto origin = *std::prev(pastOrigin);
|
||||
const auto offset = offsets[idx];
|
||||
return {offset.line, offset.column, origin.origin};
|
||||
this is guaranteed to never rewind origin.begin because the first
|
||||
key is always 0. */
|
||||
const auto pastOrigin = origins.upper_bound(idx);
|
||||
return &std::prev(pastOrigin)->second;
|
||||
}
|
||||
|
||||
public:
|
||||
Origin addOrigin(Pos::Origin origin, size_t size)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
if (auto it = origins.rbegin(); it != origins.rend())
|
||||
offset = it->first + it->second.size;
|
||||
// +1 because all PosIdx are offset by 1 to begin with (because noPos == 0), and
|
||||
// another +1 to ensure that all origins can point to EOF, eg on (invalid) empty inputs.
|
||||
if (2 + offset + size < offset)
|
||||
return Origin{origin, offset, 0};
|
||||
return origins.emplace(offset, Origin{origin, offset, size}).first->second;
|
||||
}
|
||||
|
||||
PosIdx add(const Origin & origin, size_t offset)
|
||||
{
|
||||
if (offset > origin.size)
|
||||
return PosIdx();
|
||||
return PosIdx(1 + origin.offset + offset);
|
||||
}
|
||||
|
||||
Pos operator[](PosIdx p) const;
|
||||
|
||||
Pos::Origin originOf(PosIdx p) const
|
||||
{
|
||||
if (auto o = resolve(p))
|
||||
return o->origin;
|
||||
return std::monostate{};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+68
-29
@@ -754,15 +754,6 @@ static RegisterPrimOp primop_break({
|
||||
|
||||
auto & dt = state.debugTraces.front();
|
||||
state.runDebugRepl(&error, dt.env, dt.expr);
|
||||
|
||||
if (state.debugQuit) {
|
||||
// If the user elects to quit the repl, throw an exception.
|
||||
throw Error(ErrorInfo{
|
||||
.level = lvlInfo,
|
||||
.msg = HintFmt("quit the debugger"),
|
||||
.pos = nullptr,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Return the value we were passed.
|
||||
@@ -814,7 +805,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
||||
auto message = state.coerceToString(pos, *args[0], context,
|
||||
"while evaluating the error message passed to builtins.addErrorContext",
|
||||
false, false).toOwned();
|
||||
e.addTrace(nullptr, HintFmt(message), true);
|
||||
e.addTrace(nullptr, HintFmt(message));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -873,7 +864,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
|
||||
/* increment state.trylevel, and decrement it when this function returns. */
|
||||
MaintainCount trylevel(state.trylevel);
|
||||
|
||||
void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
|
||||
ReplExitStatus (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
|
||||
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
|
||||
{
|
||||
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
|
||||
@@ -989,6 +980,10 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
printError("trace: %1%", args[0]->string.s);
|
||||
else
|
||||
printError("trace: %1%", ValuePrinter(state, *args[0]));
|
||||
if (evalSettings.builtinsTraceDebugger && state.debugRepl && !state.debugTraces.empty()) {
|
||||
const DebugTrace & last = state.debugTraces.front();
|
||||
state.runDebugRepl(nullptr, last.env, last.expr);
|
||||
}
|
||||
state.forceValue(*args[1], pos);
|
||||
v = *args[1];
|
||||
}
|
||||
@@ -1000,6 +995,12 @@ static RegisterPrimOp primop_trace({
|
||||
Evaluate *e1* and print its abstract syntax representation on
|
||||
standard error. Then return *e2*. This function is useful for
|
||||
debugging.
|
||||
|
||||
If the
|
||||
[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace)
|
||||
option is set to `true` and the `--debugger` flag is given, the
|
||||
interactive debugger will be started when `trace` is called (like
|
||||
[`break`](@docroot@/language/builtins.md#builtins-break)).
|
||||
)",
|
||||
.fun = prim_trace,
|
||||
});
|
||||
@@ -1068,7 +1069,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
|
||||
e.addTrace(nullptr, HintFmt(
|
||||
"while evaluating derivation '%s'\n"
|
||||
" whose name attribute is located at %s",
|
||||
drvName, pos), true);
|
||||
drvName, pos));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -1226,8 +1227,7 @@ drvName, Bindings * attrs, Value & v)
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[i->pos],
|
||||
HintFmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName),
|
||||
true);
|
||||
HintFmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -2492,6 +2492,54 @@ static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
|
||||
.fun = prim_unsafeGetAttrPos,
|
||||
});
|
||||
|
||||
// access to exact position information (ie, line and colum numbers) is deferred
|
||||
// due to the cost associated with calculating that information and how rarely
|
||||
// it is used in practice. this is achieved by creating thunks to otherwise
|
||||
// inaccessible primops that are not exposed as __op or under builtins to turn
|
||||
// the internal PosIdx back into a line and column number, respectively. exposing
|
||||
// these primops in any way would at best be not useful and at worst create wildly
|
||||
// indeterministic eval results depending on parse order of files.
|
||||
//
|
||||
// in a simpler world this would instead be implemented as another kind of thunk,
|
||||
// but each type of thunk has an associated runtime cost in the current evaluator.
|
||||
// as with black holes this cost is too high to justify another thunk type to check
|
||||
// for in the very hot path that is forceValue.
|
||||
static struct LazyPosAcessors {
|
||||
PrimOp primop_lineOfPos{
|
||||
.arity = 1,
|
||||
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer)].line);
|
||||
}
|
||||
};
|
||||
PrimOp primop_columnOfPos{
|
||||
.arity = 1,
|
||||
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer)].column);
|
||||
}
|
||||
};
|
||||
|
||||
Value lineOfPos, columnOfPos;
|
||||
|
||||
LazyPosAcessors()
|
||||
{
|
||||
lineOfPos.mkPrimOp(&primop_lineOfPos);
|
||||
columnOfPos.mkPrimOp(&primop_columnOfPos);
|
||||
}
|
||||
|
||||
void operator()(EvalState & state, const PosIdx pos, Value & line, Value & column)
|
||||
{
|
||||
Value * posV = state.allocValue();
|
||||
posV->mkInt(pos.id);
|
||||
line.mkApp(&lineOfPos, posV);
|
||||
column.mkApp(&columnOfPos, posV);
|
||||
}
|
||||
} makeLazyPosAccessors;
|
||||
|
||||
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column)
|
||||
{
|
||||
makeLazyPosAccessors(state, pos, line, column);
|
||||
}
|
||||
|
||||
/* Dynamic version of the `?' operator. */
|
||||
static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
@@ -4299,25 +4347,16 @@ void EvalState::createBaseEnv()
|
||||
.impureOnly = true,
|
||||
});
|
||||
|
||||
v.mkString(nixVersion);
|
||||
v.mkString("2.18.3-lix");
|
||||
addConstant("__nixVersion", v, {
|
||||
.type = nString,
|
||||
.doc = R"(
|
||||
The version of Nix.
|
||||
Legacy version of Nix. Always returns "2.18.3-lix" on Lix.
|
||||
|
||||
For example, where the command line returns the current Nix version,
|
||||
|
||||
```shell-session
|
||||
$ nix --version
|
||||
nix (Nix) 2.16.0
|
||||
```
|
||||
|
||||
the Nix language evaluator returns the same value:
|
||||
|
||||
```nix-repl
|
||||
nix-repl> builtins.nixVersion
|
||||
"2.16.0"
|
||||
```
|
||||
To determine if features exist, Nix scripts should instead use direct
|
||||
means of feature detection, such as checking for existence of
|
||||
builtins they want to use. Doing so allows for much better compatibility
|
||||
across implementations.
|
||||
)",
|
||||
});
|
||||
|
||||
|
||||
@@ -51,4 +51,6 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
*/
|
||||
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
|
||||
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "print-ambiguous.hh"
|
||||
#include "print.hh"
|
||||
#include "eval.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -17,24 +17,29 @@ struct PrintOptions
|
||||
* If true, output ANSI color sequences.
|
||||
*/
|
||||
bool ansiColors = false;
|
||||
|
||||
/**
|
||||
* If true, force values.
|
||||
*/
|
||||
bool force = false;
|
||||
|
||||
/**
|
||||
* If true and `force` is set, print derivations as
|
||||
* `«derivation /nix/store/...»` instead of as attribute sets.
|
||||
*/
|
||||
bool derivationPaths = false;
|
||||
|
||||
/**
|
||||
* If true, track which values have been printed and skip them on
|
||||
* subsequent encounters. Useful for self-referential values.
|
||||
*/
|
||||
bool trackRepeated = true;
|
||||
|
||||
/**
|
||||
* Maximum depth to evaluate to.
|
||||
*/
|
||||
size_t maxDepth = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum number of attributes in attribute sets to print.
|
||||
*
|
||||
@@ -42,6 +47,7 @@ struct PrintOptions
|
||||
* attribute set encountered.
|
||||
*/
|
||||
size_t maxAttrs = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum number of list items to print.
|
||||
*
|
||||
@@ -49,10 +55,26 @@ struct PrintOptions
|
||||
* list encountered.
|
||||
*/
|
||||
size_t maxListItems = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum string length to print.
|
||||
*/
|
||||
size_t maxStringLength = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Indentation width for pretty-printing.
|
||||
*
|
||||
* If set to 0 (the default), values are not pretty-printed.
|
||||
*/
|
||||
size_t prettyIndent = 0;
|
||||
|
||||
/**
|
||||
* True if pretty-printing is enabled.
|
||||
*/
|
||||
inline bool shouldPrettyPrint()
|
||||
{
|
||||
return prettyIndent > 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+109
-12
@@ -5,6 +5,7 @@
|
||||
#include "ansicolor.hh"
|
||||
#include "store-api.hh"
|
||||
#include "english.hh"
|
||||
#include "signals.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -151,6 +152,7 @@ struct ImportantFirstAttrNameCmp
|
||||
};
|
||||
|
||||
typedef std::set<const void *> ValuesSeen;
|
||||
typedef std::vector<std::pair<std::string, Value *>> AttrVec;
|
||||
|
||||
class Printer
|
||||
{
|
||||
@@ -161,6 +163,37 @@ private:
|
||||
std::optional<ValuesSeen> seen;
|
||||
size_t attrsPrinted = 0;
|
||||
size_t listItemsPrinted = 0;
|
||||
std::string indent;
|
||||
|
||||
void increaseIndent()
|
||||
{
|
||||
if (options.shouldPrettyPrint()) {
|
||||
indent.append(options.prettyIndent, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void decreaseIndent()
|
||||
{
|
||||
if (options.shouldPrettyPrint()) {
|
||||
assert(indent.size() >= options.prettyIndent);
|
||||
indent.resize(indent.size() - options.prettyIndent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a space (for separating items or attributes).
|
||||
*
|
||||
* If pretty-printing is enabled, a newline and the current `indent` is
|
||||
* printed instead.
|
||||
*/
|
||||
void printSpace(bool prettyPrint)
|
||||
{
|
||||
if (prettyPrint) {
|
||||
output << "\n" << indent;
|
||||
} else {
|
||||
output << " ";
|
||||
}
|
||||
}
|
||||
|
||||
void printRepeated()
|
||||
{
|
||||
@@ -258,6 +291,28 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldPrettyPrintAttrs(AttrVec & v)
|
||||
{
|
||||
if (!options.shouldPrettyPrint() || v.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pretty-print attrsets with more than one item.
|
||||
if (v.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item = v[0].second;
|
||||
if (!item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pretty-print single-item attrsets only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->type();
|
||||
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
||||
}
|
||||
|
||||
void printAttrs(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && !seen->insert(v.attrs).second) {
|
||||
@@ -268,9 +323,10 @@ private:
|
||||
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
||||
printDerivation(v);
|
||||
} else if (depth < options.maxDepth) {
|
||||
output << "{ ";
|
||||
increaseIndent();
|
||||
output << "{";
|
||||
|
||||
std::vector<std::pair<std::string, Value *>> sorted;
|
||||
AttrVec sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
||||
|
||||
@@ -279,22 +335,53 @@ private:
|
||||
else
|
||||
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
|
||||
|
||||
auto prettyPrint = shouldPrettyPrintAttrs(sorted);
|
||||
|
||||
size_t printedHere = 0;
|
||||
for (auto & i : sorted) {
|
||||
printSpace(prettyPrint);
|
||||
|
||||
if (attrsPrinted >= options.maxAttrs) {
|
||||
printElided(sorted.size() - attrsPrinted, "attribute", "attributes");
|
||||
printElided(sorted.size() - printedHere, "attribute", "attributes");
|
||||
break;
|
||||
}
|
||||
|
||||
printAttributeName(output, i.first);
|
||||
output << " = ";
|
||||
print(*i.second, depth + 1);
|
||||
output << "; ";
|
||||
output << ";";
|
||||
attrsPrinted++;
|
||||
printedHere++;
|
||||
}
|
||||
|
||||
decreaseIndent();
|
||||
printSpace(prettyPrint);
|
||||
output << "}";
|
||||
} else
|
||||
} else {
|
||||
output << "{ ... }";
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldPrettyPrintList(std::span<Value * const> list)
|
||||
{
|
||||
if (!options.shouldPrettyPrint() || list.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pretty-print lists with more than one item.
|
||||
if (list.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item = list[0];
|
||||
if (!item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pretty-print single-item lists only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->type();
|
||||
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
||||
}
|
||||
|
||||
void printList(Value & v, size_t depth)
|
||||
@@ -304,11 +391,17 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
output << "[ ";
|
||||
if (depth < options.maxDepth) {
|
||||
for (auto elem : v.listItems()) {
|
||||
increaseIndent();
|
||||
output << "[";
|
||||
auto listItems = v.listItems();
|
||||
auto prettyPrint = shouldPrettyPrintList(listItems);
|
||||
size_t printedHere = 0;
|
||||
for (auto elem : listItems) {
|
||||
printSpace(prettyPrint);
|
||||
|
||||
if (listItemsPrinted >= options.maxListItems) {
|
||||
printElided(v.listSize() - listItemsPrinted, "item", "items");
|
||||
printElided(v.listSize() - printedHere, "item", "items");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -317,13 +410,16 @@ private:
|
||||
} else {
|
||||
printNullptr();
|
||||
}
|
||||
output << " ";
|
||||
listItemsPrinted++;
|
||||
printedHere++;
|
||||
}
|
||||
|
||||
decreaseIndent();
|
||||
printSpace(prettyPrint);
|
||||
output << "]";
|
||||
} else {
|
||||
output << "[ ... ]";
|
||||
}
|
||||
else
|
||||
output << "... ";
|
||||
output << "]";
|
||||
}
|
||||
|
||||
void printFunction(Value & v)
|
||||
@@ -486,6 +582,7 @@ public:
|
||||
{
|
||||
attrsPrinted = 0;
|
||||
listItemsPrinted = 0;
|
||||
indent.clear();
|
||||
|
||||
if (options.trackRepeated) {
|
||||
seen.emplace();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Exit status returned from the REPL.
|
||||
*/
|
||||
enum class ReplExitStatus {
|
||||
/**
|
||||
* The user exited with `:quit`. The program (e.g., if the REPL was acting
|
||||
* as the debugger) should exit.
|
||||
*/
|
||||
QuitAll,
|
||||
/**
|
||||
* The user exited with `:continue`. The program should continue running.
|
||||
*/
|
||||
Continue,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "value-to-json.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "util.hh"
|
||||
#include "signals.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "xml-writer.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "util.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
||||
Path actualPath(absPath(workdir));
|
||||
|
||||
PathFilter filter = [&](const Path & p) -> bool {
|
||||
assert(hasPrefix(p, actualPath));
|
||||
assert(p.starts_with(actualPath));
|
||||
std::string file(p, actualPath.size() + 1);
|
||||
|
||||
auto st = lstat(p);
|
||||
@@ -231,7 +231,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
auto prefix = file + "/";
|
||||
auto i = files.lower_bound(prefix);
|
||||
return i != files.end() && hasPrefix(*i, prefix);
|
||||
return i != files.end() && (*i).starts_with(prefix);
|
||||
}
|
||||
|
||||
return files.count(file);
|
||||
@@ -267,7 +267,7 @@ struct GitInputScheme : InputScheme
|
||||
url.scheme != "git+file") return {};
|
||||
|
||||
auto url2(url);
|
||||
if (hasPrefix(url2.scheme, "git+")) url2.scheme = std::string(url2.scheme, 4);
|
||||
if (url2.scheme.starts_with("git+")) url2.scheme = std::string(url2.scheme, 4);
|
||||
url2.query.clear();
|
||||
|
||||
Attrs attrs;
|
||||
|
||||
@@ -181,7 +181,7 @@ struct MercurialInputScheme : InputScheme
|
||||
Path actualPath(absPath(actualUrl));
|
||||
|
||||
PathFilter filter = [&](const Path & p) -> bool {
|
||||
assert(hasPrefix(p, actualPath));
|
||||
assert(p.starts_with(actualPath));
|
||||
std::string file(p, actualPath.size() + 1);
|
||||
|
||||
auto st = lstat(p);
|
||||
@@ -189,7 +189,7 @@ struct MercurialInputScheme : InputScheme
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
auto prefix = file + "/";
|
||||
auto i = files.lower_bound(prefix);
|
||||
return i != files.end() && hasPrefix(*i, prefix);
|
||||
return i != files.end() && (*i).starts_with(prefix);
|
||||
}
|
||||
|
||||
return files.count(file);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
libfetchers_sources = files(
|
||||
'attrs.cc',
|
||||
'cache.cc',
|
||||
'fetch-settings.cc',
|
||||
'fetch-to-store.cc',
|
||||
'fetchers.cc',
|
||||
'git.cc',
|
||||
'github.cc',
|
||||
'indirect.cc',
|
||||
'mercurial.cc',
|
||||
'path.cc',
|
||||
'registry.cc',
|
||||
'tarball.cc',
|
||||
)
|
||||
|
||||
libfetchers_headers = files(
|
||||
'attrs.hh',
|
||||
'cache.hh',
|
||||
'fetch-settings.hh',
|
||||
'fetch-to-store.hh',
|
||||
'fetchers.hh',
|
||||
'registry.hh',
|
||||
)
|
||||
|
||||
libfetchers = library(
|
||||
'nixfetchers',
|
||||
libfetchers_sources,
|
||||
dependencies : [
|
||||
liblixstore,
|
||||
liblixutil,
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(libfetchers_headers, subdir : 'nix', preserve_path : true)
|
||||
|
||||
liblixfetchers = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libfetchers,
|
||||
)
|
||||
@@ -157,7 +157,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
|
||||
return std::make_shared<Registry>(Registry::Global); // empty registry
|
||||
}
|
||||
|
||||
if (!hasPrefix(path, "/")) {
|
||||
if (!path.starts_with("/")) {
|
||||
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json");
|
||||
|
||||
@@ -188,10 +188,10 @@ struct CurlInputScheme : InputScheme
|
||||
|
||||
const bool hasTarballExtension(std::string_view path) const
|
||||
{
|
||||
return hasSuffix(path, ".zip") || hasSuffix(path, ".tar")
|
||||
|| hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz")
|
||||
|| hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2")
|
||||
|| hasSuffix(path, ".tar.zst");
|
||||
return path.ends_with(".zip") || path.ends_with(".tar")
|
||||
|| path.ends_with(".tgz") || path.ends_with(".tar.gz")
|
||||
|| path.ends_with(".tar.xz") || path.ends_with(".tar.bz2")
|
||||
|| path.ends_with(".tar.zst");
|
||||
}
|
||||
|
||||
virtual bool isValidURL(const ParsedURL & url, bool requireTree) const = 0;
|
||||
|
||||
@@ -47,7 +47,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
|
||||
std::map<std::string, Config::SettingInfo> settings;
|
||||
globalConfig.getSettings(settings);
|
||||
for (auto & s : settings)
|
||||
if (hasPrefix(s.first, prefix))
|
||||
if (s.first.starts_with(prefix))
|
||||
completions->add(s.first, fmt("Set the `%s` setting.", s.first));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
libmain_sources = files(
|
||||
'common-args.cc',
|
||||
'loggers.cc',
|
||||
'progress-bar.cc',
|
||||
'shared.cc',
|
||||
'stack.cc',
|
||||
)
|
||||
|
||||
libmain_headers = files(
|
||||
'common-args.hh',
|
||||
'loggers.hh',
|
||||
'progress-bar.hh',
|
||||
'shared.hh',
|
||||
)
|
||||
|
||||
libmain = library(
|
||||
'nixmain',
|
||||
libmain_sources,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(libmain_headers, subdir : 'nix', preserve_path : true)
|
||||
|
||||
liblixmain = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libmain,
|
||||
)
|
||||
@@ -188,7 +188,7 @@ public:
|
||||
|
||||
if (type == actBuild) {
|
||||
std::string name(storePathToName(getS(fields, 0)));
|
||||
if (hasSuffix(name, ".drv"))
|
||||
if (name.ends_with(".drv"))
|
||||
name = name.substr(0, name.size() - 4);
|
||||
i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
|
||||
auto machineName = getS(fields, 1);
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
auto name = storePathToName(getS(fields, 0));
|
||||
auto sub = getS(fields, 1);
|
||||
i->s = fmt(
|
||||
hasPrefix(sub, "local")
|
||||
sub.starts_with("local")
|
||||
? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
|
||||
: "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
|
||||
name, sub);
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
|
||||
if (type == actPostBuildHook) {
|
||||
auto name = storePathToName(getS(fields, 0));
|
||||
if (hasSuffix(name, ".drv"))
|
||||
if (name.ends_with(".drv"))
|
||||
name = name.substr(0, name.size() - 4);
|
||||
i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name);
|
||||
i->name = DrvName(name).name;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "store-api.hh"
|
||||
#include "gc-store.hh"
|
||||
#include "util.hh"
|
||||
#include "signals.hh"
|
||||
#include "loggers.hh"
|
||||
#include "progress-bar.hh"
|
||||
|
||||
@@ -407,6 +408,4 @@ PrintFreed::~PrintFreed()
|
||||
showBytes(results.bytesFreed));
|
||||
}
|
||||
|
||||
Exit::~Exit() { }
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common-args.hh"
|
||||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "exit.hh"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@@ -14,15 +15,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Exit : public std::exception
|
||||
{
|
||||
public:
|
||||
int status;
|
||||
Exit() : status(0) { }
|
||||
Exit(int status) : status(status) { }
|
||||
virtual ~Exit();
|
||||
};
|
||||
|
||||
int handleExceptions(const std::string & programName, std::function<void()> fun);
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "nar-info-disk-cache.hh"
|
||||
#include "nar-accessor.hh"
|
||||
#include "thread-pool.hh"
|
||||
#include "signals.hh"
|
||||
#include "callback.hh"
|
||||
|
||||
#include <chrono>
|
||||
@@ -123,7 +124,7 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||
|
||||
AutoCloseFD openFile(const Path & path)
|
||||
{
|
||||
auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return fd;
|
||||
|
||||
@@ -846,8 +846,8 @@ int DerivationGoal::getChildStatus()
|
||||
|
||||
void DerivationGoal::closeReadPipes()
|
||||
{
|
||||
hook->builderOut.readSide = -1;
|
||||
hook->fromHook.readSide = -1;
|
||||
hook->builderOut.readSide.reset();
|
||||
hook->fromHook.readSide.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -1227,7 +1227,7 @@ HookReply DerivationGoal::tryBuildHook()
|
||||
}
|
||||
|
||||
hook->sink = FdSink();
|
||||
hook->toHook.writeSide = -1;
|
||||
hook->toHook.writeSide.reset();
|
||||
|
||||
/* Create the log file and pipe. */
|
||||
Path logFile = openLogFile();
|
||||
@@ -1273,7 +1273,7 @@ Path DerivationGoal::openLogFile()
|
||||
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
|
||||
settings.compressLog ? ".bz2" : "");
|
||||
|
||||
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
|
||||
fdLogFile = AutoCloseFD{open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666)};
|
||||
if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName);
|
||||
|
||||
logFileSink = std::make_shared<FdSink>(fdLogFile.get());
|
||||
@@ -1293,7 +1293,7 @@ void DerivationGoal::closeLogFile()
|
||||
if (logSink2) logSink2->finish();
|
||||
if (logFileSink) logFileSink->flush();
|
||||
logSink = logFileSink = 0;
|
||||
fdLogFile = -1;
|
||||
fdLogFile.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ HookInstance::HookInstance()
|
||||
});
|
||||
|
||||
pid.setSeparatePG(true);
|
||||
fromHook.writeSide = -1;
|
||||
toHook.readSide = -1;
|
||||
fromHook.writeSide.reset();
|
||||
toHook.readSide.reset();
|
||||
|
||||
sink = FdSink(toHook.writeSide.get());
|
||||
std::map<std::string, Config::SettingInfo> settings;
|
||||
@@ -76,7 +76,7 @@ HookInstance::HookInstance()
|
||||
HookInstance::~HookInstance()
|
||||
{
|
||||
try {
|
||||
toHook.writeSide = -1;
|
||||
toHook.writeSide.reset();
|
||||
if (pid != -1) pid.kill();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
|
||||
@@ -309,8 +309,8 @@ void LocalDerivationGoal::cleanupHookFinally()
|
||||
|
||||
void LocalDerivationGoal::cleanupPreChildKill()
|
||||
{
|
||||
sandboxMountNamespace = -1;
|
||||
sandboxUserNamespace = -1;
|
||||
sandboxMountNamespace.reset();
|
||||
sandboxUserNamespace.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ void LocalDerivationGoal::startBuilder()
|
||||
else
|
||||
pathsInChroot[i.substr(0, p)] = {i.substr(p + 1), optional};
|
||||
}
|
||||
if (hasPrefix(worker.store.storeDir, tmpDirInSandbox))
|
||||
if (worker.store.storeDir.starts_with(tmpDirInSandbox))
|
||||
{
|
||||
throw Error("`sandbox-build-dir` must not contain the storeDir");
|
||||
}
|
||||
@@ -807,7 +807,7 @@ void LocalDerivationGoal::startBuilder()
|
||||
Path logFile = openLogFile();
|
||||
|
||||
/* Create a pseudoterminal to get the output of the builder. */
|
||||
builderOut = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
builderOut = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)};
|
||||
if (!builderOut)
|
||||
throw SysError("opening pseudoterminal master");
|
||||
|
||||
@@ -834,7 +834,7 @@ void LocalDerivationGoal::startBuilder()
|
||||
/* Open the slave side of the pseudoterminal and use it as stderr. */
|
||||
auto openSlave = [&]()
|
||||
{
|
||||
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
|
||||
AutoCloseFD builderOut{open(slaveName.c_str(), O_RDWR | O_NOCTTY)};
|
||||
if (!builderOut)
|
||||
throw SysError("opening pseudoterminal slave");
|
||||
|
||||
@@ -937,12 +937,12 @@ void LocalDerivationGoal::startBuilder()
|
||||
if (helper.wait() != 0)
|
||||
throw Error("unable to start build process");
|
||||
|
||||
userNamespaceSync.readSide = -1;
|
||||
userNamespaceSync.readSide.reset();
|
||||
|
||||
/* Close the write side to prevent runChild() from hanging
|
||||
reading from this. */
|
||||
Finally cleanup([&]() {
|
||||
userNamespaceSync.writeSide = -1;
|
||||
userNamespaceSync.writeSide.reset();
|
||||
});
|
||||
|
||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||
@@ -981,12 +981,12 @@ void LocalDerivationGoal::startBuilder()
|
||||
|
||||
/* Save the mount- and user namespace of the child. We have to do this
|
||||
*before* the child does a chroot. */
|
||||
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
||||
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)};
|
||||
if (sandboxMountNamespace.get() == -1)
|
||||
throw SysError("getting sandbox mount namespace");
|
||||
|
||||
if (usingUserNamespace) {
|
||||
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
|
||||
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY)};
|
||||
if (sandboxUserNamespace.get() == -1)
|
||||
throw SysError("getting sandbox user namespace");
|
||||
}
|
||||
@@ -1471,8 +1471,8 @@ void LocalDerivationGoal::startDaemon()
|
||||
struct sockaddr_un remoteAddr;
|
||||
socklen_t remoteAddrLen = sizeof(remoteAddr);
|
||||
|
||||
AutoCloseFD remote = accept(daemonSocket.get(),
|
||||
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
|
||||
AutoCloseFD remote{accept(daemonSocket.get(),
|
||||
(struct sockaddr *) &remoteAddr, &remoteAddrLen)};
|
||||
if (!remote) {
|
||||
if (errno == EINTR || errno == EAGAIN) continue;
|
||||
if (errno == EINVAL || errno == ECONNABORTED) break;
|
||||
@@ -1705,12 +1705,12 @@ void LocalDerivationGoal::runChild()
|
||||
#if __linux__
|
||||
if (useChroot) {
|
||||
|
||||
userNamespaceSync.writeSide = -1;
|
||||
userNamespaceSync.writeSide.reset();
|
||||
|
||||
if (drainFD(userNamespaceSync.readSide.get()) != "1")
|
||||
throw Error("user namespace initialisation failed");
|
||||
|
||||
userNamespaceSync.readSide = -1;
|
||||
userNamespaceSync.readSide.reset();
|
||||
|
||||
if (privateNetwork) {
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "nar-info.hh"
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "substitution-goal.hh"
|
||||
#include "drv-output-substitution-goal.hh"
|
||||
#include "local-derivation-goal.hh"
|
||||
#include "signals.hh"
|
||||
#include "hook-instance.hh"
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
@@ -53,13 +53,13 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
* Python package brings its own
|
||||
* `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
|
||||
*/
|
||||
if (hasSuffix(srcFile, "/propagated-build-inputs") ||
|
||||
hasSuffix(srcFile, "/nix-support") ||
|
||||
hasSuffix(srcFile, "/perllocal.pod") ||
|
||||
hasSuffix(srcFile, "/info/dir") ||
|
||||
hasSuffix(srcFile, "/log") ||
|
||||
hasSuffix(srcFile, "/manifest.nix") ||
|
||||
hasSuffix(srcFile, "/manifest.json"))
|
||||
if (srcFile.ends_with("/propagated-build-inputs") ||
|
||||
srcFile.ends_with("/nix-support") ||
|
||||
srcFile.ends_with("/perllocal.pod") ||
|
||||
srcFile.ends_with("/info/dir") ||
|
||||
srcFile.ends_with("/log") ||
|
||||
srcFile.ends_with("/manifest.nix") ||
|
||||
srcFile.ends_with("/manifest.json"))
|
||||
continue;
|
||||
|
||||
else if (S_ISDIR(srcSt.st_mode)) {
|
||||
|
||||
@@ -41,7 +41,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
request.decompress = false;
|
||||
|
||||
auto decompressor = makeDecompressionSink(
|
||||
unpack && hasSuffix(mainUrl, ".xz") ? "xz" : "none", sink);
|
||||
unpack && mainUrl.ends_with(".xz") ? "xz" : "none", sink);
|
||||
fileTransfer->download(std::move(request), *decompressor);
|
||||
decompressor->finish();
|
||||
});
|
||||
@@ -62,7 +62,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
if (getAttr("outputHashMode") == "flat")
|
||||
for (auto hashedMirror : settings.hashedMirrors.get())
|
||||
try {
|
||||
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
|
||||
if (!hashedMirror.ends_with("/")) hashedMirror += '/';
|
||||
std::optional<HashType> ht = parseHashTypeOpt(getAttr("outputHashAlgo"));
|
||||
Hash h = newHashAllowEmpty(getAttr("outputHash"), ht);
|
||||
fetch(hashedMirror + printHashType(h.type) + "/" + h.to_string(Base16, false));
|
||||
|
||||
@@ -221,7 +221,7 @@ struct ClientSettings
|
||||
for (auto & s : ss)
|
||||
if (trusted.count(s))
|
||||
subs.push_back(s);
|
||||
else if (!hasSuffix(s, "/") && trusted.count(s + "/"))
|
||||
else if (!s.ends_with("/") && trusted.count(s + "/"))
|
||||
subs.push_back(s + "/");
|
||||
else
|
||||
warn("ignoring untrusted substituter '%s', you are not a trusted user.\n"
|
||||
|
||||
@@ -658,7 +658,7 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
|
||||
// FIXME: remove
|
||||
bool isDerivation(std::string_view fileName)
|
||||
{
|
||||
return hasSuffix(fileName, drvExtension);
|
||||
return fileName.ends_with(drvExtension);
|
||||
}
|
||||
|
||||
|
||||
@@ -927,7 +927,7 @@ std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath)
|
||||
{
|
||||
auto nameWithSuffix = drvPath.name();
|
||||
constexpr std::string_view extension = ".drv";
|
||||
assert(hasSuffix(nameWithSuffix, extension));
|
||||
assert(nameWithSuffix.ends_with(extension));
|
||||
nameWithSuffix.remove_suffix(extension.size());
|
||||
return nameWithSuffix;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "globals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "s3.hh"
|
||||
#include "signals.hh"
|
||||
#include "compression.hh"
|
||||
#include "finally.hh"
|
||||
#include "callback.hh"
|
||||
@@ -669,8 +670,8 @@ struct curlFileTransfer : public FileTransfer
|
||||
void enqueueItem(std::shared_ptr<TransferItem> item)
|
||||
{
|
||||
if (item->request.data
|
||||
&& !hasPrefix(item->request.uri, "http://")
|
||||
&& !hasPrefix(item->request.uri, "https://"))
|
||||
&& !item->request.uri.starts_with("http://")
|
||||
&& !item->request.uri.starts_with("https://"))
|
||||
throw nix::Error("uploading to '%s' is not supported", item->request.uri);
|
||||
|
||||
{
|
||||
@@ -702,7 +703,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
Callback<FileTransferResult> callback) override
|
||||
{
|
||||
/* Ugly hack to support s3:// URIs. */
|
||||
if (hasPrefix(request.uri, "s3://")) {
|
||||
if (request.uri.starts_with("s3://")) {
|
||||
// FIXME: do this on a worker thread
|
||||
try {
|
||||
#if ENABLE_S3
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
#include "derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
#include <functional>
|
||||
@@ -548,7 +549,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
if (fds[1].revents) {
|
||||
/* Accept a new connection. */
|
||||
assert(fds[1].revents & POLLIN);
|
||||
AutoCloseFD fdClient = accept(fdServer.get(), nullptr, nullptr);
|
||||
AutoCloseFD fdClient{accept(fdServer.get(), nullptr, nullptr)};
|
||||
if (!fdClient) continue;
|
||||
|
||||
debug("GC roots server accepted new client");
|
||||
@@ -646,7 +647,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
by another process. We need to be sure that we can acquire an
|
||||
exclusive lock before deleting them. */
|
||||
if (baseName.find("tmp-", 0) == 0) {
|
||||
AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY);
|
||||
AutoCloseFD tmpDirFd{open(realPath.c_str(), O_RDONLY | O_DIRECTORY)};
|
||||
if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) {
|
||||
debug("skipping locked tempdir '%s'", realPath);
|
||||
return;
|
||||
|
||||
@@ -241,7 +241,7 @@ bool Settings::isWSL1()
|
||||
uname(&utsbuf);
|
||||
// WSL1 uses -Microsoft suffix
|
||||
// WSL2 uses -microsoft-standard suffix
|
||||
return hasSuffix(utsbuf.release, "-Microsoft");
|
||||
return std::string_view(utsbuf.release).ends_with("-Microsoft");
|
||||
}
|
||||
|
||||
Path Settings::getDefaultSSLCertFile()
|
||||
@@ -342,7 +342,7 @@ void initPlugins()
|
||||
void *handle =
|
||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
throw Error("could not dynamically open plugin file '%s': %s", file, dlerror());
|
||||
warn("could not dynamically open plugin file '%s': %s", file, dlerror());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ void initLibStore() {
|
||||
sshd). This breaks build users because they don't have access
|
||||
to the TMPDIR, in particular in ‘nix-store --serve’. */
|
||||
#if __APPLE__
|
||||
if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
|
||||
if (getEnv("TMPDIR").value_or("/tmp").starts_with("/var/folders/"))
|
||||
unsetenv("TMPDIR");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ protected:
|
||||
FileTransferRequest makeRequest(const std::string & path)
|
||||
{
|
||||
return FileTransferRequest(
|
||||
hasPrefix(path, "https://") || hasPrefix(path, "http://") || hasPrefix(path, "file://")
|
||||
path.starts_with("https://") || path.starts_with("http://") || path.starts_with("file://")
|
||||
? path
|
||||
: cacheUri + "/" + path);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ protected:
|
||||
|
||||
for (auto & entry : readDirectory(binaryCacheDir)) {
|
||||
if (entry.name.size() != 40 ||
|
||||
!hasSuffix(entry.name, ".narinfo"))
|
||||
!entry.name.ends_with(".narinfo"))
|
||||
continue;
|
||||
paths.insert(parseStorePath(
|
||||
storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "references.hh"
|
||||
#include "callback.hh"
|
||||
#include "topo-sort.hh"
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "compression.hh"
|
||||
|
||||
@@ -262,7 +263,7 @@ LocalStore::LocalStore(const Params & params)
|
||||
if (stat(reservedPath.c_str(), &st) == -1 ||
|
||||
st.st_size != settings.reservedSize)
|
||||
{
|
||||
AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
|
||||
AutoCloseFD fd{open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600)};
|
||||
int res = -1;
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
res = posix_fallocate(fd.get(), 0, settings.reservedSize);
|
||||
@@ -452,7 +453,7 @@ LocalStore::LocalStore(std::string scheme, std::string path, const Params & para
|
||||
AutoCloseFD LocalStore::openGCLock()
|
||||
{
|
||||
Path fnGCLock = stateDir + "/gc.lock";
|
||||
auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
AutoCloseFD fdGCLock{open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fdGCLock)
|
||||
throw SysError("opening global GC lock '%1%'", fnGCLock);
|
||||
return fdGCLock;
|
||||
@@ -477,7 +478,7 @@ LocalStore::~LocalStore()
|
||||
try {
|
||||
auto fdTempRoots(_fdTempRoots.lock());
|
||||
if (*fdTempRoots) {
|
||||
*fdTempRoots = -1;
|
||||
fdTempRoots->reset();
|
||||
unlink(fnTempRoots.c_str());
|
||||
}
|
||||
} catch (...) {
|
||||
@@ -1483,7 +1484,7 @@ std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
|
||||
the GC between createTempDir() and when we acquire a lock on it.
|
||||
We'll repeat until 'tmpDir' exists and we've locked it. */
|
||||
tmpDirFn = createTempDir(realStoreDir, "tmp");
|
||||
tmpDirFd = open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY);
|
||||
tmpDirFd = AutoCloseFD{open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY)};
|
||||
if (tmpDirFd.get() < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct SimpleUserLock : UserLock
|
||||
|
||||
auto fnUserLock = fmt("%s/userpool/%s", settings.nixStateDir,pw->pw_uid);
|
||||
|
||||
AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fd)
|
||||
throw SysError("opening user lock '%s'", fnUserLock);
|
||||
|
||||
@@ -148,7 +148,7 @@ struct AutoUserLock : UserLock
|
||||
|
||||
auto fnUserLock = fmt("%s/userpool2/slot-%d", settings.nixStateDir, i);
|
||||
|
||||
AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fd)
|
||||
throw SysError("opening user lock '%s'", fnUserLock);
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ Machine::Machine(decltype(storeUri) storeUri,
|
||||
|| storeUri == "auto"
|
||||
|| storeUri == "daemon"
|
||||
|| storeUri == "local"
|
||||
|| hasPrefix(storeUri, "auto?")
|
||||
|| hasPrefix(storeUri, "daemon?")
|
||||
|| hasPrefix(storeUri, "local?")
|
||||
|| hasPrefix(storeUri, "?")
|
||||
|| storeUri.starts_with("auto?")
|
||||
|| storeUri.starts_with("daemon?")
|
||||
|| storeUri.starts_with("local?")
|
||||
|| storeUri.starts_with("?")
|
||||
? storeUri
|
||||
: "ssh://" + storeUri),
|
||||
systemTypes(systemTypes),
|
||||
@@ -67,12 +67,12 @@ bool Machine::mandatoryMet(const std::set<std::string> & features) const
|
||||
ref<Store> Machine::openStore() const
|
||||
{
|
||||
Store::Params storeParams;
|
||||
if (hasPrefix(storeUri, "ssh://")) {
|
||||
if (storeUri.starts_with("ssh://")) {
|
||||
storeParams["max-connections"] = "1";
|
||||
storeParams["log-fd"] = "4";
|
||||
}
|
||||
|
||||
if (hasPrefix(storeUri, "ssh://") || hasPrefix(storeUri, "ssh-ng://")) {
|
||||
if (storeUri.starts_with("ssh://") || storeUri.starts_with("ssh-ng://")) {
|
||||
if (sshKey != "")
|
||||
storeParams["ssh-key"] = sshKey;
|
||||
if (sshPublicHostKey != "")
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
schema_sql_gen = gen_header.process('schema.sql')
|
||||
ca_specific_schema_gen = gen_header.process('ca-specific-schema.sql')
|
||||
|
||||
libstore_sources = files(
|
||||
'binary-cache-store.cc',
|
||||
'build-result.cc',
|
||||
'common-protocol.cc',
|
||||
'content-address.cc',
|
||||
'crypto.cc',
|
||||
'daemon.cc',
|
||||
'derivations.cc',
|
||||
'derived-path-map.cc',
|
||||
'derived-path.cc',
|
||||
'downstream-placeholder.cc',
|
||||
'dummy-store.cc',
|
||||
'export-import.cc',
|
||||
'filetransfer.cc',
|
||||
'gc.cc',
|
||||
'globals.cc',
|
||||
'http-binary-cache-store.cc',
|
||||
'legacy-ssh-store.cc',
|
||||
'local-binary-cache-store.cc',
|
||||
'local-fs-store.cc',
|
||||
'local-store.cc',
|
||||
'lock.cc',
|
||||
'log-store.cc',
|
||||
'machines.cc',
|
||||
'make-content-addressed.cc',
|
||||
'misc.cc',
|
||||
'names.cc',
|
||||
'nar-accessor.cc',
|
||||
'nar-info-disk-cache.cc',
|
||||
'nar-info.cc',
|
||||
'optimise-store.cc',
|
||||
'outputs-spec.cc',
|
||||
'parsed-derivations.cc',
|
||||
'path-info.cc',
|
||||
'path-references.cc',
|
||||
'path-with-outputs.cc',
|
||||
'path.cc',
|
||||
'pathlocks.cc',
|
||||
'profiles.cc',
|
||||
'realisation.cc',
|
||||
'remote-fs-accessor.cc',
|
||||
'remote-store.cc',
|
||||
's3-binary-cache-store.cc',
|
||||
'serve-protocol.cc',
|
||||
'sqlite.cc',
|
||||
'ssh-store.cc',
|
||||
'ssh.cc',
|
||||
'store-api.cc',
|
||||
'uds-remote-store.cc',
|
||||
'worker-protocol.cc',
|
||||
'build/derivation-goal.cc',
|
||||
'build/drv-output-substitution-goal.cc',
|
||||
'build/entry-points.cc',
|
||||
'build/goal.cc',
|
||||
'build/hook-instance.cc',
|
||||
'build/local-derivation-goal.cc',
|
||||
'build/personality.cc',
|
||||
'build/substitution-goal.cc',
|
||||
'build/worker.cc',
|
||||
'builtins/buildenv.cc',
|
||||
'builtins/fetchurl.cc',
|
||||
'builtins/unpack-channel.cc',
|
||||
)
|
||||
|
||||
|
||||
libstore_headers = files(
|
||||
'binary-cache-store.hh',
|
||||
'build/derivation-goal.hh',
|
||||
'build/drv-output-substitution-goal.hh',
|
||||
'build/goal.hh',
|
||||
'build/hook-instance.hh',
|
||||
'build/local-derivation-goal.hh',
|
||||
'build/personality.hh',
|
||||
'build/substitution-goal.hh',
|
||||
'build/worker.hh',
|
||||
'build-result.hh',
|
||||
'builtins/buildenv.hh',
|
||||
'builtins.hh',
|
||||
'common-protocol-impl.hh',
|
||||
'common-protocol.hh',
|
||||
'content-address.hh',
|
||||
'crypto.hh',
|
||||
'daemon.hh',
|
||||
'derivations.hh',
|
||||
'derived-path-map.hh',
|
||||
'derived-path.hh',
|
||||
'downstream-placeholder.hh',
|
||||
'filetransfer.hh',
|
||||
'fs-accessor.hh',
|
||||
'gc-store.hh',
|
||||
'globals.hh',
|
||||
'indirect-root-store.hh',
|
||||
'length-prefixed-protocol-helper.hh',
|
||||
'local-fs-store.hh',
|
||||
'local-store.hh',
|
||||
'lock.hh',
|
||||
'log-store.hh',
|
||||
'machines.hh',
|
||||
'make-content-addressed.hh',
|
||||
'names.hh',
|
||||
'nar-accessor.hh',
|
||||
'nar-info-disk-cache.hh',
|
||||
'nar-info.hh',
|
||||
'outputs-spec.hh',
|
||||
'parsed-derivations.hh',
|
||||
'path-info.hh',
|
||||
'path-references.hh',
|
||||
'path-regex.hh',
|
||||
'path-with-outputs.hh',
|
||||
'path.hh',
|
||||
'pathlocks.hh',
|
||||
'profiles.hh',
|
||||
'realisation.hh',
|
||||
'remote-fs-accessor.hh',
|
||||
'remote-store-connection.hh',
|
||||
'remote-store.hh',
|
||||
's3-binary-cache-store.hh',
|
||||
's3.hh',
|
||||
'serve-protocol-impl.hh',
|
||||
'serve-protocol.hh',
|
||||
'sqlite.hh',
|
||||
'ssh-store-config.hh',
|
||||
'ssh.hh',
|
||||
'store-api.hh',
|
||||
'store-cast.hh',
|
||||
'uds-remote-store.hh',
|
||||
'worker-protocol-impl.hh',
|
||||
'worker-protocol.hh',
|
||||
)
|
||||
|
||||
# These variables (aside from LSOF) are created pseudo-dynamically, near the beginning of
|
||||
# the top-level meson.build. Aside from prefix itself, each of these was
|
||||
# made into an absolute path by joining it with prefix, unless it was already
|
||||
# an absolute path (which is the default for store-dir, state-dir, and log-dir).
|
||||
cpp_str_defines = {
|
||||
'LSOF': lsof.full_path(),
|
||||
'NIX_PREFIX': prefix,
|
||||
'NIX_STORE_DIR': store_dir,
|
||||
'NIX_DATA_DIR': datadir,
|
||||
'NIX_STATE_DIR': state_dir,
|
||||
'NIX_LOG_DIR': log_dir,
|
||||
'NIX_CONF_DIR': sysconfdir,
|
||||
'NIX_BIN_DIR': bindir,
|
||||
'NIX_MAN_DIR': mandir,
|
||||
}
|
||||
|
||||
cpp_args = []
|
||||
|
||||
foreach name, value : cpp_str_defines
|
||||
cpp_args += [
|
||||
'-D' + name + '=' + '"' + value + '"'
|
||||
]
|
||||
endforeach
|
||||
|
||||
libstore = library(
|
||||
'nixstore',
|
||||
schema_sql_gen,
|
||||
ca_specific_schema_gen,
|
||||
libstore_sources,
|
||||
dependencies : [
|
||||
libarchive,
|
||||
liblixutil, # Internal.
|
||||
seccomp,
|
||||
sqlite,
|
||||
sodium,
|
||||
seccomp,
|
||||
curl,
|
||||
openssl,
|
||||
aws_sdk,
|
||||
aws_s3,
|
||||
aws_sdk_transfer,
|
||||
],
|
||||
cpp_args : cpp_args,
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(libstore_headers, subdir : 'nix', preserve_path : true)
|
||||
|
||||
# Used by libfetchers.
|
||||
liblixstore = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libstore,
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
#include "globals.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@@ -42,7 +42,7 @@ StorePath::StorePath(const Hash & hash, std::string_view _name)
|
||||
|
||||
bool StorePath::isDerivation() const
|
||||
{
|
||||
return hasSuffix(name(), drvExtension);
|
||||
return name().ends_with(drvExtension);
|
||||
}
|
||||
|
||||
StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pathlocks.hh"
|
||||
#include "util.hh"
|
||||
#include "signals.hh"
|
||||
#include "sync.hh"
|
||||
|
||||
#include <cerrno>
|
||||
@@ -16,9 +17,7 @@ namespace nix {
|
||||
|
||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
||||
{
|
||||
AutoCloseFD fd;
|
||||
|
||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||
AutoCloseFD fd{open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600)};
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
|
||||
auto narAccessor = makeLazyNarAccessor(listing,
|
||||
[cacheFile](uint64_t offset, uint64_t length) {
|
||||
|
||||
AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd{open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", cacheFile);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user