forked from lix-project/lix
Compare commits
71 commits
release-2.
...
main
Author | SHA1 | Date | |
---|---|---|---|
jade | a510d17484 | ||
jade | 04f8a14833 | ||
jade | e6f2af06e6 | ||
jade | 4d89844207 | ||
Rebecca Turner | 422550fd68 | ||
jade | 5d31e889d7 | ||
Pierre Bourdon | 4f02255c20 | ||
Rebecca Turner | 0dc486a5bf | ||
jade | 0cc285f87b | ||
Rebecca Turner | ca08f1217d | ||
eldritch horrors | b6884388a1 | ||
Rebecca Turner | 0582999bd1 | ||
eldritch horrors | 398894b856 | ||
eldritch horrors | 30a87b4cd5 | ||
jade | 72f91767a8 | ||
jade | 3bf8819fa2 | ||
Rebecca Turner | c300efc0e1 | ||
eldritch horrors | cae260a158 | ||
eldritch horrors | 04b591dc1d | ||
jade | 686120ee4a | ||
Rebecca Turner | fabc9f29b8 | ||
Rebecca Turner | c5949bfe31 | ||
jade | 7e677d15a4 | ||
jade | af546be205 | ||
jade | 9aacf425dc | ||
Rebecca Turner | 9845637359 | ||
jade | 87fd6e0095 | ||
jade | 9896d309cb | ||
sugar🍬🍭🏳️⚧️ | f2e7f8bab8 | ||
sugar🍬🍭🏳️⚧️ | 447212fa65 | ||
jade | 651cc0e5b4 | ||
jade | dba615098d | ||
alois31 | e3c289dbe9 | ||
piegames | e38410799b | ||
piegames | 0edfea450b | ||
jade | 3cbbe22fab | ||
piegames | 0a8888d1c7 | ||
piegames | 7210ed1b87 | ||
jade | c25c43d8c8 | ||
Audrey Dutcher | ac6974777e | ||
jade | 736b5d5913 | ||
Qyriad | 95863b258b | ||
f1533160aa | |||
df49d37b71 | |||
Audrey Dutcher | ae628d4af2 | ||
Maximilian Bosch | 040e783232 | ||
eldritch horrors | e727dbc3a3 | ||
eldritch horrors | b40369942c | ||
eldritch horrors | fca523d661 | ||
eldritch horrors | 5e9db09761 | ||
eldritch horrors | e513cd2beb | ||
eldritch horrors | fb8eb539fc | ||
jade | 3d14567d0b | ||
jade | 925e08b858 | ||
eldritch horrors | 5cbca85535 | ||
jade | ecfe9345cf | ||
jade | 84543b459c | ||
eldritch horrors | e2d330aeed | ||
piegames | 007211e7a2 | ||
eldritch horrors | 7506d680ac | ||
eldritch horrors | 38f550708d | ||
eldritch horrors | 176e1058f1 | ||
eldritch horrors | 91a74ba82a | ||
eldritch horrors | b66fd9ff4b | ||
piegames | 278fddc317 | ||
piegames | 49d61b2e4b | ||
piegames | 1c080a8239 | ||
Artemis Tosini | 41a0b08e64 | ||
Artemis Tosini | b016eb0895 | ||
jade | f9a3bf6ccc | ||
Artemis Tosini | 3058029fba |
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -9,6 +9,10 @@ GTAGS
|
|||
# ccls
|
||||
/.ccls-cache
|
||||
|
||||
# auto-generated compilation database
|
||||
compile_commands.json
|
||||
rust-project.json
|
||||
|
||||
result
|
||||
result-*
|
||||
|
||||
|
@ -29,3 +33,6 @@ buildtime.bin
|
|||
/.pre-commit-config.yaml
|
||||
/.nocontribmsg
|
||||
/release
|
||||
|
||||
# Rust build files when using Cargo (not actually supported for building but it spews the files anyway)
|
||||
/target/
|
||||
|
|
28
lix-doc/Cargo.lock → Cargo.lock
generated
28
lix-doc/Cargo.lock → Cargo.lock
generated
|
@ -2,12 +2,6 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "countme"
|
||||
version = "3.0.1"
|
||||
|
@ -16,15 +10,15 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
|||
|
||||
[[package]]
|
||||
name = "dissimilar"
|
||||
version = "1.0.7"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
|
||||
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
|
||||
|
||||
[[package]]
|
||||
name = "expect-test"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
|
||||
checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"once_cell",
|
||||
|
@ -45,15 +39,6 @@ dependencies = [
|
|||
"rowan",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
|
@ -71,13 +56,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rowan"
|
||||
version = "0.15.15"
|
||||
version = "0.15.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
|
||||
checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d"
|
||||
dependencies = [
|
||||
"countme",
|
||||
"hashbrown",
|
||||
"memoffset",
|
||||
"rustc-hash",
|
||||
"text-size",
|
||||
]
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["src/lix-doc"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
|
@ -1,9 +1,14 @@
|
|||
# Usually "experimental" or "deprecated"
|
||||
kind:
|
||||
# "xp" or "dp"
|
||||
kindShort:
|
||||
|
||||
with builtins;
|
||||
with import ./utils.nix;
|
||||
|
||||
let
|
||||
showExperimentalFeature = name: doc: ''
|
||||
- [`${name}`](@docroot@/contributing/experimental-features.md#xp-feature-${name})
|
||||
- [`${name}`](@docroot@/contributing/${kind}-features.md#${kindShort}-feature-${name})
|
||||
'';
|
||||
in
|
||||
xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps)))
|
18
doc/manual/generate-features.nix
Normal file
18
doc/manual/generate-features.nix
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Usually "experimental" or "deprecated"
|
||||
_kind:
|
||||
# "xp" or "dp"
|
||||
kindShort:
|
||||
|
||||
with builtins;
|
||||
with import ./utils.nix;
|
||||
|
||||
let
|
||||
showFeature =
|
||||
name: doc:
|
||||
squash ''
|
||||
## [`${name}`]{#${kindShort}-feature-${name}}
|
||||
|
||||
${doc}
|
||||
'';
|
||||
in
|
||||
xps: (concatStringsSep "\n" (attrValues (mapAttrs showFeature xps)))
|
|
@ -1,13 +0,0 @@
|
|||
with builtins;
|
||||
with import ./utils.nix;
|
||||
|
||||
let
|
||||
showExperimentalFeature =
|
||||
name: doc:
|
||||
squash ''
|
||||
## [`${name}`]{#xp-feature-${name}}
|
||||
|
||||
${doc}
|
||||
'';
|
||||
in
|
||||
xps: (concatStringsSep "\n" (attrValues (mapAttrs showExperimentalFeature xps)))
|
|
@ -20,6 +20,8 @@ conf_file_json = custom_target(
|
|||
capture : true,
|
||||
output : 'conf-file.json',
|
||||
env : nix_env_for_docs,
|
||||
# FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though.
|
||||
build_always_stale : true,
|
||||
)
|
||||
|
||||
nix_conf_file_md_body = custom_target(
|
||||
|
@ -50,6 +52,8 @@ nix_exp_features_json = custom_target(
|
|||
command : [ nix, '__dump-xp-features' ],
|
||||
capture : true,
|
||||
output : 'xp-features.json',
|
||||
# FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though.
|
||||
build_always_stale : true,
|
||||
)
|
||||
|
||||
language_json = custom_target(
|
||||
|
@ -57,6 +61,8 @@ language_json = custom_target(
|
|||
output : 'language.json',
|
||||
capture : true,
|
||||
env : nix_env_for_docs,
|
||||
# FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though.
|
||||
build_always_stale : true,
|
||||
)
|
||||
|
||||
nix3_cli_json = custom_target(
|
||||
|
@ -64,6 +70,8 @@ nix3_cli_json = custom_target(
|
|||
capture : true,
|
||||
output : 'nix.json',
|
||||
env : nix_env_for_docs,
|
||||
# FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though.
|
||||
build_always_stale : true,
|
||||
)
|
||||
|
||||
generate_manual_deps = files(
|
||||
|
@ -72,9 +80,9 @@ generate_manual_deps = files(
|
|||
|
||||
# Generates builtins.md and builtin-constants.md.
|
||||
subdir('src/language')
|
||||
# Generates new-cli pages, experimental-features-shortlist.md, and conf-file.md.
|
||||
# Generates new-cli pages, {experimental,deprecated}-features-shortlist.md, and conf-file.md.
|
||||
subdir('src/command-ref')
|
||||
# Generates experimental-feature-descriptions.md.
|
||||
# Generates {experimental,deprecated}-feature-descriptions.md.
|
||||
subdir('src/contributing')
|
||||
# Generates rl-next-generated.md.
|
||||
subdir('src/release-notes')
|
||||
|
@ -106,6 +114,8 @@ manual = custom_target(
|
|||
nix3_cli_files,
|
||||
experimental_features_shortlist_md,
|
||||
experimental_feature_descriptions_md,
|
||||
deprecated_features_shortlist_md,
|
||||
deprecated_feature_descriptions_md,
|
||||
conf_file_md,
|
||||
builtins_md,
|
||||
builtin_constants_md,
|
||||
|
|
21
doc/manual/rl-next/allowsubstitutes-errors.md
Normal file
21
doc/manual/rl-next/allowsubstitutes-errors.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
synopsis: "Build failures caused by `allowSubstitutes = false` while being the wrong system now produce a decent error"
|
||||
issues: [fj#484]
|
||||
cls: [1841]
|
||||
category: Fixes
|
||||
credits: jade
|
||||
---
|
||||
|
||||
Nix allows derivations to set `allowSubstitutes = false` in order to force them to be built locally without querying substituters for them.
|
||||
This is useful for derivations that are very fast to build (especially if they produce large output).
|
||||
However, this can shoot you in the foot if the derivation *has* to be substituted such as if the derivation is for another architecture, which is what `--always-allow-substitutes` is for.
|
||||
|
||||
Perhaps such derivations that are known to be impossible to build locally should ignore `allowSubstitutes` (irrespective of remote builders) in the future, but this at least reports the failure and solution directly.
|
||||
|
||||
```
|
||||
$ nix build -f fail.nix
|
||||
error: a 'unicornsandrainbows-linux' with features {} is required to build '/nix/store/...-meow.drv', but I am a 'x86_64-linux' with features {...}
|
||||
|
||||
Hint: the failing derivation has allowSubstitutes set to false, forcing it to be built rather than substituted.
|
||||
Passing --always-allow-substitutes to force substitution may resolve this failure if the path is available in a substituter.
|
||||
```
|
17
doc/manual/rl-next/deprecated-features.md
Normal file
17
doc/manual/rl-next/deprecated-features.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
synopsis: Deprecated language features
|
||||
issues: [fj#437]
|
||||
cls: [1785, 1736, 1735, 1744]
|
||||
category: Breaking Changes
|
||||
credits: [piegames, horrors]
|
||||
---
|
||||
|
||||
A system for deprecation (and then the planned removal) of undesired language features has been put into place.
|
||||
It is controlled via feature flags much like experimental features, except that the deprecations are enabled default,
|
||||
and can be disabled via the flags for backwards compatibility (opt-out with `--extra-deprecated-features` or the Nix configuration file).
|
||||
|
||||
- `url-literals`: **URL literals** have long been obsolete and discouraged of use, and now they are officially deprecated.
|
||||
This means that all URLs must be properly put within quotes like all other strings.
|
||||
- `rec-set-overrides`: **__overrides** is an old arcane syntax which has not been in use for more than a decade.
|
||||
It is soft-deprecated with a warning only, with the plan to turn that into an error in a future release.
|
||||
- `ancient-let`: **The old `let` syntax** (`let { body = …; … }`) is soft-deprecated with a warning as well. Use the regular `let … in` instead.
|
10
doc/manual/rl-next/http-proxy-for-s3.md
Normal file
10
doc/manual/rl-next/http-proxy-for-s3.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
synopsis: HTTP proxy environment variables are now respected for S3 binary cache stores
|
||||
issues: [fj#433]
|
||||
cls: [1788]
|
||||
category: Fixes
|
||||
credits: jade
|
||||
---
|
||||
|
||||
Due to "legacy reasons" (according to the AWS C++ SDK docs), the AWS SDK ignores system proxy configuration by default.
|
||||
We turned it back on.
|
|
@ -192,6 +192,7 @@
|
|||
- [Hacking](contributing/hacking.md)
|
||||
- [Testing](contributing/testing.md)
|
||||
- [Experimental Features](contributing/experimental-features.md)
|
||||
- [Deprecated Features](contributing/deprecated-features.md)
|
||||
- [CLI guideline](contributing/cli-guideline.md)
|
||||
- [C++ style guide](contributing/cxx.md)
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
|
|
|
@ -1,23 +1,37 @@
|
|||
xp_features_json = custom_target(
|
||||
command : [nix, '__dump-xp-features'],
|
||||
capture : true,
|
||||
output : 'xp-features.json',
|
||||
)
|
||||
|
||||
experimental_features_shortlist_md = custom_target(
|
||||
command : nix_eval_for_docs + [
|
||||
'--expr',
|
||||
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
'import @INPUT0@ "experimental" "xp" (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
],
|
||||
input : [
|
||||
'../../generate-xp-features-shortlist.nix',
|
||||
xp_features_json,
|
||||
'../../generate-features-shortlist.nix',
|
||||
nix_exp_features_json,
|
||||
],
|
||||
capture : true,
|
||||
output : 'experimental-features-shortlist.md',
|
||||
env : nix_env_for_docs,
|
||||
)
|
||||
|
||||
dp_features_json = custom_target(
|
||||
command : [nix, '__dump-dp-features'],
|
||||
capture : true,
|
||||
output : 'dp-features.json',
|
||||
)
|
||||
|
||||
deprecated_features_shortlist_md = custom_target(
|
||||
command : nix_eval_for_docs + [
|
||||
'--expr',
|
||||
'import @INPUT0@ "deprecated" "dp" (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
],
|
||||
input : [
|
||||
'../../generate-features-shortlist.nix',
|
||||
dp_features_json,
|
||||
],
|
||||
capture : true,
|
||||
output : 'deprecated-features-shortlist.md',
|
||||
env : nix_env_for_docs,
|
||||
)
|
||||
|
||||
# Intermediate step for manpage generation.
|
||||
# This splorks the output of generate-manpage.nix as JSON,
|
||||
# which gets written as a directory tree below.
|
||||
|
@ -60,6 +74,7 @@ conf_file_md = custom_target(
|
|||
'../../utils.nix',
|
||||
conf_file_json,
|
||||
experimental_features_shortlist_md,
|
||||
deprecated_features_shortlist_md,
|
||||
],
|
||||
output : 'conf-file.md',
|
||||
env : nix_env_for_docs,
|
||||
|
|
37
doc/manual/src/contributing/deprecated-features.md
Normal file
37
doc/manual/src/contributing/deprecated-features.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
This section describes the notion of *deprecated features*, and how it fits into the big picture of the development of Lix.
|
||||
|
||||
# What are deprecated features?
|
||||
|
||||
Deprecated features are disabled by default, with the intent to eventually remove them.
|
||||
Users must explicitly enable them to keep using them, by toggling the associated [deprecated feature flags](@docroot@/command-ref/conf-file.md#conf-deprecated-features).
|
||||
This allows backwards compatibility and a graceful transition away from undesired features.
|
||||
|
||||
# Which features can be deprecated?
|
||||
|
||||
Undesired features should be soft-deprecated by yielding a warning when used for a significant amount of time before the can be deprecated.
|
||||
Legacy obsolete feature with little to no usage may go through this process faster.
|
||||
Deprecated features should have a migration path to a preferred alternative.
|
||||
|
||||
# Lifecycle of a deprecated feature
|
||||
|
||||
This description is not normative, but a feature removal may roughly happen like this:
|
||||
|
||||
1. Add a warning when the feature is being used.
|
||||
2. Disable the feature by default, putting it behind a deprecated feature flag.
|
||||
- If disabling the feature started out as an opt-in experimental feature, turn that experimental flag into a no-op or remove it entirely.
|
||||
For example, `--extra-experimental-features=no-url-literals` becomes `--extra-deprecated-features=url-literals`.
|
||||
3. Decide on a time frame for how long that feature will still be supported for backwards compatibility, and clearly communicate that in the error messages.
|
||||
- Sometimes, automatic migration to alternatives is possible, and such should be provided if possible
|
||||
- At least one NixOS release cycle should be the minimum
|
||||
4. Finally remove the feature entirely, only keeping the error message for those still using it.
|
||||
|
||||
# Relation to language versioning
|
||||
|
||||
Obviously, removing anything breaks backwards compatibility.
|
||||
In an ideal world, we'd have SemVer controls over the language and its features, cleanly allowing us to make breaking changes.
|
||||
See https://wiki.lix.systems/books/lix-contributors/page/language-versioning and [RFC 137](https://github.com/nixos/rfcs/pull/137) for efforts on that.
|
||||
However, we do not live in such an ideal world, and currently this goal is so far away, that "just disable it with some back-compat for a couple of years" is the most realistic solution, especially for comparatively minor changes.
|
||||
|
||||
# Currently available deprecated features
|
||||
|
||||
{{#include @generated@/contributing/deprecated-feature-descriptions.md}}
|
|
@ -4,12 +4,25 @@
|
|||
experimental_feature_descriptions_md = custom_target(
|
||||
command : nix_eval_for_docs + [
|
||||
'--expr',
|
||||
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
'import @INPUT0@ "experimental" "xp" (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
],
|
||||
input : [
|
||||
'../../generate-xp-features.nix',
|
||||
xp_features_json,
|
||||
'../../generate-features.nix',
|
||||
nix_exp_features_json,
|
||||
],
|
||||
capture : true,
|
||||
output : 'experimental-feature-descriptions.md',
|
||||
)
|
||||
|
||||
deprecated_feature_descriptions_md = custom_target(
|
||||
command : nix_eval_for_docs + [
|
||||
'--expr',
|
||||
'import @INPUT0@ "deprecated" "dp" (builtins.fromJSON (builtins.readFile @INPUT1@))',
|
||||
],
|
||||
input : [
|
||||
'../../generate-features.nix',
|
||||
dp_features_json,
|
||||
],
|
||||
capture : true,
|
||||
output : 'deprecated-feature-descriptions.md',
|
||||
)
|
||||
|
|
|
@ -77,12 +77,6 @@
|
|||
}
|
||||
```
|
||||
|
||||
Finally, as a convenience, *URIs* as defined in appendix B of
|
||||
[RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as
|
||||
is*, without quotes. For instance, the string
|
||||
`"http://example.org/foo.tar.bz2"` can also be written as
|
||||
`http://example.org/foo.tar.bz2`.
|
||||
|
||||
- <a id="type-number" href="#type-number">Number</a>
|
||||
|
||||
Numbers, which can be *integers* (like `123`) or *floating point*
|
||||
|
|
58
meson.build
58
meson.build
|
@ -30,6 +30,14 @@
|
|||
# FIXME: This hack should be removed when https://git.lix.systems/lix-project/lix/issues/359
|
||||
# is fixed.
|
||||
#
|
||||
# lix-doc is built with Meson in lix-doc/meson.build, and linked into libcmd in
|
||||
# src/libcmd/meson.build. When building outside the Nix sandbox, Meson will use the .wrap
|
||||
# files in subprojects/ to download and extract the dependency crates into subprojects/.
|
||||
# When building inside the Nix sandbox, Lix's derivation in package.nix uses a
|
||||
# fixed-output derivation to fetch those crates in advance instead, and then symlinks
|
||||
# them into subprojects/ with the same names that Meson uses when downloading them
|
||||
# itself -- perfect for --wrap-mode=nodownload, which mesonConfigurePhase uses.
|
||||
#
|
||||
# Unit tests are setup in tests/unit/meson.build, under the test suite "check".
|
||||
#
|
||||
# Functional tests are a bit more complicated. Generally they're defined in
|
||||
|
@ -38,10 +46,11 @@
|
|||
# be placed in specific directories' meson.build files to create the right directory tree
|
||||
# in the build directory.
|
||||
|
||||
project('lix', 'cpp',
|
||||
project('lix', 'cpp', 'rust',
|
||||
version : run_command('bash', '-c', 'echo -n $(jq -r .version < ./version.json)$VERSION_SUFFIX', check : true).stdout().strip(),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'rust_std=2021',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
|
@ -138,6 +147,17 @@ if should_pch
|
|||
# Unlike basically everything else that takes a file, Meson requires the arguments to
|
||||
# cpp_pch : to be strings and doesn't accept files(). So absolute path it is.
|
||||
cpp_pch = [meson.project_source_root() / 'src/pch/precompiled-headers.hh']
|
||||
|
||||
# Saves about 400s (30% at time of writing) from compile time on-cpu, mostly
|
||||
# by removing instantiations of nlohmann from every single damned compilation
|
||||
# unit.
|
||||
# There is no equivalent in gcc.
|
||||
if cxx.get_id() == 'clang'
|
||||
add_project_arguments(
|
||||
'-fpch-instantiate-templates',
|
||||
language : 'cpp',
|
||||
)
|
||||
endif
|
||||
else
|
||||
cpp_pch = []
|
||||
endif
|
||||
|
@ -322,13 +342,6 @@ pegtl = dependency(
|
|||
|
||||
nlohmann_json = dependency('nlohmann_json', required : true, include_type : 'system')
|
||||
|
||||
# lix-doc is a Rust project provided via buildInputs and unfortunately doesn't have any way to be detected.
|
||||
# Just declare it manually to resolve this.
|
||||
#
|
||||
# FIXME: build this with meson in the future after we drop Make (with which we
|
||||
# *absolutely* are not going to make it work)
|
||||
lix_doc = declare_dependency(link_args : [ '-llix_doc' ])
|
||||
|
||||
if is_freebsd
|
||||
libprocstat = declare_dependency(link_args : [ '-lprocstat' ])
|
||||
endif
|
||||
|
@ -417,7 +430,7 @@ check_funcs = [
|
|||
'strsignal',
|
||||
'sysconf',
|
||||
]
|
||||
if target_machine.kernel() in ['linux', 'freebsd']
|
||||
if is_linux or is_freebsd
|
||||
# musl does not have close_range as of 2024-08-10
|
||||
# patch: https://www.openwall.com/lists/musl/2024/08/01/9
|
||||
check_funcs += [ 'close_range' ]
|
||||
|
@ -543,6 +556,33 @@ if cxx.get_id() in ['clang', 'gcc']
|
|||
)
|
||||
endif
|
||||
|
||||
# Until Meson 1.5¹, we can't just give Meson a Cargo.lock file and be done with it.
|
||||
# Meson will *detect* what dependencies are needed from Cargo files; it just won't
|
||||
# fetch them. The Meson 1.5 feature essentially internally translates Cargo.lock entries
|
||||
# to .wrap files, and that translation is incredibly straightforward, so let's just
|
||||
# use a simple Python script to generate the .wrap files ourselves while we wait for
|
||||
# Meson 1.5. Weirdly, it seems Meson will only detect dependencies from other
|
||||
# dependency() calls, so we have to specify lix-doc's two top-level dependencies,
|
||||
# rnix and rowan, manually, and then their dependencies will be recursively translated
|
||||
# into more dependency() calls.
|
||||
#
|
||||
# When Meson translates a Cargo dependency, the string passed to `dependency()` follows
|
||||
# a fixed format, which is important as the .wrap files' basenames must match the string
|
||||
# passed to `dependency()` exactly.
|
||||
# In Meson 1.4, this format is `$packageName-rs`. Meson 1.5 changes this to
|
||||
# `$packageName-$shortenedVersionString-rs`, because of course it does, but we'll cross
|
||||
# that bridge when we get there...
|
||||
#
|
||||
# [1]: https://github.com/mesonbuild/meson/commit/9b8378985dbdc0112d11893dd42b33b7bc8d1e62
|
||||
# FIXME: remove (along with its generated wrap files) when we get rid of meson 1.4
|
||||
run_command(
|
||||
python,
|
||||
meson.project_source_root() / 'meson/cargo-lock-to-wraps.py',
|
||||
meson.project_source_root() / 'Cargo.lock',
|
||||
meson.project_source_root() / 'subprojects',
|
||||
check : true,
|
||||
)
|
||||
|
||||
if is_darwin
|
||||
configure_file(
|
||||
input : 'misc/launchd/org.nixos.nix-daemon.plist.in',
|
||||
|
|
43
meson/cargo-lock-to-wraps.py
Executable file
43
meson/cargo-lock-to-wraps.py
Executable file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import tomllib
|
||||
import sys
|
||||
|
||||
DOWNLOAD_URI_FORMAT = 'https://crates.io/api/v1/crates/{crate}/{version}/download'
|
||||
|
||||
WRAP_TEMPLATE = """
|
||||
[wrap-file]
|
||||
method = cargo
|
||||
directory = {crate}-{version}
|
||||
source_url = {url}
|
||||
source_filename = {crate}-{version}.tar.gz
|
||||
source_hash = {hash}
|
||||
""".lstrip()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('lockfile', help='path to the Cargo lockfile to generate wraps from')
|
||||
parser.add_argument('outdir', help="the 'subprojects' directory to write .wrap files to")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.lockfile, 'rb') as f:
|
||||
lock_toml = tomllib.load(f)
|
||||
|
||||
for dependency in lock_toml['package']:
|
||||
try:
|
||||
hash = dependency['checksum']
|
||||
except KeyError:
|
||||
# The base package, e.g. lix-doc, won't have a checksum, and conveniently
|
||||
# the base package is also not something we want a wrap file for.
|
||||
# Doesn't that work out nicely?
|
||||
continue
|
||||
|
||||
crate = dependency['name']
|
||||
version = dependency['version']
|
||||
|
||||
url = DOWNLOAD_URI_FORMAT.format(crate=crate, version=version)
|
||||
|
||||
wrap_text = WRAP_TEMPLATE.format(crate=crate, version=version, url=url, hash=hash)
|
||||
with open(f'{args.outdir}/{crate}-rs.wrap', 'w') as f:
|
||||
f.write(wrap_text)
|
89
meson/clang-tidy/clang-tidy-runner.py
Executable file
89
meson/clang-tidy/clang-tidy-runner.py
Executable file
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Runs run-clang-tidy. A bit meta. Maybe it will replace run-clang-tidy one day
|
||||
because the run-clang-tidy UX is so questionable.
|
||||
"""
|
||||
|
||||
# I hereby dedicate this script to fuck you meson.
|
||||
# I cannot simply write my code to invoke a subprocess in a meson file because
|
||||
# Meson corrupts backslashes in command line args to subprocesses.
|
||||
# This is allegedly for "Windows support", but last time I checked Windows
|
||||
# neither needs nor wants you to corrupt its command lines.
|
||||
# https://github.com/mesonbuild/meson/issues/1564
|
||||
|
||||
import multiprocessing
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def default_concurrency():
|
||||
return min(multiprocessing.cpu_count(),
|
||||
int(os.environ.get("NIX_BUILD_CORES", "16")))
|
||||
|
||||
|
||||
def go(exe: str, plugin_path: Path, compile_commands_json_dir: Path, jobs: int,
|
||||
paths: list[Path], werror: bool, fix: bool):
|
||||
args = [
|
||||
# XXX: This explicitly invokes it with python because of a nixpkgs bug
|
||||
# where clang-unwrapped does not patch interpreters in run-clang-tidy.
|
||||
# However, making clang-unwrapped depend on python is also silly, so idk.
|
||||
sys.executable,
|
||||
exe,
|
||||
'-quiet',
|
||||
'-load',
|
||||
plugin_path,
|
||||
'-p',
|
||||
compile_commands_json_dir,
|
||||
'-j',
|
||||
str(jobs),
|
||||
'-header-filter',
|
||||
r'src/[^/]+/.*\.hh'
|
||||
]
|
||||
if werror:
|
||||
args += ['-warnings-as-errors', '*']
|
||||
if fix:
|
||||
args += ['-fix']
|
||||
args += ['--']
|
||||
args += paths
|
||||
os.execvp(sys.executable, args)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser(description='Runs run-clang-tidy for you')
|
||||
ap.add_argument('--jobs',
|
||||
'-j',
|
||||
type=int,
|
||||
default=default_concurrency(),
|
||||
help='Parallel linting jobs to run')
|
||||
ap.add_argument('--plugin-path',
|
||||
type=Path,
|
||||
help='Path to the Lix clang-tidy plugin')
|
||||
# FIXME: maybe we should integrate this so it just fixes the compdb for you and throws it in a tempdir?
|
||||
ap.add_argument(
|
||||
'--compdb-path',
|
||||
type=Path,
|
||||
help=
|
||||
'Path to the directory containing the fixed-up compilation database from clean_compdb'
|
||||
)
|
||||
ap.add_argument('--werror',
|
||||
action='store_true',
|
||||
help='Warnings get turned into errors')
|
||||
ap.add_argument('--fix',
|
||||
action='store_true',
|
||||
help='Apply fixes for warnings')
|
||||
ap.add_argument('--run-clang-tidy-path',
|
||||
default='run-clang-tidy',
|
||||
help='Path to run-clang-tidy')
|
||||
ap.add_argument('paths', nargs='*', help='Source paths to check')
|
||||
args = ap.parse_args()
|
||||
|
||||
go(args.run_clang_tidy_path, args.plugin_path, args.compdb_path, args.jobs,
|
||||
args.paths, args.werror, args.fix)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -13,8 +13,8 @@ def process_compdb(compdb: list[dict]) -> list[dict]:
|
|||
out = []
|
||||
eat_next = False
|
||||
for i, arg in enumerate(args):
|
||||
if arg == '-fpch-preprocess':
|
||||
# as used with gcc
|
||||
if arg in ['-fpch-preprocess', '-fpch-instantiate-templates']:
|
||||
# -fpch-preprocess as used with gcc, -fpch-instantiate-templates as used by clang
|
||||
continue
|
||||
elif arg == '-include-pch' or (arg == '-include' and args[i + 1] == 'precompiled-headers.hh'):
|
||||
# -include-pch some-pch (clang), or -include some-pch (gcc)
|
||||
|
@ -30,7 +30,14 @@ def process_compdb(compdb: list[dict]) -> list[dict]:
|
|||
item['command'] = shlex.join(munch_command(shlex.split(item['command'])))
|
||||
return item
|
||||
|
||||
return [chomp(x) for x in compdb if not x['file'].endswith('precompiled-headers.hh')]
|
||||
def cmdfilter(item: dict) -> bool:
|
||||
file = item['file']
|
||||
return (
|
||||
not file.endswith('precompiled-headers.hh')
|
||||
and not file.endswith('.rs')
|
||||
)
|
||||
|
||||
return [chomp(x) for x in compdb if cmdfilter(x)]
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -58,26 +58,17 @@ build_all_generated_headers = custom_target(
|
|||
|
||||
if lix_clang_tidy_so_found
|
||||
run_clang_tidy_args = [
|
||||
'-load',
|
||||
lix_clang_tidy_so,
|
||||
'-p',
|
||||
# We have to workaround a run-clang-tidy bug too, so we must give the
|
||||
# directory name rather than the actual compdb file.
|
||||
# https://github.com/llvm/llvm-project/issues/101440
|
||||
meson.current_build_dir(),
|
||||
'-quiet',
|
||||
meson.current_source_dir() / 'clang-tidy-runner.py',
|
||||
'--run-clang-tidy-path', run_clang_tidy,
|
||||
'--compdb-path', meson.current_build_dir(),
|
||||
'--plugin-path', lix_clang_tidy_so,
|
||||
]
|
||||
run_target(
|
||||
'clang-tidy',
|
||||
command : [
|
||||
# XXX: This explicitly invokes it with python because of a nixpkgs bug
|
||||
# where clang-unwrapped does not patch interpreters in run-clang-tidy.
|
||||
# However, making clang-unwrapped depend on python is also silly, so idk.
|
||||
python,
|
||||
run_clang_tidy,
|
||||
run_clang_tidy_args,
|
||||
'-warnings-as-errors',
|
||||
'*',
|
||||
'--werror',
|
||||
],
|
||||
depends : [
|
||||
build_all_generated_headers,
|
||||
|
@ -87,9 +78,8 @@ if lix_clang_tidy_so_found
|
|||
'clang-tidy-fix',
|
||||
command : [
|
||||
python,
|
||||
run_clang_tidy,
|
||||
run_clang_tidy_args,
|
||||
'-fix',
|
||||
'--fix',
|
||||
],
|
||||
depends : [
|
||||
build_all_generated_headers,
|
||||
|
|
23
package.nix
23
package.nix
|
@ -41,6 +41,8 @@
|
|||
pkg-config,
|
||||
python3,
|
||||
rapidcheck,
|
||||
rustPlatform,
|
||||
rustc,
|
||||
sqlite,
|
||||
toml11,
|
||||
util-linuxMinimal ? utillinuxMinimal,
|
||||
|
@ -49,9 +51,6 @@
|
|||
|
||||
busybox-sandbox-shell,
|
||||
|
||||
# internal fork of nix-doc providing :doc in the repl
|
||||
lix-doc ? __forDefaults.lix-doc,
|
||||
|
||||
pname ? "lix",
|
||||
versionSuffix ? "",
|
||||
officialRelease ? __forDefaults.versionJson.official_release,
|
||||
|
@ -83,7 +82,6 @@
|
|||
configureFlags = prev.configureFlags or [ ] ++ [ (lib.enableFeature true "sigstop") ];
|
||||
});
|
||||
|
||||
lix-doc = callPackage ./lix-doc/package.nix { };
|
||||
build-release-notes = callPackage ./maintainers/build-release-notes.nix { };
|
||||
},
|
||||
}:
|
||||
|
@ -139,6 +137,8 @@ let
|
|||
./meson
|
||||
./scripts/meson.build
|
||||
./subprojects
|
||||
# Required for meson to generate Cargo wraps
|
||||
./Cargo.lock
|
||||
]);
|
||||
|
||||
functionalTestFiles = fileset.unions [
|
||||
|
@ -219,6 +219,7 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
meson
|
||||
ninja
|
||||
cmake
|
||||
rustc
|
||||
]
|
||||
++ [
|
||||
(lib.getBin lowdown)
|
||||
|
@ -258,7 +259,6 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
lowdown
|
||||
libsodium
|
||||
toml11
|
||||
lix-doc
|
||||
pegtl
|
||||
]
|
||||
++ lib.optionals hostPlatform.isLinux [
|
||||
|
@ -288,8 +288,15 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
env = {
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
|
||||
# Meson allows referencing a /usr/share/cargo/registry shaped thing for subproject sources.
|
||||
# Turns out the Nix-generated Cargo dependencies are named the same as they
|
||||
# would be in a Cargo registry cache.
|
||||
MESON_PACKAGE_CACHE_DIR = finalAttrs.cargoDeps;
|
||||
};
|
||||
|
||||
cargoDeps = rustPlatform.importCargoLock { lockFile = ./Cargo.lock; };
|
||||
|
||||
preConfigure =
|
||||
lib.optionalString (!finalAttrs.dontBuild && !hostPlatform.isStatic) ''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
|
@ -425,6 +432,10 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
pre-commit-checks,
|
||||
contribNotice,
|
||||
check-syscalls,
|
||||
|
||||
# debuggers
|
||||
gdb,
|
||||
rr,
|
||||
}:
|
||||
let
|
||||
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
||||
|
@ -504,6 +515,8 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
]
|
||||
++ lib.optional (pre-commit-checks ? enabledPackages) pre-commit-checks.enabledPackages
|
||||
++ lib.optional (lib.meta.availableOn buildPlatform clangbuildanalyzer) clangbuildanalyzer
|
||||
++ lib.optional (!stdenv.isDarwin) gdb
|
||||
++ lib.optional (lib.meta.availableOn buildPlatform rr) rr
|
||||
++ finalAttrs.checkInputs;
|
||||
|
||||
shellHook = ''
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <iomanip>
|
||||
#if __APPLE__
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
@ -18,6 +14,7 @@
|
|||
#include "build-result.hh"
|
||||
#include "store-api.hh"
|
||||
#include "derivations.hh"
|
||||
#include "strings.hh"
|
||||
#include "local-store.hh"
|
||||
#include "legacy.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
|
|
@ -20,13 +20,15 @@ struct SingleBuiltPathBuilt {
|
|||
DECLARE_CMP(SingleBuiltPathBuilt);
|
||||
};
|
||||
|
||||
using _SingleBuiltPathRaw = std::variant<
|
||||
namespace built_path::detail {
|
||||
using SingleBuiltPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
SingleBuiltPathBuilt
|
||||
>;
|
||||
}
|
||||
|
||||
struct SingleBuiltPath : _SingleBuiltPathRaw {
|
||||
using Raw = _SingleBuiltPathRaw;
|
||||
struct SingleBuiltPath : built_path::detail::SingleBuiltPathRaw {
|
||||
using Raw = built_path::detail::SingleBuiltPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
|
@ -65,17 +67,19 @@ struct BuiltPathBuilt {
|
|||
DECLARE_CMP(BuiltPathBuilt);
|
||||
};
|
||||
|
||||
using _BuiltPathRaw = std::variant<
|
||||
namespace built_path::detail {
|
||||
using BuiltPathRaw = std::variant<
|
||||
DerivedPath::Opaque,
|
||||
BuiltPathBuilt
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A built path. Similar to a DerivedPath, but enriched with the corresponding
|
||||
* output path(s).
|
||||
*/
|
||||
struct BuiltPath : _BuiltPathRaw {
|
||||
using Raw = _BuiltPathRaw;
|
||||
struct BuiltPath : built_path::detail::BuiltPathRaw {
|
||||
using Raw = built_path::detail::BuiltPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "editor-for.hh"
|
||||
#include "environment-variables.hh"
|
||||
#include "source-path.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ libcmd = library(
|
|||
editline,
|
||||
lowdown,
|
||||
nlohmann_json,
|
||||
lix_doc
|
||||
liblix_doc,
|
||||
],
|
||||
cpp_pch : cpp_pch,
|
||||
install : true,
|
||||
|
|
|
@ -926,7 +926,7 @@ void NixRepl::loadFiles()
|
|||
|
||||
void NixRepl::loadReplOverlays()
|
||||
{
|
||||
if (!evalSettings.replOverlays) {
|
||||
if (evalSettings.replOverlays.get().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "error.hh"
|
||||
#include "types.hh"
|
||||
#include "pos-idx.hh"
|
||||
|
||||
namespace nix {
|
||||
|
|
|
@ -31,7 +31,7 @@ Value * EvalState::allocValue()
|
|||
#endif
|
||||
|
||||
nrValues++;
|
||||
return (Value *) p;
|
||||
return static_cast<Value *>(p);
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,10 +54,10 @@ Env & EvalState::allocEnv(size_t size)
|
|||
void * p = *env1AllocCache;
|
||||
*env1AllocCache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
env = (Env *) p;
|
||||
env = static_cast<Env *>(p);
|
||||
} else
|
||||
#endif
|
||||
env = (Env *) gcAllocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||
env = static_cast<Env *>(gcAllocBytes(sizeof(Env) + size * sizeof(Value *)));
|
||||
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ struct EvalSettings : Config
|
|||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
|
||||
PathsSetting replOverlays{this, Paths(), "repl-overlays",
|
||||
PathsSetting<Paths> replOverlays{this, Paths(), "repl-overlays",
|
||||
R"(
|
||||
A list of files containing Nix expressions that can be used to add
|
||||
default bindings to [`nix
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "gc-small-vector.hh"
|
||||
#include "fetch-to-store.hh"
|
||||
#include "flake/flakeref.hh"
|
||||
#include "exit.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
@ -249,6 +250,7 @@ EvalState::EvalState(
|
|||
.findFile = symbols.create("__findFile"),
|
||||
.nixPath = symbols.create("__nixPath"),
|
||||
.body = symbols.create("body"),
|
||||
.overrides = symbols.create("__overrides"),
|
||||
}
|
||||
, repair(NoRepair)
|
||||
, emptyBindings(0)
|
||||
|
@ -2710,20 +2712,29 @@ Expr & EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta
|
|||
}
|
||||
|
||||
|
||||
Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings)
|
||||
Expr & EvalState::parseExprFromString(
|
||||
std::string s_,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const FeatureSettings & featureSettings
|
||||
)
|
||||
{
|
||||
// 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, xpSettings);
|
||||
return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv, featureSettings);
|
||||
}
|
||||
|
||||
|
||||
Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings)
|
||||
Expr & EvalState::parseExprFromString(
|
||||
std::string s,
|
||||
const SourcePath & basePath,
|
||||
const FeatureSettings & featureSettings
|
||||
)
|
||||
{
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv, xpSettings);
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv, featureSettings);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "experimental-features.hh"
|
||||
#include "search-path.hh"
|
||||
#include "repl-exit-status.hh"
|
||||
#include "backed-string-view.hh"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
@ -344,8 +345,17 @@ public:
|
|||
/**
|
||||
* Parse a Nix expression from the specified string.
|
||||
*/
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
Expr & parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
Expr & parseExprFromString(
|
||||
std::string s,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const FeatureSettings & xpSettings = featureSettings
|
||||
);
|
||||
Expr & parseExprFromString(
|
||||
std::string s,
|
||||
const SourcePath & basePath,
|
||||
const FeatureSettings & xpSettings = featureSettings
|
||||
);
|
||||
|
||||
Expr & parseStdin();
|
||||
|
||||
|
@ -569,7 +579,7 @@ private:
|
|||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
const FeatureSettings & xpSettings = featureSettings);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
|
@ -782,4 +792,4 @@ static constexpr std::string_view corepkgsPrefix{"/__corepkgs__/"};
|
|||
|
||||
}
|
||||
|
||||
#include "eval-inline.hh"
|
||||
#include "eval-inline.hh" // IWYU pragma: keep
|
||||
|
|
|
@ -342,8 +342,21 @@ static void updateOverrides(std::map<InputPath, FlakeInput> & overrideMap, const
|
|||
for (auto & [id, input] : overrides) {
|
||||
auto inputPath(inputPathPrefix);
|
||||
inputPath.push_back(id);
|
||||
// Do not override existing assignment from outer flake
|
||||
overrideMap.insert({inputPath, input});
|
||||
|
||||
/* Given
|
||||
*
|
||||
* { inputs.hydra.inputs.nix-eval-jobs.inputs.lix.follows = "lix"; }
|
||||
*
|
||||
* then `nix-eval-jobs` doesn't have an override.
|
||||
* It's neither replaced using follows nor by a different
|
||||
* URL. Thus no need to add it to overrides and thus re-fetch
|
||||
* it.
|
||||
*/
|
||||
if (input.ref || input.follows) {
|
||||
// Do not override existing assignment from outer flake
|
||||
overrideMap.insert({inputPath, input});
|
||||
}
|
||||
|
||||
updateOverrides(overrideMap, input.overrides, inputPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ inline T * gcAllocType(size_t howMany = 1)
|
|||
// However, people can and do request zero sized allocations, so we need
|
||||
// to check that neither of our multiplicands were zero before complaining
|
||||
// about it.
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression): yeah we only seem to alloc pointers with this. the calculation *is* correct though!
|
||||
auto checkedSz = checked::Checked<size_t>(howMany) * sizeof(T);
|
||||
size_t sz = checkedSz.valueWrapping();
|
||||
if (checkedSz.overflowed()) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
namespace nix {
|
||||
|
||||
ExprBlackHole eBlackHole;
|
||||
Expr *eBlackHoleAddr = &eBlackHole;
|
||||
|
||||
// FIXME: remove, because *symbols* are abstract and do not have a single
|
||||
// textual representation; see printIdentifier()
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "eval-error.hh"
|
||||
#include "pos-idx.hh"
|
||||
#include "pos-table.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -48,7 +49,7 @@ protected:
|
|||
|
||||
public:
|
||||
struct AstSymbols {
|
||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body, overrides;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ struct ExprState
|
|||
|
||||
std::unique_ptr<Expr> pipe(PosIdx pos, State & state, bool flip = false)
|
||||
{
|
||||
if (!state.xpSettings.isEnabled(Xp::PipeOperator))
|
||||
if (!state.featureSettings.isEnabled(Xp::PipeOperator))
|
||||
throw ParseError({
|
||||
.msg = HintFmt("Pipe operator is disabled"),
|
||||
.pos = state.positions[pos]
|
||||
|
@ -656,10 +656,10 @@ template<> struct BuildAST<grammar::expr::path> : p::maybe_nothing {};
|
|||
|
||||
template<> struct BuildAST<grammar::expr::uri> {
|
||||
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||
bool noURLLiterals = ps.xpSettings.isEnabled(Xp::NoUrlLiterals);
|
||||
if (noURLLiterals)
|
||||
bool URLLiterals = ps.featureSettings.isEnabled(Dep::UrlLiterals);
|
||||
if (!URLLiterals)
|
||||
throw ParseError({
|
||||
.msg = HintFmt("URL literals are disabled"),
|
||||
.msg = HintFmt("URL literals are deprecated, allow using them with --extra-deprecated-features=url-literals"),
|
||||
.pos = ps.positions[ps.at(in)]
|
||||
});
|
||||
s.pushExpr<ExprString>(ps.at(in), in.string());
|
||||
|
@ -668,6 +668,16 @@ template<> struct BuildAST<grammar::expr::uri> {
|
|||
|
||||
template<> struct BuildAST<grammar::expr::ancient_let> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
// Added 2024-09-18. Turn into an error at some point in the future.
|
||||
// See the documentation on deprecated features for more details.
|
||||
if (!ps.featureSettings.isEnabled(Dep::AncientLet))
|
||||
warn(
|
||||
"%s found at %s. This feature is deprecated and will be removed in the future. Use %s to silence this warning.",
|
||||
"let {",
|
||||
ps.positions[ps.at(in)],
|
||||
"--extra-deprecated-features ancient-let"
|
||||
);
|
||||
|
||||
b.attrs.pos = ps.at(in);
|
||||
b.attrs.recursive = true;
|
||||
s.pushExpr<ExprSelect>(b.attrs.pos, b.attrs.pos, std::make_unique<ExprAttrs>(std::move(b.attrs)), ps.s.body);
|
||||
|
@ -676,6 +686,12 @@ template<> struct BuildAST<grammar::expr::ancient_let> : change_head<BindingsSta
|
|||
|
||||
template<> struct BuildAST<grammar::expr::rec_set> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
// Before inserting new attrs, check for __override and throw an error
|
||||
// (the error will initially be a warning to ease migration)
|
||||
if (!featureSettings.isEnabled(Dep::RecSetOverrides) && b.attrs.attrs.contains(ps.s.overrides)) {
|
||||
ps.overridesFound(ps.at(in));
|
||||
}
|
||||
|
||||
b.attrs.pos = ps.at(in);
|
||||
b.attrs.recursive = true;
|
||||
s.pushExpr<ExprAttrs>(b.attrs.pos, std::move(b.attrs));
|
||||
|
@ -858,7 +874,7 @@ Expr * EvalState::parse(
|
|||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
const FeatureSettings & featureSettings)
|
||||
{
|
||||
parser::State s = {
|
||||
symbols,
|
||||
|
@ -866,7 +882,7 @@ Expr * EvalState::parse(
|
|||
basePath,
|
||||
positions.addOrigin(origin, length),
|
||||
exprSymbols,
|
||||
xpSettings
|
||||
featureSettings,
|
||||
};
|
||||
parser::ExprState x;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include "eval.hh"
|
||||
#include "logging.hh"
|
||||
|
||||
namespace nix::parser {
|
||||
|
||||
|
@ -19,10 +20,11 @@ struct State
|
|||
SourcePath basePath;
|
||||
PosTable::Origin origin;
|
||||
const Expr::AstSymbols & s;
|
||||
const ExperimentalFeatureSettings & xpSettings;
|
||||
const FeatureSettings & featureSettings;
|
||||
|
||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||
void overridesFound(const PosIdx pos);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos);
|
||||
std::unique_ptr<Formals> validateFormals(std::unique_ptr<Formals> formals, PosIdx pos = noPos, Symbol arg = {});
|
||||
std::unique_ptr<Expr> stripIndentation(const PosIdx pos,
|
||||
|
@ -58,6 +60,17 @@ inline void State::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
|||
});
|
||||
}
|
||||
|
||||
inline void State::overridesFound(const PosIdx pos) {
|
||||
// Added 2024-09-18. Turn into an error at some point in the future.
|
||||
// See the documentation on deprecated features for more details.
|
||||
warn(
|
||||
"%s found at %s. This feature is deprecated and will be removed in the future. Use %s to silence this warning.",
|
||||
"__overrides",
|
||||
positions[pos],
|
||||
"--extra-deprecated-features rec-set-overrides"
|
||||
);
|
||||
}
|
||||
|
||||
inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos)
|
||||
{
|
||||
AttrPath::iterator i;
|
||||
|
@ -123,6 +136,12 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
|
|||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
// Before inserting new attrs, check for __override and throw an error
|
||||
// (the error will initially be a warning to ease migration)
|
||||
if (attrs->recursive && !featureSettings.isEnabled(Dep::RecSetOverrides) && i->symbol == s.overrides) {
|
||||
overridesFound(pos);
|
||||
}
|
||||
|
||||
// This attr path is not defined. Let's create it.
|
||||
e->setName(i->symbol);
|
||||
attrs->attrs.emplace(std::piecewise_construct,
|
||||
|
|
|
@ -136,7 +136,9 @@ class ExternalValueBase
|
|||
|
||||
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
/** This is just the address of eBlackHole. It exists because eBlackHole has an
|
||||
* incomplete type at usage sites so is not possible to cast. */
|
||||
extern Expr *eBlackHoleAddr;
|
||||
|
||||
struct NewValueAs
|
||||
{
|
||||
|
@ -196,6 +198,7 @@ private:
|
|||
public:
|
||||
|
||||
// Discount `using NewValueAs::*;`
|
||||
// NOLINTNEXTLINE(bugprone-macro-parentheses)
|
||||
#define USING_VALUETYPE(name) using name = NewValueAs::name
|
||||
USING_VALUETYPE(integer_t);
|
||||
USING_VALUETYPE(floating_t);
|
||||
|
@ -473,7 +476,7 @@ public:
|
|||
/// Constructs an evil thunk, whose evaluation represents infinite recursion.
|
||||
explicit Value(blackhole_t)
|
||||
: internalType(tThunk)
|
||||
, thunk({ .env = nullptr, .expr = reinterpret_cast<Expr *>(&eBlackHole) })
|
||||
, thunk({ .env = nullptr, .expr = eBlackHoleAddr })
|
||||
{ }
|
||||
|
||||
Value(Value const & rhs) = default;
|
||||
|
@ -513,7 +516,10 @@ public:
|
|||
// type() == nThunk
|
||||
inline bool isThunk() const { return internalType == tThunk; };
|
||||
inline bool isApp() const { return internalType == tApp; };
|
||||
inline bool isBlackhole() const;
|
||||
inline bool isBlackhole() const
|
||||
{
|
||||
return internalType == tThunk && thunk.expr == eBlackHoleAddr;
|
||||
}
|
||||
|
||||
// type() == nFunction
|
||||
inline bool isLambda() const { return internalType == tLambda; };
|
||||
|
@ -669,11 +675,6 @@ public:
|
|||
|
||||
void mkStringMove(const char * s, const NixStringContext & context);
|
||||
|
||||
inline void mkString(const Symbol & s)
|
||||
{
|
||||
mkString(((const std::string &) s).c_str());
|
||||
}
|
||||
|
||||
void mkPath(const SourcePath & path);
|
||||
|
||||
inline void mkPath(const char * path)
|
||||
|
@ -732,7 +733,11 @@ public:
|
|||
lambda.fun = f;
|
||||
}
|
||||
|
||||
inline void mkBlackhole();
|
||||
inline void mkBlackhole()
|
||||
{
|
||||
internalType = tThunk;
|
||||
thunk.expr = eBlackHoleAddr;
|
||||
}
|
||||
|
||||
void mkPrimOp(PrimOp * p);
|
||||
|
||||
|
@ -832,18 +837,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
bool Value::isBlackhole() const
|
||||
{
|
||||
return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole;
|
||||
}
|
||||
|
||||
void Value::mkBlackhole()
|
||||
{
|
||||
internalType = tThunk;
|
||||
thunk.expr = (Expr*) &eBlackHole;
|
||||
}
|
||||
|
||||
using ValueVector = GcVector<Value *>;
|
||||
using ValueMap = GcMap<Symbol, Value *>;
|
||||
using ValueVectorMap = std::map<Symbol, ValueVector>;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "path.hh"
|
||||
#include "attrs.hh"
|
||||
#include "url.hh"
|
||||
#include "ref.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "progress-bar.hh"
|
||||
#include "file-system.hh"
|
||||
#include "sync.hh"
|
||||
#include "store-api.hh"
|
||||
#include "names.hh"
|
||||
#include "terminal.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "loggers.hh"
|
||||
#include "current-process.hh"
|
||||
#include "terminal.hh"
|
||||
#include "strings.hh"
|
||||
#include "exit.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "processes.hh"
|
||||
#include "exit.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <locale>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nar-accessor.hh"
|
||||
#include "thread-pool.hh"
|
||||
#include "signals.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <chrono>
|
||||
#include <regex>
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
#include "derivation-goal.hh"
|
||||
#include "hook-instance.hh"
|
||||
#include "worker.hh"
|
||||
#include "builtins.hh"
|
||||
#include "builtins/buildenv.hh"
|
||||
#include "references.hh"
|
||||
#include "finally.hh"
|
||||
#include "archive.hh"
|
||||
#include "compression.hh"
|
||||
#include "common-protocol.hh"
|
||||
#include "common-protocol-impl.hh"
|
||||
#include "topo-sort.hh"
|
||||
#include "common-protocol-impl.hh" // IWYU pragma: keep
|
||||
#include "local-store.hh" // TODO remove, along with remaining downcasts
|
||||
#include "logging-json.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "drv-output-substitution-goal.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
#include "strings.hh"
|
||||
|
||||
#include <fstream>
|
||||
#include <sys/types.h>
|
||||
|
@ -66,7 +59,7 @@ namespace nix {
|
|||
|
||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs })
|
||||
: Goal(worker)
|
||||
, useDerivation(true)
|
||||
, drvPath(drvPath)
|
||||
, wantedOutputs(wantedOutputs)
|
||||
|
@ -84,7 +77,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
|||
|
||||
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs })
|
||||
: Goal(worker)
|
||||
, useDerivation(false)
|
||||
, drvPath(drvPath)
|
||||
, wantedOutputs(wantedOutputs)
|
||||
|
@ -127,6 +120,7 @@ std::string DerivationGoal::key()
|
|||
void DerivationGoal::killChild()
|
||||
{
|
||||
hook.reset();
|
||||
builderOutFD = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -137,9 +131,9 @@ Goal::Finished DerivationGoal::timedOut(Error && ex)
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::work()
|
||||
Goal::WorkResult DerivationGoal::work(bool inBuildSlot)
|
||||
{
|
||||
return (this->*state)();
|
||||
return (this->*state)(inBuildSlot);
|
||||
}
|
||||
|
||||
void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
||||
|
@ -163,7 +157,7 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::getDerivation()
|
||||
Goal::WorkResult DerivationGoal::getDerivation(bool inBuildSlot)
|
||||
{
|
||||
trace("init");
|
||||
|
||||
|
@ -171,7 +165,7 @@ Goal::WorkResult DerivationGoal::getDerivation()
|
|||
exists. If it doesn't, it may be created through a
|
||||
substitute. */
|
||||
if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
|
||||
return loadDerivation();
|
||||
return loadDerivation(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,7 +174,7 @@ Goal::WorkResult DerivationGoal::getDerivation()
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::loadDerivation()
|
||||
Goal::WorkResult DerivationGoal::loadDerivation(bool inBuildSlot)
|
||||
{
|
||||
trace("loading derivation");
|
||||
|
||||
|
@ -207,11 +201,11 @@ Goal::WorkResult DerivationGoal::loadDerivation()
|
|||
}
|
||||
assert(drv);
|
||||
|
||||
return haveDerivation();
|
||||
return haveDerivation(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::haveDerivation()
|
||||
Goal::WorkResult DerivationGoal::haveDerivation(bool inBuildSlot)
|
||||
{
|
||||
trace("have derivation");
|
||||
|
||||
|
@ -239,7 +233,7 @@ Goal::WorkResult DerivationGoal::haveDerivation()
|
|||
});
|
||||
}
|
||||
|
||||
return gaveUpOnSubstitution();
|
||||
return gaveUpOnSubstitution(inBuildSlot);
|
||||
}
|
||||
|
||||
for (auto & i : drv->outputsAndOptPaths(worker.store))
|
||||
|
@ -268,34 +262,39 @@ Goal::WorkResult DerivationGoal::haveDerivation()
|
|||
through substitutes. If that doesn't work, we'll build
|
||||
them. */
|
||||
WaitForGoals result;
|
||||
if (settings.useSubstitutes && parsedDrv->substitutesAllowed())
|
||||
for (auto & [outputName, status] : initialOutputs) {
|
||||
if (!status.wanted) continue;
|
||||
if (!status.known)
|
||||
result.goals.insert(
|
||||
worker.makeDrvOutputSubstitutionGoal(
|
||||
DrvOutput{status.outputHash, outputName},
|
||||
buildMode == bmRepair ? Repair : NoRepair
|
||||
)
|
||||
);
|
||||
else {
|
||||
auto * cap = getDerivationCA(*drv);
|
||||
result.goals.insert(worker.makePathSubstitutionGoal(
|
||||
status.known->path,
|
||||
buildMode == bmRepair ? Repair : NoRepair,
|
||||
cap ? std::optional { *cap } : std::nullopt));
|
||||
if (settings.useSubstitutes) {
|
||||
if (parsedDrv->substitutesAllowed()) {
|
||||
for (auto & [outputName, status] : initialOutputs) {
|
||||
if (!status.wanted) continue;
|
||||
if (!status.known)
|
||||
result.goals.insert(
|
||||
worker.makeDrvOutputSubstitutionGoal(
|
||||
DrvOutput{status.outputHash, outputName},
|
||||
buildMode == bmRepair ? Repair : NoRepair
|
||||
)
|
||||
);
|
||||
else {
|
||||
auto * cap = getDerivationCA(*drv);
|
||||
result.goals.insert(worker.makePathSubstitutionGoal(
|
||||
status.known->path,
|
||||
buildMode == bmRepair ? Repair : NoRepair,
|
||||
cap ? std::optional { *cap } : std::nullopt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace("skipping substitute because allowSubstitutes is false");
|
||||
}
|
||||
}
|
||||
|
||||
if (result.goals.empty()) { /* to prevent hang (no wake-up event) */
|
||||
return outputsSubstitutionTried();
|
||||
return outputsSubstitutionTried(inBuildSlot);
|
||||
} else {
|
||||
state = &DerivationGoal::outputsSubstitutionTried;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::outputsSubstitutionTried()
|
||||
Goal::WorkResult DerivationGoal::outputsSubstitutionTried(bool inBuildSlot)
|
||||
{
|
||||
trace("all outputs substituted (maybe)");
|
||||
|
||||
|
@ -338,7 +337,7 @@ Goal::WorkResult DerivationGoal::outputsSubstitutionTried()
|
|||
|
||||
if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) {
|
||||
needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
||||
return haveDerivation();
|
||||
return haveDerivation(inBuildSlot);
|
||||
}
|
||||
|
||||
auto [allValid, validOutputs] = checkPathValidity();
|
||||
|
@ -354,13 +353,13 @@ Goal::WorkResult DerivationGoal::outputsSubstitutionTried()
|
|||
worker.store.printStorePath(drvPath));
|
||||
|
||||
/* Nothing to wait for; tail call */
|
||||
return gaveUpOnSubstitution();
|
||||
return gaveUpOnSubstitution(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
/* At least one of the output paths could not be
|
||||
produced using a substitute. So we have to build instead. */
|
||||
Goal::WorkResult DerivationGoal::gaveUpOnSubstitution()
|
||||
Goal::WorkResult DerivationGoal::gaveUpOnSubstitution(bool inBuildSlot)
|
||||
{
|
||||
WaitForGoals result;
|
||||
|
||||
|
@ -424,7 +423,7 @@ Goal::WorkResult DerivationGoal::gaveUpOnSubstitution()
|
|||
}
|
||||
|
||||
if (result.goals.empty()) {/* to prevent hang (no wake-up event) */
|
||||
return inputsRealised();
|
||||
return inputsRealised(inBuildSlot);
|
||||
} else {
|
||||
state = &DerivationGoal::inputsRealised;
|
||||
return result;
|
||||
|
@ -495,7 +494,7 @@ Goal::WorkResult DerivationGoal::repairClosure()
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::closureRepaired()
|
||||
Goal::WorkResult DerivationGoal::closureRepaired(bool inBuildSlot)
|
||||
{
|
||||
trace("closure repaired");
|
||||
if (nrFailed > 0)
|
||||
|
@ -505,7 +504,7 @@ Goal::WorkResult DerivationGoal::closureRepaired()
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DerivationGoal::inputsRealised()
|
||||
Goal::WorkResult DerivationGoal::inputsRealised(bool inBuildSlot)
|
||||
{
|
||||
trace("all inputs realised");
|
||||
|
||||
|
@ -519,7 +518,7 @@ Goal::WorkResult DerivationGoal::inputsRealised()
|
|||
|
||||
if (retrySubstitution == RetrySubstitution::YesNeed) {
|
||||
retrySubstitution = RetrySubstitution::AlreadyRetried;
|
||||
return haveDerivation();
|
||||
return haveDerivation(inBuildSlot);
|
||||
}
|
||||
|
||||
/* Gather information necessary for computing the closure and/or
|
||||
|
@ -653,7 +652,7 @@ Goal::WorkResult DerivationGoal::inputsRealised()
|
|||
return ContinueImmediately{};
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::started()
|
||||
void DerivationGoal::started()
|
||||
{
|
||||
auto msg = fmt(
|
||||
buildMode == bmRepair ? "repairing outputs of '%s'" :
|
||||
|
@ -664,10 +663,9 @@ Goal::WorkResult DerivationGoal::started()
|
|||
act = std::make_unique<Activity>(*logger, lvlInfo, actBuild, msg,
|
||||
Logger::Fields{worker.store.printStorePath(drvPath), hook ? machineName : "", 1, 1});
|
||||
mcRunningBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.runningBuilds);
|
||||
return StillAlive{};
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::tryToBuild()
|
||||
Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot)
|
||||
{
|
||||
trace("trying to build");
|
||||
|
||||
|
@ -739,25 +737,35 @@ Goal::WorkResult DerivationGoal::tryToBuild()
|
|||
&& settings.maxBuildJobs.get() != 0;
|
||||
|
||||
if (!buildLocally) {
|
||||
switch (tryBuildHook()) {
|
||||
case rpAccept:
|
||||
/* Yes, it has started doing so. Wait until we get
|
||||
EOF from the hook. */
|
||||
actLock.reset();
|
||||
buildResult.startTime = time(0); // inexact
|
||||
state = &DerivationGoal::buildDone;
|
||||
return started();
|
||||
case rpPostpone:
|
||||
/* Not now; wait until at least one child finishes or
|
||||
the wake-up timeout expires. */
|
||||
if (!actLock)
|
||||
actLock = std::make_unique<Activity>(*logger, lvlTalkative, actBuildWaiting,
|
||||
fmt("waiting for a machine to build '%s'", Magenta(worker.store.printStorePath(drvPath))));
|
||||
outputLocks.unlock();
|
||||
return WaitForAWhile{};
|
||||
case rpDecline:
|
||||
/* We should do it ourselves. */
|
||||
break;
|
||||
auto hookReply = tryBuildHook(inBuildSlot);
|
||||
auto result = std::visit(
|
||||
overloaded{
|
||||
[&](HookReply::Accept & a) -> std::optional<WorkResult> {
|
||||
/* Yes, it has started doing so. Wait until we get
|
||||
EOF from the hook. */
|
||||
actLock.reset();
|
||||
buildResult.startTime = time(0); // inexact
|
||||
state = &DerivationGoal::buildDone;
|
||||
started();
|
||||
return WaitForWorld{std::move(a.fds), false};
|
||||
},
|
||||
[&](HookReply::Postpone) -> std::optional<WorkResult> {
|
||||
/* Not now; wait until at least one child finishes or
|
||||
the wake-up timeout expires. */
|
||||
if (!actLock)
|
||||
actLock = std::make_unique<Activity>(*logger, lvlTalkative, actBuildWaiting,
|
||||
fmt("waiting for a machine to build '%s'", Magenta(worker.store.printStorePath(drvPath))));
|
||||
outputLocks.unlock();
|
||||
return WaitForAWhile{};
|
||||
},
|
||||
[&](HookReply::Decline) -> std::optional<WorkResult> {
|
||||
/* We should do it ourselves. */
|
||||
return std::nullopt;
|
||||
},
|
||||
},
|
||||
hookReply);
|
||||
if (result) {
|
||||
return std::move(*result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,7 +775,7 @@ Goal::WorkResult DerivationGoal::tryToBuild()
|
|||
return ContinueImmediately{};
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::tryLocalBuild() {
|
||||
Goal::WorkResult DerivationGoal::tryLocalBuild(bool inBuildSlot) {
|
||||
throw Error(
|
||||
"unable to build with a primary store that isn't a local store; "
|
||||
"either pass a different '--store' or enable remote builds."
|
||||
|
@ -822,14 +830,16 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath)
|
|||
|
||||
int DerivationGoal::getChildStatus()
|
||||
{
|
||||
builderOutFD = nullptr;
|
||||
return hook->pid.kill();
|
||||
}
|
||||
|
||||
|
||||
void DerivationGoal::closeReadPipes()
|
||||
{
|
||||
hook->builderOut.readSide.reset();
|
||||
hook->fromHook.readSide.reset();
|
||||
hook->builderOut.reset();
|
||||
hook->fromHook.reset();
|
||||
builderOutFD = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -925,7 +935,7 @@ void runPostBuildHook(
|
|||
proc.getStdout()->drainInto(sink);
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::buildDone()
|
||||
Goal::WorkResult DerivationGoal::buildDone(bool inBuildSlot)
|
||||
{
|
||||
trace("build done");
|
||||
|
||||
|
@ -1045,7 +1055,7 @@ Goal::WorkResult DerivationGoal::buildDone()
|
|||
}
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::resolvedFinished()
|
||||
Goal::WorkResult DerivationGoal::resolvedFinished(bool inBuildSlot)
|
||||
{
|
||||
trace("resolved derivation finished");
|
||||
|
||||
|
@ -1116,9 +1126,9 @@ Goal::WorkResult DerivationGoal::resolvedFinished()
|
|||
return done(status, std::move(builtOutputs));
|
||||
}
|
||||
|
||||
HookReply DerivationGoal::tryBuildHook()
|
||||
HookReply DerivationGoal::tryBuildHook(bool inBuildSlot)
|
||||
{
|
||||
if (!worker.hook.available || !useDerivation) return rpDecline;
|
||||
if (!worker.hook.available || !useDerivation) return HookReply::Decline{};
|
||||
|
||||
if (!worker.hook.instance)
|
||||
worker.hook.instance = std::make_unique<HookInstance>();
|
||||
|
@ -1128,7 +1138,7 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
/* Send the request to the hook. */
|
||||
worker.hook.instance->sink
|
||||
<< "try"
|
||||
<< (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0)
|
||||
<< (inBuildSlot ? 1 : 0)
|
||||
<< drv->platform
|
||||
<< worker.store.printStorePath(drvPath)
|
||||
<< parsedDrv->getRequiredSystemFeatures();
|
||||
|
@ -1140,7 +1150,7 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
while (true) {
|
||||
auto s = [&]() {
|
||||
try {
|
||||
return readLine(worker.hook.instance->fromHook.readSide.get());
|
||||
return readLine(worker.hook.instance->fromHook.get());
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while reading the response from the build hook");
|
||||
throw;
|
||||
|
@ -1161,14 +1171,14 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
debug("hook reply is '%1%'", reply);
|
||||
|
||||
if (reply == "decline")
|
||||
return rpDecline;
|
||||
return HookReply::Decline{};
|
||||
else if (reply == "decline-permanently") {
|
||||
worker.hook.available = false;
|
||||
worker.hook.instance.reset();
|
||||
return rpDecline;
|
||||
return HookReply::Decline{};
|
||||
}
|
||||
else if (reply == "postpone")
|
||||
return rpPostpone;
|
||||
return HookReply::Postpone{};
|
||||
else if (reply != "accept")
|
||||
throw Error("bad hook reply '%s'", reply);
|
||||
|
||||
|
@ -1176,9 +1186,9 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
if (e.errNo == EPIPE) {
|
||||
printError(
|
||||
"build hook died unexpectedly: %s",
|
||||
chomp(drainFD(worker.hook.instance->fromHook.readSide.get())));
|
||||
chomp(drainFD(worker.hook.instance->fromHook.get())));
|
||||
worker.hook.instance.reset();
|
||||
return rpDecline;
|
||||
return HookReply::Decline{};
|
||||
} else
|
||||
throw;
|
||||
}
|
||||
|
@ -1186,7 +1196,7 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
hook = std::move(worker.hook.instance);
|
||||
|
||||
try {
|
||||
machineName = readLine(hook->fromHook.readSide.get());
|
||||
machineName = readLine(hook->fromHook.get());
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while reading the machine name from the build hook");
|
||||
throw;
|
||||
|
@ -1209,17 +1219,17 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
}
|
||||
|
||||
hook->sink = FdSink();
|
||||
hook->toHook.writeSide.reset();
|
||||
hook->toHook.reset();
|
||||
|
||||
/* Create the log file and pipe. */
|
||||
Path logFile = openLogFile();
|
||||
|
||||
std::set<int> fds;
|
||||
fds.insert(hook->fromHook.readSide.get());
|
||||
fds.insert(hook->builderOut.readSide.get());
|
||||
worker.childStarted(shared_from_this(), fds, false, false);
|
||||
fds.insert(hook->fromHook.get());
|
||||
fds.insert(hook->builderOut.get());
|
||||
builderOutFD = &hook->builderOut;
|
||||
|
||||
return rpAccept;
|
||||
return HookReply::Accept{std::move(fds)};
|
||||
}
|
||||
|
||||
|
||||
|
@ -1279,24 +1289,23 @@ void DerivationGoal::closeLogFile()
|
|||
}
|
||||
|
||||
|
||||
bool DerivationGoal::isReadDesc(int fd)
|
||||
{
|
||||
return fd == hook->builderOut.readSide.get();
|
||||
}
|
||||
|
||||
Goal::WorkResult DerivationGoal::handleChildOutput(int fd, std::string_view data)
|
||||
{
|
||||
assert(builderOutFD);
|
||||
|
||||
auto tooMuchLogs = [&] {
|
||||
killChild();
|
||||
return done(
|
||||
BuildResult::LogLimitExceeded, {},
|
||||
Error("%s killed after writing more than %d bytes of log output",
|
||||
getName(), settings.maxLogSize));
|
||||
};
|
||||
|
||||
// local & `ssh://`-builds are dealt with here.
|
||||
auto isWrittenToLog = isReadDesc(fd);
|
||||
if (isWrittenToLog)
|
||||
{
|
||||
if (fd == builderOutFD->get()) {
|
||||
logSize += data.size();
|
||||
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
||||
killChild();
|
||||
return done(
|
||||
BuildResult::LogLimitExceeded, {},
|
||||
Error("%s killed after writing more than %d bytes of log output",
|
||||
getName(), settings.maxLogSize));
|
||||
return tooMuchLogs();
|
||||
}
|
||||
|
||||
for (auto c : data)
|
||||
|
@ -1311,9 +1320,10 @@ Goal::WorkResult DerivationGoal::handleChildOutput(int fd, std::string_view data
|
|||
}
|
||||
|
||||
if (logSink) (*logSink)(data);
|
||||
return StillAlive{};
|
||||
}
|
||||
|
||||
if (hook && fd == hook->fromHook.readSide.get()) {
|
||||
if (hook && fd == hook->fromHook.get()) {
|
||||
for (auto c : data)
|
||||
if (c == '\n') {
|
||||
auto json = parseJSONMessage(currentHookLine);
|
||||
|
@ -1321,11 +1331,17 @@ Goal::WorkResult DerivationGoal::handleChildOutput(int fd, std::string_view data
|
|||
auto s = handleJSONLogMessage(*json, worker.act, hook->activities, true);
|
||||
// ensure that logs from a builder using `ssh-ng://` as protocol
|
||||
// are also available to `nix log`.
|
||||
if (s && !isWrittenToLog && logSink) {
|
||||
if (s && logSink) {
|
||||
const auto type = (*json)["type"];
|
||||
const auto fields = (*json)["fields"];
|
||||
if (type == resBuildLogLine) {
|
||||
(*logSink)((fields.size() > 0 ? fields[0].get<std::string>() : "") + "\n");
|
||||
const std::string logLine =
|
||||
(fields.size() > 0 ? fields[0].get<std::string>() : "") + "\n";
|
||||
logSize += logLine.size();
|
||||
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
||||
return tooMuchLogs();
|
||||
}
|
||||
(*logSink)(logLine);
|
||||
} else if (type == resSetPhase && ! fields.is_null()) {
|
||||
const auto phase = fields[0];
|
||||
if (! phase.is_null()) {
|
||||
|
@ -1530,7 +1546,7 @@ Goal::Finished DerivationGoal::done(
|
|||
|
||||
return Finished{
|
||||
.result = buildResult.success() ? ecSuccess : ecFailed,
|
||||
.ex = ex ? std::make_unique<Error>(std::move(*ex)) : nullptr,
|
||||
.ex = ex ? std::make_shared<Error>(std::move(*ex)) : nullptr,
|
||||
.permanentFailure = buildResult.status == BuildResult::PermanentFailure,
|
||||
.timedOut = buildResult.status == BuildResult::TimedOut,
|
||||
.hashMismatch = anyHashMismatchSeen,
|
||||
|
|
|
@ -14,7 +14,21 @@ using std::map;
|
|||
|
||||
struct HookInstance;
|
||||
|
||||
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
|
||||
struct HookReplyBase {
|
||||
struct [[nodiscard]] Accept {
|
||||
std::set<int> fds;
|
||||
};
|
||||
struct [[nodiscard]] Decline {};
|
||||
struct [[nodiscard]] Postpone {};
|
||||
};
|
||||
|
||||
struct [[nodiscard]] HookReply
|
||||
: HookReplyBase,
|
||||
std::variant<HookReplyBase::Accept, HookReplyBase::Decline, HookReplyBase::Postpone>
|
||||
{
|
||||
HookReply() = delete;
|
||||
using variant::variant;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unless we are repairing, we don't both to test validity and just assume it,
|
||||
|
@ -186,12 +200,19 @@ struct DerivationGoal : public Goal
|
|||
*/
|
||||
std::unique_ptr<HookInstance> hook;
|
||||
|
||||
/**
|
||||
* Builder output is pulled from this file descriptor when not null.
|
||||
* Owned by the derivation goal or subclass, must not be reset until
|
||||
* the build has finished and no more output must be processed by us
|
||||
*/
|
||||
AutoCloseFD * builderOutFD = nullptr;
|
||||
|
||||
/**
|
||||
* The sort of derivation we are building.
|
||||
*/
|
||||
std::optional<DerivationType> derivationType;
|
||||
|
||||
typedef WorkResult (DerivationGoal::*GoalState)();
|
||||
typedef WorkResult (DerivationGoal::*GoalState)(bool inBuildSlot);
|
||||
GoalState state;
|
||||
|
||||
BuildMode buildMode;
|
||||
|
@ -224,7 +245,7 @@ struct DerivationGoal : public Goal
|
|||
|
||||
std::string key() override;
|
||||
|
||||
WorkResult work() override;
|
||||
WorkResult work(bool inBuildSlot) override;
|
||||
|
||||
/**
|
||||
* Add wanted outputs to an already existing derivation goal.
|
||||
|
@ -234,23 +255,23 @@ struct DerivationGoal : public Goal
|
|||
/**
|
||||
* The states.
|
||||
*/
|
||||
WorkResult getDerivation();
|
||||
WorkResult loadDerivation();
|
||||
WorkResult haveDerivation();
|
||||
WorkResult outputsSubstitutionTried();
|
||||
WorkResult gaveUpOnSubstitution();
|
||||
WorkResult closureRepaired();
|
||||
WorkResult inputsRealised();
|
||||
WorkResult tryToBuild();
|
||||
virtual WorkResult tryLocalBuild();
|
||||
WorkResult buildDone();
|
||||
WorkResult getDerivation(bool inBuildSlot);
|
||||
WorkResult loadDerivation(bool inBuildSlot);
|
||||
WorkResult haveDerivation(bool inBuildSlot);
|
||||
WorkResult outputsSubstitutionTried(bool inBuildSlot);
|
||||
WorkResult gaveUpOnSubstitution(bool inBuildSlot);
|
||||
WorkResult closureRepaired(bool inBuildSlot);
|
||||
WorkResult inputsRealised(bool inBuildSlot);
|
||||
WorkResult tryToBuild(bool inBuildSlot);
|
||||
virtual WorkResult tryLocalBuild(bool inBuildSlot);
|
||||
WorkResult buildDone(bool inBuildSlot);
|
||||
|
||||
WorkResult resolvedFinished();
|
||||
WorkResult resolvedFinished(bool inBuildSlot);
|
||||
|
||||
/**
|
||||
* Is the build hook willing to perform the build?
|
||||
*/
|
||||
HookReply tryBuildHook();
|
||||
HookReply tryBuildHook(bool inBuildSlot);
|
||||
|
||||
virtual int getChildStatus();
|
||||
|
||||
|
@ -290,8 +311,6 @@ struct DerivationGoal : public Goal
|
|||
virtual void cleanupPostOutputsRegisteredModeCheck();
|
||||
virtual void cleanupPostOutputsRegisteredModeNonCheck();
|
||||
|
||||
virtual bool isReadDesc(int fd);
|
||||
|
||||
/**
|
||||
* Callback used by the worker to write to the log.
|
||||
*/
|
||||
|
@ -328,7 +347,7 @@ struct DerivationGoal : public Goal
|
|||
|
||||
WorkResult repairClosure();
|
||||
|
||||
WorkResult started();
|
||||
void started();
|
||||
|
||||
Finished done(
|
||||
BuildResult::Status status,
|
||||
|
|
|
@ -11,7 +11,7 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
|
|||
Worker & worker,
|
||||
RepairFlag repair,
|
||||
std::optional<ContentAddress> ca)
|
||||
: Goal(worker, DerivedPath::Opaque { StorePath::dummy })
|
||||
: Goal(worker)
|
||||
, id(id)
|
||||
{
|
||||
state = &DrvOutputSubstitutionGoal::init;
|
||||
|
@ -20,7 +20,7 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::init()
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::init(bool inBuildSlot)
|
||||
{
|
||||
trace("init");
|
||||
|
||||
|
@ -30,17 +30,14 @@ Goal::WorkResult DrvOutputSubstitutionGoal::init()
|
|||
}
|
||||
|
||||
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::tryNext()
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::tryNext(bool inBuildSlot)
|
||||
{
|
||||
trace("trying next substituter");
|
||||
|
||||
/* Make sure that we are allowed to start a substitution. Note that even
|
||||
if maxSubstitutionJobs == 0, we still allow a substituter to run. This
|
||||
prevents infinite waiting. */
|
||||
if (worker.runningSubstitutions >= std::max(1U, settings.maxSubstitutionJobs.get())) {
|
||||
if (!inBuildSlot) {
|
||||
return WaitForSlot{};
|
||||
}
|
||||
|
||||
|
@ -78,13 +75,11 @@ Goal::WorkResult DrvOutputSubstitutionGoal::tryNext()
|
|||
return sub->queryRealisation(id);
|
||||
});
|
||||
|
||||
worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false);
|
||||
|
||||
state = &DrvOutputSubstitutionGoal::realisationFetched;
|
||||
return StillAlive{};
|
||||
return WaitForWorld{{downloadState->outPipe.readSide.get()}, true};
|
||||
}
|
||||
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched()
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched(bool inBuildSlot)
|
||||
{
|
||||
worker.childTerminated(this);
|
||||
maintainRunningSubstitutions.reset();
|
||||
|
@ -97,7 +92,7 @@ Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched()
|
|||
}
|
||||
|
||||
if (!outputInfo) {
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
|
||||
WaitForGoals result;
|
||||
|
@ -114,7 +109,7 @@ Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched()
|
|||
worker.store.printStorePath(localOutputInfo->outPath),
|
||||
worker.store.printStorePath(depPath)
|
||||
);
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
result.goals.insert(worker.makeDrvOutputSubstitutionGoal(depId));
|
||||
}
|
||||
|
@ -123,14 +118,14 @@ Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched()
|
|||
result.goals.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
||||
|
||||
if (result.goals.empty()) {
|
||||
return outPathValid();
|
||||
return outPathValid(inBuildSlot);
|
||||
} else {
|
||||
state = &DrvOutputSubstitutionGoal::outPathValid;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::outPathValid()
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::outPathValid(bool inBuildSlot)
|
||||
{
|
||||
assert(outputInfo);
|
||||
trace("output path substituted");
|
||||
|
@ -159,9 +154,9 @@ std::string DrvOutputSubstitutionGoal::key()
|
|||
return "a$" + std::string(id.to_string());
|
||||
}
|
||||
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::work()
|
||||
Goal::WorkResult DrvOutputSubstitutionGoal::work(bool inBuildSlot)
|
||||
{
|
||||
return (this->*state)();
|
||||
return (this->*state)(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -58,20 +58,20 @@ class DrvOutputSubstitutionGoal : public Goal {
|
|||
public:
|
||||
DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
|
||||
typedef WorkResult (DrvOutputSubstitutionGoal::*GoalState)();
|
||||
typedef WorkResult (DrvOutputSubstitutionGoal::*GoalState)(bool inBuildSlot);
|
||||
GoalState state;
|
||||
|
||||
WorkResult init();
|
||||
WorkResult tryNext();
|
||||
WorkResult realisationFetched();
|
||||
WorkResult outPathValid();
|
||||
WorkResult init(bool inBuildSlot);
|
||||
WorkResult tryNext(bool inBuildSlot);
|
||||
WorkResult realisationFetched(bool inBuildSlot);
|
||||
WorkResult outPathValid(bool inBuildSlot);
|
||||
WorkResult finished();
|
||||
|
||||
Finished timedOut(Error && ex) override { abort(); };
|
||||
|
||||
std::string key() override;
|
||||
|
||||
WorkResult work() override;
|
||||
WorkResult work(bool inBuildSlot) override;
|
||||
|
||||
JobCategory jobCategory() const override {
|
||||
return JobCategory::Substitution;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "substitution-goal.hh"
|
||||
#include "derivation-goal.hh"
|
||||
#include "local-store.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -16,13 +17,13 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
|||
worker.run(goals);
|
||||
|
||||
StringSet failed;
|
||||
std::optional<Error> ex;
|
||||
std::shared_ptr<Error> ex;
|
||||
for (auto & i : goals) {
|
||||
if (i->ex) {
|
||||
if (ex)
|
||||
logError(i->ex->info());
|
||||
else
|
||||
ex = std::move(*i->ex);
|
||||
ex = i->ex;
|
||||
}
|
||||
if (i->exitCode != Goal::ecSuccess) {
|
||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "goal.hh"
|
||||
#include "worker.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ enum struct JobCategory {
|
|||
Substitution,
|
||||
};
|
||||
|
||||
struct Goal : public std::enable_shared_from_this<Goal>
|
||||
struct Goal
|
||||
{
|
||||
typedef enum {ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||
|
||||
|
@ -112,9 +112,13 @@ public:
|
|||
struct [[nodiscard]] WaitForGoals {
|
||||
Goals goals;
|
||||
};
|
||||
struct [[nodiscard]] WaitForWorld {
|
||||
std::set<int> fds;
|
||||
bool inBuildSlot;
|
||||
};
|
||||
struct [[nodiscard]] Finished {
|
||||
ExitCode result;
|
||||
std::unique_ptr<Error> ex;
|
||||
std::shared_ptr<Error> ex;
|
||||
bool permanentFailure = false;
|
||||
bool timedOut = false;
|
||||
bool hashMismatch = false;
|
||||
|
@ -127,6 +131,7 @@ public:
|
|||
WaitForAWhile,
|
||||
ContinueImmediately,
|
||||
WaitForGoals,
|
||||
WaitForWorld,
|
||||
Finished>
|
||||
{
|
||||
WorkResult() = delete;
|
||||
|
@ -136,9 +141,9 @@ public:
|
|||
/**
|
||||
* Exception containing an error message, if any.
|
||||
*/
|
||||
std::unique_ptr<Error> ex;
|
||||
std::shared_ptr<Error> ex;
|
||||
|
||||
Goal(Worker & worker, DerivedPath path)
|
||||
explicit Goal(Worker & worker)
|
||||
: worker(worker)
|
||||
{ }
|
||||
|
||||
|
@ -147,7 +152,7 @@ public:
|
|||
trace("goal destroyed");
|
||||
}
|
||||
|
||||
virtual WorkResult work() = 0;
|
||||
virtual WorkResult work(bool inBuildSlot) = 0;
|
||||
|
||||
virtual void waiteeDone(GoalPtr waitee) { }
|
||||
|
||||
|
@ -160,6 +165,11 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
virtual bool respectsTimeouts()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void trace(std::string_view s);
|
||||
|
||||
std::string getName() const
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "file-system.hh"
|
||||
#include "globals.hh"
|
||||
#include "hook-instance.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -26,18 +27,21 @@ HookInstance::HookInstance()
|
|||
args.push_back(std::to_string(verbosity));
|
||||
|
||||
/* Create a pipe to get the output of the child. */
|
||||
fromHook.create();
|
||||
Pipe fromHook_;
|
||||
fromHook_.create();
|
||||
|
||||
/* Create the communication pipes. */
|
||||
toHook.create();
|
||||
Pipe toHook_;
|
||||
toHook_.create();
|
||||
|
||||
/* Create a pipe to get the output of the builder. */
|
||||
builderOut.create();
|
||||
Pipe builderOut_;
|
||||
builderOut_.create();
|
||||
|
||||
/* Fork the hook. */
|
||||
pid = startProcess([&]() {
|
||||
|
||||
if (dup2(fromHook.writeSide.get(), STDERR_FILENO) == -1)
|
||||
if (dup2(fromHook_.writeSide.get(), STDERR_FILENO) == -1)
|
||||
throw SysError("cannot pipe standard error into log file");
|
||||
|
||||
commonChildInit();
|
||||
|
@ -45,16 +49,16 @@ HookInstance::HookInstance()
|
|||
if (chdir("/") == -1) throw SysError("changing into /");
|
||||
|
||||
/* Dup the communication pipes. */
|
||||
if (dup2(toHook.readSide.get(), STDIN_FILENO) == -1)
|
||||
if (dup2(toHook_.readSide.get(), STDIN_FILENO) == -1)
|
||||
throw SysError("dupping to-hook read side");
|
||||
|
||||
/* Use fd 4 for the builder's stdout/stderr. */
|
||||
if (dup2(builderOut.writeSide.get(), 4) == -1)
|
||||
if (dup2(builderOut_.writeSide.get(), 4) == -1)
|
||||
throw SysError("dupping builder's stdout/stderr");
|
||||
|
||||
/* Hack: pass the read side of that fd to allow build-remote
|
||||
to read SSH error messages. */
|
||||
if (dup2(builderOut.readSide.get(), 5) == -1)
|
||||
if (dup2(builderOut_.readSide.get(), 5) == -1)
|
||||
throw SysError("dupping builder's stdout/stderr");
|
||||
|
||||
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
|
||||
|
@ -63,10 +67,11 @@ HookInstance::HookInstance()
|
|||
});
|
||||
|
||||
pid.setSeparatePG(true);
|
||||
fromHook.writeSide.reset();
|
||||
toHook.readSide.reset();
|
||||
fromHook = std::move(fromHook_.readSide);
|
||||
toHook = std::move(toHook_.writeSide);
|
||||
builderOut = std::move(builderOut_.readSide);
|
||||
|
||||
sink = FdSink(toHook.writeSide.get());
|
||||
sink = FdSink(toHook.get());
|
||||
std::map<std::string, Config::SettingInfo> settings;
|
||||
globalConfig.getSettings(settings);
|
||||
for (auto & setting : settings)
|
||||
|
@ -78,7 +83,7 @@ HookInstance::HookInstance()
|
|||
HookInstance::~HookInstance()
|
||||
{
|
||||
try {
|
||||
toHook.writeSide.reset();
|
||||
toHook.reset();
|
||||
if (pid) pid.kill();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
|
|
|
@ -10,19 +10,19 @@ namespace nix {
|
|||
struct HookInstance
|
||||
{
|
||||
/**
|
||||
* Pipes for talking to the build hook.
|
||||
* Pipe for talking to the build hook.
|
||||
*/
|
||||
Pipe toHook;
|
||||
AutoCloseFD toHook;
|
||||
|
||||
/**
|
||||
* Pipe for the hook's standard output/error.
|
||||
*/
|
||||
Pipe fromHook;
|
||||
AutoCloseFD fromHook;
|
||||
|
||||
/**
|
||||
* Pipe for the builder's standard output/error.
|
||||
*/
|
||||
Pipe builderOut;
|
||||
AutoCloseFD builderOut;
|
||||
|
||||
/**
|
||||
* The process ID of the hook.
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
#include "local-derivation-goal.hh"
|
||||
#include "indirect-root-store.hh"
|
||||
#include "hook-instance.hh"
|
||||
#include "machines.hh"
|
||||
#include "store-api.hh"
|
||||
#include "worker.hh"
|
||||
#include "builtins.hh"
|
||||
#include "builtins/buildenv.hh"
|
||||
#include "path-references.hh"
|
||||
#include "finally.hh"
|
||||
#include "archive.hh"
|
||||
#include "compression.hh"
|
||||
#include "daemon.hh"
|
||||
#include "topo-sort.hh"
|
||||
#include "json-utils.hh"
|
||||
|
@ -17,6 +15,8 @@
|
|||
#include "namespaces.hh"
|
||||
#include "child.hh"
|
||||
#include "unix-domain-socket.hh"
|
||||
#include "mount.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
|
@ -149,17 +149,30 @@ void LocalDerivationGoal::killSandbox(bool getStats)
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult LocalDerivationGoal::tryLocalBuild()
|
||||
Goal::WorkResult LocalDerivationGoal::tryLocalBuild(bool inBuildSlot)
|
||||
{
|
||||
#if __APPLE__
|
||||
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
|
||||
#endif
|
||||
|
||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||
if (curBuilds >= settings.maxBuildJobs) {
|
||||
if (!inBuildSlot) {
|
||||
state = &DerivationGoal::tryToBuild;
|
||||
outputLocks.unlock();
|
||||
return WaitForSlot{};
|
||||
if (0U != settings.maxBuildJobs) {
|
||||
return WaitForSlot{};
|
||||
}
|
||||
if (getMachines().empty()) {
|
||||
throw Error(
|
||||
"unable to start any build; either set '--max-jobs' to a non-zero value or enable "
|
||||
"remote builds.\n"
|
||||
"https://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html"
|
||||
);
|
||||
} else {
|
||||
throw Error(
|
||||
"unable to start any build; remote machines may not have all required system features.\n"
|
||||
"https://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert(derivationType);
|
||||
|
@ -230,7 +243,14 @@ Goal::WorkResult LocalDerivationGoal::tryLocalBuild()
|
|||
try {
|
||||
|
||||
/* Okay, we have to build. */
|
||||
startBuilder();
|
||||
auto fds = startBuilder();
|
||||
|
||||
/* This state will be reached when we get EOF on the child's
|
||||
log pipe. */
|
||||
state = &DerivationGoal::buildDone;
|
||||
|
||||
started();
|
||||
return WaitForWorld{std::move(fds), true};
|
||||
|
||||
} catch (BuildError & e) {
|
||||
outputLocks.unlock();
|
||||
|
@ -239,12 +259,6 @@ Goal::WorkResult LocalDerivationGoal::tryLocalBuild()
|
|||
report.permanentFailure = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
/* This state will be reached when we get EOF on the child's
|
||||
log pipe. */
|
||||
state = &DerivationGoal::buildDone;
|
||||
|
||||
return started();
|
||||
}
|
||||
|
||||
|
||||
|
@ -279,8 +293,10 @@ void LocalDerivationGoal::closeReadPipes()
|
|||
{
|
||||
if (hook) {
|
||||
DerivationGoal::closeReadPipes();
|
||||
} else
|
||||
builderOut.close();
|
||||
} else {
|
||||
builderOutPTY.close();
|
||||
builderOutFD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -372,40 +388,7 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
|
|||
cleanupPostOutputsRegisteredModeCheck();
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
static void doBind(const Path & source, const Path & target, bool optional = false) {
|
||||
debug("bind mounting '%1%' to '%2%'", source, target);
|
||||
|
||||
auto bindMount = [&]() {
|
||||
if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1)
|
||||
throw SysError("bind mount from '%1%' to '%2%' failed", source, target);
|
||||
};
|
||||
|
||||
auto maybeSt = maybeLstat(source);
|
||||
if (!maybeSt) {
|
||||
if (optional)
|
||||
return;
|
||||
else
|
||||
throw SysError("getting attributes of path '%1%'", source);
|
||||
}
|
||||
auto st = *maybeSt;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
createDirs(target);
|
||||
bindMount();
|
||||
} else if (S_ISLNK(st.st_mode)) {
|
||||
// Symlinks can (apparently) not be bind-mounted, so just copy it
|
||||
createDirs(dirOf(target));
|
||||
copyFile(source, target, {});
|
||||
} else {
|
||||
createDirs(dirOf(target));
|
||||
writeFile(target, "");
|
||||
bindMount();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
void LocalDerivationGoal::startBuilder()
|
||||
std::set<int> LocalDerivationGoal::startBuilder()
|
||||
{
|
||||
if ((buildUser && buildUser->getUIDCount() != 1)
|
||||
#if __linux__
|
||||
|
@ -466,13 +449,23 @@ void LocalDerivationGoal::startBuilder()
|
|||
killSandbox(false);
|
||||
|
||||
/* Right platform? */
|
||||
if (!parsedDrv->canBuildLocally(worker.store))
|
||||
throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}",
|
||||
drv->platform,
|
||||
concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()),
|
||||
worker.store.printStorePath(drvPath),
|
||||
settings.thisSystem,
|
||||
concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
|
||||
if (!parsedDrv->canBuildLocally(worker.store)) {
|
||||
HintFmt addendum{""};
|
||||
if (settings.useSubstitutes && !parsedDrv->substitutesAllowed()) {
|
||||
addendum = HintFmt("\n\nHint: the failing derivation has %s set to %s, forcing it to be built rather than substituted.\n"
|
||||
"Passing %s to force substitution may resolve this failure if the path is available in a substituter.",
|
||||
"allowSubstitutes", "false", "--always-allow-substitutes");
|
||||
}
|
||||
throw Error({
|
||||
.msg = HintFmt("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}%s",
|
||||
drv->platform,
|
||||
concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()),
|
||||
worker.store.printStorePath(drvPath),
|
||||
settings.thisSystem,
|
||||
concatStringsSep<StringSet>(", ", worker.store.systemFeatures),
|
||||
Uncolored(addendum))
|
||||
});
|
||||
}
|
||||
|
||||
/* Create a temporary directory where the build will take
|
||||
place. */
|
||||
|
@ -704,12 +697,13 @@ void LocalDerivationGoal::startBuilder()
|
|||
Path logFile = openLogFile();
|
||||
|
||||
/* Create a pseudoterminal to get the output of the builder. */
|
||||
builderOut = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)};
|
||||
if (!builderOut)
|
||||
builderOutPTY = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)};
|
||||
if (!builderOutPTY)
|
||||
throw SysError("opening pseudoterminal master");
|
||||
builderOutFD = &builderOutPTY;
|
||||
|
||||
// FIXME: not thread-safe, use ptsname_r
|
||||
std::string slaveName = ptsname(builderOut.get());
|
||||
std::string slaveName = ptsname(builderOutPTY.get());
|
||||
|
||||
if (buildUser) {
|
||||
if (chmod(slaveName.c_str(), 0600))
|
||||
|
@ -720,12 +714,12 @@ void LocalDerivationGoal::startBuilder()
|
|||
}
|
||||
#if __APPLE__
|
||||
else {
|
||||
if (grantpt(builderOut.get()))
|
||||
if (grantpt(builderOutPTY.get()))
|
||||
throw SysError("granting access to pseudoterminal slave");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlockpt(builderOut.get()))
|
||||
if (unlockpt(builderOutPTY.get()))
|
||||
throw SysError("unlocking pseudoterminal");
|
||||
|
||||
/* Open the slave side of the pseudoterminal and use it as stderr. */
|
||||
|
@ -756,14 +750,13 @@ void LocalDerivationGoal::startBuilder()
|
|||
|
||||
/* parent */
|
||||
pid.setSeparatePG(true);
|
||||
worker.childStarted(shared_from_this(), {builderOut.get()}, true, true);
|
||||
|
||||
/* Check if setting up the build environment failed. */
|
||||
std::vector<std::string> msgs;
|
||||
while (true) {
|
||||
std::string msg = [&]() {
|
||||
try {
|
||||
return readLine(builderOut.get());
|
||||
return readLine(builderOutPTY.get());
|
||||
} catch (Error & e) {
|
||||
auto status = pid.wait();
|
||||
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
||||
|
@ -775,7 +768,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
}();
|
||||
if (msg.substr(0, 1) == "\2") break;
|
||||
if (msg.substr(0, 1) == "\1") {
|
||||
FdSource source(builderOut.get());
|
||||
FdSource source(builderOutPTY.get());
|
||||
auto ex = readError(source);
|
||||
ex.addTrace({}, "while setting up the build environment");
|
||||
throw ex;
|
||||
|
@ -783,6 +776,8 @@ void LocalDerivationGoal::startBuilder()
|
|||
debug("sandbox setup: " + msg);
|
||||
msgs.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
return {builderOutPTY.get()};
|
||||
}
|
||||
|
||||
|
||||
|
@ -1307,7 +1302,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
|||
Path target = chrootRootDir + worker.store.printStorePath(path);
|
||||
|
||||
if (pathExists(target)) {
|
||||
// There is a similar debug message in doBind, so only run it in this block to not have double messages.
|
||||
// There is a similar debug message in bindPath, so only run it in this block to not have double messages.
|
||||
debug("bind-mounting %s -> %s", target, source);
|
||||
throw Error("store path '%s' already exists in the sandbox", worker.store.printStorePath(path));
|
||||
}
|
||||
|
@ -1324,7 +1319,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
|||
if (setns(sandboxMountNamespace.get(), 0) == -1)
|
||||
throw SysError("entering sandbox mount namespace");
|
||||
|
||||
doBind(source, target);
|
||||
bindPath(source, target);
|
||||
|
||||
_exit(0);
|
||||
});
|
||||
|
@ -1516,7 +1511,7 @@ void LocalDerivationGoal::runChild()
|
|||
chmodPath(dst, 0555);
|
||||
} else
|
||||
#endif
|
||||
doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
|
||||
bindPath(i.second.source, chrootRootDir + i.first, i.second.optional);
|
||||
}
|
||||
|
||||
/* Bind a new instance of procfs on /proc. */
|
||||
|
@ -1555,8 +1550,8 @@ void LocalDerivationGoal::runChild()
|
|||
} else {
|
||||
if (errno != EINVAL)
|
||||
throw SysError("mounting /dev/pts");
|
||||
doBind("/dev/pts", chrootRootDir + "/dev/pts");
|
||||
doBind("/dev/ptmx", chrootRootDir + "/dev/ptmx");
|
||||
bindPath("/dev/pts", chrootRootDir + "/dev/pts");
|
||||
bindPath("/dev/ptmx", chrootRootDir + "/dev/ptmx");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2596,13 +2591,6 @@ void LocalDerivationGoal::deleteTmpDir(bool force)
|
|||
}
|
||||
|
||||
|
||||
bool LocalDerivationGoal::isReadDesc(int fd)
|
||||
{
|
||||
return (hook && DerivationGoal::isReadDesc(fd)) ||
|
||||
(!hook && fd == builderOut.get());
|
||||
}
|
||||
|
||||
|
||||
StorePath LocalDerivationGoal::makeFallbackPath(OutputNameView outputName)
|
||||
{
|
||||
return worker.store.makeStorePath(
|
||||
|
|
|
@ -40,7 +40,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
* Master side of the pseudoterminal used for the builder's
|
||||
* standard output/error.
|
||||
*/
|
||||
AutoCloseFD builderOut;
|
||||
AutoCloseFD builderOutPTY;
|
||||
|
||||
/**
|
||||
* Pipe for synchronising updates to the builder namespaces.
|
||||
|
@ -211,12 +211,12 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
/**
|
||||
* The additional states.
|
||||
*/
|
||||
WorkResult tryLocalBuild() override;
|
||||
WorkResult tryLocalBuild(bool inBuildSlot) override;
|
||||
|
||||
/**
|
||||
* Start building a derivation.
|
||||
*/
|
||||
void startBuilder();
|
||||
std::set<int> startBuilder();
|
||||
|
||||
/**
|
||||
* Fill in the environment for the builder.
|
||||
|
@ -285,8 +285,6 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
void cleanupPostOutputsRegisteredModeCheck() override;
|
||||
void cleanupPostOutputsRegisteredModeNonCheck() override;
|
||||
|
||||
bool isReadDesc(int fd) override;
|
||||
|
||||
/**
|
||||
* Delete the temporary directory, if we have one.
|
||||
*/
|
||||
|
@ -359,6 +357,10 @@ protected:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool respectsTimeouts() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace nix {
|
||||
|
||||
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
: Goal(worker, DerivedPath::Opaque { storePath })
|
||||
: Goal(worker)
|
||||
, storePath(storePath)
|
||||
, repair(repair)
|
||||
, ca(ca)
|
||||
|
@ -39,13 +39,13 @@ Goal::Finished PathSubstitutionGoal::done(
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::work()
|
||||
Goal::WorkResult PathSubstitutionGoal::work(bool inBuildSlot)
|
||||
{
|
||||
return (this->*state)();
|
||||
return (this->*state)(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::init()
|
||||
Goal::WorkResult PathSubstitutionGoal::init(bool inBuildSlot)
|
||||
{
|
||||
trace("init");
|
||||
|
||||
|
@ -61,11 +61,11 @@ Goal::WorkResult PathSubstitutionGoal::init()
|
|||
|
||||
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
||||
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::tryNext()
|
||||
Goal::WorkResult PathSubstitutionGoal::tryNext(bool inBuildSlot)
|
||||
{
|
||||
trace("trying next substituter");
|
||||
|
||||
|
@ -97,23 +97,23 @@ Goal::WorkResult PathSubstitutionGoal::tryNext()
|
|||
if (sub->storeDir == worker.store.storeDir)
|
||||
assert(subPath == storePath);
|
||||
} else if (sub->storeDir != worker.store.storeDir) {
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
|
||||
try {
|
||||
// FIXME: make async
|
||||
info = sub->queryPathInfo(subPath ? *subPath : storePath);
|
||||
} catch (InvalidPath &) {
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
} catch (SubstituterDisabled &) {
|
||||
if (settings.tryFallback) {
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
throw;
|
||||
} catch (Error & e) {
|
||||
if (settings.tryFallback) {
|
||||
logError(e.info());
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ Goal::WorkResult PathSubstitutionGoal::tryNext()
|
|||
} else {
|
||||
printError("asked '%s' for '%s' but got '%s'",
|
||||
sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path));
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ Goal::WorkResult PathSubstitutionGoal::tryNext()
|
|||
{
|
||||
warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'",
|
||||
worker.store.printStorePath(storePath), sub->getUri());
|
||||
return tryNext();
|
||||
return tryNext(inBuildSlot);
|
||||
}
|
||||
|
||||
/* To maintain the closure invariant, we first have to realise the
|
||||
|
@ -158,7 +158,7 @@ Goal::WorkResult PathSubstitutionGoal::tryNext()
|
|||
result.goals.insert(worker.makePathSubstitutionGoal(i));
|
||||
|
||||
if (result.goals.empty()) {/* to prevent hang (no wake-up event) */
|
||||
return referencesValid();
|
||||
return referencesValid(inBuildSlot);
|
||||
} else {
|
||||
state = &PathSubstitutionGoal::referencesValid;
|
||||
return result;
|
||||
|
@ -166,7 +166,7 @@ Goal::WorkResult PathSubstitutionGoal::tryNext()
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::referencesValid()
|
||||
Goal::WorkResult PathSubstitutionGoal::referencesValid(bool inBuildSlot)
|
||||
{
|
||||
trace("all references realised");
|
||||
|
||||
|
@ -186,14 +186,11 @@ Goal::WorkResult PathSubstitutionGoal::referencesValid()
|
|||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::tryToRun()
|
||||
Goal::WorkResult PathSubstitutionGoal::tryToRun(bool inBuildSlot)
|
||||
{
|
||||
trace("trying to run");
|
||||
|
||||
/* Make sure that we are allowed to start a substitution. Note that even
|
||||
if maxSubstitutionJobs == 0, we still allow a substituter to run. This
|
||||
prevents infinite waiting. */
|
||||
if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) {
|
||||
if (!inBuildSlot) {
|
||||
return WaitForSlot{};
|
||||
}
|
||||
|
||||
|
@ -224,14 +221,12 @@ Goal::WorkResult PathSubstitutionGoal::tryToRun()
|
|||
}
|
||||
});
|
||||
|
||||
worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false);
|
||||
|
||||
state = &PathSubstitutionGoal::finished;
|
||||
return StillAlive{};
|
||||
return WaitForWorld{{outPipe.readSide.get()}, true};
|
||||
}
|
||||
|
||||
|
||||
Goal::WorkResult PathSubstitutionGoal::finished()
|
||||
Goal::WorkResult PathSubstitutionGoal::finished(bool inBuildSlot)
|
||||
{
|
||||
trace("substitute finished");
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ struct PathSubstitutionGoal : public Goal
|
|||
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
||||
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
||||
|
||||
typedef WorkResult (PathSubstitutionGoal::*GoalState)();
|
||||
typedef WorkResult (PathSubstitutionGoal::*GoalState)(bool inBuildSlot);
|
||||
GoalState state;
|
||||
|
||||
/**
|
||||
|
@ -94,17 +94,16 @@ public:
|
|||
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
|
||||
}
|
||||
|
||||
WorkResult work() override;
|
||||
WorkResult work(bool inBuildSlot) override;
|
||||
|
||||
/**
|
||||
* The states.
|
||||
*/
|
||||
WorkResult init();
|
||||
WorkResult tryNext();
|
||||
WorkResult gotInfo();
|
||||
WorkResult referencesValid();
|
||||
WorkResult tryToRun();
|
||||
WorkResult finished();
|
||||
WorkResult init(bool inBuildSlot);
|
||||
WorkResult tryNext(bool inBuildSlot);
|
||||
WorkResult referencesValid(bool inBuildSlot);
|
||||
WorkResult tryToRun(bool inBuildSlot);
|
||||
WorkResult finished(bool inBuildSlot);
|
||||
|
||||
/**
|
||||
* Callback used by the worker to write to the log.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "charptr-cast.hh"
|
||||
#include "machines.hh"
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "drv-output-substitution-goal.hh"
|
||||
|
@ -151,7 +150,7 @@ void Worker::goalFinished(GoalPtr goal, Goal::Finished & f)
|
|||
if (!goal->waiters.empty())
|
||||
logError(f.ex->info());
|
||||
else
|
||||
goal->ex = std::move(f.ex);
|
||||
goal->ex = f.ex;
|
||||
}
|
||||
|
||||
for (auto & i : goal->waiters) {
|
||||
|
@ -198,6 +197,7 @@ void Worker::handleWorkResult(GoalPtr goal, Goal::WorkResult how)
|
|||
dep->waiters.insert(goal);
|
||||
}
|
||||
},
|
||||
[&](Goal::WaitForWorld & w) { childStarted(goal, w.fds, w.inBuildSlot); },
|
||||
[&](Goal::Finished & f) { goalFinished(goal, f); },
|
||||
},
|
||||
how
|
||||
|
@ -232,20 +232,8 @@ void Worker::wakeUp(GoalPtr goal)
|
|||
}
|
||||
|
||||
|
||||
unsigned Worker::getNrLocalBuilds()
|
||||
{
|
||||
return nrLocalBuilds;
|
||||
}
|
||||
|
||||
|
||||
unsigned Worker::getNrSubstitutions()
|
||||
{
|
||||
return nrSubstitutions;
|
||||
}
|
||||
|
||||
|
||||
void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
|
||||
bool inBuildSlot, bool respectTimeouts)
|
||||
bool inBuildSlot)
|
||||
{
|
||||
Child child;
|
||||
child.goal = goal;
|
||||
|
@ -253,7 +241,6 @@ void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
|
|||
child.fds = fds;
|
||||
child.timeStarted = child.lastOutput = steady_time_point::clock::now();
|
||||
child.inBuildSlot = inBuildSlot;
|
||||
child.respectTimeouts = respectTimeouts;
|
||||
children.emplace_back(child);
|
||||
if (inBuildSlot) {
|
||||
switch (goal->jobCategory()) {
|
||||
|
@ -307,8 +294,8 @@ void Worker::waitForBuildSlot(GoalPtr goal)
|
|||
{
|
||||
goal->trace("wait for build slot");
|
||||
bool isSubstitutionGoal = goal->jobCategory() == JobCategory::Substitution;
|
||||
if ((!isSubstitutionGoal && getNrLocalBuilds() < settings.maxBuildJobs) ||
|
||||
(isSubstitutionGoal && getNrSubstitutions() < settings.maxSubstitutionJobs))
|
||||
if ((!isSubstitutionGoal && nrLocalBuilds < settings.maxBuildJobs) ||
|
||||
(isSubstitutionGoal && nrSubstitutions < settings.maxSubstitutionJobs))
|
||||
wakeUp(goal); /* we can do it right away */
|
||||
else
|
||||
wantingToBuild.insert(goal);
|
||||
|
@ -364,7 +351,12 @@ void Worker::run(const Goals & _topGoals)
|
|||
awake.clear();
|
||||
for (auto & goal : awake2) {
|
||||
checkInterrupt();
|
||||
handleWorkResult(goal, goal->work());
|
||||
/* Make sure that we are always allowed to run at least one substitution.
|
||||
This prevents infinite waiting. */
|
||||
const bool inSlot = goal->jobCategory() == JobCategory::Substitution
|
||||
? nrSubstitutions < std::max(1U, (unsigned int) settings.maxSubstitutionJobs)
|
||||
: nrLocalBuilds < settings.maxBuildJobs;
|
||||
handleWorkResult(goal, goal->work(inSlot));
|
||||
|
||||
actDerivations.progress(
|
||||
doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds
|
||||
|
@ -388,18 +380,6 @@ void Worker::run(const Goals & _topGoals)
|
|||
if (!children.empty() || !waitingForAWhile.empty())
|
||||
waitForInput();
|
||||
else {
|
||||
if (awake.empty() && 0U == settings.maxBuildJobs)
|
||||
{
|
||||
if (getMachines().empty())
|
||||
throw Error("unable to start any build; either increase '--max-jobs' "
|
||||
"or enable remote builds."
|
||||
"\nhttps://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html");
|
||||
else
|
||||
throw Error("unable to start any build; remote machines may not have "
|
||||
"all required system features."
|
||||
"\nhttps://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html");
|
||||
|
||||
}
|
||||
assert(!awake.empty());
|
||||
}
|
||||
}
|
||||
|
@ -434,11 +414,13 @@ void Worker::waitForInput()
|
|||
// Periodicallty wake up to see if we need to run the garbage collector.
|
||||
nearest = before + std::chrono::seconds(10);
|
||||
for (auto & i : children) {
|
||||
if (!i.respectTimeouts) continue;
|
||||
if (0 != settings.maxSilentTime)
|
||||
nearest = std::min(nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime));
|
||||
if (0 != settings.buildTimeout)
|
||||
nearest = std::min(nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout));
|
||||
if (auto goal = i.goal.lock()) {
|
||||
if (!goal->respectsTimeouts()) continue;
|
||||
if (0 != settings.maxSilentTime)
|
||||
nearest = std::min(nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime));
|
||||
if (0 != settings.buildTimeout)
|
||||
nearest = std::min(nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout));
|
||||
}
|
||||
}
|
||||
if (nearest != steady_time_point::max()) {
|
||||
timeout = std::max(1L, (long) std::chrono::duration_cast<std::chrono::seconds>(nearest - before).count());
|
||||
|
@ -491,7 +473,7 @@ void Worker::waitForInput()
|
|||
|
||||
if (!goal->exitCode.has_value() &&
|
||||
0 != settings.maxSilentTime &&
|
||||
j->respectTimeouts &&
|
||||
goal->respectsTimeouts() &&
|
||||
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
|
||||
{
|
||||
handleWorkResult(
|
||||
|
@ -507,7 +489,7 @@ void Worker::waitForInput()
|
|||
|
||||
else if (!goal->exitCode.has_value() &&
|
||||
0 != settings.buildTimeout &&
|
||||
j->respectTimeouts &&
|
||||
goal->respectsTimeouts() &&
|
||||
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
|
||||
{
|
||||
handleWorkResult(
|
||||
|
|
|
@ -29,7 +29,6 @@ struct Child
|
|||
WeakGoalPtr goal;
|
||||
Goal * goal2; // ugly hackery
|
||||
std::set<int> fds;
|
||||
bool respectTimeouts;
|
||||
bool inBuildSlot;
|
||||
/**
|
||||
* Time we last got output on stdout/stderr
|
||||
|
@ -153,6 +152,18 @@ private:
|
|||
*/
|
||||
void waitForInput();
|
||||
|
||||
/**
|
||||
* Remove a dead goal.
|
||||
*/
|
||||
void removeGoal(GoalPtr goal);
|
||||
|
||||
/**
|
||||
* Registers a running child process. `inBuildSlot` means that
|
||||
* the process counts towards the jobs limit.
|
||||
*/
|
||||
void childStarted(GoalPtr goal, const std::set<int> & fds,
|
||||
bool inBuildSlot);
|
||||
|
||||
public:
|
||||
|
||||
const Activity act;
|
||||
|
@ -224,29 +235,6 @@ public:
|
|||
*/
|
||||
GoalPtr makeGoal(const DerivedPath & req, BuildMode buildMode = bmNormal);
|
||||
|
||||
/**
|
||||
* Remove a dead goal.
|
||||
*/
|
||||
void removeGoal(GoalPtr goal);
|
||||
|
||||
/**
|
||||
* Return the number of local build processes currently running (but not
|
||||
* remote builds via the build hook).
|
||||
*/
|
||||
unsigned int getNrLocalBuilds();
|
||||
|
||||
/**
|
||||
* Return the number of substitution processes currently running.
|
||||
*/
|
||||
unsigned int getNrSubstitutions();
|
||||
|
||||
/**
|
||||
* Registers a running child process. `inBuildSlot` means that
|
||||
* the process counts towards the jobs limit.
|
||||
*/
|
||||
void childStarted(GoalPtr goal, const std::set<int> & fds,
|
||||
bool inBuildSlot, bool respectTimeouts);
|
||||
|
||||
/**
|
||||
* Unregisters a running child process.
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "buildenv.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "store-api.hh"
|
||||
#include "archive.hh"
|
||||
#include "compression.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace nix {
|
|||
{ \
|
||||
return LengthPrefixedProtoHelper<CommonProto, T >::read(store, conn); \
|
||||
} \
|
||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
|
||||
TEMPLATE [[nodiscard]] WireFormatGenerator CommonProto::Serialise< T >::write(const Store & store, CommonProto::WriteConn conn, const T & t) \
|
||||
{ \
|
||||
return LengthPrefixedProtoHelper<CommonProto, T >::write(store, conn, t); \
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "args.hh"
|
||||
#include "content-address.hh"
|
||||
#include "split.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "monitor-fd.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "worker-protocol-impl.hh"
|
||||
#include "build-result.hh"
|
||||
#include "build-result.hh" // IWYU pragma: keep
|
||||
#include "store-api.hh"
|
||||
#include "store-cast.hh"
|
||||
#include "gc-store.hh"
|
||||
|
@ -12,6 +12,7 @@
|
|||
#include "finally.hh"
|
||||
#include "archive.hh"
|
||||
#include "derivations.hh"
|
||||
#include "strings.hh"
|
||||
#include "args.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
#include "store-api.hh"
|
||||
#include "globals.hh"
|
||||
#include "types.hh"
|
||||
#include "split.hh"
|
||||
#include "common-protocol.hh"
|
||||
#include "common-protocol-impl.hh"
|
||||
#include "fs-accessor.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "strings.hh"
|
||||
#include "backed-string-view.hh"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "path.hh"
|
||||
#include "outputs-spec.hh"
|
||||
#include "comparator.hh"
|
||||
#include "ref.hh"
|
||||
|
||||
#include <variant>
|
||||
|
||||
|
@ -78,10 +79,12 @@ struct SingleDerivedPathBuilt {
|
|||
DECLARE_CMP(SingleDerivedPathBuilt);
|
||||
};
|
||||
|
||||
using _SingleDerivedPathRaw = std::variant<
|
||||
namespace derived_path::detail {
|
||||
using SingleDerivedPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
SingleDerivedPathBuilt
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A "derived path" is a very simple sort of expression (not a Nix
|
||||
|
@ -94,8 +97,8 @@ using _SingleDerivedPathRaw = std::variant<
|
|||
* - built, in which case it is a pair of a derivation path and an
|
||||
* output name.
|
||||
*/
|
||||
struct SingleDerivedPath : _SingleDerivedPathRaw {
|
||||
using Raw = _SingleDerivedPathRaw;
|
||||
struct SingleDerivedPath : derived_path::detail::SingleDerivedPathRaw {
|
||||
using Raw = derived_path::detail::SingleDerivedPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
|
@ -201,10 +204,12 @@ struct DerivedPathBuilt {
|
|||
DECLARE_CMP(DerivedPathBuilt);
|
||||
};
|
||||
|
||||
using _DerivedPathRaw = std::variant<
|
||||
namespace derived_path::detail {
|
||||
using DerivedPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
DerivedPathBuilt
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A "derived path" is a very simple sort of expression that evaluates
|
||||
|
@ -216,8 +221,8 @@ using _DerivedPathRaw = std::variant<
|
|||
* - built, in which case it is a pair of a derivation path and some
|
||||
* output names.
|
||||
*/
|
||||
struct DerivedPath : _DerivedPathRaw {
|
||||
using Raw = _DerivedPathRaw;
|
||||
struct DerivedPath : derived_path::detail::DerivedPathRaw {
|
||||
using Raw = derived_path::detail::DerivedPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "dummy-store.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
namespace nix {
|
||||
|
@ -73,6 +74,8 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
|
|||
{ unsupported("getFSAccessor"); }
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;
|
||||
void registerDummyStore() {
|
||||
StoreImplementations::add<DummyStore, DummyStoreConfig>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
8
src/libstore/dummy-store.hh
Normal file
8
src/libstore/dummy-store.hh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
|
||||
void registerDummyStore();
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include "s3.hh"
|
||||
#include "signals.hh"
|
||||
#include "compression.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#if ENABLE_S3
|
||||
#include <aws/core/client/ClientConfiguration.h>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include "box_ptr.hh"
|
||||
#include "ref.hh"
|
||||
#include "logging.hh"
|
||||
#include "serialise.hh"
|
||||
#include "types.hh"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "unix-domain-socket.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <queue>
|
||||
#include <regex>
|
||||
|
|
|
@ -33,6 +33,16 @@
|
|||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
// All built-in store implementations.
|
||||
#include "dummy-store.hh"
|
||||
#include "http-binary-cache-store.hh"
|
||||
#include "legacy-ssh-store.hh"
|
||||
#include "local-binary-cache-store.hh"
|
||||
#include "local-store.hh"
|
||||
#include "s3-binary-cache-store.hh"
|
||||
#include "ssh-store.hh"
|
||||
#include "uds-remote-store.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
@ -396,6 +406,17 @@ static void preloadNSS()
|
|||
});
|
||||
}
|
||||
|
||||
static void registerStoreImplementations() {
|
||||
registerDummyStore();
|
||||
registerHttpBinaryCacheStore();
|
||||
registerLegacySSHStore();
|
||||
registerLocalBinaryCacheStore();
|
||||
registerLocalStore();
|
||||
registerS3BinaryCacheStore();
|
||||
registerSSHStore();
|
||||
registerUDSRemoteStore();
|
||||
}
|
||||
|
||||
static bool initLibStoreDone = false;
|
||||
|
||||
void assertLibStoreInitialized() {
|
||||
|
@ -433,6 +454,8 @@ void initLibStore() {
|
|||
unsetenv("TMPDIR");
|
||||
#endif
|
||||
|
||||
registerStoreImplementations();
|
||||
|
||||
initLibStoreDone = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -634,7 +634,7 @@ public:
|
|||
line.
|
||||
)"};
|
||||
|
||||
OptionalPathSetting diffHook{
|
||||
PathsSetting<std::optional<Path>> diffHook{
|
||||
this, std::nullopt, "diff-hook",
|
||||
R"(
|
||||
Absolute path to an executable capable of diffing build
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "http-binary-cache-store.hh"
|
||||
#include "binary-cache-store.hh"
|
||||
#include "filetransfer.hh"
|
||||
#include "globals.hh"
|
||||
|
@ -194,6 +195,8 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regHttpBinaryCacheStore;
|
||||
void registerHttpBinaryCacheStore() {
|
||||
StoreImplementations::add<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
8
src/libstore/http-binary-cache-store.hh
Normal file
8
src/libstore/http-binary-cache-store.hh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
|
||||
void registerHttpBinaryCacheStore();
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "ssh-store-config.hh"
|
||||
#include "legacy-ssh-store.hh"
|
||||
#include "archive.hh"
|
||||
#include "pool.hh"
|
||||
#include "remote-store.hh"
|
||||
|
@ -8,6 +8,8 @@
|
|||
#include "store-api.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
#include "ssh.hh"
|
||||
#include "ssh-store.hh"
|
||||
#include "strings.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
namespace nix {
|
||||
|
@ -412,6 +414,8 @@ public:
|
|||
{ unsupported("queryRealisation"); }
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regLegacySSHStore;
|
||||
void registerLegacySSHStore() {
|
||||
StoreImplementations::add<LegacySSHStore, LegacySSHStoreConfig>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
8
src/libstore/legacy-ssh-store.hh
Normal file
8
src/libstore/legacy-ssh-store.hh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
|
||||
void registerLegacySSHStore();
|
||||
|
||||
}
|
|
@ -61,9 +61,9 @@ template<class Inner, typename... Ts>
|
|||
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::tuple<Ts...>);
|
||||
|
||||
template<class Inner, typename K, typename V>
|
||||
#define _X std::map<K, V>
|
||||
LENGTH_PREFIXED_PROTO_HELPER(Inner, _X);
|
||||
#undef _X
|
||||
#define DONT_SUBSTITUTE_KV_TYPE std::map<K, V>
|
||||
LENGTH_PREFIXED_PROTO_HELPER(Inner, DONT_SUBSTITUTE_KV_TYPE);
|
||||
#undef DONT_SUBSTITUTE_KV_TYPE
|
||||
|
||||
template<class Inner, typename T>
|
||||
std::vector<T>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "local-binary-cache-store.hh"
|
||||
#include "binary-cache-store.hh"
|
||||
#include "globals.hh"
|
||||
#include "nar-info-disk-cache.hh"
|
||||
|
@ -124,6 +125,8 @@ std::set<std::string> LocalBinaryCacheStore::uriSchemes()
|
|||
return {"file"};
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regLocalBinaryCacheStore;
|
||||
void registerLocalBinaryCacheStore() {
|
||||
StoreImplementations::add<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
8
src/libstore/local-binary-cache-store.hh
Normal file
8
src/libstore/local-binary-cache-store.hh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
|
||||
void registerLocalBinaryCacheStore();
|
||||
|
||||
}
|
|
@ -11,21 +11,21 @@ struct LocalFSStoreConfig : virtual StoreConfig
|
|||
{
|
||||
using StoreConfig::StoreConfig;
|
||||
|
||||
const OptionalPathSetting rootDir{this, std::nullopt,
|
||||
const PathsSetting<std::optional<Path>> rootDir{this, std::nullopt,
|
||||
"root",
|
||||
"Directory prefixed to all other paths."};
|
||||
|
||||
const PathSetting stateDir{this,
|
||||
const PathsSetting<Path> stateDir{this,
|
||||
rootDir.get() ? *rootDir.get() + "/nix/var/nix" : settings.nixStateDir,
|
||||
"state",
|
||||
"Directory where Lix will store state."};
|
||||
|
||||
const PathSetting logDir{this,
|
||||
const PathsSetting<Path> logDir{this,
|
||||
rootDir.get() ? *rootDir.get() + "/nix/var/log/nix" : settings.nixLogDir,
|
||||
"log",
|
||||
"directory where Lix will store log files."};
|
||||
|
||||
const PathSetting realStoreDir{this,
|
||||
const PathsSetting<Path> realStoreDir{this,
|
||||
rootDir.get() ? *rootDir.get() + "/nix/store" : storeDir, "real",
|
||||
"Physical path of the Nix store."};
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "compression.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
|
|
@ -421,4 +421,7 @@ void canonicaliseTimestampAndPermissions(const Path & path);
|
|||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
// Implemented by the relevant platform/ module being used.
|
||||
void registerLocalStore();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "types.hh"
|
||||
#include "ref.hh"
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "make-content-addressed.hh"
|
||||
#include "references.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -118,12 +118,16 @@ libstore_headers = files(
|
|||
'derived-path-map.hh',
|
||||
'derived-path.hh',
|
||||
'downstream-placeholder.hh',
|
||||
'dummy-store.hh',
|
||||
'filetransfer.hh',
|
||||
'fs-accessor.hh',
|
||||
'gc-store.hh',
|
||||
'globals.hh',
|
||||
'http-binary-cache-store.hh',
|
||||
'indirect-root-store.hh',
|
||||
'legacy-ssh-store.hh',
|
||||
'length-prefixed-protocol-helper.hh',
|
||||
'local-binary-cache-store.hh',
|
||||
'local-fs-store.hh',
|
||||
'local-store.hh',
|
||||
'lock.hh',
|
||||
|
@ -152,8 +156,8 @@ libstore_headers = files(
|
|||
'serve-protocol-impl.hh',
|
||||
'serve-protocol.hh',
|
||||
'sqlite.hh',
|
||||
'ssh-store-config.hh',
|
||||
'ssh.hh',
|
||||
'ssh-store.hh',
|
||||
'store-api.hh',
|
||||
'store-cast.hh',
|
||||
'uds-remote-store.hh',
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "derivations.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "store-api.hh"
|
||||
#include "thread-pool.hh"
|
||||
#include "topo-sort.hh"
|
||||
#include "closure.hh"
|
||||
#include "filetransfer.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "fs-accessor.hh"
|
||||
#include "ref.hh"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include "fs-accessor.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "sqlite.hh"
|
||||
#include "globals.hh"
|
||||
#include "users.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "globals.hh"
|
||||
#include "strings.hh"
|
||||
#include "nar-info.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "local-store.hh"
|
||||
#include "globals.hh"
|
||||
#include "signals.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "parsed-derivations.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <regex>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "path-info.hh"
|
||||
#include "store-api.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "path-with-outputs.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <regex>
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -112,3 +112,13 @@ PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
std::size_t std::hash<nix::StorePath>::operator()(const nix::StorePath & path) const noexcept
|
||||
{
|
||||
// It's already a cryptographic hash of 160 bits (assuming that nobody gives us bogus ones...), so just parse it.
|
||||
auto h = nix::Hash::parseNonSRIUnprefixed(path.hashPart(), nix::HashType::SHA1);
|
||||
// This need not be stable across machines, so bit casting the start of it is fine.
|
||||
size_t r;
|
||||
memcpy(&r, h.hash, sizeof(r));
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
///@file
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
#include "types.hh"
|
||||
#include "types.hh" // IWYU pragma: keep
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -89,10 +90,7 @@ const std::string drvExtension = ".drv";
|
|||
namespace std {
|
||||
|
||||
template<> struct hash<nix::StorePath> {
|
||||
std::size_t operator()(const nix::StorePath & path) const noexcept
|
||||
{
|
||||
return * (std::size_t *) path.to_string().data();
|
||||
}
|
||||
std::size_t operator()(const nix::StorePath & path) const noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -49,8 +49,12 @@ struct FdLock
|
|||
|
||||
~FdLock()
|
||||
{
|
||||
if (acquired)
|
||||
lockFile(fd, ltNone, false);
|
||||
try {
|
||||
if (acquired)
|
||||
lockFile(fd, ltNone, false);
|
||||
} catch (SysError &) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "signals.hh"
|
||||
#include "platform/darwin.hh"
|
||||
#include "regex.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <sys/proc_info.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
@ -261,4 +262,9 @@ void DarwinLocalDerivationGoal::execBuilder(std::string builder, Strings args, S
|
|||
|
||||
posix_spawn(nullptr, builder.c_str(), nullptr, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
}
|
||||
|
||||
void registerLocalStore() {
|
||||
StoreImplementations::add<DarwinLocalStore, LocalStoreConfig>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "platform/fallback.hh"
|
||||
|
||||
namespace nix {
|
||||
static RegisterStoreImplementation<FallbackLocalStore, LocalStoreConfig> regLocalStore;
|
||||
void registerLocalStore() {
|
||||
Implementations::add<FallbackLocalStore, LocalStoreConfig>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "signals.hh"
|
||||
#include "platform/linux.hh"
|
||||
#include "regex.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
#include <grp.h>
|
||||
#include <regex>
|
||||
|
@ -25,7 +26,9 @@ namespace {
|
|||
constexpr const std::string_view nativeSystem = SYSTEM;
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation<LinuxLocalStore, LocalStoreConfig> regLocalStore;
|
||||
void registerLocalStore() {
|
||||
StoreImplementations::add<LinuxLocalStore, LocalStoreConfig>();
|
||||
}
|
||||
|
||||
static void readProcLink(const std::string & file, UncheckedRoots & roots)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue