Compare commits

..

1 Commits

Author SHA1 Message Date
Sergei Zimmerman
e6935d6c18 libstore: Fix double quotes in debug logs for pathlocks 2025-11-26 03:24:57 +03:00
244 changed files with 1979 additions and 3319 deletions

View File

@@ -94,8 +94,6 @@ The underlying source files are located in [`doc/manual/source`](./doc/manual/so
For small changes you can [use GitHub to edit these files](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files)
For larger changes see the [Nix reference manual](https://nix.dev/manual/nix/development/development/contributing.html).
You're encouraged to add line breaks at semantic boundaries, per [sembr](https://sembr.org).
## Getting help
Whenever you're stuck or do not know how to proceed, you can always ask for help.

View File

@@ -24,15 +24,8 @@ def map_contents_recursively(transformer):
def process_command:
.[0] as $context |
.[1] as $body |
# mdbook 0.5.x uses 'items' instead of 'sections'
if $body.items then
$body + {
items: $body.items | map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end)),
}
else
$body + {
sections: $body.sections | map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end)),
}
end;
$body + {
sections: $body.sections | map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end)),
};
process_command

View File

@@ -24,3 +24,12 @@ renderers = ["html"]
command = "jq --from-file ./anchors.jq"
[output.markdown]
[output.linkcheck]
# no Internet during the build (in the sandbox)
follow-web-links = false
# mdbook-linkcheck does not understand [foo]{#bar} style links, resulting in
# excessive "Potential incomplete link" warnings. No other kind of warning was
# produced at the time of writing.
warning-policy = "ignore"

View File

@@ -1,223 +0,0 @@
#!/usr/bin/env python3
"""
Standalone markdown preprocessor for manpage generation.
Expands {{#include}} directives and handles @docroot@ references
without requiring mdbook.
"""
from pathlib import Path
import sys
import argparse
import re
def expand_includes(
content: str,
current_file: Path,
source_root: Path,
generated_root: Path | None,
visited: set[Path] | None = None,
) -> str:
"""
Recursively expand {{#include path}} directives.
Args:
content: Markdown content to process
current_file: Path to the current file (for resolving relative includes)
source_root: Root of the source directory
generated_root: Root of generated files (for @generated@/ includes)
visited: Set of already-visited files (for cycle detection)
"""
if visited is None:
visited = set()
# Track current file to detect cycles
visited.add(current_file.resolve())
lines = []
include_pattern = re.compile(r'^\s*\{\{#include\s+(.+?)\}\}\s*$')
for line in content.splitlines(keepends=True):
match = include_pattern.match(line)
if not match:
lines.append(line)
continue
# Found an include directive
include_path_str = match.group(1).strip()
# Resolve the include path
if include_path_str.startswith("@generated@/"):
# Generated file
if generated_root is None:
raise ValueError(
f"Cannot resolve @generated@ path '{include_path_str}' "
f"without --generated-root"
)
include_path = generated_root / include_path_str[12:]
else:
# Relative to current file
include_path = (current_file.parent / include_path_str).resolve()
# Check for cycles
if include_path.resolve() in visited:
raise RuntimeError(
f"Include cycle detected: {include_path} is already being processed"
)
# Check that file exists
if not include_path.exists():
raise FileNotFoundError(
f"Include file not found: {include_path_str}\n"
f" Resolved to: {include_path}\n"
f" From: {current_file}"
)
# Recursively expand the included file
included_content = include_path.read_text()
expanded = expand_includes(
included_content,
include_path,
source_root,
generated_root,
visited.copy(), # Copy visited set for this branch
)
lines.append(expanded)
# Add newline if the included content doesn't end with one
if not expanded.endswith('\n'):
lines.append('\n')
return ''.join(lines)
def resolve_docroot(content: str, current_file: Path, source_root: Path, docroot_url: str) -> str:
"""
Replace @docroot@ with nix.dev URL and convert .md to .html.
For manpages, absolute URLs are more useful than relative paths since
manpages are viewed standalone. lowdown will display these as proper
references in the manpage output.
"""
# Replace @docroot@ with the base URL
content = content.replace("@docroot@", docroot_url)
# Convert .md extensions to .html for web links
# Use lookahead to ensure that .md occurs before a fragment or a possible URL end.
content = re.sub(
r'(https://nix\.dev/[^)\s]*?)\.md(?=[#)\s]|$)',
r'\1.html',
content
)
return content
def resolve_at_escapes(content: str) -> str:
"""Replace @_at_ with @"""
return content.replace("@_at_", "@")
def process_file(
input_file: Path,
source_root: Path,
generated_root: Path | None,
docroot_url: str,
) -> str:
"""Process a single markdown file."""
content = input_file.read_text()
# Expand includes
content = expand_includes(content, input_file, source_root, generated_root)
# Resolve @docroot@ references
content = resolve_docroot(content, input_file, source_root, docroot_url)
# Resolve @_at_ escapes
content = resolve_at_escapes(content)
return content
def main():
parser = argparse.ArgumentParser(
description="Expand markdown includes for manpage generation",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Expand a manpage source file
%(prog)s \\
--source-root doc/manual/source \\
--generated-root build/doc/manual/source \\
doc/manual/source/command-ref/nix-store/query.md
# Pipe to lowdown for manpage generation
%(prog)s -s doc/manual/source -g build/doc/manual/source \\
doc/manual/source/command-ref/nix-env.md | \\
lowdown -sT man -M section=1 -o nix-env.1
""",
)
parser.add_argument(
"input_file",
type=Path,
help="Input markdown file to process",
)
parser.add_argument(
"-s", "--source-root",
type=Path,
required=True,
help="Root directory of markdown sources",
)
parser.add_argument(
"-g", "--generated-root",
type=Path,
help="Root directory of generated files (for @generated@/ includes)",
)
parser.add_argument(
"-o", "--output",
type=Path,
help="Output file (default: stdout)",
)
parser.add_argument(
"-u", "--doc-url",
type=str,
default="https://nix.dev/manual/nix/latest",
help="Base URL for documentation links (default: https://nix.dev/manual/nix/latest)",
)
args = parser.parse_args()
# Validate paths
if not args.input_file.exists():
print(f"Error: Input file not found: {args.input_file}", file=sys.stderr)
return 1
if not args.source_root.is_dir():
print(f"Error: Source root is not a directory: {args.source_root}", file=sys.stderr)
return 1
if args.generated_root and not args.generated_root.is_dir():
print(f"Error: Generated root is not a directory: {args.generated_root}", file=sys.stderr)
return 1
try:
# Process the file
output = process_file(args.input_file, args.source_root, args.generated_root, args.doc_url)
# Write output
if args.output:
args.output.write_text(output)
else:
print(output, end='')
return 0
except Exception as e:
print(f"Error processing {args.input_file}: {e}", file=sys.stderr)
import traceback
traceback.print_exc(file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env python3
"""Generate redirects.js from template and JSON data."""
import sys
template_path, json_path, output_path = sys.argv[1:]
with open(json_path) as f:
json_content = f.read().rstrip()
with open(template_path) as f:
template = f.read()
with open(output_path, 'w') as f:
f.write(template.replace('@REDIRECTS_JSON@', json_content))

View File

@@ -5,29 +5,11 @@ project(
license : 'LGPL-2.1-or-later',
)
# Compute documentation URL based on version and release type
version = meson.project_version()
official_release = get_option('official-release')
if official_release
# For official releases, use versioned URL (dropping patch version)
version_parts = version.split('.')
major_minor = '@0@.@1@'.format(version_parts[0], version_parts[1])
doc_url = 'https://nix.dev/manual/nix/@0@'.format(major_minor)
else
# For development builds, use /latest
doc_url = 'https://nix.dev/manual/nix/latest'
endif
nix = find_program('nix', native : true)
mdbook = find_program('mdbook', native : true)
bash = find_program('bash', native : true)
# HTML manual dependencies (conditional)
if get_option('html-manual')
mdbook = find_program('mdbook', native : true)
rsync = find_program('rsync', required : true, native : true)
endif
rsync = find_program('rsync', required : true, native : true)
pymod = import('python')
python = pymod.find_installation('python3')
@@ -75,24 +57,6 @@ generate_manual_deps = files(
'generate-deps.py',
)
# Generate redirects.js from template and JSON data
redirects_js = custom_target(
'redirects.js',
command : [
python,
'@INPUT0@',
'@INPUT1@',
'@INPUT2@',
'@OUTPUT@',
],
input : [
'generate-redirects.py',
'redirects.js.in',
'redirects.json',
],
output : 'redirects.js',
)
# Generates types
subdir('source/store')
# Generates builtins.md and builtin-constants.md.
@@ -113,71 +77,66 @@ else
nix_input = []
endif
# HTML manual build (conditional)
if get_option('html-manual')
manual = custom_target(
manual = custom_target(
'manual',
command : [
bash,
'-euo',
'pipefail',
'-c',
'''
@0@ @INPUT0@ @CURRENT_SOURCE_DIR@ > @DEPFILE@
@0@ @INPUT1@ summary @2@ < @CURRENT_SOURCE_DIR@/source/SUMMARY.md.in > @2@/source/SUMMARY.md
sed -e 's|@version@|@3@|g' < @INPUT2@ > @2@/book.toml
@4@ -r -L --exclude='*.drv' --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
(cd @2@; RUST_LOG=warn @1@ build -d @2@ 3>&2 2>&1 1>&3) | { grep -Fv "because fragment resolution isn't implemented" || :; } 3>&2 2>&1 1>&3
rm -rf @2@/manual
mv @2@/html @2@/manual
# Remove Mathjax 2.7, because we will actually use MathJax 3.x
find @2@/manual | grep .html | xargs sed -i -e '/2.7.1.MathJax.js/d'
find @2@/manual -iname meson.build -delete
'''.format(
python.full_path(),
mdbook.full_path(),
meson.current_build_dir(),
meson.project_version(),
rsync.full_path(),
),
],
input : [
generate_manual_deps,
'substitute.py',
'book.toml.in',
'anchors.jq',
'custom.css',
nix3_cli_files,
experimental_features_shortlist_md,
experimental_feature_descriptions_md,
types_dir,
conf_file_md,
builtins_md,
rl_next_generated,
summary_rl_next,
json_schema_generated_files,
nix_input,
],
output : [
'manual',
command : [
bash,
'-euo',
'pipefail',
'-c',
'''
@0@ @INPUT0@ @CURRENT_SOURCE_DIR@ > @DEPFILE@
@0@ @INPUT1@ summary @2@ < @CURRENT_SOURCE_DIR@/source/SUMMARY.md.in > @2@/source/SUMMARY.md
sed -e 's|@version@|@3@|g' < @INPUT2@ > @2@/book.toml
@4@ -r -L --exclude='*.drv' --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
(cd @2@; RUST_LOG=warn @1@ build -d @2@ 3>&2 2>&1 1>&3) | { grep -Fv "because fragment resolution isn't implemented" || :; } 3>&2 2>&1 1>&3
rm -rf @2@/manual
mv @2@/html @2@/manual
# Remove Mathjax 2.7, because we will actually use MathJax 3.x
find @2@/manual | grep .html | xargs sed -i -e '/2.7.1.MathJax.js/d'
find @2@/manual -iname meson.build -delete
'''.format(
python.full_path(),
mdbook.full_path(),
meson.current_build_dir(),
meson.project_version(),
rsync.full_path(),
),
],
input : [
generate_manual_deps,
'substitute.py',
'book.toml.in',
'anchors.jq',
'custom.css',
redirects_js,
nix3_cli_files,
experimental_features_shortlist_md,
experimental_feature_descriptions_md,
types_dir,
conf_file_md,
builtins_md,
rl_next_generated,
summary_rl_next,
json_schema_generated_files,
nix_input,
],
output : [
'manual',
'markdown',
],
depfile : 'manual.d',
build_by_default : true,
env : {
'RUST_LOG' : 'info',
'MDBOOK_SUBSTITUTE_SEARCH' : meson.current_build_dir() / 'source',
},
)
manual_html = manual[0]
manual_md = manual[1]
'markdown',
],
depfile : 'manual.d',
env : {
'RUST_LOG' : 'info',
'MDBOOK_SUBSTITUTE_SEARCH' : meson.current_build_dir() / 'source',
},
)
manual_html = manual[0]
manual_md = manual[1]
install_subdir(
manual_html.full_path(),
install_dir : get_option('datadir') / 'doc/nix',
)
endif
install_subdir(
manual_html.full_path(),
install_dir : get_option('datadir') / 'doc/nix',
)
nix_nested_manpages = [
[
@@ -223,7 +182,6 @@ nix_nested_manpages = [
],
]
# Manpage generation (standalone, no mdbook dependency)
foreach command : nix_nested_manpages
foreach page : command[1]
title = command[0] + ' --' + page
@@ -231,19 +189,15 @@ foreach command : nix_nested_manpages
custom_target(
command : [
bash,
'@INPUT0@',
files('./render-manpage.sh'),
'--out-no-smarty',
title,
section,
meson.current_source_dir() / 'source',
meson.current_build_dir() / 'source',
doc_url,
meson.current_source_dir() / 'source/command-ref' / command[0] / (page + '.md'),
'@INPUT0@/command-ref' / command[0] / (page + '.md'),
'@OUTPUT0@',
],
input : [
files('./render-manpage.sh'),
files('./expand-includes.py'),
manual_md,
nix_input,
],
output : command[0] + '-' + page + '.1',
@@ -352,21 +306,14 @@ foreach page : nix3_manpages
command : [
bash,
'@INPUT0@',
# Note: no --out-no-smarty flag (original behavior)
page,
section,
meson.current_source_dir() / 'source',
meson.current_build_dir() / 'source',
doc_url,
meson.current_build_dir() / 'source/command-ref/new-cli/@0@.md'.format(
page,
),
'@INPUT1@/command-ref/new-cli/@0@.md'.format(page),
'@OUTPUT@',
],
input : [
files('./render-manpage.sh'),
files('./expand-includes.py'),
nix3_cli_files,
manual_md,
nix_input,
],
output : page + '.1',
@@ -386,12 +333,7 @@ nix_manpages = [
[ 'nix-channel', 1 ],
[ 'nix-hash', 1 ],
[ 'nix-copy-closure', 1 ],
[
'nix.conf',
5,
conf_file_md.full_path(),
[ conf_file_md, experimental_features_shortlist_md ],
],
[ 'nix.conf', 5, conf_file_md.full_path() ],
[ 'nix-daemon', 8 ],
[ 'nix-profiles', 5, 'files/profiles.md' ],
]
@@ -403,24 +345,19 @@ foreach entry : nix_manpages
# Therefore we use an optional third element of this array to override the name pattern
md_file = entry.get(2, title + '.md')
section = entry[1].to_string()
input_file = meson.current_source_dir() / 'source/command-ref' / md_file
md_file_resolved = join_paths('@INPUT1@/command-ref/', md_file)
custom_target(
command : [
bash,
'@INPUT0@',
# Note: no --out-no-smarty flag (original behavior)
title,
section,
meson.current_source_dir() / 'source',
meson.current_build_dir() / 'source',
doc_url,
input_file,
md_file_resolved,
'@OUTPUT@',
],
input : [
files('./render-manpage.sh'),
files('./expand-includes.py'),
manual_md,
entry.get(3, []),
nix_input,
],

View File

@@ -1,13 +0,0 @@
option(
'official-release',
type : 'boolean',
value : true,
description : 'Whether this is an official release build (affects documentation URLs)',
)
option(
'html-manual',
type : 'boolean',
value : true,
description : 'Whether to build the HTML manual (requires mdbook)',
)

View File

@@ -1,13 +1,12 @@
{
lib,
callPackage,
mkMesonDerivation,
runCommand,
meson,
ninja,
lowdown-unsandboxed,
mdbook,
mdbook-linkcheck,
jq,
python3,
rsync,
@@ -19,11 +18,6 @@
# Configuration Options
version,
/**
Whether to build the HTML manual.
When false, only manpages are built, avoiding the mdbook dependency.
*/
buildHtmlManual ? true,
# `tests` attribute
testers,
@@ -62,22 +56,9 @@ mkMesonDerivation (finalAttrs: {
../../doc/manual/package.nix;
# TODO the man pages should probably be separate
outputs =
if buildHtmlManual then
[
"out"
"man"
]
else
[ "out" ]; # Only one output when HTML manual is disabled; use "out" for manpages
# When HTML manual is disabled, install manpages to "out" instead of "man"
mesonFlags = [
(lib.mesonBool "official-release" officialRelease)
(lib.mesonBool "html-manual" buildHtmlManual)
]
++ lib.optionals (!buildHtmlManual) [
"--mandir=${placeholder "out"}/share/man"
outputs = [
"out"
"man"
];
nativeBuildInputs = [
@@ -85,15 +66,15 @@ mkMesonDerivation (finalAttrs: {
meson
ninja
(lib.getBin lowdown-unsandboxed)
mdbook
mdbook-linkcheck
jq
python3
]
++ lib.optionals buildHtmlManual [
mdbook
rsync
json-schema-for-humans
changelog-d
]
++ lib.optionals (!officialRelease && buildHtmlManual) [
++ lib.optionals (!officialRelease) [
# When not an official release, we likely have changelog entries that have
# yet to be rendered.
# When released, these are rendered into a committed file to save a dependency.
@@ -105,47 +86,32 @@ mkMesonDerivation (finalAttrs: {
echo ${finalAttrs.version} > ./.version
'';
postInstall = lib.optionalString buildHtmlManual ''
postInstall = ''
mkdir -p ''$out/nix-support
echo "doc manual ''$out/share/doc/nix/manual" >> ''$out/nix-support/hydra-build-products
'';
passthru = lib.optionalAttrs buildHtmlManual {
/**
The root of the HTML manual.
E.g. "${nix-manual.site}/index.html" exists.
*/
/**
The root of the HTML manual.
E.g. "${nix-manual.site}/index.html" exists.
*/
passthru.site = finalAttrs.finalPackage + "/share/doc/nix/manual";
site = finalAttrs.finalPackage + "/share/doc/nix/manual";
tests =
let
redirect-targets = callPackage ./redirect-targets-html.nix { };
in
{
# https://nixos.org/manual/nixpkgs/stable/index.html#tester-lycheeLinkCheck
linkcheck = testers.lycheeLinkCheck {
site =
let
plain = finalAttrs.finalPackage.site;
in
runCommand "nix-manual-with-redirect-targets" { } ''
cp -r ${plain} $out
chmod -R u+w $out
cp ${redirect-targets}/redirect-targets.html $out/redirect-targets.html
'';
extraConfig = {
exclude = [
# Exclude auto-generated JSON schema documentation which has
# auto-generated fragment IDs that don't match the link references
".*/protocols/json/.*\\.html"
# Exclude undocumented builtins
".*/language/builtins\\.html#builtins-addErrorContext"
".*/language/builtins\\.html#builtins-appendContext"
];
};
};
passthru.tests = {
# https://nixos.org/manual/nixpkgs/stable/index.html#tester-lycheeLinkCheck
linkcheck = testers.lycheeLinkCheck {
inherit (finalAttrs.finalPackage) site;
extraConfig = {
exclude = [
# Exclude auto-generated JSON schema documentation which has
# auto-generated fragment IDs that don't match the link references
".*/protocols/json/.*\\.html"
# Exclude undocumented builtins
".*/language/builtins\\.html#builtins-addErrorContext"
".*/language/builtins\\.html#builtins-appendContext"
];
};
};
};
meta = {

View File

@@ -1,62 +0,0 @@
# Generates redirect-targets.html containing all redirect targets for link checking.
# Used by: doc/manual/package.nix (passthru.tests.linkcheck)
{
stdenv,
lib,
jq,
}:
stdenv.mkDerivation {
name = "redirect-targets-html";
src = lib.fileset.toSource {
root = ./.;
fileset = ./redirects.json;
};
nativeBuildInputs = [ jq ];
installPhase = ''
mkdir -p $out
{
echo '<!DOCTYPE html>'
echo '<html><head><title>Nix Manual Redirect Targets</title></head><body>'
echo '<h1>Redirect Targets to Check</h1>'
echo '<p>This document contains all redirect targets from the Nix manual.</p>'
echo '<h2>Client-side redirects (from redirects.json)</h2>'
echo '<ul>'
# Extract all redirects with their source pages to properly resolve relative paths
jq -r 'to_entries[] | .key as $page | .value | to_entries[] | "\($page)\t\(.value)"' \
redirects.json | while IFS=$'\t' read -r page target; do
page_dir=$(dirname "$page")
# Handle fragment-only targets (e.g., #primitives)
if [[ "$target" == \#* ]]; then
# Fragment is on the same page
resolved="$page$target"
echo "<li><a href=\"$resolved\">$resolved</a> (fragment on $page)</li>"
continue
fi
# Resolve relative path based on the source page location
resolved="$page_dir/$target"
echo "<li><a href=\"$resolved\">$resolved</a> (from $page)</li>"
done
echo '</ul>'
echo '</body></html>'
} > $out/redirect-targets.html
echo "Generated redirect targets document with $(grep -c '<li>' $out/redirect-targets.html) links"
'';
meta = {
description = "HTML document listing all Nix manual redirect targets for link checking";
};
}

460
doc/manual/redirects.js Normal file
View File

@@ -0,0 +1,460 @@
// redirect rules for URL fragments (client-side) to prevent link rot.
// this must be done on the client side, as web servers do not see the fragment part of the URL.
// it will only work with JavaScript enabled in the browser, but this is the best we can do here.
// see source/_redirects for path redirects (server-side)
// redirects are declared as follows:
// each entry has as its key a path matching the requested URL path, relative to the mdBook document root.
//
// IMPORTANT: it must specify the full path with file name and suffix
//
// each entry is itself a set of key-value pairs, where
// - keys are anchors on the matched path.
// - values are redirection targets relative to the current path.
const redirects = {
"index.html": {
"part-advanced-topics": "advanced-topics/index.html",
"chap-tuning-cores-and-jobs": "advanced-topics/cores-vs-jobs.html",
"chap-diff-hook": "advanced-topics/diff-hook.html",
"check-dirs-are-unregistered": "advanced-topics/diff-hook.html#check-dirs-are-unregistered",
"chap-distributed-builds": "command-ref/conf-file.html#conf-builders",
"chap-post-build-hook": "advanced-topics/post-build-hook.html",
"chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats",
"chap-writing-nix-expressions": "language/index.html",
"part-command-ref": "command-ref/index.html",
"conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation",
"conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges",
"conf-allowed-uris": "command-ref/conf-file.html#conf-allowed-uris",
"conf-allowed-users": "command-ref/conf-file.html#conf-allowed-users",
"conf-auto-optimise-store": "command-ref/conf-file.html#conf-auto-optimise-store",
"conf-binary-cache-public-keys": "command-ref/conf-file.html#conf-binary-cache-public-keys",
"conf-binary-caches": "command-ref/conf-file.html#conf-binary-caches",
"conf-build-compress-log": "command-ref/conf-file.html#conf-build-compress-log",
"conf-build-cores": "command-ref/conf-file.html#conf-build-cores",
"conf-build-extra-chroot-dirs": "command-ref/conf-file.html#conf-build-extra-chroot-dirs",
"conf-build-extra-sandbox-paths": "command-ref/conf-file.html#conf-build-extra-sandbox-paths",
"conf-build-fallback": "command-ref/conf-file.html#conf-build-fallback",
"conf-build-max-jobs": "command-ref/conf-file.html#conf-build-max-jobs",
"conf-build-max-log-size": "command-ref/conf-file.html#conf-build-max-log-size",
"conf-build-max-silent-time": "command-ref/conf-file.html#conf-build-max-silent-time",
"conf-build-timeout": "command-ref/conf-file.html#conf-build-timeout",
"conf-build-use-chroot": "command-ref/conf-file.html#conf-build-use-chroot",
"conf-build-use-sandbox": "command-ref/conf-file.html#conf-build-use-sandbox",
"conf-build-use-substitutes": "command-ref/conf-file.html#conf-build-use-substitutes",
"conf-build-users-group": "command-ref/conf-file.html#conf-build-users-group",
"conf-builders": "command-ref/conf-file.html#conf-builders",
"conf-builders-use-substitutes": "command-ref/conf-file.html#conf-builders-use-substitutes",
"conf-compress-build-log": "command-ref/conf-file.html#conf-compress-build-log",
"conf-connect-timeout": "command-ref/conf-file.html#conf-connect-timeout",
"conf-cores": "command-ref/conf-file.html#conf-cores",
"conf-diff-hook": "command-ref/conf-file.html#conf-diff-hook",
"conf-env-keep-derivations": "command-ref/conf-file.html#conf-env-keep-derivations",
"conf-extra-binary-caches": "command-ref/conf-file.html#conf-extra-binary-caches",
"conf-extra-platforms": "command-ref/conf-file.html#conf-extra-platforms",
"conf-extra-sandbox-paths": "command-ref/conf-file.html#conf-extra-sandbox-paths",
"conf-extra-substituters": "command-ref/conf-file.html#conf-extra-substituters",
"conf-fallback": "command-ref/conf-file.html#conf-fallback",
"conf-fsync-metadata": "command-ref/conf-file.html#conf-fsync-metadata",
"conf-gc-keep-derivations": "command-ref/conf-file.html#conf-gc-keep-derivations",
"conf-gc-keep-outputs": "command-ref/conf-file.html#conf-gc-keep-outputs",
"conf-hashed-mirrors": "command-ref/conf-file.html#conf-hashed-mirrors",
"conf-http-connections": "command-ref/conf-file.html#conf-http-connections",
"conf-keep-build-log": "command-ref/conf-file.html#conf-keep-build-log",
"conf-keep-derivations": "command-ref/conf-file.html#conf-keep-derivations",
"conf-keep-env-derivations": "command-ref/conf-file.html#conf-keep-env-derivations",
"conf-keep-outputs": "command-ref/conf-file.html#conf-keep-outputs",
"conf-max-build-log-size": "command-ref/conf-file.html#conf-max-build-log-size",
"conf-max-free": "command-ref/conf-file.html#conf-max-free",
"conf-max-jobs": "command-ref/conf-file.html#conf-max-jobs",
"conf-max-silent-time": "command-ref/conf-file.html#conf-max-silent-time",
"conf-min-free": "command-ref/conf-file.html#conf-min-free",
"conf-narinfo-cache-negative-ttl": "command-ref/conf-file.html#conf-narinfo-cache-negative-ttl",
"conf-narinfo-cache-positive-ttl": "command-ref/conf-file.html#conf-narinfo-cache-positive-ttl",
"conf-netrc-file": "command-ref/conf-file.html#conf-netrc-file",
"conf-plugin-files": "command-ref/conf-file.html#conf-plugin-files",
"conf-post-build-hook": "command-ref/conf-file.html#conf-post-build-hook",
"conf-pre-build-hook": "command-ref/conf-file.html#conf-pre-build-hook",
"conf-require-sigs": "command-ref/conf-file.html#conf-require-sigs",
"conf-restrict-eval": "command-ref/conf-file.html#conf-restrict-eval",
"conf-run-diff-hook": "command-ref/conf-file.html#conf-run-diff-hook",
"conf-sandbox": "command-ref/conf-file.html#conf-sandbox",
"conf-sandbox-dev-shm-size": "command-ref/conf-file.html#conf-sandbox-dev-shm-size",
"conf-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
"conf-secret-key-files": "command-ref/conf-file.html#conf-secret-key-files",
"conf-show-trace": "command-ref/conf-file.html#conf-show-trace",
"conf-stalled-download-timeout": "command-ref/conf-file.html#conf-stalled-download-timeout",
"conf-substitute": "command-ref/conf-file.html#conf-substitute",
"conf-substituters": "command-ref/conf-file.html#conf-substituters",
"conf-system": "command-ref/conf-file.html#conf-system",
"conf-system-features": "command-ref/conf-file.html#conf-system-features",
"conf-tarball-ttl": "command-ref/conf-file.html#conf-tarball-ttl",
"conf-timeout": "command-ref/conf-file.html#conf-timeout",
"conf-trace-function-calls": "command-ref/conf-file.html#conf-trace-function-calls",
"conf-trusted-binary-caches": "command-ref/conf-file.html#conf-trusted-binary-caches",
"conf-trusted-public-keys": "command-ref/conf-file.html#conf-trusted-public-keys",
"conf-trusted-substituters": "command-ref/conf-file.html#conf-trusted-substituters",
"conf-trusted-users": "command-ref/conf-file.html#conf-trusted-users",
"extra-sandbox-paths": "command-ref/conf-file.html#extra-sandbox-paths",
"sec-conf-file": "command-ref/conf-file.html",
"env-NIX_PATH": "command-ref/env-common.html#env-NIX_PATH",
"env-common": "command-ref/env-common.html",
"envar-remote": "command-ref/env-common.html#env-NIX_REMOTE",
"sec-common-env": "command-ref/env-common.html",
"ch-files": "command-ref/files.html",
"ch-main-commands": "command-ref/main-commands.html",
"opt-out-link": "command-ref/nix-build.html#opt-out-link",
"sec-nix-build": "command-ref/nix-build.html",
"sec-nix-channel": "command-ref/nix-channel.html",
"sec-nix-collect-garbage": "command-ref/nix-collect-garbage.html",
"sec-nix-copy-closure": "command-ref/nix-copy-closure.html",
"sec-nix-daemon": "command-ref/nix-daemon.html",
"refsec-nix-env-install-examples": "command-ref/nix-env.html#examples",
"rsec-nix-env-install": "command-ref/nix-env.html#operation---install",
"rsec-nix-env-set": "command-ref/nix-env.html#operation---set",
"rsec-nix-env-set-flag": "command-ref/nix-env.html#operation---set-flag",
"rsec-nix-env-upgrade": "command-ref/nix-env.html#operation---upgrade",
"sec-nix-env": "command-ref/nix-env.html",
"ssec-version-comparisons": "command-ref/nix-env.html#versions",
"sec-nix-hash": "command-ref/nix-hash.html",
"sec-nix-instantiate": "command-ref/nix-instantiate.html",
"sec-nix-prefetch-url": "command-ref/nix-prefetch-url.html",
"sec-nix-shell": "command-ref/nix-shell.html",
"ssec-nix-shell-shebang": "command-ref/nix-shell.html#use-as-a--interpreter",
"nixref-queries": "command-ref/nix-store.html#queries",
"opt-add-root": "command-ref/nix-store.html#opt-add-root",
"refsec-nix-store-dump": "command-ref/nix-store.html#operation---dump",
"refsec-nix-store-export": "command-ref/nix-store.html#operation---export",
"refsec-nix-store-import": "command-ref/nix-store.html#operation---import",
"refsec-nix-store-query": "command-ref/nix-store.html#operation---query",
"refsec-nix-store-verify": "command-ref/nix-store.html#operation---verify",
"rsec-nix-store-gc": "command-ref/nix-store.html#operation---gc",
"rsec-nix-store-generate-binary-cache-key": "command-ref/nix-store.html#operation---generate-binary-cache-key",
"rsec-nix-store-realise": "command-ref/nix-store.html#operation---realise",
"rsec-nix-store-serve": "command-ref/nix-store.html#operation---serve",
"sec-nix-store": "command-ref/nix-store.html",
"opt-I": "command-ref/opt-common.html#opt-I",
"opt-attr": "command-ref/opt-common.html#opt-attr",
"opt-common": "command-ref/opt-common.html",
"opt-cores": "command-ref/opt-common.html#opt-cores",
"opt-log-format": "command-ref/opt-common.html#opt-log-format",
"opt-max-jobs": "command-ref/opt-common.html#opt-max-jobs",
"opt-max-silent-time": "command-ref/opt-common.html#opt-max-silent-time",
"opt-timeout": "command-ref/opt-common.html#opt-timeout",
"sec-common-options": "command-ref/opt-common.html",
"ch-utilities": "command-ref/utilities.html",
"chap-hacking": "development/building.html",
"adv-attr-allowSubstitutes": "language/advanced-attributes.html#adv-attr-allowSubstitutes",
"adv-attr-allowedReferences": "language/advanced-attributes.html#adv-attr-allowedReferences",
"adv-attr-allowedRequisites": "language/advanced-attributes.html#adv-attr-allowedRequisites",
"adv-attr-disallowedReferences": "language/advanced-attributes.html#adv-attr-disallowedReferences",
"adv-attr-disallowedRequisites": "language/advanced-attributes.html#adv-attr-disallowedRequisites",
"adv-attr-exportReferencesGraph": "language/advanced-attributes.html#adv-attr-exportReferencesGraph",
"adv-attr-impureEnvVars": "language/advanced-attributes.html#adv-attr-impureEnvVars",
"adv-attr-outputHash": "language/advanced-attributes.html#adv-attr-outputHash",
"adv-attr-outputHashAlgo": "language/advanced-attributes.html#adv-attr-outputHashAlgo",
"adv-attr-outputHashMode": "language/advanced-attributes.html#adv-attr-outputHashMode",
"adv-attr-passAsFile": "language/advanced-attributes.html#adv-attr-passAsFile",
"adv-attr-preferLocalBuild": "language/advanced-attributes.html#adv-attr-preferLocalBuild",
"fixed-output-drvs": "language/advanced-attributes.html#adv-attr-outputHash",
"sec-advanced-attributes": "language/advanced-attributes.html",
"builtin-abort": "language/builtins.html#builtins-abort",
"builtin-add": "language/builtins.html#builtins-add",
"builtin-all": "language/builtins.html#builtins-all",
"builtin-any": "language/builtins.html#builtins-any",
"builtin-attrNames": "language/builtins.html#builtins-attrNames",
"builtin-attrValues": "language/builtins.html#builtins-attrValues",
"builtin-baseNameOf": "language/builtins.html#builtins-baseNameOf",
"builtin-bitAnd": "language/builtins.html#builtins-bitAnd",
"builtin-bitOr": "language/builtins.html#builtins-bitOr",
"builtin-bitXor": "language/builtins.html#builtins-bitXor",
"builtin-builtins": "language/builtins.html#builtins-builtins",
"builtin-compareVersions": "language/builtins.html#builtins-compareVersions",
"builtin-concatLists": "language/builtins.html#builtins-concatLists",
"builtin-concatStringsSep": "language/builtins.html#builtins-concatStringsSep",
"builtin-currentSystem": "language/builtins.html#builtins-currentSystem",
"builtin-deepSeq": "language/builtins.html#builtins-deepSeq",
"builtin-derivation": "language/builtins.html#builtins-derivation",
"builtin-dirOf": "language/builtins.html#builtins-dirOf",
"builtin-div": "language/builtins.html#builtins-div",
"builtin-elem": "language/builtins.html#builtins-elem",
"builtin-elemAt": "language/builtins.html#builtins-elemAt",
"builtin-fetchGit": "language/builtins.html#builtins-fetchGit",
"builtin-fetchTarball": "language/builtins.html#builtins-fetchTarball",
"builtin-fetchurl": "language/builtins.html#builtins-fetchurl",
"builtin-filterSource": "language/builtins.html#builtins-filterSource",
"builtin-foldl-prime": "language/builtins.html#builtins-foldl-prime",
"builtin-fromJSON": "language/builtins.html#builtins-fromJSON",
"builtin-functionArgs": "language/builtins.html#builtins-functionArgs",
"builtin-genList": "language/builtins.html#builtins-genList",
"builtin-getAttr": "language/builtins.html#builtins-getAttr",
"builtin-getEnv": "language/builtins.html#builtins-getEnv",
"builtin-hasAttr": "language/builtins.html#builtins-hasAttr",
"builtin-hashFile": "language/builtins.html#builtins-hashFile",
"builtin-hashString": "language/builtins.html#builtins-hashString",
"builtin-head": "language/builtins.html#builtins-head",
"builtin-import": "language/builtins.html#builtins-import",
"builtin-intersectAttrs": "language/builtins.html#builtins-intersectAttrs",
"builtin-isAttrs": "language/builtins.html#builtins-isAttrs",
"builtin-isBool": "language/builtins.html#builtins-isBool",
"builtin-isFloat": "language/builtins.html#builtins-isFloat",
"builtin-isFunction": "language/builtins.html#builtins-isFunction",
"builtin-isInt": "language/builtins.html#builtins-isInt",
"builtin-isList": "language/builtins.html#builtins-isList",
"builtin-isNull": "language/builtins.html#builtins-isNull",
"builtin-isString": "language/builtins.html#builtins-isString",
"builtin-length": "language/builtins.html#builtins-length",
"builtin-lessThan": "language/builtins.html#builtins-lessThan",
"builtin-listToAttrs": "language/builtins.html#builtins-listToAttrs",
"builtin-map": "language/builtins.html#builtins-map",
"builtin-match": "language/builtins.html#builtins-match",
"builtin-mul": "language/builtins.html#builtins-mul",
"builtin-parseDrvName": "language/builtins.html#builtins-parseDrvName",
"builtin-path": "language/builtins.html#builtins-path",
"builtin-pathExists": "language/builtins.html#builtins-pathExists",
"builtin-placeholder": "language/builtins.html#builtins-placeholder",
"builtin-readDir": "language/builtins.html#builtins-readDir",
"builtin-readFile": "language/builtins.html#builtins-readFile",
"builtin-removeAttrs": "language/builtins.html#builtins-removeAttrs",
"builtin-replaceStrings": "language/builtins.html#builtins-replaceStrings",
"builtin-seq": "language/builtins.html#builtins-seq",
"builtin-sort": "language/builtins.html#builtins-sort",
"builtin-split": "language/builtins.html#builtins-split",
"builtin-splitVersion": "language/builtins.html#builtins-splitVersion",
"builtin-stringLength": "language/builtins.html#builtins-stringLength",
"builtin-sub": "language/builtins.html#builtins-sub",
"builtin-substring": "language/builtins.html#builtins-substring",
"builtin-tail": "language/builtins.html#builtins-tail",
"builtin-throw": "language/builtins.html#builtins-throw",
"builtin-toFile": "language/builtins.html#builtins-toFile",
"builtin-toJSON": "language/builtins.html#builtins-toJSON",
"builtin-toPath": "language/builtins.html#builtins-toPath",
"builtin-toString": "language/builtins.html#builtins-toString",
"builtin-toXML": "language/builtins.html#builtins-toXML",
"builtin-trace": "language/builtins.html#builtins-trace",
"builtin-tryEval": "language/builtins.html#builtins-tryEval",
"builtin-typeOf": "language/builtins.html#builtins-typeOf",
"ssec-builtins": "language/builtins.html",
"attr-system": "language/derivations.html#attr-system",
"ssec-derivation": "language/derivations.html",
"ch-expression-language": "language/index.html",
"sec-constructs": "language/syntax.html",
"sect-let-language": "language/syntax.html#let-expressions",
"ss-functions": "language/syntax.html#functions",
"sec-language-operators": "language/operators.html",
"table-operators": "language/operators.html",
"ssec-values": "language/types.html",
"gloss-closure": "glossary.html#gloss-closure",
"gloss-derivation": "glossary.html#gloss-derivation",
"gloss-deriver": "glossary.html#gloss-deriver",
"gloss-nar": "glossary.html#gloss-nar",
"gloss-output-path": "glossary.html#gloss-output-path",
"gloss-profile": "glossary.html#gloss-profile",
"gloss-reachable": "glossary.html#gloss-reachable",
"gloss-reference": "glossary.html#gloss-reference",
"gloss-substitute": "glossary.html#gloss-substitute",
"gloss-user-env": "glossary.html#gloss-user-env",
"gloss-validity": "glossary.html#gloss-validity",
"part-glossary": "glossary.html",
"sec-building-source": "installation/building-source.html",
"ch-env-variables": "installation/env-variables.html",
"sec-installer-proxy-settings": "installation/env-variables.html#proxy-environment-variables",
"sec-nix-ssl-cert-file": "installation/env-variables.html#nix_ssl_cert_file",
"sec-nix-ssl-cert-file-with-nix-daemon-and-macos": "installation/env-variables.html#nix_ssl_cert_file-with-macos-and-the-nix-daemon",
"chap-installation": "installation/index.html",
"ch-installing-binary": "installation/installing-binary.html",
"sect-macos-installation": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-change-store-prefix": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-encrypted-volume": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-recommended-notes": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-symlink": "installation/installing-binary.html#macos-installation",
"sect-multi-user-installation": "installation/installing-binary.html#multi-user-installation",
"sect-nix-install-binary-tarball": "installation/installing-binary.html#installing-from-a-binary-tarball",
"sect-nix-install-pinned-version-url": "installation/installing-binary.html#installing-a-pinned-nix-version-from-a-url",
"sect-single-user-installation": "installation/installing-binary.html#single-user-installation",
"ch-installing-source": "installation/installing-source.html",
"ssec-multi-user": "installation/multi-user.html",
"ch-nix-security": "installation/nix-security.html",
"sec-obtaining-source": "installation/obtaining-source.html",
"sec-prerequisites-source": "installation/prerequisites-source.html",
"sec-single-user": "installation/single-user.html",
"ch-supported-platforms": "installation/supported-platforms.html",
"ch-upgrading-nix": "installation/upgrading.html",
"ch-about-nix": "introduction.html",
"chap-introduction": "introduction.html",
"ch-basic-package-mgmt": "package-management/basic-package-mgmt.html",
"ssec-binary-cache-substituter": "package-management/binary-cache-substituter.html",
"sec-channels": "command-ref/nix-channel.html",
"ssec-copy-closure": "command-ref/nix-copy-closure.html",
"sec-garbage-collection": "package-management/garbage-collection.html",
"ssec-gc-roots": "package-management/garbage-collector-roots.html",
"chap-package-management": "package-management/index.html",
"sec-profiles": "package-management/profiles.html",
"ssec-s3-substituter": "store/types/s3-substituter.html",
"ssec-s3-substituter-anonymous-reads": "store/types/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache",
"ssec-s3-substituter-authenticated-reads": "store/types/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache",
"ssec-s3-substituter-authenticated-writes": "store/types/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache",
"sec-sharing-packages": "package-management/sharing-packages.html",
"ssec-ssh-substituter": "package-management/ssh-substituter.html",
"chap-quick-start": "quick-start.html",
"sec-relnotes": "release-notes/index.html",
"ch-relnotes-0.10.1": "release-notes/rl-0.10.1.html",
"ch-relnotes-0.10": "release-notes/rl-0.10.html",
"ssec-relnotes-0.11": "release-notes/rl-0.11.html",
"ssec-relnotes-0.12": "release-notes/rl-0.12.html",
"ssec-relnotes-0.13": "release-notes/rl-0.13.html",
"ssec-relnotes-0.14": "release-notes/rl-0.14.html",
"ssec-relnotes-0.15": "release-notes/rl-0.15.html",
"ssec-relnotes-0.16": "release-notes/rl-0.16.html",
"ch-relnotes-0.5": "release-notes/rl-0.5.html",
"ch-relnotes-0.6": "release-notes/rl-0.6.html",
"ch-relnotes-0.7": "release-notes/rl-0.7.html",
"ch-relnotes-0.8.1": "release-notes/rl-0.8.1.html",
"ch-relnotes-0.8": "release-notes/rl-0.8.html",
"ch-relnotes-0.9.1": "release-notes/rl-0.9.1.html",
"ch-relnotes-0.9.2": "release-notes/rl-0.9.2.html",
"ch-relnotes-0.9": "release-notes/rl-0.9.html",
"ssec-relnotes-1.0": "release-notes/rl-1.0.html",
"ssec-relnotes-1.1": "release-notes/rl-1.1.html",
"ssec-relnotes-1.10": "release-notes/rl-1.10.html",
"ssec-relnotes-1.11.10": "release-notes/rl-1.11.10.html",
"ssec-relnotes-1.11": "release-notes/rl-1.11.html",
"ssec-relnotes-1.2": "release-notes/rl-1.2.html",
"ssec-relnotes-1.3": "release-notes/rl-1.3.html",
"ssec-relnotes-1.4": "release-notes/rl-1.4.html",
"ssec-relnotes-1.5.1": "release-notes/rl-1.5.1.html",
"ssec-relnotes-1.5.2": "release-notes/rl-1.5.2.html",
"ssec-relnotes-1.5": "release-notes/rl-1.5.html",
"ssec-relnotes-1.6.1": "release-notes/rl-1.6.1.html",
"ssec-relnotes-1.6.0": "release-notes/rl-1.6.html",
"ssec-relnotes-1.7": "release-notes/rl-1.7.html",
"ssec-relnotes-1.8": "release-notes/rl-1.8.html",
"ssec-relnotes-1.9": "release-notes/rl-1.9.html",
"ssec-relnotes-2.0": "release-notes/rl-2.0.html",
"ssec-relnotes-2.1": "release-notes/rl-2.1.html",
"ssec-relnotes-2.2": "release-notes/rl-2.2.html",
"ssec-relnotes-2.3": "release-notes/rl-2.3.html",
},
"language/types.html": {
"simple-values": "#primitives",
"lists": "#list",
"strings": "#string",
"attribute-sets": "#attribute-set",
"type-number": "#type-int",
},
"language/syntax.html": {
"scoping-rules": "scoping.html",
"string-literal": "string-literals.html",
},
"language/derivations.md": {
"builder-execution": "store/drv/building.md#builder-execution",
},
"installation/installing-binary.html": {
"linux": "uninstall.html#linux",
"macos": "uninstall.html#macos",
"uninstalling": "uninstall.html",
},
"development/building.html": {
"nix-with-flakes": "#building-nix-with-flakes",
"classic-nix": "#building-nix",
"running-tests": "testing.html#running-tests",
"unit-tests": "testing.html#unit-tests",
"functional-tests": "testing.html#functional-tests",
"debugging-failing-functional-tests": "testing.html#debugging-failing-functional-tests",
"integration-tests": "testing.html#integration-tests",
"installer-tests": "testing.html#installer-tests",
"one-time-setup": "testing.html#one-time-setup",
"using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing",
"characterization-testing": "testing.html#characterisation-testing-unit",
"add-a-release-note": "contributing.html#add-a-release-note",
"add-an-entry": "contributing.html#add-an-entry",
"build-process": "contributing.html#build-process",
"reverting": "contributing.html#reverting",
"branches": "contributing.html#branches",
},
"glossary.html": {
"gloss-local-store": "store/types/local-store.html",
"package-attribute-set": "#package",
"gloss-chroot-store": "store/types/local-store.html",
"gloss-content-addressed-derivation": "#gloss-content-addressing-derivation",
},
};
// the following code matches the current page's URL against the set of redirects.
//
// it is written to minimize the latency between page load and redirect.
// therefore we avoid function calls, copying data, and unnecessary loops.
// IMPORTANT: we use stateful array operations and their order matters!
//
// matching URLs is more involved than it should be:
//
// 1. `document.location.pathname` can have an arbitrary prefix.
//
// 2. `path_to_root` is set by mdBook. it consists only of `../`s and
// determines the depth of `<path>` relative to the prefix:
//
// `document.location.pathname`
// |------------------------------|
// /<prefix>/<path>/[<file>[.html]][#<anchor>]
// |----|
// `path_to_root` has same number of path segments
//
// source: https://phaiax.github.io/mdBook/format/theme/index-hbs.html#data
//
// 3. the following paths are equivalent:
//
// /foo/bar/
// /foo/bar/index.html
// /foo/bar/index
//
// 4. the following paths are also equivalent:
//
// /foo/bar/baz
// /foo/bar/baz.html
//
let segments = document.location.pathname.split('/');
let file = segments.pop();
// normalize file name
if (file === '') { file = "index.html"; }
else if (!file.endsWith('.html')) { file = file + '.html'; }
segments.push(file);
// use `path_to_root` to discern prefix from path.
const depth = path_to_root.split('/').length;
// remove segments containing prefix. the following works because
// 1. the original `document.location.pathname` is absolute,
// hence first element of `segments` is always empty.
// 2. last element of splitting `path_to_root` is also always empty.
// 3. last element of `segments` is the file name.
//
// visual example:
//
// '/foo/bar/baz.html'.split('/') -> [ '', 'foo', 'bar', 'baz.html' ]
// '../'.split('/') -> [ '..', '' ]
//
// the following operations will then result in
//
// path = 'bar/baz.html'
//
segments.splice(0, segments.length - depth);
const path = segments.join('/');
// anchor starts with the hash character (`#`),
// but our redirect declarations don't, so we strip it.
// example:
// document.location.hash -> '#foo'
// document.location.hash.substring(1) -> 'foo'
const anchor = document.location.hash.substring(1);
const redirect = redirects[path];
if (redirect) {
const target = redirect[anchor];
if (target) {
document.location.href = target;
}
}

View File

@@ -1,94 +0,0 @@
// redirect rules for URL fragments (client-side) to prevent link rot.
// this must be done on the client side, as web servers do not see the fragment part of the URL.
// it will only work with JavaScript enabled in the browser, but this is the best we can do here.
// see source/_redirects for path redirects (server-side)
// redirects are declared as follows:
// each entry has as its key a path matching the requested URL path, relative to the mdBook document root.
//
// IMPORTANT: it must specify the full path with file name and suffix
//
// each entry is itself a set of key-value pairs, where
// - keys are anchors on the matched path.
// - values are redirection targets relative to the current path.
const redirects = @REDIRECTS_JSON@;
// the following code matches the current page's URL against the set of redirects.
//
// it is written to minimize the latency between page load and redirect.
// therefore we avoid function calls, copying data, and unnecessary loops.
// IMPORTANT: we use stateful array operations and their order matters!
//
// matching URLs is more involved than it should be:
//
// 1. `document.location.pathname` can have an arbitrary prefix.
//
// 2. `path_to_root` is set by mdBook. it consists only of `../`s and
// determines the depth of `<path>` relative to the prefix:
//
// `document.location.pathname`
// |------------------------------|
// /<prefix>/<path>/[<file>[.html]][#<anchor>]
// |----|
// `path_to_root` has same number of path segments
//
// source: https://phaiax.github.io/mdBook/format/theme/index-hbs.html#data
//
// 3. the following paths are equivalent:
//
// /foo/bar/
// /foo/bar/index.html
// /foo/bar/index
//
// 4. the following paths are also equivalent:
//
// /foo/bar/baz
// /foo/bar/baz.html
//
let segments = document.location.pathname.split('/');
let file = segments.pop();
// normalize file name
if (file === '') { file = "index.html"; }
else if (!file.endsWith('.html')) { file = file + '.html'; }
segments.push(file);
// use `path_to_root` to discern prefix from path.
const depth = path_to_root.split('/').length;
// remove segments containing prefix. the following works because
// 1. the original `document.location.pathname` is absolute,
// hence first element of `segments` is always empty.
// 2. last element of splitting `path_to_root` is also always empty.
// 3. last element of `segments` is the file name.
//
// visual example:
//
// '/foo/bar/baz.html'.split('/') -> [ '', 'foo', 'bar', 'baz.html' ]
// '../'.split('/') -> [ '..', '' ]
//
// the following operations will then result in
//
// path = 'bar/baz.html'
//
segments.splice(0, segments.length - depth);
const path = segments.join('/');
// anchor starts with the hash character (`#`),
// but our redirect declarations don't, so we strip it.
// example:
// document.location.hash -> '#foo'
// document.location.hash.substring(1) -> 'foo'
const anchor = document.location.hash.substring(1);
const redirect = redirects[path];
if (redirect) {
const target = redirect[anchor];
if (target) {
document.location.href = target;
}
}

View File

@@ -1,372 +0,0 @@
{
"index.html": {
"part-advanced-topics": "advanced-topics/index.html",
"chap-tuning-cores-and-jobs": "advanced-topics/cores-vs-jobs.html",
"chap-diff-hook": "advanced-topics/diff-hook.html",
"check-dirs-are-unregistered": "advanced-topics/diff-hook.html#check-dirs-are-unregistered",
"chap-distributed-builds": "command-ref/conf-file.html#conf-builders",
"chap-post-build-hook": "advanced-topics/post-build-hook.html",
"chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats",
"chap-writing-nix-expressions": "language/index.html",
"part-command-ref": "command-ref/index.html",
"conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation",
"conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges",
"conf-allowed-uris": "command-ref/conf-file.html#conf-allowed-uris",
"conf-allowed-users": "command-ref/conf-file.html#conf-allowed-users",
"conf-auto-optimise-store": "command-ref/conf-file.html#conf-auto-optimise-store",
"conf-binary-cache-public-keys": "command-ref/conf-file.html#conf-trusted-public-keys",
"conf-binary-caches": "command-ref/conf-file.html#conf-substituters",
"conf-build-compress-log": "command-ref/conf-file.html#conf-compress-build-log",
"conf-build-cores": "command-ref/conf-file.html#conf-cores",
"conf-build-extra-chroot-dirs": "command-ref/conf-file.html#conf-sandbox-paths",
"conf-build-extra-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
"conf-build-fallback": "command-ref/conf-file.html#conf-fallback",
"conf-build-max-jobs": "command-ref/conf-file.html#conf-max-jobs",
"conf-build-max-log-size": "command-ref/conf-file.html#conf-max-build-log-size",
"conf-build-max-silent-time": "command-ref/conf-file.html#conf-max-silent-time",
"conf-build-timeout": "command-ref/conf-file.html#conf-timeout",
"conf-build-use-chroot": "command-ref/conf-file.html#conf-sandbox",
"conf-build-use-sandbox": "command-ref/conf-file.html#conf-sandbox",
"conf-build-use-substitutes": "command-ref/conf-file.html#conf-substitute",
"conf-build-users-group": "command-ref/conf-file.html#conf-build-users-group",
"conf-builders": "command-ref/conf-file.html#conf-builders",
"conf-builders-use-substitutes": "command-ref/conf-file.html#conf-builders-use-substitutes",
"conf-compress-build-log": "command-ref/conf-file.html#conf-compress-build-log",
"conf-connect-timeout": "command-ref/conf-file.html#conf-connect-timeout",
"conf-cores": "command-ref/conf-file.html#conf-cores",
"conf-diff-hook": "command-ref/conf-file.html#conf-diff-hook",
"conf-env-keep-derivations": "command-ref/conf-file.html#conf-keep-env-derivations",
"conf-extra-binary-caches": "command-ref/conf-file.html#conf-substituters",
"conf-extra-platforms": "command-ref/conf-file.html#conf-extra-platforms",
"conf-extra-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
"conf-extra-substituters": "command-ref/conf-file.html#conf-substituters",
"conf-fallback": "command-ref/conf-file.html#conf-fallback",
"conf-fsync-metadata": "command-ref/conf-file.html#conf-fsync-metadata",
"conf-gc-keep-derivations": "command-ref/conf-file.html#conf-keep-derivations",
"conf-gc-keep-outputs": "command-ref/conf-file.html#conf-keep-outputs",
"conf-hashed-mirrors": "command-ref/conf-file.html#conf-hashed-mirrors",
"conf-http-connections": "command-ref/conf-file.html#conf-http-connections",
"conf-keep-build-log": "command-ref/conf-file.html#conf-keep-build-log",
"conf-keep-derivations": "command-ref/conf-file.html#conf-keep-derivations",
"conf-keep-env-derivations": "command-ref/conf-file.html#conf-keep-env-derivations",
"conf-keep-outputs": "command-ref/conf-file.html#conf-keep-outputs",
"conf-max-build-log-size": "command-ref/conf-file.html#conf-max-build-log-size",
"conf-max-free": "command-ref/conf-file.html#conf-max-free",
"conf-max-jobs": "command-ref/conf-file.html#conf-max-jobs",
"conf-max-silent-time": "command-ref/conf-file.html#conf-max-silent-time",
"conf-min-free": "command-ref/conf-file.html#conf-min-free",
"conf-narinfo-cache-negative-ttl": "command-ref/conf-file.html#conf-narinfo-cache-negative-ttl",
"conf-narinfo-cache-positive-ttl": "command-ref/conf-file.html#conf-narinfo-cache-positive-ttl",
"conf-netrc-file": "command-ref/conf-file.html#conf-netrc-file",
"conf-plugin-files": "command-ref/conf-file.html#conf-plugin-files",
"conf-post-build-hook": "command-ref/conf-file.html#conf-post-build-hook",
"conf-pre-build-hook": "command-ref/conf-file.html#conf-pre-build-hook",
"conf-require-sigs": "command-ref/conf-file.html#conf-require-sigs",
"conf-restrict-eval": "command-ref/conf-file.html#conf-restrict-eval",
"conf-run-diff-hook": "command-ref/conf-file.html#conf-run-diff-hook",
"conf-sandbox": "command-ref/conf-file.html#conf-sandbox",
"conf-sandbox-dev-shm-size": "command-ref/conf-file.html#conf-sandbox-dev-shm-size",
"conf-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
"conf-secret-key-files": "command-ref/conf-file.html#conf-secret-key-files",
"conf-show-trace": "command-ref/conf-file.html#conf-show-trace",
"conf-stalled-download-timeout": "command-ref/conf-file.html#conf-stalled-download-timeout",
"conf-substitute": "command-ref/conf-file.html#conf-substitute",
"conf-substituters": "command-ref/conf-file.html#conf-substituters",
"conf-system": "command-ref/conf-file.html#conf-system",
"conf-system-features": "command-ref/conf-file.html#conf-system-features",
"conf-tarball-ttl": "command-ref/conf-file.html#conf-tarball-ttl",
"conf-timeout": "command-ref/conf-file.html#conf-timeout",
"conf-trace-function-calls": "command-ref/conf-file.html#conf-trace-function-calls",
"conf-trusted-binary-caches": "command-ref/conf-file.html#conf-trusted-substituters",
"conf-trusted-public-keys": "command-ref/conf-file.html#conf-trusted-public-keys",
"conf-trusted-substituters": "command-ref/conf-file.html#conf-trusted-substituters",
"conf-trusted-users": "command-ref/conf-file.html#conf-trusted-users",
"extra-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
"sec-conf-file": "command-ref/conf-file.html",
"env-NIX_PATH": "command-ref/env-common.html#env-NIX_PATH",
"env-common": "command-ref/env-common.html",
"envar-remote": "command-ref/env-common.html#env-NIX_REMOTE",
"sec-common-env": "command-ref/env-common.html",
"ch-files": "command-ref/files.html",
"ch-main-commands": "command-ref/main-commands.html",
"opt-out-link": "command-ref/nix-build.html#opt-out-link",
"sec-nix-build": "command-ref/nix-build.html",
"sec-nix-channel": "command-ref/nix-channel.html",
"sec-nix-collect-garbage": "command-ref/nix-collect-garbage.html",
"sec-nix-copy-closure": "command-ref/nix-copy-closure.html",
"sec-nix-daemon": "command-ref/nix-daemon.html",
"refsec-nix-env-install-examples": "command-ref/nix-env/install.html#examples",
"rsec-nix-env-install": "command-ref/nix-env/install.html",
"rsec-nix-env-set": "command-ref/nix-env/set.html",
"rsec-nix-env-set-flag": "command-ref/nix-env/set-flag.html",
"rsec-nix-env-upgrade": "command-ref/nix-env/upgrade.html",
"sec-nix-env": "command-ref/nix-env.html",
"ssec-version-comparisons": "command-ref/nix-env.html#selectors",
"sec-nix-hash": "command-ref/nix-hash.html",
"sec-nix-instantiate": "command-ref/nix-instantiate.html",
"sec-nix-prefetch-url": "command-ref/nix-prefetch-url.html",
"sec-nix-shell": "command-ref/nix-shell.html",
"ssec-nix-shell-shebang": "command-ref/nix-shell.html#use-as-a--interpreter",
"nixref-queries": "command-ref/nix-store/query.html#queries",
"opt-add-root": "command-ref/nix-store/query.html#opt-add-root",
"refsec-nix-store-dump": "command-ref/nix-store/dump.html",
"refsec-nix-store-export": "command-ref/nix-store/export.html",
"refsec-nix-store-import": "command-ref/nix-store/import.html",
"refsec-nix-store-query": "command-ref/nix-store/query.html",
"refsec-nix-store-verify": "command-ref/nix-store/verify.html",
"rsec-nix-store-gc": "command-ref/nix-store/gc.html",
"rsec-nix-store-generate-binary-cache-key": "command-ref/nix-store/generate-binary-cache-key.html",
"rsec-nix-store-realise": "command-ref/nix-store/realise.html",
"rsec-nix-store-serve": "command-ref/nix-store/serve.html",
"sec-nix-store": "command-ref/nix-store.html",
"opt-I": "command-ref/opt-common.html#opt-I",
"opt-attr": "command-ref/opt-common.html#opt-attr",
"opt-common": "command-ref/opt-common.html",
"opt-cores": "command-ref/opt-common.html#opt-cores",
"opt-log-format": "command-ref/opt-common.html#opt-log-format",
"opt-max-jobs": "command-ref/opt-common.html#opt-max-jobs",
"opt-max-silent-time": "command-ref/opt-common.html#opt-max-silent-time",
"opt-timeout": "command-ref/opt-common.html#opt-timeout",
"sec-common-options": "command-ref/opt-common.html",
"ch-utilities": "command-ref/utilities.html",
"chap-hacking": "development/building.html",
"adv-attr-allowSubstitutes": "language/advanced-attributes.html#adv-attr-allowSubstitutes",
"adv-attr-allowedReferences": "language/advanced-attributes.html#adv-attr-allowedReferences",
"adv-attr-allowedRequisites": "language/advanced-attributes.html#adv-attr-allowedRequisites",
"adv-attr-disallowedReferences": "language/advanced-attributes.html#adv-attr-disallowedReferences",
"adv-attr-disallowedRequisites": "language/advanced-attributes.html#adv-attr-disallowedRequisites",
"adv-attr-exportReferencesGraph": "language/advanced-attributes.html#adv-attr-exportReferencesGraph",
"adv-attr-impureEnvVars": "language/advanced-attributes.html#adv-attr-impureEnvVars",
"adv-attr-outputHash": "language/advanced-attributes.html#adv-attr-outputHash",
"adv-attr-outputHashAlgo": "language/advanced-attributes.html#adv-attr-outputHashAlgo",
"adv-attr-outputHashMode": "language/advanced-attributes.html#adv-attr-outputHashMode",
"adv-attr-passAsFile": "language/advanced-attributes.html#adv-attr-passAsFile",
"adv-attr-preferLocalBuild": "language/advanced-attributes.html#adv-attr-preferLocalBuild",
"fixed-output-drvs": "language/advanced-attributes.html#adv-attr-outputHash",
"sec-advanced-attributes": "language/advanced-attributes.html",
"builtin-abort": "language/builtins.html#builtins-abort",
"builtin-add": "language/builtins.html#builtins-add",
"builtin-all": "language/builtins.html#builtins-all",
"builtin-any": "language/builtins.html#builtins-any",
"builtin-attrNames": "language/builtins.html#builtins-attrNames",
"builtin-attrValues": "language/builtins.html#builtins-attrValues",
"builtin-baseNameOf": "language/builtins.html#builtins-baseNameOf",
"builtin-bitAnd": "language/builtins.html#builtins-bitAnd",
"builtin-bitOr": "language/builtins.html#builtins-bitOr",
"builtin-bitXor": "language/builtins.html#builtins-bitXor",
"builtin-builtins": "language/builtins.html#builtins-builtins",
"builtin-compareVersions": "language/builtins.html#builtins-compareVersions",
"builtin-concatLists": "language/builtins.html#builtins-concatLists",
"builtin-concatStringsSep": "language/builtins.html#builtins-concatStringsSep",
"builtin-currentSystem": "language/builtins.html#builtins-currentSystem",
"builtin-deepSeq": "language/builtins.html#builtins-deepSeq",
"builtin-derivation": "language/builtins.html#builtins-derivation",
"builtin-dirOf": "language/builtins.html#builtins-dirOf",
"builtin-div": "language/builtins.html#builtins-div",
"builtin-elem": "language/builtins.html#builtins-elem",
"builtin-elemAt": "language/builtins.html#builtins-elemAt",
"builtin-fetchGit": "language/builtins.html#builtins-fetchGit",
"builtin-fetchTarball": "language/builtins.html#builtins-fetchTarball",
"builtin-fetchurl": "language/builtins.html#builtins-fetchurl",
"builtin-filterSource": "language/builtins.html#builtins-filterSource",
"builtin-foldl-prime": "language/builtins.html#builtins-foldl'",
"builtin-fromJSON": "language/builtins.html#builtins-fromJSON",
"builtin-functionArgs": "language/builtins.html#builtins-functionArgs",
"builtin-genList": "language/builtins.html#builtins-genList",
"builtin-getAttr": "language/builtins.html#builtins-getAttr",
"builtin-getEnv": "language/builtins.html#builtins-getEnv",
"builtin-hasAttr": "language/builtins.html#builtins-hasAttr",
"builtin-hashFile": "language/builtins.html#builtins-hashFile",
"builtin-hashString": "language/builtins.html#builtins-hashString",
"builtin-head": "language/builtins.html#builtins-head",
"builtin-import": "language/builtins.html#builtins-import",
"builtin-intersectAttrs": "language/builtins.html#builtins-intersectAttrs",
"builtin-isAttrs": "language/builtins.html#builtins-isAttrs",
"builtin-isBool": "language/builtins.html#builtins-isBool",
"builtin-isFloat": "language/builtins.html#builtins-isFloat",
"builtin-isFunction": "language/builtins.html#builtins-isFunction",
"builtin-isInt": "language/builtins.html#builtins-isInt",
"builtin-isList": "language/builtins.html#builtins-isList",
"builtin-isNull": "language/builtins.html#builtins-isNull",
"builtin-isString": "language/builtins.html#builtins-isString",
"builtin-length": "language/builtins.html#builtins-length",
"builtin-lessThan": "language/builtins.html#builtins-lessThan",
"builtin-listToAttrs": "language/builtins.html#builtins-listToAttrs",
"builtin-map": "language/builtins.html#builtins-map",
"builtin-match": "language/builtins.html#builtins-match",
"builtin-mul": "language/builtins.html#builtins-mul",
"builtin-parseDrvName": "language/builtins.html#builtins-parseDrvName",
"builtin-path": "language/builtins.html#builtins-path",
"builtin-pathExists": "language/builtins.html#builtins-pathExists",
"builtin-placeholder": "language/builtins.html#builtins-placeholder",
"builtin-readDir": "language/builtins.html#builtins-readDir",
"builtin-readFile": "language/builtins.html#builtins-readFile",
"builtin-removeAttrs": "language/builtins.html#builtins-removeAttrs",
"builtin-replaceStrings": "language/builtins.html#builtins-replaceStrings",
"builtin-seq": "language/builtins.html#builtins-seq",
"builtin-sort": "language/builtins.html#builtins-sort",
"builtin-split": "language/builtins.html#builtins-split",
"builtin-splitVersion": "language/builtins.html#builtins-splitVersion",
"builtin-stringLength": "language/builtins.html#builtins-stringLength",
"builtin-sub": "language/builtins.html#builtins-sub",
"builtin-substring": "language/builtins.html#builtins-substring",
"builtin-tail": "language/builtins.html#builtins-tail",
"builtin-throw": "language/builtins.html#builtins-throw",
"builtin-toFile": "language/builtins.html#builtins-toFile",
"builtin-toJSON": "language/builtins.html#builtins-toJSON",
"builtin-toPath": "language/builtins.html#builtins-toPath",
"builtin-toString": "language/builtins.html#builtins-toString",
"builtin-toXML": "language/builtins.html#builtins-toXML",
"builtin-trace": "language/builtins.html#builtins-trace",
"builtin-tryEval": "language/builtins.html#builtins-tryEval",
"builtin-typeOf": "language/builtins.html#builtins-typeOf",
"ssec-builtins": "language/builtins.html",
"attr-system": "language/derivations.html#attr-system",
"ssec-derivation": "language/derivations.html",
"ch-expression-language": "language/index.html",
"sec-constructs": "language/syntax.html",
"sect-let-language": "language/syntax.html#let-expressions",
"ss-functions": "language/syntax.html#functions",
"sec-language-operators": "language/operators.html",
"table-operators": "language/operators.html",
"ssec-values": "language/types.html",
"gloss-closure": "glossary.html#gloss-closure",
"gloss-derivation": "glossary.html#gloss-derivation",
"gloss-deriver": "glossary.html#gloss-deriver",
"gloss-nar": "glossary.html#gloss-nar",
"gloss-output-path": "glossary.html#gloss-output-path",
"gloss-profile": "glossary.html#gloss-profile",
"gloss-reachable": "glossary.html#gloss-reachable",
"gloss-reference": "glossary.html#gloss-reference",
"gloss-substitute": "glossary.html#gloss-substitute",
"gloss-user-env": "glossary.html#gloss-user-env",
"gloss-validity": "glossary.html#gloss-validity",
"part-glossary": "glossary.html",
"sec-building-source": "installation/building-source.html",
"ch-env-variables": "installation/env-variables.html",
"sec-installer-proxy-settings": "installation/env-variables.html#proxy-environment-variables",
"sec-nix-ssl-cert-file": "installation/env-variables.html#nix_ssl_cert_file",
"sec-nix-ssl-cert-file-with-nix-daemon-and-macos": "installation/env-variables.html#nix_ssl_cert_file",
"chap-installation": "installation/index.html",
"ch-installing-binary": "installation/installing-binary.html",
"sect-macos-installation": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-change-store-prefix": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-encrypted-volume": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-recommended-notes": "installation/installing-binary.html#macos-installation",
"sect-macos-installation-symlink": "installation/installing-binary.html#macos-installation",
"sect-multi-user-installation": "installation/installing-binary.html#multi-user-installation",
"sect-nix-install-binary-tarball": "installation/installing-binary.html#installing-from-a-binary-tarball",
"sect-nix-install-pinned-version-url":
"installation/installing-binary.html#installing-a-pinned-nix-version-from-a-url",
"sect-single-user-installation": "installation/installing-binary.html#single-user-installation",
"ch-installing-source": "installation/installing-source.html",
"ssec-multi-user": "installation/multi-user.html",
"ch-nix-security": "installation/nix-security.html",
"sec-obtaining-source": "installation/obtaining-source.html",
"sec-prerequisites-source": "installation/prerequisites-source.html",
"sec-single-user": "installation/single-user.html",
"ch-supported-platforms": "installation/supported-platforms.html",
"ch-upgrading-nix": "installation/upgrading.html",
"ch-about-nix": "introduction.html",
"chap-introduction": "introduction.html",
"ch-basic-package-mgmt": "package-management/index.html",
"ssec-binary-cache-substituter": "package-management/binary-cache-substituter.html",
"sec-channels": "command-ref/nix-channel.html",
"ssec-copy-closure": "command-ref/nix-copy-closure.html",
"sec-garbage-collection": "package-management/garbage-collection.html",
"ssec-gc-roots": "package-management/garbage-collector-roots.html",
"chap-package-management": "package-management/index.html",
"sec-profiles": "package-management/profiles.html",
"ssec-s3-substituter": "store/types/s3-binary-cache-store.html",
"ssec-s3-substituter-anonymous-reads":
"store/types/s3-binary-cache-store.html#anonymous-reads-to-your-s3-compatible-binary-cache",
"ssec-s3-substituter-authenticated-reads":
"store/types/s3-binary-cache-store.html#authenticated-reads-to-your-s3-binary-cache",
"ssec-s3-substituter-authenticated-writes":
"store/types/s3-binary-cache-store.html#authenticated-writes-to-your-s3-compatible-binary-cache",
"sec-sharing-packages": "package-management/sharing-packages.html",
"ssec-ssh-substituter": "package-management/ssh-substituter.html",
"chap-quick-start": "quick-start.html",
"sec-relnotes": "release-notes/index.html",
"ch-relnotes-0.10.1": "release-notes/rl-0.10.1.html",
"ch-relnotes-0.10": "release-notes/rl-0.10.html",
"ssec-relnotes-0.11": "release-notes/rl-0.11.html",
"ssec-relnotes-0.12": "release-notes/rl-0.12.html",
"ssec-relnotes-0.13": "release-notes/rl-0.13.html",
"ssec-relnotes-0.14": "release-notes/rl-0.14.html",
"ssec-relnotes-0.15": "release-notes/rl-0.15.html",
"ssec-relnotes-0.16": "release-notes/rl-0.16.html",
"ch-relnotes-0.5": "release-notes/rl-0.5.html",
"ch-relnotes-0.6": "release-notes/rl-0.6.html",
"ch-relnotes-0.7": "release-notes/rl-0.7.html",
"ch-relnotes-0.8.1": "release-notes/rl-0.8.1.html",
"ch-relnotes-0.8": "release-notes/rl-0.8.html",
"ch-relnotes-0.9.1": "release-notes/rl-0.9.1.html",
"ch-relnotes-0.9.2": "release-notes/rl-0.9.2.html",
"ch-relnotes-0.9": "release-notes/rl-0.9.html",
"ssec-relnotes-1.0": "release-notes/rl-1.0.html",
"ssec-relnotes-1.1": "release-notes/rl-1.1.html",
"ssec-relnotes-1.10": "release-notes/rl-1.10.html",
"ssec-relnotes-1.11.10": "release-notes/rl-1.11.10.html",
"ssec-relnotes-1.11": "release-notes/rl-1.11.html",
"ssec-relnotes-1.2": "release-notes/rl-1.2.html",
"ssec-relnotes-1.3": "release-notes/rl-1.3.html",
"ssec-relnotes-1.4": "release-notes/rl-1.4.html",
"ssec-relnotes-1.5.1": "release-notes/rl-1.5.html",
"ssec-relnotes-1.5.2": "release-notes/rl-1.5.2.html",
"ssec-relnotes-1.5": "release-notes/rl-1.5.html",
"ssec-relnotes-1.6.1": "release-notes/rl-1.6.1.html",
"ssec-relnotes-1.6.0": "release-notes/rl-1.6.html",
"ssec-relnotes-1.7": "release-notes/rl-1.7.html",
"ssec-relnotes-1.8": "release-notes/rl-1.8.html",
"ssec-relnotes-1.9": "release-notes/rl-1.9.html",
"ssec-relnotes-2.0": "release-notes/rl-2.0.html",
"ssec-relnotes-2.1": "release-notes/rl-2.1.html",
"ssec-relnotes-2.2": "release-notes/rl-2.2.html",
"ssec-relnotes-2.3": "release-notes/rl-2.3.html"
},
"language/types.html": {
"simple-values": "#primitives",
"lists": "#type-list",
"strings": "#type-string",
"attribute-sets": "#type-attrs",
"type-number": "#type-int"
},
"language/syntax.html": {
"scoping-rules": "scope.html",
"string-literal": "string-literals.html"
},
"language/derivations.html": {
"builder-execution": "../store/building.html#builder-execution"
},
"installation/installing-binary.html": {
"linux": "uninstall.html#linux",
"macos": "uninstall.html#macos",
"uninstalling": "uninstall.html"
},
"development/building.html": {
"nix-with-flakes": "#building-nix-with-flakes",
"classic-nix": "#building-nix",
"running-tests": "testing.html#running-tests",
"unit-tests": "testing.html#unit-tests",
"functional-tests": "testing.html#functional-tests",
"debugging-failing-functional-tests": "testing.html#debugging-failing-functional-tests",
"integration-tests": "testing.html#integration-tests",
"installer-tests": "testing.html#installer-tests",
"one-time-setup": "testing.html#one-time-setup",
"using-the-ci-generated-installer-for-manual-testing":
"testing.html#using-the-ci-generated-installer-for-manual-testing",
"characterization-testing": "testing.html#characterisation-testing-unit",
"add-a-release-note": "contributing.html#add-a-release-note",
"add-an-entry": "contributing.html#add-an-entry",
"build-process": "contributing.html#build-process",
"reverting": "contributing.html#reverting",
"branches": "contributing.html#branches"
},
"glossary.html": {
"gloss-local-store": "store/types/local-store.html",
"package-attribute-set": "#package",
"gloss-chroot-store": "store/types/local-store.html",
"gloss-content-addressed-derivation": "#gloss-content-addressing-derivation"
}
}

40
doc/manual/render-manpage.sh Normal file → Executable file
View File

@@ -1,55 +1,25 @@
#!/usr/bin/env bash
#
# Standalone manpage renderer that doesn't require mdbook.
# Uses expand-includes.py to preprocess markdown, then lowdown to generate manpages.
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
lowdown_args=
# Optional --out-no-smarty flag for compatibility with nix_nested_manpages
if [ "$1" = --out-no-smarty ]; then
lowdown_args=--out-no-smarty
shift
fi
[ "$#" = 7 ] || {
cat >&2 <<EOF
Usage: $0 [--out-no-smarty] <title> <section> <source-root> <generated-root> <doc-url> <infile> <outfile>
Arguments:
title - Manpage title (e.g., "nix-env --install")
section - Manpage section number (1, 5, 8, etc.)
source-root - Root directory of markdown sources
generated-root - Root directory of generated markdown files
doc-url - Base URL for documentation links
infile - Input markdown file (relative to build directory)
outfile - Output manpage file
Examples:
$0 "nix-store --query" 1 doc/manual/source build/doc/manual/source \\
https://nix.dev/manual/nix/latest \\
build/doc/manual/source/command-ref/nix-store/query.md nix-store-query.1
EOF
[ "$#" = 4 ] || {
echo "wrong number of args passed" >&2
exit 1
}
title="$1"
section="$2"
source_root="$3"
generated_root="$4"
doc_url="$5"
infile="$6"
outfile="$7"
infile="$3"
outfile="$4"
# Expand includes and pipe to lowdown
(
printf "Title: %s\n\n" "$title"
python3 "$script_dir/expand-includes.py" \
--source-root "$source_root" \
--generated-root "$generated_root" \
--doc-url "$doc_url" \
"$infile"
cat "$infile"
) | lowdown -sT man --nroff-nolinks $lowdown_args -M section="$section" -o "$outfile"

View File

@@ -6,66 +6,33 @@ issues: []
JSON formats for store path info and derivations have been updated with new versions and structured fields.
## Store Path Info JSON
## Store Path Info JSON (Version 2)
`nix path-info --json` now requires a `--json-format` flag to specify the output format version.
Using `--json` without `--json-format` is deprecated and will become an error in a future release.
For now, it defaults to version 1 with a warning, for a smoother migration.
The store path info JSON format has been updated from version 1 to version 2:
### Version 1 (`--json-format 1`)
- **Added `version` field**:
This is the legacy format, preserved for backwards compatibility:
- String-based hash values (e.g., `"narHash": "sha256:FePFYIlM..."`)
- String-based content addresses (e.g., `"ca": "fixed:r:sha256:1abc..."`)
- Full store paths for map keys and references (e.g., `"/nix/store/abc...-foo"`)
- Now includes `"storeDir"` field at the top level
### Version 2 (`--json-format 2`)
The new structured format follows the [JSON guidelines](@docroot@/development/json-guideline.md) with the following changes:
- **Nested structure with top-level metadata**:
The output is now wrapped in an object with `version`, `storeDir`, and `info` fields:
```json
{
"version": 2,
"storeDir": "/nix/store",
"info": { ... }
}
```
The map from store bath base names to store object info is nested under the `info` field.
- **Store path base names instead of full paths**:
Map keys and references use store path base names (e.g., `"abc...-foo"`) instead of full absolute store paths.
Combined with `storeDir`, the full path can be reconstructed.
All store path info JSON now includes `"version": 2`.
- **Structured `ca` field**:
Content address is now a structured JSON object instead of a string:
- Old: `"ca": "fixed:r:sha256:1abc..."`
- New: `"ca": {"method": "nar", "hash": "sha256-ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="}`
- New: `"ca": {"method": "nar", "hash": {"algorithm": "sha256", "format": "base64", "hash": "EMIJ+giQ..."}}`
- Still `null` values for input-addressed store objects
The `hash` field uses the [SRI](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) format like other hashes.
- **Structured hash fields**:
Hash values (`narHash` and `downloadHash`) are now structured JSON objects instead of strings:
- Old: `"narHash": "sha256:FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="`
- New: `"narHash": {"algorithm": "sha256", "format": "base64", "hash": "FePFYIlM..."}`
- Same structure applies to `downloadHash` in NAR info contexts
Nix currently only produces, and doesn't consume this format.
Additionally the following field is added to both formats.
(The `version` tracks breaking changes, and adding fields to outputted JSON is not a breaking change.)
- **`version` field**:
All store path info JSON now includes `"version": <1|2>`.
- **`storeDir` field**:
Top-level `"storeDir"` field contains the store directory path (e.g., `"/nix/store"`).
**Affected command**: `nix path-info --json`
## Derivation JSON (Version 4)
@@ -80,9 +47,9 @@ The derivation JSON format has been updated from version 3 to version 4:
- **Consistent content addresses**:
Fixed content-addressed outputs now use structured JSON format.
This is the same format as `ca` in store path info (after the new version).
Floating content-addressed outputs now use structured JSON format.
This is the same format as `ca` in in store path info (after the new version).
Version 3 and earlier formats are *not* accepted when reading.
**Affected command**: `nix derivation`, namely its `show` and `add` sub-commands.
**Affected command**: `nix derivation`, namely it's `show` and `add` sub-commands.

View File

@@ -1,8 +0,0 @@
---
synopsis: Interrupting REPL commands works more than once
issues: [13481]
---
Previously, this only worked once per REPL session; further attempts would be ignored.
This issue is now fixed, so REPL commands such as `:b` or `:p` can be canceled consistently.
This is a cherry-pick of the change from the [Lix project](https://gerrit.lix.systems/c/lix/+/1097).

View File

@@ -34,7 +34,7 @@ md5sum`.
Print the cryptographic hash of the contents of each regular file *path*.
That is, instead of computing
the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*,
just [directly hash](@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is.
just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is.
This requires *path* to resolve to a regular file rather than directory.
The result is identical to that produced by the GNU commands
`md5sum` and `sha1sum`.

View File

@@ -23,7 +23,7 @@ Some built-ins are also exposed directly in the global scope:
- [`null`](#builtins-null)
- [`placeholder`](#builtins-placeholder)
- [`removeAttrs`](#builtins-removeAttrs)
- [`scopedImport`](#builtins-scopedImport)
- `scopedImport`
- [`throw`](#builtins-throw)
- [`toString`](#builtins-toString)
- [`true`](#builtins-true)

View File

@@ -74,48 +74,4 @@ in f { x = throw "error"; y = throw "error"; }
=> "ok"
```
## Evaluation order
The order in which expressions are evaluated is generally unspecified, because it does not affect successful evaluation outcomes.
This allows more freedom for the evaluator to evolve and to evaluate efficiently.
Data dependencies naturally impose some ordering constraints: a value cannot be used before it is computed.
Beyond these constraints, the evaluator is free to choose any order.
The order in which side effects such as [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) output occurs is not defined, but may be expected to follow data dependencies. <!-- we may want to be more specific about this. -->
In a lazy language, evaluation order is often opposite to expectations from strict languages.
For example, in `let wrap = x: { wrapped = x; }; in wrap (1 + 2)`, the function body produces a result (`{ wrapped = ...; }`) *before* evaluating `x`.
## Infinite recursion and stack overflow
During evaluation, two types of errors can occur when expressions reference themselves or call functions too deeply:
### Infinite recursion
This error occurs when a value depends on itself through a cycle, making it impossible to compute.
```nix
let x = x; in x
=> error: infinite recursion encountered
```
Infinite recursion happens at the value level when evaluating an expression requires evaluating the same expression again.
Despite the name, infinite recursion is cheap to compute and does not involve a stack overflow.
The cycle is finite and fairly easy to detect.
### Stack overflow
This error occurs when the call depth exceeds the maximum allowed limit.
```nix
let f = x: f (x + 1);
in f 0
=> error: stack overflow; max-call-depth exceeded
```
Stack overflow happens when too many function calls are nested without returning.
The maximum call depth is controlled by the [`max-call-depth` setting](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
[C API]: @docroot@/c-api.md

View File

@@ -23,8 +23,8 @@
| [Greater than or equal to][Comparison] | *expr* `>=` *expr* | none | 10 |
| [Equality] | *expr* `==` *expr* | none | 11 |
| Inequality | *expr* `!=` *expr* | none | 11 |
| [Logical conjunction] (`AND`) | *bool* `&&` *bool* | left | [12](#precedence-and-disjunctive-normal-form) |
| [Logical disjunction] (`OR`) | *bool* <code>\|\|</code> *bool* | left | [13](#precedence-and-disjunctive-normal-form) |
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
| [Logical implication] | *bool* `->` *bool* | right | 14 |
| [Pipe operator] (experimental) | *expr* `\|>` *func* | left | 15 |
| [Pipe operator] (experimental) | *func* `<\|` *expr* | right | 15 |
@@ -162,9 +162,6 @@ Update [attribute set] *attrset1* with names and values from *attrset2*.
The returned attribute set will have all of the attributes in *attrset1* and *attrset2*.
If an attribute name is present in both, the attribute value from the latter is taken.
This operator is [strict](@docroot@/language/evaluation.md#strictness) in both *attrset1* and *attrset2*.
That means that both arguments are evaluated to [weak head normal form](@docroot@/language/evaluation.md#values), so the attribute sets themselves are evaluated, but their attribute values are not evaluated.
[Update]: #update
## Comparison
@@ -188,95 +185,18 @@ All comparison operators are implemented in terms of `<`, and the following equi
## Equality
- [Attribute sets][attribute set] are compared first by attribute names and then by items until a difference is found.
- [Lists][list] are compared first by length and then by items until a difference is found.
- Comparison of distinct [functions][function] returns `false`, but identical functions may be subject to [value identity optimization](#value-identity-optimization).
- [Attribute sets][attribute set] and [lists][list] are compared recursively, and therefore are fully evaluated.
- Comparison of [functions][function] always returns `false`.
- Numbers are type-compatible, see [arithmetic] operators.
- Floating point numbers only differ up to a limited precision.
The `==` operator is [strict](@docroot@/language/evaluation.md#strictness) in both arguments; when comparing composite types ([attribute sets][attribute set] and [lists][list]), it is partially strict in their contained values: they are evaluated until a difference is found. <!-- this is woefully underspecified, affecting which expressions evaluate correctly; not just "ordering" or error messages. -->
### Value identity optimization
Nix performs equality comparisons of nested values by pointer equality or more abstractly, _identity_.
Nix semantics ideally do not assign a unique identity to values as they are created, but equality is an exception to this rule.
The disputable benefit of this is that it is more efficient, and it allows cyclical structures to be compared, e.g. `let x = { x = x; }; in x == x` evaluates to `true`.
However, as a consequence, it makes a function equal to itself when the comparison is made in a list or attribute set, in contradiction to a simple direct comparison.
[function]: ./syntax.md#functions
[Equality]: #equality
## Logical conjunction
> **Syntax**
>
> *bool1* `&&` *bool2*
Logical AND. Equivalent to `if` *bool1* `then` *bool2* `else false`.
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
> **Example**
>
> ```nix
> true && false
> => false
>
> false && throw "never evaluated"
> => false
> ```
[Logical conjunction]: #logical-conjunction
## Logical disjunction
> **Syntax**
>
> *bool1* `||` *bool2*
Logical OR. Equivalent to `if` *bool1* `then true` `else` *bool2*.
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `false`.
> **Example**
>
> ```nix
> true || false
> => true
>
> true || throw "never evaluated"
> => true
> ```
[Logical disjunction]: #logical-disjunction
### Precedence and disjunctive normal form
The precedence of `&&` and `||` aligns with disjunctive normal form.
Without parentheses, an expression describes multiple "permissible situations" (connected by `||`), where each situation consists of multiple simultaneous conditions (connected by `&&`).
For example, `A || B && C || D && E` is parsed as `A || (B && C) || (D && E)`, describing three permissible situations: A holds, or both B and C hold, or both D and E hold.
## Logical implication
> **Syntax**
>
> *bool1* `->` *bool2*
Logical implication. Equivalent to `!`*bool1* `||` *bool2* (or `if` *bool1* `then` *bool2* `else true`).
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
> **Example**
>
> ```nix
> true -> false
> => false
>
> false -> throw "never evaluated"
> => true
> ```
Equivalent to `!`*b1* `||` *b2* (or `if` *b1* `then` *b2* `else true`)
[Logical implication]: #logical-implication

View File

@@ -7,25 +7,25 @@
#### Default options
```json
{{#include ../schema/derivation-options-v1/ia/derivation-options/defaults.json}}
{{#include ../schema/derivation-options-v1/ia/defaults.json}}
```
#### All options set
```json
{{#include ../schema/derivation-options-v1/ia/derivation-options/all_set.json}}
{{#include ../schema/derivation-options-v1/ia/all_set.json}}
```
#### Default options (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ia/derivation-options/structuredAttrs_defaults.json}}
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_defaults.json}}
```
#### All options set (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ia/derivation-options/structuredAttrs_all_set.json}}
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_all_set.json}}
```
### Content-addressed derivations
@@ -33,13 +33,13 @@
#### All options set
```json
{{#include ../schema/derivation-options-v1/ca/derivation-options/all_set.json}}
{{#include ../schema/derivation-options-v1/ca/all_set.json}}
```
#### All options set (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ca/derivation-options/structuredAttrs_all_set.json}}
{{#include ../schema/derivation-options-v1/ca/structuredAttrs_all_set.json}}
```
<!-- need to convert YAML to JSON first

View File

@@ -2,16 +2,28 @@
## Examples
### SHA-256
### SHA-256 with Base64 encoding
```json
{{#include schema/hash-v1/sha256.json}}
{{#include schema/hash-v1/sha256-base64.json}}
```
### BLAKE3
### SHA-256 with Base16 (hexadecimal) encoding
```json
{{#include schema/hash-v1/blake3.json}}
{{#include schema/hash-v1/sha256-base16.json}}
```
### SHA-256 with Nix32 encoding
```json
{{#include schema/hash-v1/sha256-nix32.json}}
```
### BLAKE3 with Base64 encoding
```json
{{#include schema/hash-v1/blake3-base64.json}}
```
<!-- need to convert YAML to JSON first

View File

@@ -35,27 +35,27 @@ endforeach
json_schema_generated_files = []
if json_schema_for_humans.found()
# Generate markdown documentation from JSON schema
# Note: output must be just a filename, not a path
gen_file = custom_target(
schema_name + '-schema-docs.tmp',
command : [
json_schema_for_humans,
'--config-file',
json_schema_config,
meson.current_source_dir() / 'schema',
meson.current_build_dir(),
],
input : schema_files + [
json_schema_config,
],
output : schema_outputs,
capture : false,
build_by_default : true,
)
# Generate markdown documentation from JSON schema
# Note: output must be just a filename, not a path
gen_file = custom_target(
schema_name + '-schema-docs.tmp',
command : [
json_schema_for_humans,
'--config-file',
json_schema_config,
meson.current_source_dir() / 'schema',
meson.current_build_dir(),
],
input : schema_files + [
json_schema_config,
],
output : schema_outputs,
capture : false,
build_by_default : true,
)
idx = 0
idx = 0
if json_schema_for_humans.found()
foreach schema_name : schemas
#schema_file = 'schema' / schema_name + '.yaml'

View File

@@ -4,13 +4,40 @@ title: Hash
description: |
A cryptographic hash value used throughout Nix for content addressing and integrity verification.
This schema describes the JSON representation of Nix's `Hash` type as an [SRI](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) string.
type: string
pattern: "^(blake3|md5|sha1|sha256|sha512)-[A-Za-z0-9+/]+=*$"
examples:
- "sha256-ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
- "sha512-IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
This schema describes the JSON representation of Nix's `Hash` type.
type: object
properties:
algorithm:
"$ref": "#/$defs/algorithm"
format:
type: string
enum:
- base64
- nix32
- base16
- sri
title: Hash format
description: |
The encoding format of the hash value.
- `base64` uses standard Base64 encoding [RFC 4648, section 4](https://datatracker.ietf.org/doc/html/rfc4648#section-4)
- `nix32` is Nix-specific base-32 encoding
- `base16` is lowercase hexadecimal
- `sri` is the [Subresource Integrity format](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
hash:
type: string
title: Hash
description: |
The encoded hash value, itself.
It is specified in the format specified by the `format` field.
It must be the right length for the hash algorithm specified in the `algorithm` field, also.
The hash value does not include any algorithm prefix.
required:
- algorithm
- format
- hash
additionalProperties: false
"$defs":
algorithm:
type: string

View File

@@ -0,0 +1 @@
../../../../../../src/libstore-tests/data/nar-info

View File

@@ -1 +0,0 @@
../../../../../../src/libstore-tests/data/nar-info/json-2

View File

@@ -1 +1 @@
../../../../../../src/libstore-tests/data/path-info/json-2
../../../../../../src/libstore-tests/data/path-info

View File

@@ -46,7 +46,6 @@ $defs:
- narSize
- references
- ca
- storeDir
properties:
version:
type: integer
@@ -64,7 +63,7 @@ $defs:
- Version 2: Use structured JSON type for `ca`
path:
"$ref": "./store-path-v1.yaml"
type: string
title: Store Path
description: |
[Store path](@docroot@/store/store-path.md) to the given store object.
@@ -90,7 +89,7 @@ $defs:
description: |
An array of [store paths](@docroot@/store/store-path.md), possibly including this one.
items:
"$ref": "./store-path-v1.yaml"
type: string
ca:
oneOf:
@@ -102,12 +101,6 @@ $defs:
If the store object is [content-addressed](@docroot@/store/store-object/content-address.md),
this is the content address of this store object's file system object, used to compute its store path.
Otherwise (i.e. if it is [input-addressed](@docroot@/glossary.md#gloss-input-addressed-store-object)), this is `null`.
storeDir:
type: string
title: Store Directory
description: |
The [store directory](@docroot@/store/store-path.md#store-directory) this store object belongs to (e.g. `/nix/store`).
additionalProperties: false
impure:
@@ -122,7 +115,6 @@ $defs:
- narSize
- references
- ca
- storeDir
# impure
- deriver
- registrationTime
@@ -135,11 +127,8 @@ $defs:
narSize: { $ref: "#/$defs/base/properties/narSize" }
references: { $ref: "#/$defs/base/properties/references" }
ca: { $ref: "#/$defs/base/properties/ca" }
storeDir: { $ref: "#/$defs/base/properties/storeDir" }
deriver:
oneOf:
- "$ref": "./store-path-v1.yaml"
- type: "null"
type: ["string", "null"]
title: Deriver
description: |
If known, the path to the [store derivation](@docroot@/glossary.md#gloss-store-derivation) from which this store object was produced.
@@ -201,7 +190,6 @@ $defs:
- narSize
- references
- ca
- storeDir
# impure
- deriver
- registrationTime
@@ -219,7 +207,6 @@ $defs:
narSize: { $ref: "#/$defs/base/properties/narSize" }
references: { $ref: "#/$defs/base/properties/references" }
ca: { $ref: "#/$defs/base/properties/ca" }
storeDir: { $ref: "#/$defs/base/properties/storeDir" }
deriver: { $ref: "#/$defs/impure/properties/deriver" }
registrationTime: { $ref: "#/$defs/impure/properties/registrationTime" }
ultimate: { $ref: "#/$defs/impure/properties/ultimate" }

View File

@@ -29,13 +29,13 @@
### NAR info (minimal)
```json
{{#include schema/nar-info-v2/pure.json}}
{{#include schema/nar-info-v1/pure.json}}
```
### NAR info (with binary cache fields)
```json
{{#include schema/nar-info-v2/impure.json}}
{{#include schema/nar-info-v1/impure.json}}
```
<!-- need to convert YAML to JSON first

View File

@@ -112,7 +112,7 @@ This release was made possible by the following 45 contributors:
- Connor Baker [**(@ConnorBaker)**](https://github.com/ConnorBaker)
- Cole Helbling [**(@cole-h)**](https://github.com/cole-h)
- Jack Wilsdon [**(@jackwilsdon)**](https://github.com/jackwilsdon)
- Martin Häcker [**(@dwt)**](https://github.com/dwt)
- rekcäH nitraM [**(@dwt)**](https://github.com/dwt)
- Martin Fischer [**(@not-my-profile)**](https://github.com/not-my-profile)
- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314)
- Graham Christensen [**(@grahamc)**](https://github.com/grahamc)

View File

@@ -12,11 +12,10 @@
The [`builder`](./derivation/index.md#builder) is executed as follows:
- A temporary directory is created where the build will take place. The
- A temporary directory is created under the directory specified by
`TMPDIR` (default `/tmp`) where the build will take place. The
current directory is changed to this directory.
See the per-store [`build-dir`](@docroot@/store/types/local-store.md#store-local-store-build-dir) setting for more information.
- The environment is cleared and set to the derivation attributes, as
specified above.

View File

@@ -41,10 +41,6 @@ def recursive_replace(data: dict[str, t.Any], book_root: Path, search_path: Path
return data | dict(
sections = [recursive_replace(section, book_root, search_path) for section in sections],
)
case {'items': items}:
return data | dict(
items = [recursive_replace(item, book_root, search_path) for item in items],
)
case {'Chapter': chapter}:
path_to_chapter = Path(chapter['path'])
chapter_content = chapter['content']

17
flake.lock generated
View File

@@ -63,15 +63,18 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1763948260,
"narHash": "sha256-zZk7fn2ARAqmLwaYTpxBJmj81KIdz11NiWt7ydHHD/M=",
"rev": "1c8ba8d3f7634acac4a2094eef7c32ad9106532c",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.813095.1c8ba8d3f763/nixexprs.tar.xz"
"lastModified": 1761597516,
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
"type": "github"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz"
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-23-11": {

View File

@@ -1,7 +1,7 @@
{
description = "The purely functional package manager";
inputs.nixpkgs.url = "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446";
@@ -369,7 +369,6 @@
# TODO probably should be `nix-cli`
nix = self.packages.${system}.nix-everything;
nix-manual = nixpkgsFor.${system}.native.nixComponents2.nix-manual;
nix-manual-manpages-only = nixpkgsFor.${system}.native.nixComponents2.nix-manual-manpages-only;
nix-internal-api-docs = nixpkgsFor.${system}.native.nixComponents2.nix-internal-api-docs;
nix-external-api-docs = nixpkgsFor.${system}.native.nixComponents2.nix-external-api-docs;
}

View File

@@ -118,7 +118,7 @@
"wh0": null,
"mupdt": "Matej Urbas",
"momeemt": "Mutsuha Asada",
"dwt": "Martin H\u00e4cker",
"dwt": "\u202erekc\u00e4H nitraM\u202e",
"aidenfoxivey": "Aiden Fox Ivey",
"ilya-bobyr": "Illia Bobyr",
"B4dM4n": "Fabian M\u00f6ller",

View File

@@ -429,15 +429,6 @@ in
The manual as would be published on https://nix.dev/reference/nix-manual
*/
nix-manual = callPackage ../doc/manual/package.nix { version = fineVersion; };
/**
Manpages only (no HTML manual, no mdbook dependency)
*/
nix-manual-manpages-only = callPackage ../doc/manual/package.nix {
version = fineVersion;
buildHtmlManual = false;
};
/**
Doxygen pages for C++ code
*/

View File

@@ -39,7 +39,7 @@ scope: {
nativeBuildInputs = prevAttrs.nativeBuildInputs ++ [ pkgs.buildPackages.bmake ];
postInstall =
lib.replaceStrings [ "lowdown.so.1" "lowdown.1.dylib" ] [ "lowdown.so.2" "lowdown.2.dylib" ]
(prevAttrs.postInstall or "");
prevAttrs.postInstall;
});
# TODO: Remove this when https://github.com/NixOS/nixpkgs/pull/442682 is included in a stable release

View File

@@ -63,16 +63,15 @@ let
"nix-cli"
"nix-functional-tests"
"nix-json-schema-checks"
"nix-kaitai-struct-checks"
]
++ lib.optionals enableBindings [
"nix-perl-bindings"
]
++ lib.optionals enableDocs [
"nix-manual"
"nix-manual-manpages-only"
"nix-internal-api-docs"
"nix-external-api-docs"
"nix-kaitai-struct-checks"
]
);
in

View File

@@ -32,8 +32,10 @@ schemas = [
'stem' : 'hash',
'schema' : schema_dir / 'hash-v1.yaml',
'files' : [
'sha256.json',
'blake3.json',
'sha256-base64.json',
'sha256-base16.json',
'sha256-nix32.json',
'blake3-base64.json',
],
},
{
@@ -73,12 +75,12 @@ schemas = [
'stem' : 'derivation-options',
'schema' : schema_dir / 'derivation-options-v1.yaml',
'files' : [
'ia' / 'derivation-options' / 'defaults.json',
'ia' / 'derivation-options' / 'all_set.json',
'ia' / 'derivation-options' / 'structuredAttrs_defaults.json',
'ia' / 'derivation-options' / 'structuredAttrs_all_set.json',
'ca' / 'derivation-options' / 'all_set.json',
'ca' / 'derivation-options' / 'structuredAttrs_all_set.json',
'ia' / 'defaults.json',
'ia' / 'all_set.json',
'ia' / 'structuredAttrs_defaults.json',
'ia' / 'structuredAttrs_all_set.json',
'ca' / 'all_set.json',
'ca' / 'structuredAttrs_all_set.json',
],
},
]
@@ -154,18 +156,18 @@ schemas += [
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-2' / 'empty_pure.json',
'json-2' / 'empty_impure.json',
'pure.json',
'impure.json',
'empty_pure.json',
'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'pure.json',
'impure.json',
],
},
{
@@ -182,30 +184,30 @@ schemas += [
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'empty_pure.json',
'pure.json',
'empty_pure.json',
],
},
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
'files' : [
'json-2' / 'impure.json',
'json-2' / 'empty_impure.json',
'impure.json',
'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'pure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
'files' : [
'json-2' / 'impure.json',
'impure.json',
],
},
]

View File

@@ -171,7 +171,7 @@ std::vector<ref<eval_cache::AttrCursor>> InstallableFlake::getCursors(EvalState
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(AttrPath::parse(state, attrPath));
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) {
res.push_back(ref(*attr));
} else {

View File

@@ -355,9 +355,9 @@ void completeFlakeRefWithFragment(
attrPathPrefixes.push_back("");
for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = AttrPath::parse(*evalState, attrPathPrefixS);
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment);
auto attrPath = AttrPath::parse(*evalState, attrPathS);
auto attrPath = parseAttrPath(*evalState, attrPathS);
std::string lastAttr;
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
@@ -375,7 +375,9 @@ void completeFlakeRefWithFragment(
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
// FIXME: handle names with dots
completions.add(flakeRefS + "#" + prefixRoot + attrPath2.to_string(*evalState));
completions.add(
flakeRefS + "#" + prefixRoot
+ concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
}
}
}
@@ -384,7 +386,7 @@ void completeFlakeRefWithFragment(
attrpaths. */
if (fragment.empty()) {
for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(AttrPath::parse(*evalState, attrPath));
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
if (!attr)
continue;
completions.add(flakeRefS + "#" + prefixRoot);

View File

@@ -142,7 +142,7 @@ NixRepl::NixRepl(
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
, runNixPtr{runNix}
, interacter(make_unique<ReadlineLikeInteracter>((getDataDir() / "repl-history").string()))
, interacter(make_unique<ReadlineLikeInteracter>(getDataDir() + "/repl-history"))
{
}

View File

@@ -58,7 +58,7 @@ static nix::Value & check_value_out(nix_value * value)
return v;
}
static nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem)
static inline nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem)
{
nix_value * ret = new (mem.allocBytes(sizeof(nix_value))) nix_value{
.value = v,

View File

@@ -18,13 +18,13 @@ TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
{
auto tmpDir = nix::createTempDir();
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
auto nixpkgs = tmpDir / "pkgs";
auto nixos = tmpDir / "cfg";
auto nixpkgs = tmpDir + "/pkgs";
auto nixos = tmpDir + "/cfg";
std::filesystem::create_directories(nixpkgs);
std::filesystem::create_directories(nixos);
std::string nixpkgsEntry = "nixpkgs=" + nixpkgs.string();
std::string nixosEntry = "nixos-config=" + nixos.string();
std::string nixpkgsEntry = "nixpkgs=" + nixpkgs;
std::string nixosEntry = "nixos-config=" + nixos;
const char * lookupPath[] = {nixpkgsEntry.c_str(), nixosEntry.c_str(), nullptr};
auto builder = nix_eval_state_builder_new(ctx, store);
@@ -49,7 +49,7 @@ TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
auto pathStr = nix_get_path_string(ctx, value);
assert_ctx_ok();
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.string().c_str()));
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
nix_gc_decref(nullptr, value);
}
@@ -232,22 +232,22 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context)
nix_realised_string_free(r);
}
static const char SAMPLE_USER_DATA = 0;
const char * SAMPLE_USER_DATA = "whatever";
static void
primop_square(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
{
assert(context);
assert(state);
assert(user_data == &SAMPLE_USER_DATA);
assert(user_data == SAMPLE_USER_DATA);
auto i = nix_get_int(context, args[0]);
nix_init_int(context, ret, i * i);
}
TEST_F(nix_api_expr_test, nix_expr_primop)
{
PrimOp * primop = nix_alloc_primop(
ctx, primop_square, 1, "square", nullptr, "square an integer", const_cast<char *>(&SAMPLE_USER_DATA));
PrimOp * primop =
nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
@@ -273,7 +273,7 @@ primop_repeat(void * user_data, nix_c_context * context, EvalState * state, nix_
{
assert(context);
assert(state);
assert(user_data == &SAMPLE_USER_DATA);
assert(user_data == SAMPLE_USER_DATA);
// Get the string to repeat
std::string s;
@@ -295,8 +295,8 @@ primop_repeat(void * user_data, nix_c_context * context, EvalState * state, nix_
TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls)
{
PrimOp * primop = nix_alloc_primop(
ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", const_cast<char *>(&SAMPLE_USER_DATA));
PrimOp * primop =
nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
@@ -330,8 +330,8 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls)
TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call)
{
PrimOp * primop = nix_alloc_primop(
ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", const_cast<char *>(&SAMPLE_USER_DATA));
PrimOp * primop =
nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();

View File

@@ -14,4 +14,12 @@
namespace nixC {
TEST_F(nix_api_expr_test, as_nix_value_ptr)
{
// nix_alloc_value casts nix::Value to nix_value
// It should be obvious from the decl that that works, but if it doesn't,
// the whole implementation would be utterly broken.
ASSERT_EQ(sizeof(nix::Value), sizeof(nix_value));
}
} // namespace nixC

View File

@@ -1,6 +1,5 @@
#include "nix/expr/attr-path.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/util/strings-inline.hh"
namespace nix {
@@ -31,24 +30,14 @@ static Strings parseAttrPath(std::string_view s)
return res;
}
AttrPath AttrPath::parse(EvalState & state, std::string_view s)
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
{
AttrPath res;
std::vector<Symbol> res;
for (auto & a : parseAttrPath(s))
res.push_back(state.symbols.create(a));
return res;
}
std::string AttrPath::to_string(EvalState & state) const
{
return dropEmptyInitThenConcatStringsSep(".", state.symbols.resolve({*this}));
}
std::vector<SymbolStr> AttrPath::resolve(EvalState & state) const
{
return state.symbols.resolve({*this});
}
std::pair<Value *, PosIdx>
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn)
{

View File

@@ -364,7 +364,7 @@ void AttrCursor::fetchCachedValue()
throw CachedEvalError(parent->first, parent->second);
}
AttrPath AttrCursor::getAttrPath() const
std::vector<Symbol> AttrCursor::getAttrPath() const
{
if (parent) {
auto attrPath = parent->first->getAttrPath();
@@ -374,7 +374,7 @@ AttrPath AttrCursor::getAttrPath() const
return {};
}
AttrPath AttrCursor::getAttrPath(Symbol name) const
std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const
{
auto attrPath = getAttrPath();
attrPath.push_back(name);
@@ -383,12 +383,12 @@ AttrPath AttrCursor::getAttrPath(Symbol name) const
std::string AttrCursor::getAttrPathStr() const
{
return getAttrPath().to_string(root->state);
return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
}
std::string AttrCursor::getAttrPathStr(Symbol name) const
{
return getAttrPath(name).to_string(root->state);
return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
}
Value & AttrCursor::forceValue()
@@ -511,7 +511,7 @@ ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
return getAttr(root->state.symbols.create(name));
}
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const AttrPath & attrPath)
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
{
auto res = shared_from_this();
for (auto & attr : attrPath) {

View File

@@ -103,9 +103,9 @@ const std::string & EvalSettings::getCurrentSystem() const
return evalSystem != "" ? evalSystem : settings.thisSystem.get();
}
std::filesystem::path getNixDefExpr()
Path getNixDefExpr()
{
return settings.useXDGBaseDirectories ? getStateDir() / "defexpr" : getHome() / ".nix-defexpr";
return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
}
} // namespace nix

View File

@@ -1368,7 +1368,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
v = *v2;
}
static std::string showAttrSelectionPath(EvalState & state, Env & env, std::span<const AttrName> attrPath)
static std::string showAttrPath(EvalState & state, Env & env, std::span<const AttrName> attrPath)
{
std::ostringstream out;
bool first = true;
@@ -1404,7 +1404,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
env,
getPos(),
"while evaluating the attribute '%1%'",
showAttrSelectionPath(state, env, getAttrPath()))
showAttrPath(state, env, getAttrPath()))
: nullptr;
for (auto & i : getAttrPath()) {
@@ -1445,7 +1445,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
auto origin = std::get_if<SourcePath>(&pos2r.origin);
if (!(origin && *origin == state.derivationInternal))
state.addErrorTrace(
e, pos2, "while evaluating the attribute '%1%'", showAttrSelectionPath(state, env, getAttrPath()));
e, pos2, "while evaluating the attribute '%1%'", showAttrPath(state, env, getAttrPath()));
}
throw;
}

View File

@@ -19,15 +19,6 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au
*/
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
struct AttrPath : std::vector<Symbol>
{
using std::vector<Symbol>::vector;
static AttrPath parse(EvalState & state, std::string_view s);
std::string to_string(EvalState & state) const;
std::vector<SymbolStr> resolve(EvalState & state) const;
};
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
} // namespace nix

View File

@@ -4,7 +4,6 @@
#include "nix/util/sync.hh"
#include "nix/util/hash.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/attr-path.hh"
#include <functional>
#include <variant>
@@ -125,9 +124,9 @@ public:
Value * value = nullptr,
std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {});
AttrPath getAttrPath() const;
std::vector<Symbol> getAttrPath() const;
AttrPath getAttrPath(Symbol name) const;
std::vector<Symbol> getAttrPath(Symbol name) const;
std::string getAttrPathStr() const;
@@ -147,7 +146,7 @@ public:
* Get an attribute along a chain of attrsets. Note that this does
* not auto-call functors or functions.
*/
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const AttrPath & attrPath);
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath);
std::string getString();

View File

@@ -366,6 +366,6 @@ struct EvalSettings : Config
/**
* Conventionally part of the default nix path in impure mode.
*/
std::filesystem::path getNixDefExpr();
Path getNixDefExpr();
} // namespace nix

View File

@@ -87,9 +87,9 @@ struct AttrName
static_assert(std::is_trivially_copy_constructible_v<AttrName>);
typedef std::vector<AttrName> AttrSelectionPath;
typedef std::vector<AttrName> AttrPath;
std::string showAttrSelectionPath(const SymbolTable & symbols, std::span<const AttrName> attrPath);
std::string showAttrPath(const SymbolTable & symbols, std::span<const AttrName> attrPath);
using UpdateQueue = SmallTemporaryValueVector<conservativeStackReservation>;

View File

@@ -163,25 +163,20 @@ struct ParserState
static constexpr Expr::AstSymbols s = StaticEvalSymbols::create().exprSymbols;
const EvalSettings & settings;
void dupAttr(const AttrSelectionPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
void addAttr(
ExprAttrs * attrs,
AttrSelectionPath && attrPath,
const ParserLocation & loc,
Expr * e,
const ParserLocation & exprLoc);
void addAttr(ExprAttrs * attrs, AttrSelectionPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
void validateFormals(FormalsBuilder & formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es);
PosIdx at(const ParserLocation & loc);
};
inline void ParserState::dupAttr(const AttrSelectionPath & attrPath, const PosIdx pos, const PosIdx prevPos)
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{
throw ParseError(
{.msg = HintFmt(
"attribute '%1%' already defined at %2%", showAttrSelectionPath(symbols, attrPath), positions[prevPos]),
{.msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]),
.pos = positions[pos]});
}
@@ -193,13 +188,9 @@ inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx pre
}
inline void ParserState::addAttr(
ExprAttrs * attrs,
AttrSelectionPath && attrPath,
const ParserLocation & loc,
Expr * e,
const ParserLocation & exprLoc)
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
{
AttrSelectionPath::iterator i;
AttrPath::iterator i;
// All attrpaths have at least one attr
assert(!attrPath.empty());
auto pos = at(loc);
@@ -245,7 +236,7 @@ inline void ParserState::addAttr(
* symbol as its last element.
*/
inline void
ParserState::addAttr(ExprAttrs * attrs, AttrSelectionPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
{
ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(symbol);
if (j != attrs->attrs->end()) {

View File

@@ -290,7 +290,7 @@ public:
return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first);
}
std::vector<SymbolStr> resolve(const std::span<const Symbol> & symbols) const
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
{
std::vector<SymbolStr> result;
result.reserve(symbols.size());

View File

@@ -57,7 +57,7 @@ void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
e->show(symbols, str);
str << ")." << showAttrSelectionPath(symbols, getAttrPath());
str << ")." << showAttrPath(symbols, getAttrPath());
if (def) {
str << " or (";
def->show(symbols, str);
@@ -69,7 +69,7 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "((";
e->show(symbols, str);
str << ") ? " << showAttrSelectionPath(symbols, attrPath) << ")";
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
}
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
@@ -261,7 +261,7 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
str << "__curPos";
}
std::string showAttrSelectionPath(const SymbolTable & symbols, std::span<const AttrName> attrPath)
std::string showAttrPath(const SymbolTable & symbols, std::span<const AttrName> attrPath)
{
std::ostringstream out;
bool first = true;

View File

@@ -384,7 +384,7 @@ path_start
std::string_view($1.p, $1.l)
);
}
Path path(getHome().string() + std::string($1.p + 1, $1.l - 1));
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, ref<SourceAccessor>(state->rootFS), path);
}
;

View File

@@ -317,51 +317,10 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
}
static RegisterPrimOp primop_scopedImport(
{.name = "scopedImport",
.args = {"scope", "path"},
.doc = R"(
Load, parse, and return the Nix expression in the file *path*, with the attributes from *scope* available as variables in the lexical scope of the imported file.
This function is similar to [`import`](#builtins-import), but allows you to provide additional variables that will be available in the scope of the imported expression.
The *scope* argument must be an attribute set; each attribute becomes a variable available in the imported file.
Built-in functions and values remain accessible unless shadowed by *scope* attributes.
> **Note**
>
> Variables from *scope* shadow built-ins with the same name, allowing you to override built-ins for the imported expression.
> **Note**
>
> Unlike [`import`](#builtins-import), `scopedImport` does not memoize evaluation results.
> While the parsing result may be reused, each call produces a distinct value.
> This is observable through performance and side effects such as [`builtins.trace`](#builtins-trace).
The *path* argument must meet the same criteria as an [interpolated expression](@docroot@/language/string-interpolation.md#interpolated-expression).
If *path* is a directory, the file `default.nix` in that directory is used if it exists.
> **Example**
>
> Create a file `greet.nix`:
>
> ```nix
> # greet.nix
> "${greeting}, ${name}!"
> ```
>
> Import it with additional variables in scope:
>
> ```nix
> scopedImport { greeting = "Hello"; name = "World"; } ./greet.nix
> ```
>
> "Hello, World!"
Evaluation aborts if the file doesn't exist or contains an invalid Nix expression.
)",
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
import(state, pos, *args[1], args[0], v);
}});
PrimOp{
.name = "scopedImport", .arity = 2, .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
import(state, pos, *args[1], args[0], v);
}});
static RegisterPrimOp primop_import(
{.name = "import",
@@ -2807,18 +2766,11 @@ static void addPath(
const NixStringContext & context)
{
try {
StorePathSet refs;
if (path.accessor == state.rootFS && state.store->isInStore(path.path.abs()) && !context.empty()) {
if (path.accessor == state.rootFS && state.store->isInStore(path.path.abs())) {
// FIXME: handle CA derivation outputs (where path needs to
// be rewritten to the actual output).
auto rewrites = state.realiseContext(context);
path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))};
auto [storePath, subPath] = state.store->toStorePath(path.path.abs());
try {
refs = state.store->queryPathInfo(storePath)->references;
} catch (Error &) { // FIXME: should be InvalidPathError
}
}
std::unique_ptr<PathFilter> filter;
@@ -2831,27 +2783,18 @@ static void addPath(
std::optional<StorePath> expectedStorePath;
if (expectedHash)
expectedStorePath = state.store->makeFixedOutputPathFromCA(
name, ContentAddressWithReferences::fromParts(method, *expectedHash, {refs}));
name, ContentAddressWithReferences::fromParts(method, *expectedHash, {}));
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
// FIXME: support refs in fetchToStore()?
auto dstPath = refs.empty() ? fetchToStore(
state.fetchSettings,
*state.store,
path.resolveSymlinks(),
settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
name,
method,
filter.get(),
state.repair)
: state.store->addToStore(
name,
path.resolveSymlinks(),
method,
HashAlgorithm::SHA256,
refs,
filter ? *filter.get() : defaultPathFilter,
state.repair);
auto dstPath = fetchToStore(
state.fetchSettings,
*state.store,
path.resolveSymlinks(),
settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
name,
method,
filter.get(),
state.repair);
if (expectedHash && expectedStorePath != dstPath)
state.error<EvalError>("store path mismatch in (possibly filtered) path added from '%s'", path)
.atPos(pos)

View File

@@ -38,7 +38,7 @@ struct CacheImpl : Cache
{
auto state(_state.lock());
auto dbPath = (getCacheDir() / "fetcher-cache-v4.sqlite").string();
auto dbPath = getCacheDir() + "/fetcher-cache-v4.sqlite";
createDirs(dirOf(dbPath));
state->db = SQLite(dbPath);

View File

@@ -268,10 +268,10 @@ void Fetch::fetch(
return;
}
std::filesystem::path cacheDir = getCacheDir() / "git-lfs";
Path cacheDir = getCacheDir() + "/git-lfs";
std::string key = hashString(HashAlgorithm::SHA256, pointerFilePath.rel()).to_string(HashFormat::Base16, false)
+ "/" + pointer->oid;
std::filesystem::path cachePath = cacheDir / key;
Path cachePath = cacheDir + "/" + key;
if (pathExists(cachePath)) {
debug("using cache entry %s -> %s", key, cachePath);
sink(readFile(cachePath));
@@ -302,8 +302,8 @@ void Fetch::fetch(
downloadToSink(ourl, authHeader, sink, sha256, size);
debug("creating cache entry %s -> %s", key, cachePath);
if (!pathExists(cachePath.parent_path()))
createDirs(cachePath.parent_path());
if (!pathExists(dirOf(cachePath)))
createDirs(dirOf(cachePath));
writeFile(cachePath, sink.s);
debug("%s fetched with git-lfs", pointerFilePath);

View File

@@ -24,7 +24,6 @@
#include <git2/indexer.h>
#include <git2/object.h>
#include <git2/odb.h>
#include <git2/odb_backend.h>
#include <git2/refs.h>
#include <git2/remote.h>
#include <git2/repository.h>
@@ -32,7 +31,6 @@
#include <git2/status.h>
#include <git2/submodule.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/repository.h>
#include <git2/sys/mempack.h>
#include <git2/tag.h>
#include <git2/tree.h>
@@ -91,7 +89,7 @@ typedef std::unique_ptr<git_odb, Deleter<git_odb_free>> ObjectDb;
typedef std::unique_ptr<git_packbuilder, Deleter<git_packbuilder_free>> PackBuilder;
typedef std::unique_ptr<git_indexer, Deleter<git_indexer_free>> Indexer;
static Hash toHash(const git_oid & oid)
Hash toHash(const git_oid & oid)
{
#ifdef GIT_EXPERIMENTAL_SHA256
assert(oid.type == GIT_OID_SHA1);
@@ -110,7 +108,7 @@ static void initLibGit2()
});
}
static git_oid hashToOID(const Hash & hash)
git_oid hashToOID(const Hash & hash)
{
git_oid oid;
if (git_oid_fromstr(&oid, hash.gitRev().c_str()))
@@ -118,7 +116,7 @@ static git_oid hashToOID(const Hash & hash)
return oid;
}
static Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t type = GIT_OBJECT_ANY)
Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t type = GIT_OBJECT_ANY)
{
Object obj;
if (git_object_lookup(Setter(obj), repo, &oid, type)) {
@@ -129,7 +127,7 @@ static Object lookupObject(git_repository * repo, const git_oid & oid, git_objec
}
template<typename T>
static T peelObject(git_object * obj, git_object_t type)
T peelObject(git_object * obj, git_object_t type)
{
T obj2;
if (git_object_peel((git_object **) (typename T::pointer *) Setter(obj2), obj, type)) {
@@ -140,7 +138,7 @@ static T peelObject(git_object * obj, git_object_t type)
}
template<typename T>
static T dupObject(typename T::pointer obj)
T dupObject(typename T::pointer obj)
{
T obj2;
if (git_object_dup((git_object **) (typename T::pointer *) Setter(obj2), (git_object *) obj))
@@ -208,11 +206,11 @@ static void initRepoAtomically(std::filesystem::path & path, bool bare)
if (pathExists(path.string()))
return;
std::filesystem::path tmpDir = createTempDir(path.parent_path());
Path tmpDir = createTempDir(os_string_to_string(PathViewNG{std::filesystem::path(path).parent_path()}));
AutoDelete delTmpDir(tmpDir, true);
Repository tmpRepo;
if (git_repository_init(Setter(tmpRepo), tmpDir.string().c_str(), bare))
if (git_repository_init(Setter(tmpRepo), tmpDir.c_str(), bare))
throw Error("creating Git repository %s: %s", path, git_error_last()->message);
try {
std::filesystem::rename(tmpDir, path);
@@ -247,15 +245,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
* In-memory object store for efficient batched writing to packfiles.
* Owned by `repo`.
*/
git_odb_backend * mempackBackend = nullptr;
git_odb_backend * mempack_backend;
/**
* On-disk packfile object store.
* Owned by `repo`.
*/
git_odb_backend * packBackend = nullptr;
GitRepoImpl(std::filesystem::path _path, bool create, bool bare, bool packfilesOnly = false)
GitRepoImpl(std::filesystem::path _path, bool create, bool bare)
: path(std::move(_path))
, bare(bare)
{
@@ -266,39 +258,15 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
throw Error("opening Git repository %s: %s", path, git_error_last()->message);
ObjectDb odb;
if (packfilesOnly) {
/* Create a fresh object database because by default the repo also
loose object backends. We are not using any of those for the
tarball cache, but libgit2 still does a bunch of unnecessary
syscalls that always fail with ENOENT. NOTE: We are only creating
a libgit2 object here and not modifying the repo. Think of this as
enabling the specific backend.
*/
if (git_odb_new(Setter(odb)))
throw Error("creating Git object database: %s", git_error_last()->message);
if (git_odb_backend_pack(&packBackend, (path / "objects").string().c_str()))
throw Error("creating pack backend: %s", git_error_last()->message);
if (git_odb_add_backend(odb.get(), packBackend, 1))
throw Error("adding pack backend to Git object database: %s", git_error_last()->message);
} else {
if (git_repository_odb(Setter(odb), repo.get()))
throw Error("getting Git object database: %s", git_error_last()->message);
}
if (git_repository_odb(Setter(odb), repo.get()))
throw Error("getting Git object database: %s", git_error_last()->message);
// mempack_backend will be owned by the repository, so we are not expected to free it ourselves.
if (git_mempack_new(&mempackBackend))
if (git_mempack_new(&mempack_backend))
throw Error("creating mempack backend: %s", git_error_last()->message);
if (git_odb_add_backend(odb.get(), mempackBackend, 999))
if (git_odb_add_backend(odb.get(), mempack_backend, 999))
throw Error("adding mempack backend to Git object database: %s", git_error_last()->message);
if (packfilesOnly) {
if (git_repository_set_odb(repo.get(), odb.get()))
throw Error("setting Git object database: %s", git_error_last()->message);
}
}
operator git_repository *()
@@ -319,7 +287,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
git_packbuilder_set_threads(packBuilder.get(), 0 /* autodetect */);
packBuilderContext.handleException(
"preparing packfile", git_mempack_write_thin_pack(mempackBackend, packBuilder.get()));
"preparing packfile", git_mempack_write_thin_pack(mempack_backend, packBuilder.get()));
checkInterrupt();
packBuilderContext.handleException("writing packfile", git_packbuilder_write_buf(&buf, packBuilder.get()));
checkInterrupt();
@@ -352,7 +320,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
if (git_indexer_commit(indexer.get(), &stats))
throw Error("committing git packfile index: %s", git_error_last()->message);
if (git_mempack_reset(mempackBackend))
if (git_mempack_reset(mempack_backend))
throw Error("resetting git mempack backend: %s", git_error_last()->message);
checkInterrupt();
@@ -585,6 +553,27 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
ref<GitFileSystemObjectSink> getFileSystemObjectSink() override;
static int sidebandProgressCallback(const char * str, int len, void * payload)
{
auto act = (Activity *) payload;
act->result(resFetchStatus, trim(std::string_view(str, len)));
return getInterrupted() ? -1 : 0;
}
static int transferProgressCallback(const git_indexer_progress * stats, void * payload)
{
auto act = (Activity *) payload;
act->result(
resFetchStatus,
fmt("%d/%d objects received, %d/%d deltas indexed, %s",
stats->received_objects,
stats->total_objects,
stats->indexed_deltas,
stats->total_deltas,
renderSize(stats->received_bytes)));
return getInterrupted() ? -1 : 0;
}
void fetch(const std::string & url, const std::string & refspec, bool shallow) override
{
Activity act(*logger, lvlTalkative, actFetchTree, fmt("fetching Git repository '%s'", url));
@@ -712,9 +701,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
}
};
ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare, bool packfilesOnly)
ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare)
{
return make_ref<GitRepoImpl>(path, create, bare, packfilesOnly);
return make_ref<GitRepoImpl>(path, create, bare);
}
/**
@@ -1063,18 +1052,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
std::vector<PendingDir> pendingDirs;
/**
* Temporary buffer used by createRegularFile for storing small file contents.
*/
std::string regularFileContentsBuffer;
/**
* If repo has a non-null packBackend, this has a copy of the refresh function
* from the backend virtual table. This is needed to restore it after we've flushed
* the sink. We modify it to avoid unnecessary I/O on non-existent oids.
*/
decltype(::git_odb_backend::refresh) packfileOdbRefresh = nullptr;
void pushBuilder(std::string name)
{
const git_tree_entry * entry;
@@ -1100,29 +1077,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
GitFileSystemObjectSinkImpl(ref<GitRepoImpl> repo)
: repo(repo)
{
/* Monkey-patching the pack backend to only read the pack directory
once. Otherwise it will do a readdir for each added oid when it's
not found and that translates to ~6 syscalls. Since we are never
writing pack files until flushing we can force the odb backend to
read the directory just once. It's very convenient that the vtable is
semi-public interface and is up for grabs.
This is purely an optimization for our use-case with a tarball cache.
libgit2 calls refresh() if the backend provides it when an oid isn't found.
We are only writing objects to a mempack (it has higher priority) and there isn't
a realistic use-case where a previously missing object would appear from thin air
on the disk (unless another process happens to be unpacking a similar tarball to
the cache at the same time, but that's a very unrealistic scenario).
*/
if (auto * backend = repo->packBackend) {
if (backend->refresh(backend)) /* Refresh just once manually. */
throw Error("refreshing packfiles: %s", git_error_last()->message);
/* Save the function pointer to restore it later in flush() and
unset it in the vtable. libgit2 does nothing if it's a nullptr:
https://github.com/libgit2/libgit2/blob/58d9363f02f1fa39e46d49b604f27008e75b72f2/src/libgit2/odb.c#L1922
*/
packfileOdbRefresh = std::exchange(backend->refresh, nullptr);
}
pushBuilder("");
}
@@ -1179,83 +1133,41 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
if (!prepareDirs(pathComponents, false))
return;
using WriteStream = std::unique_ptr<::git_writestream, decltype([](::git_writestream * stream) {
if (stream)
stream->free(stream);
})>;
/* Maximum file size that gets buffered in memory before flushing to a WriteStream,
that's backed by a temporary objects/streamed_git2_* file. We should avoid that
for common cases, since creating (and deleting) a temporary file for each blob
is insanely expensive. */
static constexpr std::size_t maxBufferSize = 1024 * 1024; /* 1 MiB */
git_writestream * stream = nullptr;
if (git_blob_create_from_stream(&stream, *repo, nullptr))
throw Error("creating a blob stream object: %s", git_error_last()->message);
struct CRF : CreateRegularFileSink
{
const CanonPath & path;
GitFileSystemObjectSinkImpl & back;
WriteStream stream;
std::string & contents;
git_writestream * stream;
bool executable = false;
CRF(const CanonPath & path, GitFileSystemObjectSinkImpl & back, std::string & regularFileContentsBuffer)
CRF(const CanonPath & path, GitFileSystemObjectSinkImpl & back, git_writestream * stream)
: path(path)
, back(back)
, stream(nullptr)
, contents(regularFileContentsBuffer)
, stream(stream)
{
contents.clear();
}
void writeToStream(std::string_view data)
{
/* Lazily create the stream. */
if (!stream) {
::git_writestream * stream2 = nullptr;
if (git_blob_create_from_stream(&stream2, *back.repo, nullptr))
throw Error("creating a blob stream object: %s", git_error_last()->message);
stream = WriteStream{stream2};
assert(stream);
}
if (stream->write(stream.get(), data.data(), data.size()))
throw Error("writing a blob for tarball member '%s': %s", path, git_error_last()->message);
}
void operator()(std::string_view data) override
{
/* Already in slow path. Just write to the slow stream. */
if (stream) {
writeToStream(data);
return;
}
contents += data;
if (contents.size() > maxBufferSize) {
writeToStream(contents); /* Will initialize stream. */
contents.clear();
}
if (stream->write(stream, data.data(), data.size()))
throw Error("writing a blob for tarball member '%s': %s", path, git_error_last()->message);
}
void isExecutable() override
{
executable = true;
}
} crf{path, *this, regularFileContentsBuffer};
} crf{path, *this, stream};
func(crf);
git_oid oid;
if (crf.stream) {
/* Call .release(), since git_blob_create_from_stream_commit
acquires ownership and frees the stream. */
if (git_blob_create_from_stream_commit(&oid, crf.stream.release()))
throw Error("creating a blob object for '%s': %s", path, git_error_last()->message);
} else {
if (git_blob_create_from_buffer(&oid, *repo, crf.contents.data(), crf.contents.size()))
throw Error(
"creating a blob object for '%s' from in-memory buffer: %s", path, git_error_last()->message);
}
if (git_blob_create_from_stream_commit(&oid, stream))
throw Error("creating a blob object for tarball member '%s': %s", path, git_error_last()->message);
addToTree(*pathComponents.rbegin(), oid, crf.executable ? GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB);
}
@@ -1339,11 +1251,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
auto [oid, _name] = popBuilder();
if (auto * backend = repo->packBackend) {
/* We are done writing blobs, can restore refresh functionality. */
backend->refresh = packfileOdbRefresh;
}
repo->flush();
return toHash(oid);
@@ -1428,7 +1335,7 @@ namespace fetchers {
ref<GitRepo> Settings::getTarballCache() const
{
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
return GitRepo::openRepo(repoDir, /*create=*/true, /*bare=*/true, /*packfilesOnly=*/true);
return GitRepo::openRepo(repoDir, true, true);
}
} // namespace fetchers

View File

@@ -42,10 +42,10 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st)
return st.st_mtime + static_cast<time_t>(settings.tarballTtl) > now;
}
std::filesystem::path getCachePath(std::string_view key, bool shallow)
Path getCachePath(std::string_view key, bool shallow)
{
return getCacheDir() / "gitv3"
/ (hashString(HashAlgorithm::SHA256, key).to_string(HashFormat::Nix32, false) + (shallow ? "-shallow" : ""));
return getCacheDir() + "/gitv3/" + hashString(HashAlgorithm::SHA256, key).to_string(HashFormat::Nix32, false)
+ (shallow ? "-shallow" : "");
}
// Returns the name of the HEAD branch.
@@ -55,13 +55,13 @@ std::filesystem::path getCachePath(std::string_view key, bool shallow)
//
// ref: refs/heads/main HEAD
// ...
std::optional<std::string> readHead(const std::filesystem::path & path)
std::optional<std::string> readHead(const Path & path)
{
auto [status, output] = runProgram(
RunOptions{
.program = "git",
// FIXME: use 'HEAD' to avoid returning all refs
.args = {"ls-remote", "--symref", path.string()},
.args = {"ls-remote", "--symref", path},
.isInteractive = true,
});
if (status != 0)
@@ -86,9 +86,9 @@ std::optional<std::string> readHead(const std::filesystem::path & path)
// Persist the HEAD ref from the remote repo in the local cached repo.
bool storeCachedHead(const std::string & actualUrl, bool shallow, const std::string & headRef)
{
std::filesystem::path cacheDir = getCachePath(actualUrl, shallow);
Path cacheDir = getCachePath(actualUrl, shallow);
try {
runProgram("git", true, {"-C", cacheDir.string(), "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef});
runProgram("git", true, {"-C", cacheDir, "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef});
} catch (ExecError & e) {
if (
#ifndef WIN32 // TODO abstract over exit status handling on Windows
@@ -109,13 +109,13 @@ std::optional<std::string> readHeadCached(const std::string & actualUrl, bool sh
{
// Create a cache path to store the branch of the HEAD ref. Append something
// in front of the URL to prevent collision with the repository itself.
std::filesystem::path cacheDir = getCachePath(actualUrl, shallow);
std::filesystem::path headRefFile = cacheDir / "HEAD";
Path cacheDir = getCachePath(actualUrl, shallow);
Path headRefFile = cacheDir + "/HEAD";
time_t now = time(0);
struct stat st;
std::optional<std::string> cachedRef;
if (stat(headRefFile.string().c_str(), &st) == 0) {
if (stat(headRefFile.c_str(), &st) == 0) {
cachedRef = readHead(cacheDir);
if (cachedRef != std::nullopt && *cachedRef != gitInitialBranch && isCacheFileWithinTtl(now, st)) {
debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, actualUrl);
@@ -450,7 +450,7 @@ struct GitInputScheme : InputScheme
if (input.getRev())
throw UnimplementedError("cloning a specific revision is not implemented");
args.push_back(destDir.string());
args.push_back(destDir);
runProgram("git", true, args, {}, true);
}
@@ -836,7 +836,7 @@ struct GitInputScheme : InputScheme
auto fetchRef = getAllRefsAttr(input) ? "refs/*:refs/*"
: input.getRev() ? input.getRev()->gitRev()
: ref.compare(0, 5, "refs/") == 0 ? fmt("%1%:%1%", ref)
: ref == "HEAD" ? "HEAD:HEAD"
: ref == "HEAD" ? ref
: fmt("%1%:%1%", "refs/heads/" + ref);
repo->fetch(repoUrl.to_string(), fetchRef, shallow);

View File

@@ -41,16 +41,18 @@ struct GitArchiveInputScheme : InputScheme
/* This ignores empty path segments for back-compat. Older versions used a tokenizeString here. */
auto path = url.pathSegments(/*skipEmpty=*/true) | std::ranges::to<std::vector<std::string>>();
std::optional<std::string> rev;
std::optional<Hash> rev;
std::optional<std::string> ref;
std::optional<std::string> host_url;
auto size = path.size();
if (size == 3) {
if (std::regex_match(path[2], revRegex))
rev = path[2];
else
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
else if (isLegalRefName(path[2]))
ref = path[2];
else
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url, path[2]);
} else if (size > 3) {
std::string rs;
for (auto i = std::next(path.begin(), 2); i != path.end(); i++) {
@@ -59,7 +61,12 @@ struct GitArchiveInputScheme : InputScheme
rs += "/";
}
}
ref = rs;
if (isLegalRefName(rs)) {
ref = rs;
} else {
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url, rs);
}
} else if (size < 2)
throw BadURL("URL '%s' is invalid", url);
@@ -67,32 +74,40 @@ struct GitArchiveInputScheme : InputScheme
if (name == "rev") {
if (rev)
throw BadURL("URL '%s' contains multiple commit hashes", url);
rev = value;
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
} else if (name == "ref") {
if (!isLegalRefName(value))
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
if (ref)
throw BadURL("URL '%s' contains multiple branch/tag names", url);
ref = value;
} else if (name == "host")
} else if (name == "host") {
if (!std::regex_match(value, hostRegex))
throw BadURL("URL '%s' contains an invalid instance host", url);
host_url = value;
}
// FIXME: barf on unsupported attributes
}
Attrs attrs;
attrs.insert_or_assign("type", std::string{schemeName()});
attrs.insert_or_assign("owner", path[0]);
attrs.insert_or_assign("repo", path[1]);
if (ref && rev)
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev());
Input input{};
input.attrs.insert_or_assign("type", std::string{schemeName()});
input.attrs.insert_or_assign("owner", path[0]);
input.attrs.insert_or_assign("repo", path[1]);
if (rev)
attrs.insert_or_assign("rev", *rev);
input.attrs.insert_or_assign("rev", rev->gitRev());
if (ref)
attrs.insert_or_assign("ref", *ref);
input.attrs.insert_or_assign("ref", *ref);
if (host_url)
attrs.insert_or_assign("host", *host_url);
input.attrs.insert_or_assign("host", *host_url);
auto narHash = url.query.find("narHash");
if (narHash != url.query.end())
attrs.insert_or_assign("narHash", narHash->second);
input.attrs.insert_or_assign("narHash", narHash->second);
return inputFromAttrs(settings, attrs);
return input;
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
@@ -139,24 +154,6 @@ struct GitArchiveInputScheme : InputScheme
getStrAttr(attrs, "owner");
getStrAttr(attrs, "repo");
auto ref = maybeGetStrAttr(attrs, "ref");
auto rev = maybeGetStrAttr(attrs, "rev");
if (ref && rev)
throw BadURL(
"input %s contains both a commit hash ('%s') and a branch/tag name ('%s')",
attrsToJSON(attrs),
*rev,
*ref);
if (rev)
Hash::parseAny(*rev, HashAlgorithm::SHA1);
if (ref && !isLegalRefName(*ref))
throw BadURL("input %s contains an invalid branch/tag name", attrsToJSON(attrs));
if (auto host = maybeGetStrAttr(attrs, "host"); host && !std::regex_match(*host, hostRegex))
throw BadURL("input %s contains an invalid instance host", attrsToJSON(attrs));
Input input{};
input.attrs = attrs;
return input;

View File

@@ -32,8 +32,7 @@ struct GitRepo
{
virtual ~GitRepo() {}
static ref<GitRepo>
openRepo(const std::filesystem::path & path, bool create = false, bool bare = false, bool packfilesOnly = false);
static ref<GitRepo> openRepo(const std::filesystem::path & path, bool create = false, bool bare = false);
virtual uint64_t getRevCount(const Hash & rev) = 0;

View File

@@ -39,7 +39,7 @@ struct Registry
static std::shared_ptr<Registry> read(const Settings & settings, const SourcePath & path, RegistryType type);
void write(const std::filesystem::path & path);
void write(const Path & path);
void add(const Input & from, const Input & to, const Attrs & extraAttrs);
@@ -50,9 +50,9 @@ typedef std::vector<std::shared_ptr<Registry>> Registries;
std::shared_ptr<Registry> getUserRegistry(const Settings & settings);
std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const std::filesystem::path & p);
std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Path & p);
std::filesystem::path getUserRegistryPath();
Path getUserRegistryPath();
Registries getRegistries(const Settings & settings, Store & store);

View File

@@ -213,11 +213,11 @@ struct MercurialInputScheme : InputScheme
runHg({"status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0"}),
"\0"s);
std::filesystem::path actualPath(absPath(actualUrl));
Path actualPath(absPath(actualUrl));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath.string()));
std::string file(p, actualPath.string().size() + 1);
assert(hasPrefix(p, actualPath));
std::string file(p, actualPath.size() + 1);
auto st = lstat(p);
@@ -232,7 +232,7 @@ struct MercurialInputScheme : InputScheme
auto storePath = store.addToStore(
input.getName(),
{getFSSourceAccessor(), CanonPath(actualPath.string())},
{getFSSourceAccessor(), CanonPath(actualPath)},
ContentAddressMethod::Raw::NixArchive,
HashAlgorithm::SHA256,
{},
@@ -275,34 +275,35 @@ struct MercurialInputScheme : InputScheme
return makeResult(res->value, res->storePath);
}
std::filesystem::path cacheDir =
getCacheDir() / "hg" / hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false);
Path cacheDir =
fmt("%s/hg/%s",
getCacheDir(),
hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false));
/* If this is a commit hash that we already have, we don't
have to pull again. */
if (!(input.getRev() && pathExists(cacheDir)
&& runProgram(
hgOptions({"log", "-R", cacheDir.string(), "-r", input.getRev()->gitRev(), "--template", "1"}))
&& runProgram(hgOptions({"log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1"}))
.second
== "1")) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
if (pathExists(cacheDir)) {
try {
runHg({"pull", "-R", cacheDir.string(), "--", actualUrl});
runHg({"pull", "-R", cacheDir, "--", actualUrl});
} catch (ExecError & e) {
auto transJournal = cacheDir / ".hg" / "store" / "journal";
auto transJournal = cacheDir + "/.hg/store/journal";
/* hg throws "abandoned transaction" error only if this file exists */
if (pathExists(transJournal)) {
runHg({"recover", "-R", cacheDir.string()});
runHg({"pull", "-R", cacheDir.string(), "--", actualUrl});
runHg({"recover", "-R", cacheDir});
runHg({"pull", "-R", cacheDir, "--", actualUrl});
} else {
throw ExecError(e.status, "'hg pull' %s", statusToString(e.status));
}
}
} else {
createDirs(dirOf(cacheDir.string()));
runHg({"clone", "--noupdate", "--", actualUrl, cacheDir.string()});
createDirs(dirOf(cacheDir));
runHg({"clone", "--noupdate", "--", actualUrl, cacheDir});
}
}
@@ -310,7 +311,7 @@ struct MercurialInputScheme : InputScheme
auto tokens = tokenizeString<std::vector<std::string>>(runHg(
{"log",
"-R",
cacheDir.string(),
cacheDir,
"-r",
input.getRev() ? input.getRev()->gitRev() : *input.getRef(),
"--template",
@@ -327,14 +328,14 @@ struct MercurialInputScheme : InputScheme
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), store))
return makeResult(res->value, res->storePath);
std::filesystem::path tmpDir = createTempDir();
Path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runHg({"archive", "-R", cacheDir.string(), "-r", rev.gitRev(), tmpDir.string()});
runHg({"archive", "-R", cacheDir, "-r", rev.gitRev(), tmpDir});
deletePath(tmpDir / ".hg_archival.txt");
deletePath(tmpDir + "/.hg_archival.txt");
auto storePath = store.addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir.string())});
auto storePath = store.addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)});
Attrs infoAttrs({
{"revCount", (uint64_t) revCount},

View File

@@ -56,7 +56,7 @@ std::shared_ptr<Registry> Registry::read(const Settings & settings, const Source
return registry;
}
void Registry::write(const std::filesystem::path & path)
void Registry::write(const Path & path)
{
nlohmann::json arr;
for (auto & entry : entries) {
@@ -74,7 +74,7 @@ void Registry::write(const std::filesystem::path & path)
json["version"] = 2;
json["flakes"] = std::move(arr);
createDirs(path.parent_path());
createDirs(dirOf(path));
writeFile(path, json.dump(2));
}
@@ -90,38 +90,38 @@ void Registry::remove(const Input & input)
entries.end());
}
static std::filesystem::path getSystemRegistryPath()
static Path getSystemRegistryPath()
{
return settings.nixConfDir / "registry.json";
return settings.nixConfDir + "/registry.json";
}
static std::shared_ptr<Registry> getSystemRegistry(const Settings & settings)
{
static auto systemRegistry = Registry::read(
settings,
SourcePath{getFSSourceAccessor(), CanonPath{getSystemRegistryPath().string()}}.resolveSymlinks(),
SourcePath{getFSSourceAccessor(), CanonPath{getSystemRegistryPath()}}.resolveSymlinks(),
Registry::System);
return systemRegistry;
}
std::filesystem::path getUserRegistryPath()
Path getUserRegistryPath()
{
return getConfigDir() / "registry.json";
return getConfigDir() + "/registry.json";
}
std::shared_ptr<Registry> getUserRegistry(const Settings & settings)
{
static auto userRegistry = Registry::read(
settings,
SourcePath{getFSSourceAccessor(), CanonPath{getUserRegistryPath().string()}}.resolveSymlinks(),
SourcePath{getFSSourceAccessor(), CanonPath{getUserRegistryPath()}}.resolveSymlinks(),
Registry::User);
return userRegistry;
}
std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const std::filesystem::path & p)
std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Path & p)
{
static auto customRegistry = Registry::read(
settings, SourcePath{getFSSourceAccessor(), CanonPath{p.string()}}.resolveSymlinks(), Registry::Custom);
static auto customRegistry =
Registry::read(settings, SourcePath{getFSSourceAccessor(), CanonPath{p}}.resolveSymlinks(), Registry::Custom);
return customRegistry;
}
@@ -150,7 +150,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, St
if (!isAbsolute(path)) {
auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
if (auto store2 = dynamic_cast<LocalFSStore *>(&store))
store2->addPermRoot(storePath, (getCacheDir() / "flake-registry.json").string());
store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
return {store.requireStoreObjectAccessor(storePath)};
} else {
return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks();

View File

@@ -86,7 +86,7 @@ TEST_F(nix_api_store_test, nix_api_load_flake)
auto tmpDir = nix::createTempDir();
nix::AutoDelete delTmpDir(tmpDir, true);
nix::writeFile(tmpDir / "flake.nix", R"(
nix::writeFile(tmpDir + "/flake.nix", R"(
{
outputs = { ... }: {
hello = "potato";
@@ -121,8 +121,7 @@ TEST_F(nix_api_store_test, nix_api_load_flake)
assert_ctx_ok();
ASSERT_NE(nullptr, parseFlags);
auto r0 = nix_flake_reference_parse_flags_set_base_directory(
ctx, parseFlags, tmpDir.string().c_str(), tmpDir.string().size());
auto r0 = nix_flake_reference_parse_flags_set_base_directory(ctx, parseFlags, tmpDir.c_str(), tmpDir.size());
assert_ctx_ok();
ASSERT_EQ(NIX_OK, r0);
@@ -178,8 +177,8 @@ TEST_F(nix_api_store_test, nix_api_load_flake_with_flags)
auto tmpDir = nix::createTempDir();
nix::AutoDelete delTmpDir(tmpDir, true);
nix::createDirs(tmpDir / "b");
nix::writeFile(tmpDir / "b" / "flake.nix", R"(
nix::createDirs(tmpDir + "/b");
nix::writeFile(tmpDir + "/b/flake.nix", R"(
{
outputs = { ... }: {
hello = "BOB";
@@ -187,18 +186,18 @@ TEST_F(nix_api_store_test, nix_api_load_flake_with_flags)
}
)");
nix::createDirs(tmpDir / "a");
nix::writeFile(tmpDir / "a" / "flake.nix", R"(
nix::createDirs(tmpDir + "/a");
nix::writeFile(tmpDir + "/a/flake.nix", R"(
{
inputs.b.url = ")" + tmpDir.string() + R"(/b";
inputs.b.url = ")" + tmpDir + R"(/b";
outputs = { b, ... }: {
hello = b.hello;
};
}
)");
nix::createDirs(tmpDir / "c");
nix::writeFile(tmpDir / "c" / "flake.nix", R"(
nix::createDirs(tmpDir + "/c");
nix::writeFile(tmpDir + "/c/flake.nix", R"(
{
outputs = { ... }: {
hello = "Claire";
@@ -231,8 +230,7 @@ TEST_F(nix_api_store_test, nix_api_load_flake_with_flags)
assert_ctx_ok();
ASSERT_NE(nullptr, parseFlags);
auto r0 = nix_flake_reference_parse_flags_set_base_directory(
ctx, parseFlags, tmpDir.string().c_str(), tmpDir.string().size());
auto r0 = nix_flake_reference_parse_flags_set_base_directory(ctx, parseFlags, tmpDir.c_str(), tmpDir.size());
assert_ctx_ok();
ASSERT_EQ(NIX_OK, r0);

View File

@@ -31,9 +31,9 @@ namespace nix::flake {
// setting name -> setting value -> allow or ignore.
typedef std::map<std::string, std::map<std::string, bool>> TrustedList;
std::filesystem::path trustedListPath()
Path trustedListPath()
{
return getDataDir() / "trusted-settings.json";
return getDataDir() + "/trusted-settings.json";
}
static TrustedList readTrustedList()
@@ -48,7 +48,7 @@ static TrustedList readTrustedList()
static void writeTrustedList(const TrustedList & trustedList)
{
auto path = trustedListPath();
createDirs(path.parent_path());
createDirs(dirOf(path));
writeFile(path, nlohmann::json(trustedList).dump());
}

View File

@@ -74,7 +74,7 @@ FlakeRef::resolve(const fetchers::Settings & fetchSettings, Store & store, fetch
FlakeRef parseFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake,
bool preserveRelativePaths)
@@ -101,7 +101,7 @@ fromParsedURL(const fetchers::Settings & fetchSettings, ParsedURL && parsedURL,
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake,
bool preserveRelativePaths)
@@ -121,7 +121,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
path = absPath(path, baseDir->string(), true);
path = absPath(path, baseDir, true);
if (isFlake) {
@@ -243,7 +243,7 @@ parseFlakeIdRef(const fetchers::Settings & fetchSettings, const std::string & ur
std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir,
const std::optional<Path> & baseDir,
bool isFlake)
{
try {
@@ -252,7 +252,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
/* Here we know that the path must not contain encoded '/' or NUL bytes. */
auto path = renderUrlPathEnsureLegal(parsed.path);
if (!isAbsolute(path))
parsed.path = splitString<std::vector<std::string>>(absPath(path, baseDir->string()), "/");
parsed.path = splitString<std::vector<std::string>>(absPath(path, *baseDir), "/");
}
return fromParsedURL(fetchSettings, std::move(parsed), isFlake);
} catch (BadURL &) {
@@ -263,7 +263,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake,
bool preserveRelativePaths)
@@ -347,7 +347,7 @@ FlakeRef FlakeRef::canonicalize() const
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
{

View File

@@ -200,7 +200,7 @@ struct LockFlags
/**
* The path to a lock file to write to instead of the `flake.lock` file in the top-level flake
*/
std::optional<std::filesystem::path> outputLockFilePath;
std::optional<Path> outputLockFilePath;
/**
* Flake inputs to be overridden.

View File

@@ -95,7 +95,7 @@ std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef);
FlakeRef parseFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir = {},
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true,
bool preserveRelativePaths = false);
@@ -106,7 +106,7 @@ FlakeRef parseFlakeRef(
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir = {},
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true,
bool preserveRelativePaths = false);
@@ -117,7 +117,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<std::filesystem::path> & baseDir = {},
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true);

View File

@@ -10,30 +10,30 @@
namespace nix {
struct PluginFilesSetting : public BaseSetting<std::list<std::filesystem::path>>
struct PluginFilesSetting : public BaseSetting<Paths>
{
bool pluginsLoaded = false;
PluginFilesSetting(
Config * options,
const std::list<std::filesystem::path> & def,
const Paths & def,
const std::string & name,
const std::string & description,
const StringSet & aliases = {})
: BaseSetting<std::list<std::filesystem::path>>(def, true, name, description, aliases)
: BaseSetting<Paths>(def, true, name, description, aliases)
{
options->addSetting(this);
}
std::list<std::filesystem::path> parse(const std::string & str) const override;
Paths parse(const std::string & str) const override;
};
std::list<std::filesystem::path> PluginFilesSetting::parse(const std::string & str) const
Paths PluginFilesSetting::parse(const std::string & str) const
{
if (pluginsLoaded)
throw UsageError(
"plugin-files set after plugins were loaded, you may need to move the flag before the subcommand");
return BaseSetting<std::list<std::filesystem::path>>::parse(str);
return BaseSetting<Paths>::parse(str);
}
struct PluginSettings : Config

View File

@@ -304,7 +304,7 @@ void printVersion(const std::string & programName)
std::cout << "System type: " << settings.thisSystem << "\n";
std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n";
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
std::cout << "System configuration file: " << (settings.nixConfDir / "nix.conf") << "\n";
std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n";
std::cout << "User configuration files: " << concatStringsSep(":", settings.nixUserConfFiles) << "\n";
std::cout << "Store directory: " << settings.nixStore << "\n";
std::cout << "State directory: " << settings.nixStateDir << "\n";
@@ -321,7 +321,16 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
try {
fun();
try {
fun();
} catch (...) {
/* Subtle: we have to make sure that any `interrupted'
condition is discharged before we reach printMsg()
below, since otherwise it will throw an (uncaught)
exception. */
setInterruptThrown();
throw;
}
} catch (Exit & e) {
return e.status;
} catch (UsageError & e) {

View File

@@ -41,9 +41,9 @@ protected:
{
#ifdef _WIN32
// no `mkdtemp` with MinGW
auto tmpl = nix::defaultTempDir() / "tests_nix-store.";
auto tmpl = nix::defaultTempDir() + "/tests_nix-store.";
for (size_t i = 0; true; ++i) {
nixDir = tmpl.string() + std::to_string(i);
nixDir = tmpl + std::string{i};
if (std::filesystem::create_directory(nixDir))
break;
}

View File

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

View File

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

View File

@@ -1,4 +1,8 @@
{
"hash": "sha256-9vLqj0XYoFfJVmoz+ZR02i5camYE1zYSFlDicwxvsKM=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "9vLqj0XYoFfJVmoz+ZR02i5camYE1zYSFlDicwxvsKM="
},
"method": "nar"
}

View File

@@ -1,4 +1,8 @@
{
"hash": "sha256-8OTC92xYkW7CWPJGhRvqCR0U1CR6L8PhhpRGGxgW4Ts=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "8OTC92xYkW7CWPJGhRvqCR0U1CR6L8PhhpRGGxgW4Ts="
},
"method": "text"
}

View File

@@ -1,4 +1,8 @@
{
"hash": "sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
},
"method": "flat"
}

View File

@@ -1,4 +1,8 @@
{
"hash": "sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
},
"method": "nar"
}

View File

@@ -1,4 +1,8 @@
{
"hash": "sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
},
"method": "text"
}

View File

@@ -12,16 +12,23 @@
},
"info": {
"ca": {
"hash": "sha256-f1eduuSIYC1BofXA1tycF79Ai2NSMJQtUErx5DxLYSU=",
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "f1eduuSIYC1BofXA1tycF79Ai2NSMJQtUErx5DxLYSU="
},
"method": "nar"
},
"deriver": null,
"narHash": "sha256-f1eduuSIYC1BofXA1tycF79Ai2NSMJQtUErx5DxLYSU=",
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "f1eduuSIYC1BofXA1tycF79Ai2NSMJQtUErx5DxLYSU="
},
"narSize": 120,
"references": [],
"registrationTime": null,
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
}

View File

@@ -0,0 +1,36 @@
{
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
},
"method": "nar"
},
"compression": "xz",
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"downloadSize": 4029176,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"registrationTime": 23423,
"signatures": [
"asdf",
"qwer"
],
"ultimate": true,
"url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz",
"version": 2
}

View File

@@ -1,22 +0,0 @@
{
"ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
"compression": "xz",
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"downloadSize": 4029176,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"registrationTime": 23423,
"signatures": [
"asdf",
"qwer"
],
"storeDir": "/nix/store",
"ultimate": true,
"url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz",
"version": 1
}

View File

@@ -1,11 +0,0 @@
{
"ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"storeDir": "/nix/store",
"version": 1
}

View File

@@ -1,9 +0,0 @@
{
"ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
]
}

View File

@@ -1,25 +0,0 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"compression": "xz",
"deriver": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"downloadSize": 4029176,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"registrationTime": 23423,
"signatures": [
"asdf",
"qwer"
],
"storeDir": "/nix/store",
"ultimate": true,
"url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz",
"version": 2
}

View File

@@ -1,14 +0,0 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"storeDir": "/nix/store",
"version": 2
}

View File

@@ -0,0 +1,21 @@
{
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
},
"method": "nar"
},
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"version": 2
}

View File

@@ -1,12 +1,15 @@
{
"ca": null,
"deriver": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narSize": 0,
"references": [],
"registrationTime": null,
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
}

View File

@@ -0,0 +1,11 @@
{
"ca": null,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narSize": 0,
"references": [],
"version": 2
}

View File

@@ -1,7 +1,18 @@
{
"ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
},
"method": "nar"
},
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
},
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
@@ -12,7 +23,6 @@
"asdf",
"qwer"
],
"storeDir": "/nix/store",
"ultimate": true,
"version": 1
"version": 2
}

View File

@@ -1,12 +0,0 @@
{
"ca": null,
"deriver": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 0,
"references": [],
"registrationTime": null,
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 1
}

View File

@@ -1,8 +0,0 @@
{
"ca": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 0,
"references": [],
"storeDir": "/nix/store",
"version": 1
}

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