Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9697754418 |
@@ -1,9 +0,0 @@
|
||||
# shellcheck shell=bash
|
||||
source_env_if_exists .envrc.local
|
||||
# TODO: `use flake .#native-clangStdenvPackages` on macOS?
|
||||
use flake ".#${LIX_SHELL_VARIANT:-default}" "${LIX_SHELL_EXTRA_ARGS[@]}"
|
||||
export MAKEFLAGS="$MAKEFLAGS -e"
|
||||
if [[ -n "$NIX_BUILD_CORES" ]]; then
|
||||
export MAKEFLAGS="$MAKEFLAGS -j $NIX_BUILD_CORES"
|
||||
fi
|
||||
export GTEST_BRIEF=1
|
||||
@@ -25,7 +25,7 @@ you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
## `nix --version` output
|
||||
## `nix-env --version` output
|
||||
|
||||
## Additional context
|
||||
|
||||
|
||||
@@ -146,16 +146,9 @@ result
|
||||
result-*
|
||||
|
||||
.vscode/
|
||||
.direnv/
|
||||
.envrc.local
|
||||
|
||||
# clangd and possibly more
|
||||
.cache/
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
# ClangBuildAnalyzer output, see maintainers/buildtime_report.sh
|
||||
buildtime.bin
|
||||
|
||||
.envrc.local
|
||||
|
||||
@@ -20,7 +20,8 @@ makefiles = \
|
||||
misc/fish/local.mk \
|
||||
misc/zsh/local.mk \
|
||||
misc/systemd/local.mk \
|
||||
misc/launchd/local.mk
|
||||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
BasedOnStyle: llvm
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "HasPrefixSuffix.hh"
|
||||
#include <clang/AST/ASTTypeTraits.h>
|
||||
#include <clang/AST/Expr.h>
|
||||
#include <clang/AST/PrettyPrinter.h>
|
||||
#include <clang/AST/Type.h>
|
||||
#include <clang/ASTMatchers/ASTMatchers.h>
|
||||
#include <clang/Basic/Diagnostic.h>
|
||||
#include <clang/Frontend/FrontendAction.h>
|
||||
#include <clang/Frontend/FrontendPluginRegistry.h>
|
||||
#include <clang/Tooling/Transformer/SourceCode.h>
|
||||
#include <clang/Tooling/Transformer/SourceCodeBuilders.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
void HasPrefixSuffixCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
traverse(clang::TK_AsIs,
|
||||
callExpr(callee(functionDecl(anyOf(hasName("hasPrefix"),
|
||||
hasName("hasSuffix")))
|
||||
.bind("callee-decl")),
|
||||
optionally(hasArgument(
|
||||
0, cxxConstructExpr(
|
||||
hasDeclaration(functionDecl(hasParameter(
|
||||
0, parmVarDecl(hasType(
|
||||
asString("const char *")))))))
|
||||
.bind("implicit-cast"))))
|
||||
.bind("call")),
|
||||
this);
|
||||
}
|
||||
|
||||
void HasPrefixSuffixCheck::check(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
|
||||
const auto *CalleeDecl = Result.Nodes.getNodeAs<FunctionDecl>("callee-decl");
|
||||
auto FuncName = std::string(CalleeDecl->getName());
|
||||
std::string NewName;
|
||||
if (FuncName == "hasPrefix") {
|
||||
NewName = "starts_with";
|
||||
} else if (FuncName == "hasSuffix") {
|
||||
NewName = "ends_with";
|
||||
} else {
|
||||
llvm_unreachable("nix-has-prefix: invalid callee");
|
||||
}
|
||||
|
||||
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("call");
|
||||
const auto *ImplicitConvertArg =
|
||||
Result.Nodes.getNodeAs<CXXConstructExpr>("implicit-cast");
|
||||
|
||||
const auto *Lhs = MatchedDecl->getArg(0);
|
||||
const auto *Rhs = MatchedDecl->getArg(1);
|
||||
auto Diag = diag(MatchedDecl->getExprLoc(), FuncName + " is deprecated");
|
||||
|
||||
std::string Text = "";
|
||||
|
||||
// Form possible cast to string_view, or nothing.
|
||||
if (ImplicitConvertArg) {
|
||||
Text = "std::string_view(";
|
||||
Text.append(tooling::getText(*Lhs, *Result.Context));
|
||||
Text.append(").");
|
||||
} else {
|
||||
Text.append(*tooling::buildAccess(*Lhs, *Result.Context));
|
||||
}
|
||||
|
||||
// Call .starts_with.
|
||||
Text.append(NewName);
|
||||
Text.push_back('(');
|
||||
Text.append(tooling::getText(*Rhs, *Result.Context));
|
||||
Text.push_back(')');
|
||||
|
||||
Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(), Text);
|
||||
|
||||
// for (const auto *arg : MatchedDecl->arguments()) {
|
||||
// arg->dumpColor();
|
||||
// arg->getType().dump();
|
||||
// }
|
||||
}
|
||||
}; // namespace nix::clang_tidy
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
///@file
|
||||
/// This is an example of a clang-tidy automated refactoring against the Nix
|
||||
/// codebase. The refactoring has been completed in
|
||||
/// https://gerrit.lix.systems/c/lix/+/565 so this code is around as
|
||||
/// an example.
|
||||
|
||||
#include <clang-tidy/ClangTidyCheck.h>
|
||||
#include <clang/ASTMatchers/ASTMatchFinder.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::tidy;
|
||||
using namespace llvm;
|
||||
|
||||
class HasPrefixSuffixCheck : public ClangTidyCheck {
|
||||
public:
|
||||
HasPrefixSuffixCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
}; // namespace nix::clang_tidy
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <clang-tidy/ClangTidyModule.h>
|
||||
#include <clang-tidy/ClangTidyModuleRegistry.h>
|
||||
#include "HasPrefixSuffix.hh"
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
using namespace clang;
|
||||
using namespace clang::tidy;
|
||||
|
||||
class NixClangTidyChecks : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<HasPrefixSuffixCheck>("nix-hasprefixsuffix");
|
||||
}
|
||||
};
|
||||
|
||||
static ClangTidyModuleRegistry::Add<NixClangTidyChecks> X("nix-module", "Adds nix specific checks");
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
# Clang tidy lints for Nix
|
||||
|
||||
This is a skeleton of a clang-tidy lints library for Nix.
|
||||
|
||||
Currently there is one check (which is already obsolete as it has served its
|
||||
goal and is there as an example), `HasPrefixSuffixCheck`.
|
||||
|
||||
## Running fixes/checks
|
||||
|
||||
One file:
|
||||
|
||||
```
|
||||
ninja -C build && clang-tidy --checks='-*,nix-*' --load=build/libnix-clang-tidy.so -p ../compile_commands.json --fix ../src/libcmd/installables.cc
|
||||
```
|
||||
|
||||
Several files, in parallel:
|
||||
|
||||
```
|
||||
ninja -C build && run-clang-tidy -checks='-*,nix-*' -load=build/libnix-clang-tidy.so -p .. -fix ../src | tee -a clang-tidy-result
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
* https://firefox-source-docs.mozilla.org/code-quality/static-analysis/writing-new/clang-query.html
|
||||
* https://clang.llvm.org/docs/LibASTMatchersReference.html
|
||||
* https://devblogs.microsoft.com/cppblog/exploring-clang-tooling-part-3-rewriting-code-with-clang-tidy/
|
||||
|
||||
## Developing new checks
|
||||
|
||||
Put something like so in `myquery.txt`:
|
||||
|
||||
```
|
||||
set traversal IgnoreUnlessSpelledInSource
|
||||
# ^ Ignore implicit AST nodes. May need to use AsIs depending on how you are
|
||||
# working.
|
||||
set bind-root true
|
||||
# ^ true unless you use any .bind("foo") commands
|
||||
set print-matcher true
|
||||
enable output dump
|
||||
match callExpr(callee(functionDecl(hasName("hasPrefix"))), optionally(hasArgument( 0, cxxConstructExpr(hasDeclaration(functionDecl(hasParameter(0, parmVarDecl(hasType(asString("const char *"))).bind("meow2"))))))))
|
||||
```
|
||||
|
||||
Then run, e.g. `clang-query --preload hasprefix.query -p compile_commands.json src/libcmd/installables.cc`.
|
||||
|
||||
With this you can iterate a query before writing it in C++ and suffering from
|
||||
C++.
|
||||
|
||||
### Tips and tricks for the C++
|
||||
|
||||
There is a function `dump()` on many things that will dump to stderr. Also
|
||||
`llvm::errs()` lets you print to stderr.
|
||||
|
||||
When I wrote `HasPrefixSuffixCheck`, I was not really able to figure out how
|
||||
the structured replacement system was supposed to work. In principle you can
|
||||
describe the replacement with a nice DSL. Look up the Stencil system in Clang
|
||||
for details.
|
||||
@@ -1,8 +0,0 @@
|
||||
project('nix-clang-tidy', ['cpp', 'c'],
|
||||
version : '0.1',
|
||||
default_options : ['warning_level=3', 'cpp_std=c++20'])
|
||||
|
||||
llvm = dependency('Clang', version: '>= 14', modules: ['libclang'])
|
||||
sources = ['HasPrefixSuffix.cc', 'NixClangTidyChecks.cc']
|
||||
shared_module('nix-clang-tidy', sources,
|
||||
dependencies: llvm)
|
||||
@@ -342,13 +342,6 @@ AC_SUBST(doc_generate)
|
||||
# Look for lowdown library.
|
||||
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Look for toml11, a required dependency.
|
||||
AC_ARG_VAR([TOML11_HEADERS], [include path of toml11 headers])
|
||||
AC_LANG_PUSH(C++)
|
||||
[CXXFLAGS="-I $TOML11_HEADERS $CXXFLAGS"]
|
||||
AC_CHECK_HEADER([toml.hpp], [], [AC_MSG_ERROR([toml11 is not found.])])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
# Setuid installations.
|
||||
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
|
||||
|
||||
+2
-2
@@ -151,9 +151,9 @@ $(d)/language.json: $(doc_nix)
|
||||
|
||||
# Generate "Upcoming release" notes (or clear it and remove from menu)
|
||||
$(d)/src/release-notes/rl-next.md: $(d)/rl-next $(d)/rl-next/*
|
||||
@if type -p build-release-notes > /dev/null; then \
|
||||
@if type -p changelog-d > /dev/null; then \
|
||||
echo " GEN " $@; \
|
||||
build-release-notes doc/manual/rl-next > $@; \
|
||||
changelog-d doc/manual/rl-next > $@; \
|
||||
else \
|
||||
echo " NULL " $@; \
|
||||
true > $@; \
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
synopsis: Clang build timing analysis
|
||||
cls: 587
|
||||
---
|
||||
|
||||
We now have Clang build profiling available, which generates Chrome
|
||||
tracing files for each compilation unit. To enable it, run `meson configure
|
||||
build -Dprofile-build=enabled` then rerun the compilation.
|
||||
|
||||
If you want to make the build go faster, do a clang build with meson, then run
|
||||
`maintainers/buildtime_report.sh build`, then contemplate how to improve the
|
||||
build time.
|
||||
|
||||
You can also look at individual object files' traces in
|
||||
<https://ui.perfetto.dev>.
|
||||
@@ -0,0 +1,2 @@
|
||||
organization: NixOS
|
||||
repository: nix
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
synopsis: "`--debugger` can now access bindings from `let` expressions"
|
||||
prs: 9918
|
||||
issues: 8827
|
||||
issues: 8827.
|
||||
---
|
||||
|
||||
Breakpoints and errors in the bindings of a `let` expression can now access
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
synopsis: Stop vendoring toml11
|
||||
cls: 675
|
||||
---
|
||||
|
||||
We don't apply any patches to it, and vendoring it locks users into
|
||||
bugs (it hasn't been updated since its introduction in late 2021).
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
synopsis: Duplicate attribute reports are more accurate
|
||||
cls: 557
|
||||
# prs: cl 557
|
||||
---
|
||||
|
||||
Duplicate attribute errors are now more accurate, showing the path at which an error was detected rather than the full, possibly longer, path that caused the error.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
synopsis: Disallow empty search regex in `nix search`
|
||||
prs: 9481
|
||||
---
|
||||
prs: #9481
|
||||
description: {
|
||||
|
||||
[`nix search`](@docroot@/command-ref/new-cli/nix3-search.md) now requires a search regex to be passed. To show all packages, use `^`.
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
synopsis: "`Overhaul `nix flake update` and `nix flake lock` UX"
|
||||
prs: 8817
|
||||
---
|
||||
|
||||
The interface for creating and updating lock files has been overhauled:
|
||||
|
||||
- [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
|
||||
It will *never* update existing inputs.
|
||||
|
||||
- [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
|
||||
- Passing no arguments will update all inputs of the current flake, just like it already did.
|
||||
- Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
|
||||
- To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.
|
||||
|
||||
- The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
|
||||
They are superceded by `nix flake update`.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
synopsis: "`builtins.nixVersion` now returns a fixed value \"2.18.3-lix\""
|
||||
cls: 558
|
||||
---
|
||||
|
||||
`builtins.nixVersion` now returns a fixed value `"2.18.3-lix"`. This prevents
|
||||
feature detection assuming that features that exist in Nix post-Lix-branch-off
|
||||
might exist, even though the Lix version is greater than the Nix version.
|
||||
|
||||
In the future, check for builtins for feature detection. If a feature cannot be
|
||||
detected by *those* means, please file a Lix bug.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
synopsis: Coercion errors include the failing value
|
||||
issues: 561
|
||||
prs: 9754
|
||||
issues: #561
|
||||
prs: #9754
|
||||
---
|
||||
|
||||
The `error: cannot coerce a <TYPE> to a string` message now includes the value
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
synopsis: Type errors include the failing value
|
||||
issues: 561
|
||||
prs: 9753
|
||||
issues: #561
|
||||
prs: #9753
|
||||
---
|
||||
|
||||
In errors like `value is an integer while a list was expected`, the message now
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
synopsis: Add `repl-overlays` option
|
||||
prs: 10203
|
||||
---
|
||||
|
||||
A `repl-overlays` option has been added, which specifies files that can overlay
|
||||
and modify the top-level bindings in `nix repl`. For example, with the
|
||||
following contents in `~/.config/nix/repl.nix`:
|
||||
|
||||
```nix
|
||||
info: final: prev: let
|
||||
optionalAttrs = predicate: attrs:
|
||||
if predicate
|
||||
then attrs
|
||||
else {};
|
||||
in
|
||||
optionalAttrs (prev ? legacyPackages && prev.legacyPackages ? ${info.currentSystem})
|
||||
{
|
||||
pkgs = prev.legacyPackages.${info.currentSystem};
|
||||
}
|
||||
```
|
||||
|
||||
We can run `nix repl` and use `pkgs` to refer to `legacyPackages.${currentSystem}`:
|
||||
|
||||
```ShellSession
|
||||
$ nix repl --repl-overlays ~/.config/nix/repl.nix nixpkgs
|
||||
Nix 2.21.0pre20240309_4111bb6
|
||||
Type :? for help.
|
||||
Loading installable 'flake:nixpkgs#'...
|
||||
Added 5 variables.
|
||||
Loading 'repl-overlays'...
|
||||
Added 6 variables.
|
||||
nix-repl> pkgs.bash
|
||||
«derivation /nix/store/g08b5vkwwh0j8ic9rkmd8mpj878rk62z-bash-5.2p26.drv»
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
synopsis: reintroduce shortened `-E` form for `--expr` to new CLI
|
||||
cls: 605
|
||||
---
|
||||
|
||||
In the past, it was possible to supply a shorter `-E` flag instead of fully
|
||||
specifying `--expr` every time you wished to provide an expression that would
|
||||
be evaluated to produce the given command's input. This was retained for the
|
||||
`--file` flag when the new CLI utilities were written with `-f`, but `-E` was
|
||||
dropped.
|
||||
|
||||
We now restore the `-E` short form for better UX. This is most useful for
|
||||
`nix eval` but most any command that takes an Installable argument should benefit
|
||||
from it as well.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
synopsis: Upstart scripts removed
|
||||
cls: 574
|
||||
---
|
||||
|
||||
Upstart scripts have been removed from Lix, since Upstart is obsolete and has
|
||||
not been shipped by any major distributions for many years. If these are
|
||||
necessary to your use case, please back port them to your packaging.
|
||||
@@ -340,6 +340,9 @@ Significant changes should add the following header, which moves them to the top
|
||||
significance: significant
|
||||
```
|
||||
|
||||
<!-- Keep an eye on https://codeberg.org/fgaz/changelog-d/issues/1 -->
|
||||
See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog).
|
||||
|
||||
### Build process
|
||||
|
||||
Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`.
|
||||
|
||||
@@ -150,9 +150,7 @@
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
build-release-notes =
|
||||
final.buildPackages.callPackage ./maintainers/build-release-notes.nix { };
|
||||
clangbuildanalyzer = final.buildPackages.callPackage ./misc/clangbuildanalyzer.nix { };
|
||||
changelog-d = final.buildPackages.callPackage ./misc/changelog-d.nix { };
|
||||
boehmgc-nix = (final.boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs (o: {
|
||||
@@ -207,21 +205,9 @@
|
||||
build = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
|
||||
# NOTE: mesonBuildClang depends on mesonBuild depends on build to avoid OOMs
|
||||
# on aarch64 builders caused by too many parallel compiler/linker processes.
|
||||
mesonBuild = forAllSystems (system:
|
||||
(self.packages.${system}.nix.override {
|
||||
buildWithMeson = true;
|
||||
}).overrideAttrs (prev: {
|
||||
buildInputs = prev.buildInputs ++ [ self.packages.${system}.nix ];
|
||||
}));
|
||||
mesonBuildClang = forAllSystems (system:
|
||||
(nixpkgsFor.${system}.stdenvs.clangStdenvPackages.nix.override {
|
||||
buildWithMeson = true;
|
||||
}).overrideAttrs (prev: {
|
||||
buildInputs = prev.buildInputs ++ [ self.hydraJobs.mesonBuild.${system} ];
|
||||
})
|
||||
);
|
||||
mesonBuild = forAllSystems (system: self.packages.${system}.nix.override {
|
||||
buildWithMeson = true;
|
||||
});
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
|
||||
@@ -240,7 +226,7 @@
|
||||
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit versionSuffix fileset officialRelease buildUnreleasedNotes;
|
||||
inherit (pkgs) build-release-notes;
|
||||
inherit (pkgs) changelog-d;
|
||||
internalApiDocs = true;
|
||||
boehmgc = pkgs.boehmgc-nix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
|
||||
@@ -280,21 +266,17 @@
|
||||
};
|
||||
};
|
||||
|
||||
checks = forAllSystems (system: let
|
||||
rl-next-check = name: dir:
|
||||
let pkgs = nixpkgsFor.${system}.native;
|
||||
in pkgs.buildPackages.runCommand "test-${name}-release-notes" { } ''
|
||||
LANG=C.UTF-8 ${lib.getExe pkgs.build-release-notes} ${dir} >$out
|
||||
'';
|
||||
in {
|
||||
checks = forAllSystems (system: {
|
||||
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
|
||||
mesonBuild = self.hydraJobs.mesonBuild.${system};
|
||||
mesonBuildClang = self.hydraJobs.mesonBuildClang.${system};
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||
rl-next = rl-next-check "rl-next" ./doc/manual/rl-next;
|
||||
rl-next-dev = rl-next-check "rl-next-dev" ./doc/manual/rl-next-dev;
|
||||
rl-next =
|
||||
let pkgs = nixpkgsFor.${system}.native;
|
||||
in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } ''
|
||||
LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
|
||||
'';
|
||||
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||
});
|
||||
@@ -341,28 +323,16 @@
|
||||
forDevShell = true;
|
||||
};
|
||||
in
|
||||
(nix.override {
|
||||
buildUnreleasedNotes = true;
|
||||
officialRelease = false;
|
||||
}).overrideAttrs (prev: {
|
||||
# Required for clang-tidy checks
|
||||
buildInputs = prev.buildInputs ++ lib.optionals (stdenv.cc.isClang) [ pkgs.llvmPackages.llvm pkgs.llvmPackages.clang-unwrapped.dev ];
|
||||
nix.overrideAttrs (prev: {
|
||||
nativeBuildInputs = prev.nativeBuildInputs
|
||||
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
|
||||
# Required for clang-tidy checks
|
||||
++ lib.optionals (stdenv.cc.isClang) [ pkgs.buildPackages.cmake pkgs.buildPackages.ninja pkgs.buildPackages.llvmPackages.llvm.dev ]
|
||||
++ lib.optional
|
||||
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
|
||||
# for some reason that seems accidental and was changed in
|
||||
# NixOS 24.05-pre, clang-tools is pinned to LLVM 14 when
|
||||
# default LLVM is newer.
|
||||
(pkgs.buildPackages.clang-tools.override { inherit (pkgs.buildPackages) llvmPackages; })
|
||||
pkgs.buildPackages.clang-tools
|
||||
++ [
|
||||
# FIXME(Qyriad): remove once the migration to Meson is complete.
|
||||
pkgs.buildPackages.meson
|
||||
pkgs.buildPackages.ninja
|
||||
|
||||
pkgs.buildPackages.clangbuildanalyzer
|
||||
];
|
||||
|
||||
src = null;
|
||||
@@ -378,7 +348,7 @@
|
||||
# Make bash completion work.
|
||||
XDG_DATA_DIRS+=:$out/share
|
||||
'';
|
||||
} // lib.optionalAttrs (stdenv.buildPlatform.isLinux && pkgs.glibcLocales != null) {
|
||||
} // lib.optionalAttrs (stdenv.isLinux && pkgs.glibcLocales != null) {
|
||||
# Required to make non-NixOS Linux not complain about missing locale files during configure in a dev shell
|
||||
LOCALE_ARCHIVE = "${lib.getLib pkgs.glibcLocales}/lib/locale/locale-archive";
|
||||
});
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
# 2024-03-24: jade benchmarked the default sanitize reporting in clang and got
|
||||
# a regression of about 10% on hackage-packages.nix with clang. So we are trapping instead.
|
||||
#
|
||||
# This has an overhead of 0-4% on gcc and unmeasurably little on clang, in
|
||||
# Nix evaluation benchmarks.
|
||||
DEFAULT_SANITIZE_FLAGS = -fsanitize=signed-integer-overflow -fsanitize-undefined-trap-on-error
|
||||
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch $(DEFAULT_SANITIZE_FLAGS)
|
||||
GLOBAL_LDFLAGS += $(DEFAULT_SANITIZE_FLAGS)
|
||||
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch
|
||||
# Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers.
|
||||
ERROR_SWITCH_ENUM = -Werror=switch-enum
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{ lib, python3, writeShellScriptBin }:
|
||||
|
||||
writeShellScriptBin "build-release-notes" ''
|
||||
exec ${lib.getExe (python3.withPackages (p: [ p.python-frontmatter ]))} \
|
||||
${./build-release-notes.py} "$@"
|
||||
''
|
||||
@@ -1,66 +0,0 @@
|
||||
import frontmatter
|
||||
import sys
|
||||
import pathlib
|
||||
import textwrap
|
||||
|
||||
GH_BASE = "https://github.com/NixOS/nix"
|
||||
FORGEJO_BASE = "https://git.lix.systems/lix-project/lix"
|
||||
GERRIT_BASE = "https://gerrit.lix.systems/c/lix/+"
|
||||
|
||||
SIGNIFICANCECES = {
|
||||
None: 0,
|
||||
'significant': 10,
|
||||
}
|
||||
|
||||
def format_link(ident: str, gh_part: str, fj_part: str) -> str:
|
||||
# FIXME: deprecate github as default
|
||||
if ident.isdigit():
|
||||
num, link, base = int(ident), f"#{ident}", f"{GH_BASE}/{gh_part}"
|
||||
elif ident.startswith("gh#"):
|
||||
num, link, base = int(ident[3:]), ident, f"{GH_BASE}/{gh_part}"
|
||||
elif ident.startswith("fj#"):
|
||||
num, link, base = int(ident[3:]), ident, f"{FORGEJO_BASE}/{fj_part}"
|
||||
else:
|
||||
raise Exception("unrecognized reference format", ident)
|
||||
return f"[{link}]({base}/{num})"
|
||||
|
||||
def format_issue(issue: str) -> str:
|
||||
return format_link(issue, "issues", "issues")
|
||||
def format_pr(pr: str) -> str:
|
||||
return format_link(pr, "pull", "pulls")
|
||||
def format_cl(cl: str) -> str:
|
||||
clid = int(cl)
|
||||
return f"[cl/{clid}]({GERRIT_BASE}/{clid})"
|
||||
|
||||
paths = pathlib.Path(sys.argv[1]).glob('*.md')
|
||||
entries = []
|
||||
for p in paths:
|
||||
try:
|
||||
e = frontmatter.load(p)
|
||||
if 'synopsis' not in e.metadata:
|
||||
raise Exception('missing synposis')
|
||||
unknownKeys = set(e.metadata.keys()) - set(('synopsis', 'cls', 'issues', 'prs', 'significance'))
|
||||
if unknownKeys:
|
||||
raise Exception('unknown keys', unknownKeys)
|
||||
entries.append((p, e))
|
||||
except Exception as e:
|
||||
e.add_note(f"in {p}")
|
||||
raise
|
||||
|
||||
for p, entry in sorted(entries, key=lambda e: (-SIGNIFICANCECES[e[1].metadata.get('significance')], e[0])):
|
||||
try:
|
||||
header = entry.metadata['synopsis']
|
||||
links = []
|
||||
links += map(format_issue, str(entry.metadata.get('issues', "")).split())
|
||||
links += map(format_pr, str(entry.metadata.get('prs', "")).split())
|
||||
links += map(format_cl, str(entry.metadata.get('cls', "")).split())
|
||||
if links != []:
|
||||
header += " " + " ".join(links)
|
||||
if header:
|
||||
print(f"- {header}")
|
||||
print()
|
||||
print(textwrap.indent(entry.content, ' '))
|
||||
print()
|
||||
except Exception as e:
|
||||
e.add_note(f"in {p}")
|
||||
raise
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Generates a report of build time based on a meson build using -ftime-trace in
|
||||
# Clang.
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: $0 BUILD-DIR [filename]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scriptdir=$(cd "$(dirname -- "$0")" || exit ; pwd -P)
|
||||
filename=${2:-$scriptdir/../buildtime.bin}
|
||||
|
||||
if [ "$(meson introspect "$1" --buildoptions | jq -r '.[] | select(.name == "profile-build") | .value')" != enabled ]; then
|
||||
echo 'This build was not done with profile-build enabled, so cannot generate a report' >&2
|
||||
# shellcheck disable=SC2016
|
||||
echo 'Run `meson configure build -Dprofile-build=enabled` then rebuild, first' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ClangBuildAnalyzer --all "$1" "$filename" && ClangBuildAnalyzer --analyze "$filename"
|
||||
@@ -96,7 +96,7 @@ def issues_to_import():
|
||||
yield from paginate('GET', '/repos/nixos/nix/issues?state=open&labels=lix-import')
|
||||
|
||||
def issues_already_imported():
|
||||
yield from paginate('GET', '/repos/lix-project/lix/issues?state=all&labels=imported')
|
||||
yield from paginate('GET', '/repos/lix-project/lix/issues?state=open&labels=imported')
|
||||
|
||||
|
||||
UPSTREAM_ISSUE_RE = re.compile(r'^Upstream-Issue: https://git\.lix\.systems/NixOS/nix/issues/(\d+)$', re.MULTILINE)
|
||||
@@ -117,7 +117,6 @@ def new_issue(title, body, labels):
|
||||
'labels': labels,
|
||||
'body': body,
|
||||
'title': title,
|
||||
'dont_notify': True,
|
||||
})
|
||||
|
||||
already_imported = make_already_imported()
|
||||
@@ -140,6 +139,11 @@ def import_issue(iss: Issue):
|
||||
new_issue(new_title, new_body, new_labels)
|
||||
|
||||
def go():
|
||||
print('Have you turned off the forgejo mailer or limited the queue workers to 0 (assuming that works)? Enter "We have" if so:')
|
||||
answer = input('> ')
|
||||
if answer != 'We have':
|
||||
return
|
||||
|
||||
log.info('Importing issues!')
|
||||
for issue in issues_to_import():
|
||||
import_issue(DataClassUnpack.instantiate(Issue, issue))
|
||||
|
||||
@@ -152,7 +152,7 @@ section_title="Release $version_full ($DATE)"
|
||||
# TODO add minor number, and append?
|
||||
echo "# $section_title"
|
||||
echo
|
||||
build-release-notes doc/manual/rl-next
|
||||
changelog-d doc/manual/rl-next | sed -e 's/ *$//'
|
||||
) | tee -a $file
|
||||
|
||||
log "Wrote $file"
|
||||
|
||||
+34
-103
@@ -16,24 +16,15 @@
|
||||
# that become part of the final Nix command (things like `src/nix-build/*.cc`).
|
||||
#
|
||||
# Finally, src/nix/meson.build defines the Nix command itself, relying on all prior meson files.
|
||||
#
|
||||
# Unit tests are setup in tests/unit/meson.build, under the test suite "check".
|
||||
#
|
||||
# Functional tests are a bit more complicated. Generally they're defined in
|
||||
# tests/functional/meson.build, and rely on helper scripts meson/setup-functional-tests.py
|
||||
# and meson/run-test.py. Scattered around also are configure_file() invocations, which must
|
||||
# be placed in specific directories' meson.build files to create the right directory tree
|
||||
# in the build directory.
|
||||
|
||||
project('lix', 'cpp',
|
||||
version : run_command('bash', '-c', 'echo -n $(cat ./.version)$VERSION_SUFFIX', check : true).stdout().strip(),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'cpp_std=c++20',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
)
|
||||
|
||||
@@ -55,11 +46,6 @@ path_opts = [
|
||||
'state-dir',
|
||||
'log-dir',
|
||||
]
|
||||
# For your grepping pleasure, this loop sets the following variables that aren't mentioned
|
||||
# literally above:
|
||||
# store_dir
|
||||
# state_dir
|
||||
# log_dir
|
||||
foreach optname : path_opts
|
||||
varname = optname.replace('-', '_')
|
||||
path = get_option(optname)
|
||||
@@ -70,28 +56,11 @@ foreach optname : path_opts
|
||||
endif
|
||||
endforeach
|
||||
|
||||
|
||||
enable_tests = get_option('enable-tests')
|
||||
|
||||
tests_args = []
|
||||
|
||||
if get_option('tests-color')
|
||||
tests_args += '--gtest_color=yes'
|
||||
endif
|
||||
|
||||
if get_option('tests-brief')
|
||||
tests_args += '--gtest_brief=1'
|
||||
endif
|
||||
|
||||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
host_system = host_machine.cpu_family() + '-' + host_machine.system()
|
||||
message('canonical Nix system name:', host_system)
|
||||
|
||||
is_linux = host_machine.system() == 'linux'
|
||||
is_x64 = host_machine.cpu_family() == 'x86_64'
|
||||
|
||||
deps = [ ]
|
||||
configdata = { }
|
||||
|
||||
@@ -110,20 +79,16 @@ configdata += {
|
||||
boost = dependency('boost', required : true, modules : ['context', 'coroutine', 'container'])
|
||||
deps += boost
|
||||
|
||||
# cpuid only makes sense on x86_64
|
||||
cpuid_required = is_x64 ? get_option('cpuid') : false
|
||||
cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required)
|
||||
cpuid = dependency('libcpuid', 'cpuid', required : get_option('cpuid'))
|
||||
configdata += {
|
||||
'HAVE_LIBCPUID': cpuid.found().to_int(),
|
||||
}
|
||||
}
|
||||
deps += cpuid
|
||||
|
||||
# seccomp only makes sense on Linux
|
||||
seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false
|
||||
seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required)
|
||||
seccomp = dependency('libseccomp', 'seccomp', required : get_option('seccomp-sandboxing'))
|
||||
configdata += {
|
||||
'HAVE_SECCOMP': seccomp.found().to_int(),
|
||||
}
|
||||
}
|
||||
|
||||
libarchive = dependency('libarchive', required : true)
|
||||
deps += libarchive
|
||||
@@ -132,16 +97,15 @@ brotli = [
|
||||
dependency('libbrotlicommon', required : true),
|
||||
dependency('libbrotlidec', required : true),
|
||||
dependency('libbrotlienc', required : true),
|
||||
]
|
||||
]
|
||||
deps += brotli
|
||||
|
||||
openssl = dependency('libcrypto', 'openssl', required : true)
|
||||
deps += openssl
|
||||
|
||||
aws_sdk = dependency('aws-cpp-sdk-core', required : false)
|
||||
aws_sdk = dependency('aws-cpp-sdk-core', required : true)
|
||||
if aws_sdk.found()
|
||||
# The AWS pkg-config adds -std=c++11.
|
||||
# https://github.com/aws/aws-sdk-cpp/issues/2673
|
||||
aws_sdk = aws_sdk.partial_dependency(
|
||||
compile_args : false,
|
||||
includes : true,
|
||||
@@ -165,10 +129,9 @@ if aws_sdk.found()
|
||||
)
|
||||
endif
|
||||
|
||||
aws_s3 = dependency('aws-cpp-sdk-s3', required : false)
|
||||
aws_s3 = dependency('aws-cpp-sdk-s3', required : true)
|
||||
if aws_s3.found()
|
||||
# The AWS pkg-config adds -std=c++11.
|
||||
# https://github.com/aws/aws-sdk-cpp/issues/2673
|
||||
aws_s3 = aws_s3.partial_dependency(
|
||||
compile_args : false,
|
||||
includes : true,
|
||||
@@ -198,33 +161,16 @@ deps += editline
|
||||
lowdown = dependency('lowdown', version : '>=0.9.0', required : true)
|
||||
deps += lowdown
|
||||
|
||||
# HACK(Qyriad): rapidcheck's pkg-config doesn't include the libs lol
|
||||
rapidcheck_meson = dependency('rapidcheck', required : enable_tests)
|
||||
rapidcheck = declare_dependency(dependencies : rapidcheck_meson, link_args : ['-lrapidcheck'])
|
||||
rapidcheck = dependency('rapidcheck', required : false)
|
||||
deps += rapidcheck
|
||||
|
||||
gtest = [
|
||||
dependency('gtest', required : enable_tests),
|
||||
dependency('gtest_main', required : enable_tests),
|
||||
dependency('gmock', required : enable_tests),
|
||||
dependency('gmock_main', required : enable_tests),
|
||||
]
|
||||
gtest = dependency('gtest', required : false)
|
||||
deps += gtest
|
||||
|
||||
toml11 = dependency('toml11', version : '>=3.7.0', required : true)
|
||||
deps += toml11
|
||||
|
||||
nlohmann_json = dependency('nlohmann_json', required : true)
|
||||
deps += nlohmann_json
|
||||
|
||||
#
|
||||
# Build-time tools
|
||||
#
|
||||
bash = find_program('bash')
|
||||
coreutils = find_program('coreutils')
|
||||
dot = find_program('dot', required : false)
|
||||
pymod = import('python')
|
||||
python = pymod.find_installation('python3')
|
||||
|
||||
# Used to workaround https://github.com/mesonbuild/meson/issues/2320 in src/nix/meson.build.
|
||||
installcmd = find_program('install')
|
||||
@@ -247,12 +193,13 @@ bison = find_program('bison')
|
||||
flex = find_program('flex')
|
||||
|
||||
# This is how Nix does generated headers...
|
||||
# other instances of header generation use a very similar command.
|
||||
# FIXME(Qyriad): do we really need to use the shell for this?
|
||||
gen_header_sh = 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\''
|
||||
gen_header = generator(
|
||||
bash,
|
||||
arguments : [ '-c', gen_header_sh ],
|
||||
arguments : [
|
||||
'-c',
|
||||
'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'',
|
||||
],
|
||||
capture : true,
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
)
|
||||
@@ -298,6 +245,19 @@ foreach funcspec : check_funcs
|
||||
}
|
||||
endforeach
|
||||
|
||||
# extern "C++" functions can't be checked with the normal mechanism (much less member functions),
|
||||
# so we have to do this one ourselves.
|
||||
has_pubsetbuf = cxx.compiles('''
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
static char buf[1024];
|
||||
decltype(cerr.rdbuf()->pubsetbuf(buf, sizeof(buf))) _;
|
||||
''')
|
||||
summary('have std::basic_streambuf::pubsetbuf', has_pubsetbuf, bool_yn : true)
|
||||
configdata += {
|
||||
'HAVE_PUBSETBUF': has_pubsetbuf.to_int(),
|
||||
}
|
||||
|
||||
config_h = configure_file(
|
||||
configuration : {
|
||||
'PACKAGE_NAME': '"' + meson.project_name() + '"',
|
||||
@@ -323,41 +283,12 @@ add_project_arguments(
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
if cxx.get_id() in ['gcc', 'clang']
|
||||
# 2024-03-24: jade benchmarked the default sanitize reporting in clang and got
|
||||
# a regression of about 10% on hackage-packages.nix with clang. So we are trapping instead.
|
||||
#
|
||||
# This has an overhead of 0-4% on gcc and unmeasurably little on clang, in
|
||||
# Nix evaluation benchmarks.
|
||||
#
|
||||
# N.B. Meson generates a completely nonsense warning here:
|
||||
# https://github.com/mesonbuild/meson/issues/9822
|
||||
# Both of these args cannot be written in the default meson configuration.
|
||||
# b_sanitize=signed-integer-overflow is ignored, and
|
||||
# -fsanitize-undefined-trap-on-error is not representable.
|
||||
sanitize_args = ['-fsanitize=signed-integer-overflow', '-fsanitize-undefined-trap-on-error']
|
||||
add_project_arguments(sanitize_args, language: 'cpp')
|
||||
add_project_link_arguments(sanitize_args, language: 'cpp')
|
||||
endif
|
||||
|
||||
add_project_link_arguments('-pthread', language : 'cpp')
|
||||
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
|
||||
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Generate Chromium tracing files for each compiled file, which enables
|
||||
# maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds.
|
||||
#
|
||||
# They can also be manually viewed at https://ui.perfetto.dev
|
||||
if get_option('profile-build').require(meson.get_compiler('cpp').get_id() == 'clang').enabled()
|
||||
add_project_arguments('-ftime-trace', language: 'cpp')
|
||||
endif
|
||||
add_project_link_arguments(
|
||||
'-pthread',
|
||||
# FIXME(Qyriad): autoconf did this only if not Darwin, Solaris, or FreeBSD
|
||||
# (...so only if Linux?)
|
||||
'-Wl,--no-copy-dt-needed-entries',
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
subdir('src')
|
||||
subdir('scripts')
|
||||
subdir('misc')
|
||||
|
||||
if enable_tests
|
||||
subdir('tests/unit')
|
||||
subdir('tests/functional')
|
||||
endif
|
||||
|
||||
+2
-18
@@ -4,7 +4,7 @@ option('gc', type : 'feature',
|
||||
)
|
||||
# TODO(Qyriad): is this feature maintained?
|
||||
option('embedded-sandbox-shell', type : 'feature',
|
||||
description : 'include the sandbox shell in the Nix binary',
|
||||
description : 'include the sandbox shell in the Nix bindary',
|
||||
)
|
||||
|
||||
option('cpuid', type : 'feature',
|
||||
@@ -19,27 +19,11 @@ option('sandbox-shell', type : 'string', value : 'busybox',
|
||||
description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)',
|
||||
)
|
||||
|
||||
option('enable-tests', type : 'boolean', value : true,
|
||||
description : 'whether to enable tests or not (requires rapidcheck and gtest)',
|
||||
)
|
||||
|
||||
option('tests-color', type : 'boolean', value : true,
|
||||
description : 'set to false to disable color output in gtest',
|
||||
)
|
||||
|
||||
option('tests-brief', type : 'boolean', value : false,
|
||||
description : 'set to true for shorter tests output',
|
||||
)
|
||||
|
||||
option('profile-build', type : 'feature', value: 'disabled',
|
||||
description : 'whether to enable -ftime-trace in clang builds, allowing for speeding up the build.'
|
||||
)
|
||||
|
||||
option('store-dir', type : 'string', value : '/nix/store',
|
||||
description : 'path of the Nix store',
|
||||
)
|
||||
|
||||
option('state-dir', type : 'string', value : '/nix/var',
|
||||
option('state-dir', type : 'string', value : '/nix/var/nix',
|
||||
description : 'path to store state in for Nix',
|
||||
)
|
||||
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
# Meson will call this with an absolute path to Bash.
|
||||
# The shebang is just for convenience.
|
||||
|
||||
# The parser and lexer tab are generated via custom Meson targets in src/libexpr/meson.build,
|
||||
# but Meson doesn't support marking only part of a target for install. The generation creates
|
||||
# both headers (parser-tab.hh, lexer-tab.hh) and source files (parser-tab.cc, lexer-tab.cc),
|
||||
# and we definitely want the former installed, but not the latter. This script is added to
|
||||
# Meson's install steps to correct this, as the logic for it is just complex enough to
|
||||
# warrant separate and careful handling, because both Meson's configured include directory
|
||||
# may or may not be an absolute path, and DESTDIR may or may not be set at all, but can't be
|
||||
# manipulated in Meson logic.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "cleanup-install: removing Meson-placed C++ sources from dest includedir"
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
This script is a helper for this project's Meson buildsystem to run Lix's
|
||||
functional tests. It is an analogue to mk/run-test.sh in the autoconf+Make
|
||||
buildsystem.
|
||||
|
||||
These tests are run in the installCheckPhase in Lix's derivation, and as such
|
||||
expect to be run after the project has already been "installed" to some extent.
|
||||
Look at meson/setup-functional-tests.py for more details.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
name = 'run-test.py'
|
||||
|
||||
if 'MESON_BUILD_ROOT' not in os.environ:
|
||||
raise ValueError(f'{name}: this script must be run from the Meson build system')
|
||||
|
||||
def main():
|
||||
|
||||
tests_dir = Path(os.path.join(os.environ['MESON_BUILD_ROOT'], 'tests/functional'))
|
||||
|
||||
parser = argparse.ArgumentParser(name)
|
||||
parser.add_argument('target', help='the script path relative to tests/functional to run')
|
||||
args = parser.parse_args()
|
||||
|
||||
target = Path(args.target)
|
||||
# The test suite considers the test's name to be the path to the test relative to
|
||||
# `tests/functional`, but without the file extension.
|
||||
# e.g. for `tests/functional/flakes/develop.sh`, the test name is `flakes/develop`
|
||||
test_name = target.with_suffix('').as_posix()
|
||||
if not target.is_absolute():
|
||||
target = tests_dir.joinpath(target).resolve()
|
||||
|
||||
assert target.exists(), f'{name}: test {target} does not exist; did you run `meson install`?'
|
||||
|
||||
bash = os.environ.get('BASH', shutil.which('bash'))
|
||||
if bash is None:
|
||||
raise ValueError(f'{name}: bash executable not found and BASH environment variable not set')
|
||||
|
||||
test_environment = os.environ | {
|
||||
'TEST_NAME': test_name,
|
||||
# mk/run-test.sh did this, but I don't know if it has any effect since it seems
|
||||
# like the tests that interact with remote stores set it themselves?
|
||||
'NIX_REMOTE': '',
|
||||
}
|
||||
|
||||
# Initialize testing.
|
||||
init_result = subprocess.run([bash, '-e', 'init.sh'], cwd=tests_dir, env=test_environment)
|
||||
if init_result.returncode != 0:
|
||||
print(f'{name}: internal error initializing {args.target}', file=sys.stderr)
|
||||
print('[ERROR]')
|
||||
# Meson interprets exit code 99 as indicating an *error* in the testing process.
|
||||
return 99
|
||||
|
||||
# Run the test itself.
|
||||
test_result = subprocess.run([bash, '-e', target.name], cwd=target.parent, env=test_environment)
|
||||
|
||||
if test_result.returncode == 0:
|
||||
print('[PASS]')
|
||||
elif test_result.returncode == 99:
|
||||
print('[SKIP]')
|
||||
# Meson interprets exit code 77 as indicating a skipped test.
|
||||
return 77
|
||||
else:
|
||||
print('[FAIL]')
|
||||
|
||||
return test_result.returncode
|
||||
|
||||
try:
|
||||
sys.exit(main())
|
||||
except AssertionError as e:
|
||||
# This should mean that this test was run not-from-Meson, probably without
|
||||
# having run `meson install` first, which is not an bug in this script.
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(99)
|
||||
except Exception as e:
|
||||
print(f'{name}: INTERNAL ERROR running test ({sys.argv}): {e}', file=sys.stderr)
|
||||
print(f'this is a bug in {name}')
|
||||
sys.exit(99)
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
So like. This script is cursed.
|
||||
It's a helper for this project's Meson buildsystem for Lix's functional tests.
|
||||
The functional tests are a bunch of bash scripts, that each expect to be run from the
|
||||
directory from the directory that that script is in, and also expect modifications to have
|
||||
happened to the source tree, and even splork files around. The last is against the spirit
|
||||
of Meson (and personally annoying), to have build processes that aren't self-contained in the
|
||||
out-of-source build directory, but more problematically we need configured files in the test
|
||||
tree.
|
||||
So. We copy the tests tree into the build directory.
|
||||
Meson doesn't have a good way of doing this natively -- the best you could do is subdir()
|
||||
into every directory in the tests tree and configure_file(copy : true) on every file,
|
||||
but this won't copy symlinks as symlinks, which we need since the test suite has, well,
|
||||
tests on symlinks.
|
||||
However, the functional tests are normally run during Lix's derivation's installCheckPhase,
|
||||
after Lix has already been "installed" somewhere. So in Meson we setup add this file as an
|
||||
install script and copy everything in tests/functional to the build directory, preserving
|
||||
things like symlinks, even broken ones (which are intentional).
|
||||
|
||||
TODO(Qyriad): when we remove the old build system entirely, we can instead fix the tests.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os, os.path
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
name = 'setup-functional-tests.py'
|
||||
|
||||
if 'MESON_SOURCE_ROOT' not in os.environ or 'MESON_BUILD_ROOT' not in os.environ:
|
||||
raise ValueError(f'{name}: this script must be run from the Meson build system')
|
||||
|
||||
print(f'{name}: mirroring tests/functional to build directory')
|
||||
|
||||
tests_source = Path(os.environ['MESON_SOURCE_ROOT']) / 'tests/functional'
|
||||
tests_build = Path(os.environ['MESON_BUILD_ROOT']) / 'tests/functional'
|
||||
|
||||
def main():
|
||||
|
||||
os.chdir(tests_build)
|
||||
|
||||
for src_dirpath, src_dirnames, src_filenames in os.walk(tests_source):
|
||||
src_dirpath = Path(src_dirpath)
|
||||
assert src_dirpath.is_absolute(), f'{src_dirpath=} is not absolute'
|
||||
|
||||
# os.walk() gives us the absolute path to the directory we're currently in as src_dirpath.
|
||||
# We want to mirror from the perspective of `tests_source`.
|
||||
rel_subdir = src_dirpath.relative_to(tests_source)
|
||||
assert (not rel_subdir.is_absolute()), f'{rel_subdir=} is not relative'
|
||||
|
||||
# And then join that relative path on `tests_build` to get the absolute
|
||||
# path in the build directory that corresponds to `src_dirpath`.
|
||||
build_dirpath = tests_build / rel_subdir
|
||||
assert build_dirpath.is_absolute(), f'{build_dirpath=} is not absolute'
|
||||
|
||||
# More concretely, for the test file tests/functional/ca/build.sh:
|
||||
# - src_dirpath is `$MESON_SOURCE_ROOT/tests/functional/ca`
|
||||
# - rel_subidr is `ca`
|
||||
# - build_dirpath is `$MESON_BUILD_ROOT/tests/functional/ca`
|
||||
|
||||
# `src_dirname` are directories underneath `src_dirpath`, and will be relative
|
||||
# to `src_dirpath`.
|
||||
for src_dirname in src_dirnames:
|
||||
# Take the name of the directory in the tests source and join it on `src_dirpath`
|
||||
# to get the full path to this specific directory in the tests source.
|
||||
src = src_dirpath / src_dirname
|
||||
# If there isn't *something* here, then our logic is wrong.
|
||||
# Path.exists(follow_symlinks=False) wasn't added until Python 3.12, so we use
|
||||
# os.path.lexists() here.
|
||||
assert os.path.lexists(src), f'{src=} does not exist'
|
||||
|
||||
# Take the name of this directory and join it on `build_dirpath` to get the full
|
||||
# path to the directory in `build/tests/functional` that we need to create.
|
||||
dest = build_dirpath / src_dirname
|
||||
if src.is_symlink():
|
||||
src_target = src.readlink()
|
||||
dest.unlink(missing_ok=True)
|
||||
dest.symlink_to(src_target)
|
||||
else:
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for src_filename in src_filenames:
|
||||
# os.walk() should be giving us relative filenames.
|
||||
# If it isn't, our path joins will be veeeery wrong.
|
||||
assert (not Path(src_filename).is_absolute()), f'{src_filename=} is not relative'
|
||||
|
||||
src = src_dirpath / src_filename
|
||||
dst = build_dirpath / src_filename
|
||||
# Mildly misleading name -- unlink removes ordinary files as well as symlinks.
|
||||
dst.unlink(missing_ok=True)
|
||||
# shutil.copy2() best-effort preserves metadata.
|
||||
shutil.copy2(src, dst, follow_symlinks=False)
|
||||
|
||||
|
||||
try:
|
||||
sys.exit(main())
|
||||
except Exception as e:
|
||||
# Any error is likely a bug in this script.
|
||||
print(f'{name}: INTERNAL ERROR setting up functional tests: {e}', file=sys.stderr)
|
||||
print(traceback.format_exc())
|
||||
print(f'this is a bug in {name}')
|
||||
sys.exit(1)
|
||||
@@ -1,8 +0,0 @@
|
||||
configure_file(
|
||||
input : 'completion.sh',
|
||||
output : 'nix',
|
||||
install : true,
|
||||
install_dir : datadir / 'bash-completion/completions',
|
||||
install_mode : 'rw-r--r--',
|
||||
copy : true,
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
{ mkDerivation, aeson, base, bytestring, cabal-install-parsers
|
||||
, Cabal-syntax, containers, directory, filepath, frontmatter
|
||||
, generic-lens-lite, lib, mtl, optparse-applicative, parsec, pretty
|
||||
, regex-applicative, text, pkgs
|
||||
}:
|
||||
let rev = "f30f6969e9cd8b56242309639d58acea21c99d06";
|
||||
in
|
||||
mkDerivation {
|
||||
pname = "changelog-d";
|
||||
version = "0.1";
|
||||
src = pkgs.fetchurl {
|
||||
name = "changelog-d-${rev}.tar.gz";
|
||||
url = "https://codeberg.org/roberth/changelog-d/archive/${rev}.tar.gz";
|
||||
hash = "sha256-8a2+i5u7YoszAgd5OIEW0eYUcP8yfhtoOIhLJkylYJ4=";
|
||||
} // { inherit rev; };
|
||||
isLibrary = false;
|
||||
isExecutable = true;
|
||||
libraryHaskellDepends = [
|
||||
aeson base bytestring cabal-install-parsers Cabal-syntax containers
|
||||
directory filepath frontmatter generic-lens-lite mtl parsec pretty
|
||||
regex-applicative text
|
||||
];
|
||||
executableHaskellDepends = [
|
||||
base bytestring Cabal-syntax directory filepath
|
||||
optparse-applicative
|
||||
];
|
||||
doHaddock = false;
|
||||
description = "Concatenate changelog entries into a single one";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
mainProgram = "changelog-d";
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Taken temporarily from <nixpkgs/pkgs/by-name/ch/changelog-d/package.nix>
|
||||
{
|
||||
callPackage,
|
||||
lib,
|
||||
haskell,
|
||||
haskellPackages,
|
||||
}:
|
||||
|
||||
let
|
||||
hsPkg = haskellPackages.callPackage ./changelog-d.cabal.nix { };
|
||||
|
||||
addCompletions = haskellPackages.generateOptparseApplicativeCompletions ["changelog-d"];
|
||||
|
||||
haskellModifications =
|
||||
lib.flip lib.pipe [
|
||||
addCompletions
|
||||
haskell.lib.justStaticExecutables
|
||||
];
|
||||
|
||||
mkDerivationOverrides = finalAttrs: oldAttrs: {
|
||||
|
||||
version = oldAttrs.version + "-git-${lib.strings.substring 0 7 oldAttrs.src.rev}";
|
||||
|
||||
meta = oldAttrs.meta // {
|
||||
homepage = "https://codeberg.org/roberth/changelog-d";
|
||||
maintainers = [ lib.maintainers.roberth ];
|
||||
};
|
||||
|
||||
};
|
||||
in
|
||||
(haskellModifications hsPkg).overrideAttrs mkDerivationOverrides
|
||||
@@ -1,27 +0,0 @@
|
||||
# Upstreaming here, can be deleted once it's upstreamed:
|
||||
# https://github.com/NixOS/nixpkgs/pull/297102
|
||||
{ stdenv, lib, cmake, fetchFromGitHub }:
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "clangbuildanalyzer";
|
||||
version = "1.5.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "aras-p";
|
||||
repo = "ClangBuildAnalyzer";
|
||||
rev = "v${finalAttrs.version}";
|
||||
sha256 = "sha256-kmgdk634zM0W0OoRoP/RzepArSipa5bNqdVgdZO9gxo=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "Tool for analyzing Clang's -ftrace-time files";
|
||||
homepage = "https://github.com/aras-p/ClangBuildAnalyzer";
|
||||
maintainers = with lib.maintainers; [ lf- ];
|
||||
license = lib.licenses.unlicense;
|
||||
platforms = lib.platforms.unix;
|
||||
mainProgram = "ClangBuildAnalyzer";
|
||||
};
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
configure_file(
|
||||
input : 'completion.fish',
|
||||
output : 'nix.fish',
|
||||
install : true,
|
||||
install_dir : datadir / 'fish/vendor_completions.d',
|
||||
install_mode : 'rw-r--r--',
|
||||
copy : true,
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
subdir('bash')
|
||||
subdir('fish')
|
||||
subdir('zsh')
|
||||
|
||||
subdir('systemd')
|
||||
@@ -1,25 +0,0 @@
|
||||
foreach config : [ 'nix-daemon.socket', 'nix-daemon.service' ]
|
||||
configure_file(
|
||||
input : config + '.in',
|
||||
output : config,
|
||||
install : true,
|
||||
install_dir : prefix / 'lib/systemd/system',
|
||||
install_mode : 'rw-r--r--',
|
||||
configuration : {
|
||||
'storedir' : store_dir,
|
||||
'localstatedir' : state_dir,
|
||||
'bindir' : bindir,
|
||||
},
|
||||
)
|
||||
endforeach
|
||||
|
||||
configure_file(
|
||||
input : 'nix-daemon.conf.in',
|
||||
output : 'nix-daemon.conf',
|
||||
install : true,
|
||||
install_dir : prefix / 'lib/tmpfiles.d',
|
||||
install_mode : 'rw-r--r--',
|
||||
configuration : {
|
||||
'localstatedir' : state_dir,
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
ifdef HOST_LINUX
|
||||
|
||||
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
|
||||
|
||||
clean-files += $(d)/nix-daemon.conf
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,5 @@
|
||||
description "Nix Daemon"
|
||||
start on filesystem
|
||||
stop on shutdown
|
||||
respawn
|
||||
exec @bindir@/nix-daemon --daemon
|
||||
@@ -1,10 +0,0 @@
|
||||
foreach script : [ [ 'completion.zsh', '_nix' ], [ 'run-help-nix' ] ]
|
||||
configure_file(
|
||||
input : script[0],
|
||||
output : script.get(1, script[0]),
|
||||
install : true,
|
||||
install_dir : datadir / 'zsh/site-functions',
|
||||
install_mode : 'rw-r--r--',
|
||||
copy : true,
|
||||
)
|
||||
endforeach
|
||||
+5
-1
@@ -78,7 +78,11 @@ define build-library
|
||||
$(1)_LDFLAGS += -undefined suppress -flat_namespace
|
||||
endif
|
||||
else
|
||||
# -Wl,-z,defs is broken with sanitizers on Linux/clang at least.
|
||||
ifndef HOST_DARWIN
|
||||
ifndef HOST_CYGWIN
|
||||
$(1)_LDFLAGS += -Wl,-z,defs
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef HOST_DARWIN
|
||||
|
||||
+9
-41
@@ -8,11 +8,10 @@
|
||||
boehmgc,
|
||||
nlohmann_json,
|
||||
bison,
|
||||
build-release-notes,
|
||||
changelog-d,
|
||||
boost,
|
||||
brotli,
|
||||
bzip2,
|
||||
cmake,
|
||||
curl,
|
||||
doxygen,
|
||||
editline,
|
||||
@@ -36,7 +35,6 @@
|
||||
pkg-config,
|
||||
rapidcheck,
|
||||
sqlite,
|
||||
toml11,
|
||||
util-linuxMinimal ? utillinuxMinimal,
|
||||
utillinuxMinimal ? null,
|
||||
xz,
|
||||
@@ -103,8 +101,7 @@
|
||||
] ++ lib.optionals buildWithMeson [
|
||||
./meson.build
|
||||
./meson.options
|
||||
./meson
|
||||
./scripts/meson.build
|
||||
./meson/cleanup-install.bash
|
||||
]);
|
||||
|
||||
functionalTestFiles = fileset.unions [
|
||||
@@ -145,9 +142,6 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
"-Dsandbox-shell=${lib.getBin busybox-sandbox-shell}/bin/busybox"
|
||||
];
|
||||
|
||||
# We only include CMake so that Meson can locate toml11, which only ships CMake dependency metadata.
|
||||
dontUseCmakeConfigure = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
bison
|
||||
flex
|
||||
@@ -165,12 +159,11 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
jq
|
||||
lsof
|
||||
] ++ lib.optional stdenv.hostPlatform.isLinux util-linuxMinimal
|
||||
++ lib.optional (!officialRelease && buildUnreleasedNotes) build-release-notes
|
||||
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
|
||||
++ lib.optional internalApiDocs doxygen
|
||||
++ lib.optionals buildWithMeson [
|
||||
meson
|
||||
ninja
|
||||
cmake
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
@@ -185,7 +178,6 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
boost
|
||||
lowdown
|
||||
libsodium
|
||||
toml11
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ libseccomp busybox-sandbox-shell ]
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
|
||||
@@ -247,12 +239,7 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
++ lib.optionals (finalAttrs.doCheck || internalApiDocs) testConfigureFlags
|
||||
++ lib.optional (!canRunInstalled) "--disable-doc-gen"
|
||||
++ [ (lib.enableFeature internalApiDocs "internal-api-docs") ]
|
||||
++ lib.optional (!forDevShell) "--sysconfdir=/etc"
|
||||
++ [
|
||||
"TOML11_HEADERS=${lib.getDev toml11}/include"
|
||||
];
|
||||
|
||||
mesonBuildType = lib.optional (buildWithMeson || forDevShell) "debugoptimized";
|
||||
++ lib.optional (!forDevShell) "--sysconfdir=/etc";
|
||||
|
||||
installTargets = lib.optional internalApiDocs "internal-api-html";
|
||||
|
||||
@@ -262,10 +249,6 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
|
||||
doCheck = true;
|
||||
|
||||
mesonCheckFlags = lib.optionals (buildWithMeson || forDevShell) [
|
||||
"--suite=check"
|
||||
];
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
postInstall = lib.optionalString (!finalAttrs.dontBuild) ''
|
||||
@@ -275,12 +258,10 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||
'' + lib.optionalString stdenv.isDarwin ''
|
||||
for lib in libnixutil.dylib libnixexpr.dylib; do
|
||||
install_name_tool \
|
||||
-change "${lib.getLib boost}/lib/libboost_context.dylib" \
|
||||
"$out/lib/libboost_context.dylib" \
|
||||
"$out/lib/$lib"
|
||||
done
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
'' + lib.optionalString internalApiDocs ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> "$out/nix-support/hydra-build-products"
|
||||
@@ -290,28 +271,15 @@ in stdenv.mkDerivation (finalAttrs: {
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
mesonInstallCheckFlags = [
|
||||
"--suite=installcheck"
|
||||
];
|
||||
|
||||
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'';
|
||||
|
||||
installCheckPhase = lib.optionalString buildWithMeson ''
|
||||
runHook preInstallCheck
|
||||
flagsArray=($mesonInstallCheckFlags "''${mesonInstallCheckFlagsArray[@]}")
|
||||
meson test --no-rebuild "''${flagsArray[@]}"
|
||||
runHook postInstallCheck
|
||||
'';
|
||||
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic && !finalAttrs.dontBuild;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
# strictoverflow is disabled because we trap on signed overflow instead
|
||||
hardeningDisable = [ "strictoverflow" ]
|
||||
++ lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
|
||||
|
||||
@@ -7,7 +7,5 @@ profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.fish, $(profiledir)/nix.fish, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile-daemon.fish, $(profiledir)/nix-daemon.fish, 0644))
|
||||
|
||||
clean-files += $(nix_noinst_scripts)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# configures `scripts/nix-profile.sh.in` (and copies the original to the build directory).
|
||||
# this is only needed for tests, but running it unconditionally does not hurt enough to care.
|
||||
configure_file(
|
||||
input : 'nix-profile.sh.in',
|
||||
output : 'nix-profile.sh',
|
||||
configuration : {
|
||||
'localstatedir': state_dir,
|
||||
}
|
||||
)
|
||||
|
||||
# https://github.com/mesonbuild/meson/issues/860
|
||||
configure_file(
|
||||
input : 'nix-profile.sh.in',
|
||||
output : 'nix-profile.sh.in',
|
||||
copy : true,
|
||||
)
|
||||
|
||||
foreach rc : [ '.sh', '.fish', '-daemon.sh', '-daemon.fish' ]
|
||||
configure_file(
|
||||
input : 'nix-profile' + rc + '.in',
|
||||
output : 'nix' + rc,
|
||||
install : true,
|
||||
install_dir : sysconfdir / 'profile.d',
|
||||
install_mode : 'rw-r--r--',
|
||||
configuration : {
|
||||
'localstatedir': state_dir,
|
||||
},
|
||||
)
|
||||
endforeach
|
||||
@@ -1,57 +0,0 @@
|
||||
function add_path --argument-names new_path
|
||||
if type -q fish_add_path
|
||||
# fish 3.2.0 or newer
|
||||
fish_add_path --prepend --global $new_path
|
||||
else
|
||||
# older versions of fish
|
||||
if not contains $new_path $fish_user_paths
|
||||
set --global fish_user_paths $new_path $fish_user_paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Only execute this file once per shell.
|
||||
if test -n "$__ETC_PROFILE_NIX_SOURCED"
|
||||
exit
|
||||
end
|
||||
|
||||
set __ETC_PROFILE_NIX_SOURCED 1
|
||||
|
||||
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if test -z "$XDG_DATA_DIRS"
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
|
||||
end
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if test -n "$NIX_SSH_CERT_FILE"
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
|
||||
else if test -e /etc/ssl/ca-bundle.pem # openSUSE Tumbleweed
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/ca-bundle.pem
|
||||
else if test -e /etc/ssl/certs/ca-bundle.crt # Old NixOS
|
||||
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-bundle.crt
|
||||
else if test -e /etc/pki/tls/certs/ca-bundle.crt # Fedora, CentOS
|
||||
set --export NIX_SSL_CERT_FILE /etc/pki/tls/certs/ca-bundle.crt
|
||||
else if test -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" # fall back to cacert in Nix profile
|
||||
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
|
||||
else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile
|
||||
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt"
|
||||
else
|
||||
# Fall back to what is in the nix profiles, favouring whatever is defined last.
|
||||
for i in $NIX_PROFILES
|
||||
if test -e "$i/etc/ssl/certs/ca-bundle.crt"
|
||||
set --export NIX_SSL_CERT_FILE "$i/etc/ssl/certs/ca-bundle.crt"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add_path "@localstatedir@/nix/profiles/default/bin"
|
||||
add_path "$HOME/.nix-profile/bin"
|
||||
|
||||
functions -e add_path
|
||||
@@ -1,72 +0,0 @@
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
||||
__ETC_PROFILE_NIX_SOURCED=1
|
||||
|
||||
NIX_LINK=$HOME/.nix-profile
|
||||
if [ -n "${XDG_STATE_HOME-}" ]; then
|
||||
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||
else
|
||||
NIX_LINK_NEW=$HOME/.local/state/nix/profile
|
||||
fi
|
||||
if [ -e "$NIX_LINK_NEW" ]; then
|
||||
NIX_LINK="$NIX_LINK_NEW"
|
||||
else
|
||||
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||
warning="\033[1;35mwarning:\033[0m"
|
||||
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||
else
|
||||
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||
# 1. Update to newer Nix;
|
||||
# 2. Remove .nix-profile;
|
||||
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||
# 4. Roll back to older Nix.
|
||||
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if [ -z "${XDG_DATA_DIRS-}" ]; then
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
fi
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
|
||||
elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
|
||||
elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
|
||||
export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
|
||||
else
|
||||
# Fall back to what is in the nix profiles, favouring whatever is defined last.
|
||||
check_nix_profiles() {
|
||||
if [ -n "$ZSH_VERSION" ]; then
|
||||
# Zsh by default doesn't split words in unquoted parameter expansion.
|
||||
# Set local_options for these options to be reverted at the end of the function
|
||||
# and shwordsplit to force splitting words in $NIX_PROFILES below.
|
||||
setopt local_options shwordsplit
|
||||
fi
|
||||
for i in $NIX_PROFILES; do
|
||||
if [ -e "$i/etc/ssl/certs/ca-bundle.crt" ]; then
|
||||
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
|
||||
fi
|
||||
done
|
||||
}
|
||||
check_nix_profiles
|
||||
unset -f check_nix_profiles
|
||||
fi
|
||||
|
||||
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
||||
unset NIX_LINK
|
||||
@@ -185,6 +185,16 @@ static int main_build_remote(int argc, char * * argv)
|
||||
std::cerr << "# postpone\n";
|
||||
else
|
||||
{
|
||||
// build the hint template.
|
||||
std::string errorText =
|
||||
"Failed to find a machine for remote build!\n"
|
||||
"derivation: %s\nrequired (system, features): (%s, [%s])";
|
||||
errorText += "\n%s available machines:";
|
||||
errorText += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)";
|
||||
|
||||
for (unsigned int i = 0; i < machines.size(); ++i)
|
||||
errorText += "\n([%s], %s, [%s], [%s])";
|
||||
|
||||
// add the template values.
|
||||
std::string drvstr;
|
||||
if (drvPath.has_value())
|
||||
@@ -192,30 +202,19 @@ static int main_build_remote(int argc, char * * argv)
|
||||
else
|
||||
drvstr = "<unknown>";
|
||||
|
||||
std::string machinesFormatted;
|
||||
auto error = HintFmt(errorText);
|
||||
error
|
||||
% drvstr
|
||||
% neededSystem
|
||||
% concatStringsSep<StringSet>(", ", requiredFeatures)
|
||||
% machines.size();
|
||||
|
||||
for (auto & m : machines) {
|
||||
machinesFormatted += HintFmt(
|
||||
"\n([%s], %s, [%s], [%s])",
|
||||
concatStringsSep<StringSet>(", ", m.systemTypes),
|
||||
m.maxJobs,
|
||||
concatStringsSep<StringSet>(", ", m.supportedFeatures),
|
||||
concatStringsSep<StringSet>(", ", m.mandatoryFeatures)
|
||||
).str();
|
||||
}
|
||||
|
||||
auto error = HintFmt(
|
||||
"Failed to find a machine for remote build!\n"
|
||||
"derivation: %s\n"
|
||||
"required (system, features): (%s, [%s])\n"
|
||||
"%s available machines:\n"
|
||||
"(systems, maxjobs, supportedFeatures, mandatoryFeatures)%s",
|
||||
drvstr,
|
||||
neededSystem,
|
||||
concatStringsSep<StringSet>(", ", requiredFeatures),
|
||||
machines.size(),
|
||||
Uncolored(machinesFormatted)
|
||||
);
|
||||
for (auto & m : machines)
|
||||
error
|
||||
% concatStringsSep<StringSet>(", ", m.systemTypes)
|
||||
% m.maxJobs
|
||||
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
|
||||
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
|
||||
|
||||
printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str());
|
||||
|
||||
|
||||
+14
-18
@@ -97,6 +97,8 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
{
|
||||
flake::LockFlags lockFlags;
|
||||
|
||||
std::optional<std::string> needsFlakeInputCompletion = {};
|
||||
|
||||
MixFlakeOptions();
|
||||
|
||||
/**
|
||||
@@ -107,8 +109,12 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
* command is operating with (presumably specified via some other
|
||||
* arguments) so that the completions for these flags can use them.
|
||||
*/
|
||||
virtual std::vector<FlakeRef> getFlakeRefsForCompletion()
|
||||
virtual std::vector<std::string> getFlakesForCompletion()
|
||||
{ return {}; }
|
||||
|
||||
void completeFlakeInput(std::string_view prefix);
|
||||
|
||||
void completionHook() override;
|
||||
};
|
||||
|
||||
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
@@ -131,13 +137,7 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
/**
|
||||
* Complete an installable from the given prefix.
|
||||
*/
|
||||
void completeInstallable(AddCompletions & completions, std::string_view prefix);
|
||||
|
||||
/**
|
||||
* Convenience wrapper around the underlying function to make setting the
|
||||
* callback easier.
|
||||
*/
|
||||
CompleterClosure getCompleteInstallable();
|
||||
void completeInstallable(std::string_view prefix);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ struct RawInstallablesCommand : virtual Args, SourceExprCommand
|
||||
|
||||
bool readFromStdIn = false;
|
||||
|
||||
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
|
||||
std::vector<std::string> getFlakesForCompletion() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -199,7 +199,10 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||
|
||||
void run(ref<Store> store) override;
|
||||
|
||||
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
|
||||
std::vector<std::string> getFlakesForCompletion() override
|
||||
{
|
||||
return {_installable};
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -326,16 +329,9 @@ struct MixEnvironment : virtual Args {
|
||||
void setEnviron();
|
||||
};
|
||||
|
||||
void completeFlakeInputPath(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
const std::vector<FlakeRef> & flakeRefs,
|
||||
std::string_view prefix);
|
||||
|
||||
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix);
|
||||
void completeFlakeRef(ref<Store> store, std::string_view prefix);
|
||||
|
||||
void completeFlakeRefWithFragment(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
flake::LockFlags lockFlags,
|
||||
Strings attrPathPrefixes,
|
||||
|
||||
@@ -132,8 +132,8 @@ MixEvalArgs::MixEvalArgs()
|
||||
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
||||
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeFlakeRef(completions, openStore(), prefix);
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeFlakeRef(openStore(), prefix);
|
||||
}}
|
||||
});
|
||||
|
||||
|
||||
@@ -28,10 +28,6 @@ namespace nix {
|
||||
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){
|
||||
res.push_back(attrPaths.front().substr(1));
|
||||
return res;
|
||||
}
|
||||
|
||||
for (auto & prefix : prefixes)
|
||||
res.push_back(prefix + *attrPaths.begin());
|
||||
|
||||
+67
-72
@@ -28,24 +28,17 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void completeFlakeInputPath(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
const std::vector<FlakeRef> & flakeRefs,
|
||||
std::string_view prefix)
|
||||
{
|
||||
for (auto & flakeRef : flakeRefs) {
|
||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||
for (auto & input : flake.inputs)
|
||||
if (input.first.starts_with(prefix))
|
||||
completions.add(input.first);
|
||||
}
|
||||
}
|
||||
|
||||
MixFlakeOptions::MixFlakeOptions()
|
||||
{
|
||||
auto category = "Common flake-related options";
|
||||
|
||||
addFlag({
|
||||
.longName = "recreate-lock-file",
|
||||
.description = "Recreate the flake's lock file from scratch.",
|
||||
.category = category,
|
||||
.handler = {&lockFlags.recreateLockFile, true}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "no-update-lock-file",
|
||||
.description = "Do not allow any updates to the flake's lock file.",
|
||||
@@ -78,6 +71,19 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
.handler = {&lockFlags.commitLockFile, true}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "update-input",
|
||||
.description = "Update a specific flake input (ignoring its previous entry in the lock file).",
|
||||
.category = category,
|
||||
.labels = {"input-path"},
|
||||
.handler = {[&](std::string s) {
|
||||
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
}}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "override-input",
|
||||
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.",
|
||||
@@ -89,12 +95,11 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
flake::parseInputPath(inputPath),
|
||||
parseFlakeRef(flakeRef, absPath("."), true));
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
|
||||
if (n == 0) {
|
||||
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
|
||||
} else if (n == 1) {
|
||||
completeFlakeRef(completions, getEvalState()->store, prefix);
|
||||
}
|
||||
.completer = {[&](size_t n, std::string_view prefix) {
|
||||
if (n == 0)
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
else if (n == 1)
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
}}
|
||||
});
|
||||
|
||||
@@ -141,12 +146,30 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
}
|
||||
}
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeFlakeRef(completions, getEvalState()->store, prefix);
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
}}
|
||||
});
|
||||
}
|
||||
|
||||
void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
|
||||
{
|
||||
auto evalState = getEvalState();
|
||||
for (auto & flakeRefS : getFlakesForCompletion()) {
|
||||
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
|
||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||
for (auto & input : flake.inputs)
|
||||
if (input.first.starts_with(prefix))
|
||||
completions->add(input.first);
|
||||
}
|
||||
}
|
||||
|
||||
void MixFlakeOptions::completionHook()
|
||||
{
|
||||
if (auto & prefix = needsFlakeInputCompletion)
|
||||
completeFlakeInput(*prefix);
|
||||
}
|
||||
|
||||
SourceExprCommand::SourceExprCommand()
|
||||
{
|
||||
addFlag({
|
||||
@@ -164,7 +187,6 @@ SourceExprCommand::SourceExprCommand()
|
||||
|
||||
addFlag({
|
||||
.longName = "expr",
|
||||
.shortName = 'E',
|
||||
.description = "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.",
|
||||
.category = installablesCategory,
|
||||
.labels = {"expr"},
|
||||
@@ -204,18 +226,11 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||
};
|
||||
}
|
||||
|
||||
Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
|
||||
{
|
||||
return [this](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeInstallable(completions, prefix);
|
||||
};
|
||||
}
|
||||
|
||||
void SourceExprCommand::completeInstallable(AddCompletions & completions, std::string_view prefix)
|
||||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
{
|
||||
try {
|
||||
if (file) {
|
||||
completions.setType(AddCompletions::Type::Attrs);
|
||||
completionType = ctAttrs;
|
||||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
@@ -250,15 +265,14 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
||||
std::string name = state->symbols[i.name];
|
||||
if (name.find(searchWord) == 0) {
|
||||
if (prefix_ == "")
|
||||
completions.add(name);
|
||||
completions->add(name);
|
||||
else
|
||||
completions.add(prefix_ + "." + name);
|
||||
completions->add(prefix_ + "." + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completeFlakeRefWithFragment(
|
||||
completions,
|
||||
getEvalState(),
|
||||
lockFlags,
|
||||
getDefaultFlakeAttrPathPrefixes(),
|
||||
@@ -271,7 +285,6 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
||||
}
|
||||
|
||||
void completeFlakeRefWithFragment(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
flake::LockFlags lockFlags,
|
||||
Strings attrPathPrefixes,
|
||||
@@ -283,16 +296,11 @@ void completeFlakeRefWithFragment(
|
||||
try {
|
||||
auto hash = prefix.find('#');
|
||||
if (hash == std::string::npos) {
|
||||
completeFlakeRef(completions, evalState->store, prefix);
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
} else {
|
||||
completions.setType(AddCompletions::Type::Attrs);
|
||||
completionType = ctAttrs;
|
||||
|
||||
auto fragment = prefix.substr(hash + 1);
|
||||
std::string prefixRoot = "";
|
||||
if (fragment.starts_with(".")){
|
||||
fragment = fragment.substr(1);
|
||||
prefixRoot = ".";
|
||||
}
|
||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
|
||||
|
||||
@@ -301,9 +309,6 @@ void completeFlakeRefWithFragment(
|
||||
|
||||
auto root = evalCache->getRoot();
|
||||
|
||||
if (prefixRoot == "."){
|
||||
attrPathPrefixes.clear();
|
||||
}
|
||||
/* Complete 'fragment' relative to all the
|
||||
attrpath prefixes as well as the root of the
|
||||
flake. */
|
||||
@@ -328,7 +333,7 @@ void completeFlakeRefWithFragment(
|
||||
auto attrPath2 = (*attr)->getAttrPath(attr2);
|
||||
/* Strip the attrpath prefix. */
|
||||
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
||||
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +344,7 @@ void completeFlakeRefWithFragment(
|
||||
for (auto & attrPath : defaultFlakeAttrPaths) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
|
||||
if (!attr) continue;
|
||||
completions.add(flakeRefS + "#" + prefixRoot);
|
||||
completions->add(flakeRefS + "#");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,15 +353,15 @@ void completeFlakeRefWithFragment(
|
||||
}
|
||||
}
|
||||
|
||||
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix)
|
||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
{
|
||||
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
return;
|
||||
|
||||
if (prefix == "")
|
||||
completions.add(".");
|
||||
completions->add(".");
|
||||
|
||||
Args::completeDir(completions, 0, prefix);
|
||||
completeDir(0, prefix);
|
||||
|
||||
/* Look for registry entries that match the prefix. */
|
||||
for (auto & registry : fetchers::getRegistries(store)) {
|
||||
@@ -365,10 +370,10 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
|
||||
if (!prefix.starts_with("flake:") && from.starts_with("flake:")) {
|
||||
std::string from2(from, 6);
|
||||
if (from2.starts_with(prefix))
|
||||
completions.add(from2);
|
||||
completions->add(from2);
|
||||
} else {
|
||||
if (from.starts_with(prefix))
|
||||
completions.add(from);
|
||||
completions->add(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,7 +753,9 @@ RawInstallablesCommand::RawInstallablesCommand()
|
||||
expectArgs({
|
||||
.label = "installables",
|
||||
.handler = {&rawInstallables},
|
||||
.completer = getCompleteInstallable(),
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeInstallable(prefix);
|
||||
}}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -761,17 +768,6 @@ void RawInstallablesCommand::applyDefaultInstallables(std::vector<std::string> &
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
|
||||
{
|
||||
applyDefaultInstallables(rawInstallables);
|
||||
std::vector<FlakeRef> res;
|
||||
for (auto i : rawInstallables)
|
||||
res.push_back(parseFlakeRefWithFragment(
|
||||
expandTilde(i),
|
||||
absPath(".")).first);
|
||||
return res;
|
||||
}
|
||||
|
||||
void RawInstallablesCommand::run(ref<Store> store)
|
||||
{
|
||||
if (readFromStdIn && !isatty(STDIN_FILENO)) {
|
||||
@@ -785,13 +781,10 @@ void RawInstallablesCommand::run(ref<Store> store)
|
||||
run(store, std::move(rawInstallables));
|
||||
}
|
||||
|
||||
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
|
||||
std::vector<std::string> RawInstallablesCommand::getFlakesForCompletion()
|
||||
{
|
||||
return {
|
||||
parseFlakeRefWithFragment(
|
||||
expandTilde(_installable),
|
||||
absPath(".")).first
|
||||
};
|
||||
applyDefaultInstallables(rawInstallables);
|
||||
return rawInstallables;
|
||||
}
|
||||
|
||||
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
|
||||
@@ -807,7 +800,9 @@ InstallableCommand::InstallableCommand()
|
||||
.label = "installable",
|
||||
.optional = true,
|
||||
.handler = {&_installable},
|
||||
.completer = getCompleteInstallable(),
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeInstallable(prefix);
|
||||
}}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,3 @@ libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
||||
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
||||
$(d)/repl.cc: $(d)/repl-overlays.nix.gen.hh
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
libcmd_generated_headers = []
|
||||
foreach header : [ 'repl-overlays.nix' ]
|
||||
libcmd_generated_headers += custom_target(
|
||||
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
|
||||
input : header,
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
capture : true,
|
||||
install : true,
|
||||
install_dir : includedir / 'nix',
|
||||
)
|
||||
endforeach
|
||||
|
||||
libcmd_sources = files(
|
||||
'built-path.cc',
|
||||
'command-installable-value.cc',
|
||||
@@ -46,7 +34,6 @@ libcmd_headers = files(
|
||||
|
||||
libcmd = library(
|
||||
'nixcmd',
|
||||
libcmd_generated_headers,
|
||||
libcmd_sources,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
@@ -57,7 +44,6 @@ libcmd = library(
|
||||
boehm,
|
||||
editline,
|
||||
lowdown,
|
||||
nlohmann_json,
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
@@ -70,17 +56,3 @@ liblixcmd = declare_dependency(
|
||||
include_directories : '.',
|
||||
link_with : libcmd,
|
||||
)
|
||||
|
||||
# FIXME: not using the pkg-config module because it creates way too many deps
|
||||
# while meson migration is in progress, and we want to not include boost here
|
||||
configure_file(
|
||||
input : 'nix-cmd.pc.in',
|
||||
output : 'nix-cmd.pc',
|
||||
install_dir : libdir / 'pkgconfig',
|
||||
configuration : {
|
||||
'prefix' : prefix,
|
||||
'libdir' : libdir,
|
||||
'includedir' : includedir,
|
||||
'PACKAGE_VERSION' : meson.project_version(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
info:
|
||||
initial:
|
||||
functions:
|
||||
let final = builtins.foldl'
|
||||
(prev: function: prev // (function info final prev))
|
||||
initial
|
||||
functions;
|
||||
in final
|
||||
+6
-174
@@ -29,8 +29,6 @@
|
||||
#include "local-fs-store.hh"
|
||||
#include "signals.hh"
|
||||
#include "print.hh"
|
||||
#include "progress-bar.hh"
|
||||
#include "gc-small-vector.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#define GC_INCLUDE_NEW
|
||||
@@ -101,45 +99,6 @@ struct NixRepl
|
||||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
/**
|
||||
* Load the `repl-overlays` and add the resulting AttrSet to the top-level
|
||||
* bindings.
|
||||
*/
|
||||
void loadReplOverlays();
|
||||
|
||||
/**
|
||||
* Get a list of each of the `repl-overlays` (parsed and evaluated).
|
||||
*/
|
||||
Value * replOverlays();
|
||||
|
||||
/**
|
||||
* Get the Nix function that composes the `repl-overlays` together.
|
||||
*/
|
||||
Value * getReplOverlaysEvalFunction();
|
||||
|
||||
/**
|
||||
* Cached return value of `getReplOverlaysEvalFunction`.
|
||||
*
|
||||
* Note: This is `shared_ptr` to avoid garbage collection.
|
||||
*/
|
||||
std::shared_ptr<Value *> replOverlaysEvalFunction =
|
||||
std::allocate_shared<Value *>(traceable_allocator<Value *>(), nullptr);
|
||||
|
||||
/**
|
||||
* Get the `info` AttrSet that's passed as the first argument to each
|
||||
* of the `repl-overlays`.
|
||||
*/
|
||||
Value * replInitInfo();
|
||||
|
||||
/**
|
||||
* Get the current top-level bindings as an AttrSet.
|
||||
*/
|
||||
Value * bindingsToAttrs();
|
||||
/**
|
||||
* Parse a file, evaluate its result, and force the resulting value.
|
||||
*/
|
||||
Value * evalFile(SourcePath & path);
|
||||
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
@@ -227,7 +186,7 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
if (state->debugRepl) {
|
||||
debuggerNotice = " debugger";
|
||||
}
|
||||
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
|
||||
notice("Nix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
|
||||
}
|
||||
|
||||
isFirstRepl = false;
|
||||
@@ -236,13 +195,11 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
|
||||
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
||||
|
||||
/* Stop the progress bar because it interferes with the display of
|
||||
the repl. */
|
||||
stopProgressBar();
|
||||
|
||||
std::string input;
|
||||
|
||||
while (true) {
|
||||
// Hide the progress bar while waiting for user input, so that it won't interfere.
|
||||
logger->pause();
|
||||
// When continuing input from previous lines, don't print a prompt, just align to the same
|
||||
// number of chars as the prompt.
|
||||
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
||||
@@ -253,6 +210,7 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
// the entire program?
|
||||
return ReplExitStatus::QuitAll;
|
||||
}
|
||||
logger->resume();
|
||||
try {
|
||||
switch (processLine(input)) {
|
||||
case ProcessLineResult::Quit:
|
||||
@@ -777,119 +735,14 @@ void NixRepl::loadFiles()
|
||||
loadedFiles.clear();
|
||||
|
||||
for (auto & i : old) {
|
||||
notice("Loading '%1%'...", Magenta(i));
|
||||
notice("Loading '%1%'...", i);
|
||||
loadFile(i);
|
||||
}
|
||||
|
||||
for (auto & [i, what] : getValues()) {
|
||||
notice("Loading installable '%1%'...", Magenta(what));
|
||||
notice("Loading installable '%1%'...", what);
|
||||
addAttrsToScope(*i);
|
||||
}
|
||||
|
||||
loadReplOverlays();
|
||||
}
|
||||
|
||||
void NixRepl::loadReplOverlays()
|
||||
{
|
||||
if (!evalSettings.replOverlays) {
|
||||
return;
|
||||
}
|
||||
|
||||
notice("Loading '%1%'...", Magenta("repl-overlays"));
|
||||
auto replInitFilesFunction = getReplOverlaysEvalFunction();
|
||||
|
||||
Value &newAttrs(*state->allocValue());
|
||||
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
|
||||
state->callFunction(
|
||||
*replInitFilesFunction,
|
||||
args.size(),
|
||||
args.data(),
|
||||
newAttrs,
|
||||
replInitFilesFunction->determinePos(noPos)
|
||||
);
|
||||
|
||||
addAttrsToScope(newAttrs);
|
||||
}
|
||||
|
||||
Value * NixRepl::getReplOverlaysEvalFunction()
|
||||
{
|
||||
if (replOverlaysEvalFunction && *replOverlaysEvalFunction) {
|
||||
return *replOverlaysEvalFunction;
|
||||
}
|
||||
|
||||
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
|
||||
*replOverlaysEvalFunction = state->allocValue();
|
||||
auto code =
|
||||
#include "repl-overlays.nix.gen.hh"
|
||||
;
|
||||
auto expr = state->parseExprFromString(
|
||||
code,
|
||||
SourcePath(evalReplInitFilesPath),
|
||||
state->staticBaseEnv
|
||||
);
|
||||
|
||||
state->eval(expr, **replOverlaysEvalFunction);
|
||||
state->forceValue(**replOverlaysEvalFunction, (*replOverlaysEvalFunction)->determinePos(noPos));
|
||||
|
||||
return *replOverlaysEvalFunction;
|
||||
}
|
||||
|
||||
Value * NixRepl::replOverlays()
|
||||
{
|
||||
Value * replInits(state->allocValue());
|
||||
state->mkList(*replInits, evalSettings.replOverlays.get().size());
|
||||
Value ** replInitElems = replInits->listElems();
|
||||
|
||||
size_t i = 0;
|
||||
for (auto path : evalSettings.replOverlays.get()) {
|
||||
debug("Loading '%1%' path '%2%'...", "repl-overlays", path);
|
||||
SourcePath sourcePath((CanonPath(path)));
|
||||
auto replInit = evalFile(sourcePath);
|
||||
|
||||
if (!replInit->isLambda()) {
|
||||
state->error<TypeError>(
|
||||
"Expected `repl-overlays` to be a lambda but found %1%: %2%",
|
||||
showType(*replInit),
|
||||
ValuePrinter(*state, *replInit, errorPrintOptions)
|
||||
)
|
||||
.atPos(replInit->determinePos(noPos))
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (replInit->lambda.fun->hasFormals()
|
||||
&& !replInit->lambda.fun->formals->ellipsis) {
|
||||
state->error<TypeError>(
|
||||
"Expected first argument of %1% to have %2% to allow future versions of Lix to add additional attributes to the argument",
|
||||
"repl-overlays",
|
||||
"..."
|
||||
)
|
||||
.atPos(replInit->determinePos(noPos))
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
replInitElems[i] = replInit;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
return replInits;
|
||||
}
|
||||
|
||||
Value * NixRepl::replInitInfo()
|
||||
{
|
||||
auto builder = state->buildBindings(2);
|
||||
|
||||
Value * currentSystem(state->allocValue());
|
||||
currentSystem->mkString(evalSettings.getCurrentSystem());
|
||||
builder.insert(state->symbols.create("currentSystem"), currentSystem);
|
||||
|
||||
Value * valueNull(state->allocValue());
|
||||
valueNull->mkNull();
|
||||
builder.insert(state->symbols.create("__pleaseUseDotDotDot"), valueNull);
|
||||
|
||||
Value * info(state->allocValue());
|
||||
info->mkAttrs(builder.finish());
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -922,18 +775,6 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
|
||||
varNames.emplace(state->symbols[name]);
|
||||
}
|
||||
|
||||
Value * NixRepl::bindingsToAttrs()
|
||||
{
|
||||
auto builder = state->buildBindings(staticEnv->vars.size());
|
||||
for (auto & [symbol, displacement] : staticEnv->vars) {
|
||||
builder.insert(symbol, env->values[displacement]);
|
||||
}
|
||||
|
||||
Value * attrs(state->allocValue());
|
||||
attrs->mkAttrs(builder.finish());
|
||||
return attrs;
|
||||
}
|
||||
|
||||
|
||||
Expr * NixRepl::parseString(std::string s)
|
||||
{
|
||||
@@ -948,15 +789,6 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
}
|
||||
|
||||
Value * NixRepl::evalFile(SourcePath & path)
|
||||
{
|
||||
auto expr = state->parseExprFromFile(path, staticEnv);
|
||||
Value * result(state->allocValue());
|
||||
expr->eval(*state, *env, *result);
|
||||
state->forceValue(*result, result->determinePos(noPos));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
|
||||
@@ -124,42 +124,6 @@ struct EvalSettings : Config
|
||||
|
||||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
|
||||
PathsSetting replOverlays{this, Paths(), "repl-overlays",
|
||||
R"(
|
||||
A list of files containing Nix expressions that can be used to add
|
||||
default bindings to [`nix
|
||||
repl`](@docroot@/command-ref/new-cli/nix3-repl.md) sessions.
|
||||
|
||||
Each file is called with three arguments:
|
||||
1. An [attribute set](@docroot@/language/values.html#attribute-set)
|
||||
containing at least a
|
||||
[`currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem)
|
||||
attribute (this is identical to
|
||||
[`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem),
|
||||
except that it's available in
|
||||
[`pure-eval`](@docroot@/command-ref/conf-file.html#conf-pure-eval)
|
||||
mode).
|
||||
2. The top-level bindings produced by the previous `repl-overlays`
|
||||
value (or the default top-level bindings).
|
||||
3. The final top-level bindings produced by calling all
|
||||
`repl-overlays`.
|
||||
|
||||
For example, the following file would alias `pkgs` to
|
||||
`legacyPackages.${info.currentSystem}` (if that attribute is defined):
|
||||
|
||||
```nix
|
||||
info: prev: final:
|
||||
if prev ? legacyPackages
|
||||
&& prev.legacyPackages ? ${info.currentSystem}
|
||||
then
|
||||
{
|
||||
pkgs = prev.legacyPackages.${info.currentSystem};
|
||||
}
|
||||
else
|
||||
{ }
|
||||
```
|
||||
)"};
|
||||
};
|
||||
|
||||
extern EvalSettings evalSettings;
|
||||
|
||||
+37
-38
@@ -451,8 +451,8 @@ LockedFlake lockFlake(
|
||||
|
||||
assert(input.ref);
|
||||
|
||||
/* Do we have an entry in the existing lock file?
|
||||
And the input is not in updateInputs? */
|
||||
/* Do we have an entry in the existing lock file? And we
|
||||
don't have a --update-input flag for this input? */
|
||||
std::shared_ptr<LockedNode> oldLock;
|
||||
|
||||
updatesUsed.insert(inputPath);
|
||||
@@ -476,8 +476,9 @@ LockedFlake lockFlake(
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
/* If we have this input in updateInputs, then we
|
||||
must fetch the flake to update it. */
|
||||
/* If we have an --update-input flag for an input
|
||||
of this input, then we must fetch the flake to
|
||||
update it. */
|
||||
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
|
||||
|
||||
auto mustRefetch =
|
||||
@@ -619,14 +620,19 @@ LockedFlake lockFlake(
|
||||
|
||||
for (auto & i : lockFlags.inputUpdates)
|
||||
if (!updatesUsed.count(i))
|
||||
warn("'%s' does not match any input of this flake", printInputPath(i));
|
||||
warn("the flag '--update-input %s' does not match any input", printInputPath(i));
|
||||
|
||||
/* Check 'follows' inputs. */
|
||||
newLockFile.check();
|
||||
|
||||
debug("new lock file: %s", newLockFile);
|
||||
|
||||
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
||||
auto sourcePath = topRef.input.getSourcePath();
|
||||
auto outputLockFilePath = sourcePath ? std::optional{*sourcePath + "/" + relPath} : std::nullopt;
|
||||
if (lockFlags.outputLockFilePath) {
|
||||
outputLockFilePath = lockFlags.outputLockFilePath;
|
||||
}
|
||||
|
||||
/* Check whether we need to / can write the new lock file. */
|
||||
if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) {
|
||||
@@ -634,7 +640,7 @@ LockedFlake lockFlake(
|
||||
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
||||
|
||||
if (lockFlags.writeLockFile) {
|
||||
if (sourcePath || lockFlags.outputLockFilePath) {
|
||||
if (outputLockFilePath) {
|
||||
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
||||
if (fetchSettings.warnDirty)
|
||||
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
||||
@@ -642,48 +648,41 @@ LockedFlake lockFlake(
|
||||
if (!lockFlags.updateLockFile)
|
||||
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
||||
|
||||
auto newLockFileS = fmt("%s\n", newLockFile);
|
||||
|
||||
if (lockFlags.outputLockFilePath) {
|
||||
if (lockFlags.commitLockFile)
|
||||
throw Error("'--commit-lock-file' and '--output-lock-file' are incompatible");
|
||||
writeFile(*lockFlags.outputLockFilePath, newLockFileS);
|
||||
} else {
|
||||
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
||||
auto outputLockFilePath = *sourcePath + "/" + relPath;
|
||||
|
||||
bool lockFileExists = pathExists(outputLockFilePath);
|
||||
bool lockFileExists = pathExists(*outputLockFilePath);
|
||||
|
||||
if (lockFileExists) {
|
||||
auto s = chomp(diff);
|
||||
if (lockFileExists) {
|
||||
if (s.empty())
|
||||
warn("updating lock file '%s'", outputLockFilePath);
|
||||
else
|
||||
warn("updating lock file '%s':\n%s", outputLockFilePath, s);
|
||||
} else
|
||||
warn("creating lock file '%s':\n%s", outputLockFilePath, s);
|
||||
if (s.empty())
|
||||
warn("updating lock file '%s'", *outputLockFilePath);
|
||||
else
|
||||
warn("updating lock file '%s':\n%s", *outputLockFilePath, s);
|
||||
} else
|
||||
warn("creating lock file '%s'", *outputLockFilePath);
|
||||
|
||||
std::optional<std::string> commitMessage = std::nullopt;
|
||||
newLockFile.write(*outputLockFilePath);
|
||||
|
||||
if (lockFlags.commitLockFile) {
|
||||
std::string cm;
|
||||
std::optional<std::string> commitMessage = std::nullopt;
|
||||
if (lockFlags.commitLockFile) {
|
||||
if (lockFlags.outputLockFilePath) {
|
||||
throw Error("--commit-lock-file and --output-lock-file are currently incompatible");
|
||||
}
|
||||
std::string cm;
|
||||
|
||||
cm = fetchSettings.commitLockFileSummary.get();
|
||||
cm = fetchSettings.commitLockFileSummary.get();
|
||||
|
||||
if (cm == "") {
|
||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||
}
|
||||
|
||||
cm += "\n\nFlake lock file updates:\n\n";
|
||||
cm += filterANSIEscapes(diff, true);
|
||||
commitMessage = cm;
|
||||
if (cm == "") {
|
||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||
}
|
||||
|
||||
topRef.input.putFile(
|
||||
CanonPath((topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"),
|
||||
newLockFileS, commitMessage);
|
||||
cm += "\n\nFlake lock file updates:\n\n";
|
||||
cm += filterANSIEscapes(diff, true);
|
||||
commitMessage = cm;
|
||||
}
|
||||
|
||||
topRef.input.markChangedFile(
|
||||
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
||||
commitMessage);
|
||||
|
||||
/* Rewriting the lockfile changed the top-level
|
||||
repo, so we should re-read it. FIXME: we could
|
||||
also just clear the 'rev' field... */
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
libexpr_generated_headers += custom_target(
|
||||
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
|
||||
input : 'call-flake.nix',
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
capture : true,
|
||||
install : true,
|
||||
install_dir : includedir / 'nix/flake',
|
||||
)
|
||||
+15
-34
@@ -49,20 +49,10 @@ meson.add_install_script(
|
||||
'@0@'.format(includedir),
|
||||
)
|
||||
|
||||
libexpr_generated_headers = [
|
||||
gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir()),
|
||||
]
|
||||
foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix' ]
|
||||
libexpr_generated_headers += custom_target(
|
||||
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
|
||||
input : header,
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
capture : true,
|
||||
install : true,
|
||||
install_dir : includedir / 'nix',
|
||||
)
|
||||
endforeach
|
||||
subdir('flake')
|
||||
imported_drv_to_derivation_gen = gen_header.process('imported-drv-to-derivation.nix')
|
||||
fetchurl_gen = gen_header.process('fetchurl.nix')
|
||||
derivation_gen = gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir())
|
||||
call_flake_gen = gen_header.process('flake/call-flake.nix')
|
||||
|
||||
libexpr_sources = files(
|
||||
'attr-path.cc',
|
||||
@@ -131,19 +121,24 @@ libexpr = library(
|
||||
libexpr_sources,
|
||||
parser_tab,
|
||||
lexer_tab,
|
||||
libexpr_generated_headers,
|
||||
imported_drv_to_derivation_gen,
|
||||
fetchurl_gen,
|
||||
derivation_gen,
|
||||
call_flake_gen,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixfetchers,
|
||||
boehm,
|
||||
boost,
|
||||
toml11,
|
||||
nlohmann_json,
|
||||
],
|
||||
# for shared.hh
|
||||
include_directories : [
|
||||
'../libmain',
|
||||
include_directories : '../libmain',
|
||||
cpp_args : [
|
||||
# FIXME(Qyriad): can we please fix this. toml11 pls.
|
||||
# Technically this only applies to fromTOML.cc, but, well
|
||||
# https://github.com/mesonbuild/meson/issues/1367
|
||||
'-Wno-error=switch-enum',
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
@@ -153,24 +148,10 @@ libexpr = library(
|
||||
install_headers(
|
||||
libexpr_headers,
|
||||
subdir : 'nix',
|
||||
preserve_path : true,
|
||||
preserve_path : true
|
||||
)
|
||||
|
||||
liblixexpr = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libexpr,
|
||||
)
|
||||
|
||||
# FIXME: not using the pkg-config module because it creates way too many deps
|
||||
# while meson migration is in progress, and we want to not include boost here
|
||||
configure_file(
|
||||
input : 'nix-expr.pc.in',
|
||||
output : 'nix-expr.pc',
|
||||
install_dir : libdir / 'pkgconfig',
|
||||
configuration : {
|
||||
'prefix' : prefix,
|
||||
'libdir' : libdir,
|
||||
'includedir' : includedir,
|
||||
'PACKAGE_VERSION' : meson.project_version(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "symbol-table.hh"
|
||||
#include "util.hh"
|
||||
#include "print.hh"
|
||||
#include "escape-string.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -37,7 +36,7 @@ void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
|
||||
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
escapeString(str, s);
|
||||
printLiteralString(str, s);
|
||||
}
|
||||
|
||||
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
@@ -594,7 +593,7 @@ Pos PosTable::operator[](PosIdx p) const
|
||||
|
||||
Pos result{0, 0, origin->origin};
|
||||
auto lines = this->lines.lock();
|
||||
auto & linesForInput = (*lines)[origin->offset];
|
||||
auto linesForInput = (*lines)[origin->offset];
|
||||
|
||||
if (linesForInput.empty()) {
|
||||
auto source = result.getSource().value_or("");
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
|
||||
#include "../../toml11/toml.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <toml.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "print.hh"
|
||||
#include "eval.hh"
|
||||
#include "signals.hh"
|
||||
#include "escape-string.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -28,7 +27,7 @@ void printAmbiguous(
|
||||
printLiteralBool(str, v.boolean);
|
||||
break;
|
||||
case nString:
|
||||
escapeString(str, v.string.s);
|
||||
printLiteralString(str, v.string.s);
|
||||
break;
|
||||
case nPath:
|
||||
str << v.path().to_string(); // !!! escaping?
|
||||
@@ -94,7 +93,7 @@ void printAmbiguous(
|
||||
str << v.fpoint;
|
||||
break;
|
||||
default:
|
||||
printError("Lix evaluator internal error: printAmbiguous: invalid value type");
|
||||
printError("Nix evaluator internal error: printAmbiguous: invalid value type");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
+56
-15
@@ -1,8 +1,6 @@
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "escape-string.hh"
|
||||
#include "print.hh"
|
||||
#include "ansicolor.hh"
|
||||
#include "store-api.hh"
|
||||
@@ -12,6 +10,57 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void printElided(
|
||||
std::ostream & output,
|
||||
unsigned int value,
|
||||
const std::string_view single,
|
||||
const std::string_view plural,
|
||||
bool ansiColors)
|
||||
{
|
||||
if (ansiColors)
|
||||
output << ANSI_FAINT;
|
||||
output << "«";
|
||||
pluralize(output, value, single, plural);
|
||||
output << " elided»";
|
||||
if (ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
||||
{
|
||||
size_t charsPrinted = 0;
|
||||
if (ansiColors)
|
||||
str << ANSI_MAGENTA;
|
||||
str << "\"";
|
||||
for (auto i = string.begin(); i != string.end(); ++i) {
|
||||
if (charsPrinted >= maxLength) {
|
||||
str << "\" ";
|
||||
printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
|
||||
return str;
|
||||
}
|
||||
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
||||
else str << *i;
|
||||
charsPrinted++;
|
||||
}
|
||||
str << "\"";
|
||||
if (ansiColors)
|
||||
str << ANSI_NORMAL;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
{
|
||||
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralBool(std::ostream & str, bool boolean)
|
||||
{
|
||||
@@ -43,7 +92,7 @@ printIdentifier(std::ostream & str, std::string_view s) {
|
||||
else {
|
||||
char c = s[0];
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
||||
escapeString(str, s);
|
||||
printLiteralString(str, s);
|
||||
return str;
|
||||
}
|
||||
for (auto c : s)
|
||||
@@ -51,7 +100,7 @@ printIdentifier(std::ostream & str, std::string_view s) {
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '_' || c == '\'' || c == '-')) {
|
||||
escapeString(str, s);
|
||||
printLiteralString(str, s);
|
||||
return str;
|
||||
}
|
||||
str << s;
|
||||
@@ -79,7 +128,7 @@ printAttributeName(std::ostream & str, std::string_view name) {
|
||||
if (isVarName(name))
|
||||
str << name;
|
||||
else
|
||||
escapeString(str, name);
|
||||
printLiteralString(str, name);
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -198,15 +247,7 @@ private:
|
||||
|
||||
void printString(Value & v)
|
||||
{
|
||||
escapeString(
|
||||
output,
|
||||
v.string.s,
|
||||
{
|
||||
.maxLength = options.maxStringLength,
|
||||
.ansiColors = options.ansiColors,
|
||||
// NB: Non-printing characters won't be escaped.
|
||||
}
|
||||
);
|
||||
printLiteralString(output, v.string.s, options.maxStringLength, options.ansiColors);
|
||||
}
|
||||
|
||||
void printPath(Value & v)
|
||||
@@ -566,7 +607,7 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
|
||||
}
|
||||
|
||||
template<>
|
||||
fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & value)
|
||||
HintFmt & HintFmt::operator%(const ValuePrinter & value)
|
||||
{
|
||||
fmt % value;
|
||||
return *this;
|
||||
|
||||
+17
-2
@@ -11,13 +11,28 @@
|
||||
|
||||
#include "fmt.hh"
|
||||
#include "print-options.hh"
|
||||
#include "print-elided.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
/**
|
||||
* Print a string as a Nix string literal.
|
||||
*
|
||||
* Quotes and fairly minimal escaping are added.
|
||||
*
|
||||
* @param o The output stream to print to
|
||||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
/** Print `true` or `false`. */
|
||||
std::ostream & printLiteralBool(std::ostream & o, bool b);
|
||||
|
||||
@@ -71,6 +86,6 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
* magenta.
|
||||
*/
|
||||
template<>
|
||||
fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & value);
|
||||
HintFmt & HintFmt::operator%(const ValuePrinter & value);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
///@file
|
||||
|
||||
#include "fetchers.hh"
|
||||
#include "path.hh"
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
|
||||
@@ -200,13 +200,12 @@ std::optional<Path> Input::getSourcePath() const
|
||||
return scheme->getSourcePath(*this);
|
||||
}
|
||||
|
||||
void Input::putFile(
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
void Input::markChangedFile(
|
||||
std::string_view file,
|
||||
std::optional<std::string> commitMsg) const
|
||||
{
|
||||
assert(scheme);
|
||||
return scheme->putFile(*this, path, contents, commitMsg);
|
||||
return scheme->markChangedFile(*this, file, commitMsg);
|
||||
}
|
||||
|
||||
std::string Input::getName() const
|
||||
@@ -296,18 +295,14 @@ Input InputScheme::applyOverrides(
|
||||
return input;
|
||||
}
|
||||
|
||||
std::optional<Path> InputScheme::getSourcePath(const Input & input) const
|
||||
std::optional<Path> InputScheme::getSourcePath(const Input & input)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void InputScheme::putFile(
|
||||
const Input & input,
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const
|
||||
void InputScheme::markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg)
|
||||
{
|
||||
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void InputScheme::clone(const Input & input, const Path & destDir) const
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
#include "canon-path.hh"
|
||||
#include "path.hh"
|
||||
#include "attrs.hh"
|
||||
#include "url.hh"
|
||||
@@ -98,13 +97,8 @@ public:
|
||||
|
||||
std::optional<Path> getSourcePath() const;
|
||||
|
||||
/**
|
||||
* Write a file to this input, for input types that support
|
||||
* writing. Optionally commit the change (for e.g. Git inputs).
|
||||
*/
|
||||
void putFile(
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
void markChangedFile(
|
||||
std::string_view file,
|
||||
std::optional<std::string> commitMsg) const;
|
||||
|
||||
std::string getName() const;
|
||||
@@ -150,13 +144,9 @@ struct InputScheme
|
||||
|
||||
virtual void clone(const Input & input, const Path & destDir) const;
|
||||
|
||||
virtual std::optional<Path> getSourcePath(const Input & input) const;
|
||||
virtual std::optional<Path> getSourcePath(const Input & input);
|
||||
|
||||
virtual void putFile(
|
||||
const Input & input,
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const;
|
||||
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
|
||||
|
||||
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
|
||||
};
|
||||
|
||||
+7
-15
@@ -363,7 +363,7 @@ struct GitInputScheme : InputScheme
|
||||
runProgram("git", true, args, {}, true);
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<Path> getSourcePath(const Input & input) override
|
||||
{
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
if (url.scheme == "file" && !input.getRef() && !input.getRev())
|
||||
@@ -371,30 +371,22 @@ struct GitInputScheme : InputScheme
|
||||
return {};
|
||||
}
|
||||
|
||||
void putFile(
|
||||
const Input & input,
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const override
|
||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
||||
{
|
||||
auto root = getSourcePath(input);
|
||||
if (!root)
|
||||
throw Error("cannot commit '%s' to Git repository '%s' because it's not a working tree", path, input.to_string());
|
||||
|
||||
writeFile((CanonPath(*root) + path).abs(), contents);
|
||||
|
||||
auto sourcePath = getSourcePath(input);
|
||||
assert(sourcePath);
|
||||
auto gitDir = ".git";
|
||||
|
||||
auto result = runProgram(RunOptions {
|
||||
.program = "git",
|
||||
.args = {"-C", *root, "--git-dir", gitDir, "check-ignore", "--quiet", std::string(path.rel())},
|
||||
.args = {"-C", *sourcePath, "--git-dir", gitDir, "check-ignore", "--quiet", std::string(file)},
|
||||
});
|
||||
auto exitCode = WEXITSTATUS(result.first);
|
||||
|
||||
if (exitCode != 0) {
|
||||
// The path is not `.gitignore`d, we can add the file.
|
||||
runProgram("git", true,
|
||||
{ "-C", *root, "--git-dir", gitDir, "add", "--intent-to-add", "--", std::string(path.rel()) });
|
||||
{ "-C", *sourcePath, "--git-dir", gitDir, "add", "--intent-to-add", "--", std::string(file) });
|
||||
|
||||
|
||||
if (commitMsg) {
|
||||
@@ -402,7 +394,7 @@ struct GitInputScheme : InputScheme
|
||||
logger->pause();
|
||||
Finally restoreLogger([]() { logger->resume(); });
|
||||
runProgram("git", true,
|
||||
{ "-C", *root, "--git-dir", gitDir, "commit", std::string(path.rel()), "-m", *commitMsg });
|
||||
{ "-C", *sourcePath, "--git-dir", gitDir, "commit", std::string(file), "-m", *commitMsg });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "fetchers.hh"
|
||||
#include "url-parts.hh"
|
||||
#include "path.hh"
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ struct MercurialInputScheme : InputScheme
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<Path> getSourcePath(const Input & input) override
|
||||
{
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
if (url.scheme == "file" && !input.getRef() && !input.getRev())
|
||||
@@ -124,27 +124,18 @@ struct MercurialInputScheme : InputScheme
|
||||
return {};
|
||||
}
|
||||
|
||||
void putFile(
|
||||
const Input & input,
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const override
|
||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
||||
{
|
||||
auto [isLocal, repoPath] = getActualUrl(input);
|
||||
if (!isLocal)
|
||||
throw Error("cannot commit '%s' to Mercurial repository '%s' because it's not a working tree", path, input.to_string());
|
||||
|
||||
auto absPath = CanonPath(repoPath) + path;
|
||||
|
||||
writeFile(absPath.abs(), contents);
|
||||
auto sourcePath = getSourcePath(input);
|
||||
assert(sourcePath);
|
||||
|
||||
// FIXME: shut up if file is already tracked.
|
||||
runHg(
|
||||
{ "add", absPath.abs() });
|
||||
{ "add", *sourcePath + "/" + std::string(file) });
|
||||
|
||||
if (commitMsg)
|
||||
runHg(
|
||||
{ "commit", absPath.abs(), "-m", *commitMsg });
|
||||
{ "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg });
|
||||
}
|
||||
|
||||
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
||||
|
||||
@@ -28,7 +28,6 @@ libfetchers = library(
|
||||
dependencies : [
|
||||
liblixstore,
|
||||
liblixutil,
|
||||
nlohmann_json,
|
||||
],
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
|
||||
+3
-17
@@ -71,28 +71,14 @@ struct PathInputScheme : InputScheme
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<Path> getSourcePath(const Input & input) override
|
||||
{
|
||||
return getStrAttr(input.attrs, "path");
|
||||
}
|
||||
|
||||
void putFile(
|
||||
const Input & input,
|
||||
const CanonPath & path,
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const override
|
||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
||||
{
|
||||
writeFile((CanonPath(getAbsPath(input)) + path).abs(), contents);
|
||||
}
|
||||
|
||||
CanonPath getAbsPath(const Input & input) const
|
||||
{
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] == '/')
|
||||
return CanonPath(path);
|
||||
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "common-args.hh"
|
||||
#include "args/root.hh"
|
||||
#include "globals.hh"
|
||||
#include "loggers.hh"
|
||||
|
||||
@@ -35,21 +34,21 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
|
||||
.description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).",
|
||||
.category = miscCategory,
|
||||
.labels = {"name", "value"},
|
||||
.handler = {[this](std::string name, std::string value) {
|
||||
.handler = {[](std::string name, std::string value) {
|
||||
try {
|
||||
globalConfig.set(name, value);
|
||||
} catch (UsageError & e) {
|
||||
if (!getRoot().completions)
|
||||
if (!completions)
|
||||
warn(e.what());
|
||||
}
|
||||
}},
|
||||
.completer = [](AddCompletions & completions, size_t index, std::string_view prefix) {
|
||||
.completer = [](size_t index, std::string_view prefix) {
|
||||
if (index == 0) {
|
||||
std::map<std::string, Config::SettingInfo> settings;
|
||||
globalConfig.getSettings(settings);
|
||||
for (auto & s : settings)
|
||||
if (s.first.starts_with(prefix))
|
||||
completions.add(s.first, fmt("Set the `%s` setting.", s.first));
|
||||
completions->add(s.first, fmt("Set the `%s` setting.", s.first));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,17 +31,3 @@ liblixmain = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libmain,
|
||||
)
|
||||
|
||||
# FIXME: not using the pkg-config module because it creates way too many deps
|
||||
# while meson migration is in progress, and we want to not include boost here
|
||||
configure_file(
|
||||
input : 'nix-main.pc.in',
|
||||
output : 'nix-main.pc',
|
||||
install_dir : libdir / 'pkgconfig',
|
||||
configuration : {
|
||||
'prefix' : prefix,
|
||||
'libdir' : libdir,
|
||||
'includedir' : includedir,
|
||||
'PACKAGE_VERSION' : meson.project_version(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
static std::string_view getS(const std::vector<Logger::Field> & fields, size_t n)
|
||||
{
|
||||
assert(n < fields.size());
|
||||
@@ -35,9 +33,6 @@ static std::string_view storePathToName(std::string_view path)
|
||||
return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1);
|
||||
}
|
||||
|
||||
// 100 years ought to be enough for anyone (yet sufficiently smaller than max() to not cause signed integer overflow).
|
||||
constexpr const auto A_LONG_TIME = std::chrono::duration_cast<std::chrono::milliseconds>(100 * 365 * 86400s);
|
||||
|
||||
class ProgressBar : public Logger
|
||||
{
|
||||
private:
|
||||
@@ -98,7 +93,7 @@ public:
|
||||
state_.lock()->active = isTTY;
|
||||
updateThread = std::thread([&]() {
|
||||
auto state(state_.lock());
|
||||
auto nextWakeup = A_LONG_TIME;
|
||||
auto nextWakeup = std::chrono::milliseconds::max();
|
||||
while (state->active) {
|
||||
if (!state->haveUpdate)
|
||||
state.wait_for(updateCV, nextWakeup);
|
||||
@@ -355,7 +350,7 @@ public:
|
||||
|
||||
std::chrono::milliseconds draw(State & state)
|
||||
{
|
||||
auto nextWakeup = A_LONG_TIME;
|
||||
auto nextWakeup = std::chrono::milliseconds::max();
|
||||
|
||||
state.haveUpdate = false;
|
||||
if (state.paused || !state.active) return nextWakeup;
|
||||
|
||||
@@ -114,8 +114,10 @@ static void sigHandler(int signo) { }
|
||||
void initNix()
|
||||
{
|
||||
/* Turn on buffering for cerr. */
|
||||
#if HAVE_PUBSETBUF
|
||||
static char buf[1024];
|
||||
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
||||
#endif
|
||||
|
||||
initLibStore();
|
||||
|
||||
@@ -281,7 +283,7 @@ void parseCmdLine(const std::string & programName, const Strings & args,
|
||||
|
||||
void printVersion(const std::string & programName)
|
||||
{
|
||||
std::cout << fmt("%1% (Lix, like Nix) %2%", programName, nixVersion) << std::endl;
|
||||
std::cout << fmt("%1% (Nix) %2%", programName, nixVersion) << std::endl;
|
||||
if (verbosity > lvlInfo) {
|
||||
Strings cfg;
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "util.hh"
|
||||
#include "args.hh"
|
||||
#include "args/root.hh"
|
||||
#include "common-args.hh"
|
||||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
@@ -59,7 +58,7 @@ template<class N> N getIntArg(const std::string & opt,
|
||||
}
|
||||
|
||||
|
||||
struct LegacyArgs : public MixCommonArgs, public RootArgs
|
||||
struct LegacyArgs : public MixCommonArgs
|
||||
{
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "topo-sort.hh"
|
||||
#include "callback.hh"
|
||||
#include "local-store.hh" // TODO remove, along with remaining downcasts
|
||||
#include "logging-json.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
|
||||
@@ -453,7 +453,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
command. (We don't trust `addToStoreFromDump` to not
|
||||
eagerly consume the entire stream it's given, past the
|
||||
length of the Nar. */
|
||||
copyNAR(from, saved);
|
||||
TeeSource savedNARSource(from, saved);
|
||||
ParseSink sink; /* null sink; just parse the NAR */
|
||||
parseDump(sink, savedNARSource);
|
||||
} else {
|
||||
/* Incrementally parse the NAR file, stripping the
|
||||
metadata, and streaming the sole file we expect into
|
||||
@@ -905,7 +907,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
|
||||
source = std::make_unique<TunnelSource>(from, to);
|
||||
else {
|
||||
copyNAR(from, saved);
|
||||
TeeSource tee { from, saved };
|
||||
ParseSink ether;
|
||||
parseDump(ether, tee);
|
||||
source = std::make_unique<StringSource>(saved.s);
|
||||
}
|
||||
|
||||
@@ -1086,7 +1090,7 @@ void processConnection(
|
||||
tunnelLogger->stopWork(&e);
|
||||
if (!errorAllowed) throw;
|
||||
} catch (std::bad_alloc & e) {
|
||||
auto ex = Error("Lix daemon out of memory");
|
||||
auto ex = Error("Nix daemon out of memory");
|
||||
tunnelLogger->stopWork(&ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common-protocol.hh"
|
||||
#include "common-protocol-impl.hh"
|
||||
#include "fs-accessor.hh"
|
||||
#include "json-utils.hh"
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
||||
|
||||
/* Extract the NAR from the source. */
|
||||
StringSink saved;
|
||||
copyNAR(source, saved);
|
||||
TeeSource tee { source, saved };
|
||||
ParseSink ether;
|
||||
parseDump(ether, tee);
|
||||
|
||||
uint32_t magic = readInt(source);
|
||||
if (magic != exportMagic)
|
||||
|
||||
@@ -296,7 +296,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
curl_easy_setopt(req, CURLOPT_MAXREDIRS, 10);
|
||||
curl_easy_setopt(req, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(req, CURLOPT_USERAGENT,
|
||||
("curl/" LIBCURL_VERSION " Lix/" + nixVersion +
|
||||
("curl/" LIBCURL_VERSION " Nix/" + nixVersion +
|
||||
(fileTransferSettings.userAgentSuffix != "" ? " " + fileTransferSettings.userAgentSuffix.get() : "")).c_str());
|
||||
#if LIBCURL_VERSION_NUM >= 0x072b00
|
||||
curl_easy_setopt(req, CURLOPT_PIPEWAIT, 1);
|
||||
@@ -724,7 +724,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
res.data = std::move(*s3Res.data);
|
||||
callback(std::move(res));
|
||||
#else
|
||||
throw nix::Error("cannot download '%s' because Lix is not built with S3 support", request.uri);
|
||||
throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri);
|
||||
#endif
|
||||
} catch (...) { callback.rethrow(); }
|
||||
return;
|
||||
|
||||
@@ -402,6 +402,8 @@ void assertLibStoreInitialized() {
|
||||
|
||||
void initLibStore() {
|
||||
|
||||
initLibUtil();
|
||||
|
||||
if (sodium_init() == -1)
|
||||
throw Error("could not initialise libsodium");
|
||||
|
||||
|
||||
@@ -124,9 +124,20 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
|
||||
conn->to << SERVE_MAGIC_1 << SERVE_PROTOCOL_VERSION;
|
||||
conn->to.flush();
|
||||
|
||||
uint64_t magic = readLongLong(conn->from);
|
||||
if (magic != SERVE_MAGIC_2)
|
||||
throw Error("'nix-store --serve' protocol mismatch from '%s'", host);
|
||||
StringSink saved;
|
||||
try {
|
||||
TeeSource tee(conn->from, saved);
|
||||
unsigned int magic = readInt(tee);
|
||||
if (magic != SERVE_MAGIC_2)
|
||||
throw Error("'nix-store --serve' protocol mismatch from '%s'", host);
|
||||
} catch (SerialisationError & e) {
|
||||
/* In case the other side is waiting for our input,
|
||||
close it. */
|
||||
conn->sshConn->in.close();
|
||||
auto msg = conn->from.drain();
|
||||
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
|
||||
host, chomp(saved.s + msg));
|
||||
}
|
||||
conn->remoteVersion = readInt(conn->from);
|
||||
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
|
||||
throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host);
|
||||
|
||||
@@ -548,15 +548,6 @@ void LocalStore::openDB(State & state, bool create)
|
||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "setting journal mode");
|
||||
|
||||
if (mode == "wal" ) {
|
||||
/* persist the WAL files when the DB connection is closed.
|
||||
* This allows for read-only connections without any write permissions
|
||||
* on the state directory to succeed on a closed database. */
|
||||
int enable = 1;
|
||||
if (sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, &enable) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "setting persistent WAL mode");
|
||||
}
|
||||
|
||||
/* Increase the auto-checkpoint interval to 40000 pages. This
|
||||
seems enough to ensure that instantiating the NixOS system
|
||||
derivation is done in a single fsync(). */
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
libstore_generated_headers = []
|
||||
foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ]
|
||||
libstore_generated_headers += custom_target(
|
||||
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
|
||||
input : header,
|
||||
output : '@PLAINNAME@.gen.hh',
|
||||
capture : true,
|
||||
install : true,
|
||||
install_dir : includedir / 'nix',
|
||||
)
|
||||
endforeach
|
||||
schema_sql_gen = gen_header.process('schema.sql')
|
||||
ca_specific_schema_gen = gen_header.process('ca-specific-schema.sql')
|
||||
|
||||
libstore_sources = files(
|
||||
'binary-cache-store.cc',
|
||||
@@ -149,7 +140,7 @@ cpp_str_defines = {
|
||||
'NIX_PREFIX': prefix,
|
||||
'NIX_STORE_DIR': store_dir,
|
||||
'NIX_DATA_DIR': datadir,
|
||||
'NIX_STATE_DIR': state_dir / 'nix',
|
||||
'NIX_STATE_DIR': state_dir,
|
||||
'NIX_LOG_DIR': log_dir,
|
||||
'NIX_CONF_DIR': sysconfdir,
|
||||
'NIX_BIN_DIR': bindir,
|
||||
@@ -166,7 +157,8 @@ endforeach
|
||||
|
||||
libstore = library(
|
||||
'nixstore',
|
||||
libstore_generated_headers,
|
||||
schema_sql_gen,
|
||||
ca_specific_schema_gen,
|
||||
libstore_sources,
|
||||
dependencies : [
|
||||
libarchive,
|
||||
@@ -180,7 +172,6 @@ libstore = library(
|
||||
aws_sdk,
|
||||
aws_s3,
|
||||
aws_sdk_transfer,
|
||||
nlohmann_json,
|
||||
],
|
||||
cpp_args : cpp_args,
|
||||
install : true,
|
||||
@@ -195,17 +186,3 @@ liblixstore = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libstore,
|
||||
)
|
||||
|
||||
# FIXME: not using the pkg-config module because it creates way too many deps
|
||||
# while meson migration is in progress, and we want to not include boost here
|
||||
configure_file(
|
||||
input : 'nix-store.pc.in',
|
||||
output : 'nix-store.pc',
|
||||
install_dir : libdir / 'pkgconfig',
|
||||
configuration : {
|
||||
'prefix' : prefix,
|
||||
'libdir' : libdir,
|
||||
'includedir' : includedir,
|
||||
'PACKAGE_VERSION' : meson.project_version(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -69,10 +69,19 @@ void RemoteStore::initConnection(Connection & conn)
|
||||
conn.from.endOfFileError = "Nix daemon disconnected unexpectedly (maybe it crashed?)";
|
||||
conn.to << WORKER_MAGIC_1;
|
||||
conn.to.flush();
|
||||
|
||||
uint64_t magic = readLongLong(conn.from);
|
||||
if (magic != WORKER_MAGIC_2)
|
||||
throw Error("protocol mismatch");
|
||||
StringSink saved;
|
||||
try {
|
||||
TeeSource tee(conn.from, saved);
|
||||
unsigned int magic = readInt(tee);
|
||||
if (magic != WORKER_MAGIC_2)
|
||||
throw Error("protocol mismatch");
|
||||
} catch (SerialisationError & e) {
|
||||
/* In case the other side is waiting for our input, close
|
||||
it. */
|
||||
conn.closeWrite();
|
||||
auto msg = conn.from.drain();
|
||||
throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg));
|
||||
}
|
||||
|
||||
conn.from >> conn.daemonVersion;
|
||||
if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "config.hh"
|
||||
#include "json-utils.hh"
|
||||
// Required for instances of to_json and from_json for ExperimentalFeature
|
||||
#include "experimental-features-json.hh"
|
||||
|
||||
namespace nix {
|
||||
template<typename T>
|
||||
|
||||
+43
-82
@@ -1,8 +1,6 @@
|
||||
#include "args.hh"
|
||||
#include "args/root.hh"
|
||||
#include "hash.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "experimental-features-json.hh"
|
||||
|
||||
#include <glob.h>
|
||||
|
||||
@@ -28,11 +26,6 @@ void Args::removeFlag(const std::string & longName)
|
||||
longFlags.erase(flag);
|
||||
}
|
||||
|
||||
void Completions::setType(AddCompletions::Type t)
|
||||
{
|
||||
type = t;
|
||||
}
|
||||
|
||||
void Completions::add(std::string completion, std::string description)
|
||||
{
|
||||
description = trim(description);
|
||||
@@ -44,7 +37,7 @@ void Completions::add(std::string completion, std::string description)
|
||||
if (needs_ellipsis)
|
||||
description.append(" [...]");
|
||||
}
|
||||
completions.insert(Completion {
|
||||
insert(Completion {
|
||||
.completion = completion,
|
||||
.description = description
|
||||
});
|
||||
@@ -53,20 +46,12 @@ void Completions::add(std::string completion, std::string description)
|
||||
bool Completion::operator<(const Completion & other) const
|
||||
{ return completion < other.completion || (completion == other.completion && description < other.description); }
|
||||
|
||||
CompletionType completionType = ctNormal;
|
||||
std::shared_ptr<Completions> completions;
|
||||
|
||||
std::string completionMarker = "___COMPLETE___";
|
||||
|
||||
RootArgs & Args::getRoot()
|
||||
{
|
||||
Args * p = this;
|
||||
while (p->parent)
|
||||
p = p->parent;
|
||||
|
||||
auto * res = dynamic_cast<RootArgs *>(p);
|
||||
assert(res);
|
||||
return *res;
|
||||
}
|
||||
|
||||
std::optional<std::string> RootArgs::needsCompletion(std::string_view s)
|
||||
static std::optional<std::string> needsCompletion(std::string_view s)
|
||||
{
|
||||
if (!completions) return {};
|
||||
auto i = s.find(completionMarker);
|
||||
@@ -75,7 +60,7 @@ std::optional<std::string> RootArgs::needsCompletion(std::string_view s)
|
||||
return {};
|
||||
}
|
||||
|
||||
void RootArgs::parseCmdline(const Strings & _cmdline)
|
||||
void Args::parseCmdline(const Strings & _cmdline)
|
||||
{
|
||||
Strings pendingArgs;
|
||||
bool dashDash = false;
|
||||
@@ -86,7 +71,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline)
|
||||
size_t n = std::stoi(*s);
|
||||
assert(n > 0 && n <= cmdline.size());
|
||||
*std::next(cmdline.begin(), n - 1) += completionMarker;
|
||||
completions = std::make_shared<Completions>();
|
||||
completions = std::make_shared<decltype(completions)::element_type>();
|
||||
verbosity = lvlError;
|
||||
}
|
||||
|
||||
@@ -140,23 +125,17 @@ void RootArgs::parseCmdline(const Strings & _cmdline)
|
||||
for (auto & f : flagExperimentalFeatures)
|
||||
experimentalFeatureSettings.require(f);
|
||||
|
||||
/* Now that all the other args are processed, run the deferred completions.
|
||||
*/
|
||||
for (auto d : deferredCompletions)
|
||||
d.completer(*completions, d.n, d.prefix);
|
||||
}
|
||||
|
||||
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
{
|
||||
assert(pos != end);
|
||||
|
||||
auto & rootArgs = getRoot();
|
||||
|
||||
auto process = [&](const std::string & name, const Flag & flag) -> bool {
|
||||
++pos;
|
||||
|
||||
if (auto & f = flag.experimentalFeature)
|
||||
rootArgs.flagExperimentalFeatures.insert(*f);
|
||||
flagExperimentalFeatures.insert(*f);
|
||||
|
||||
std::vector<std::string> args;
|
||||
bool anyCompleted = false;
|
||||
@@ -167,15 +146,10 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
"flag '%s' requires %d argument(s), but only %d were given",
|
||||
name, flag.handler.arity, n);
|
||||
}
|
||||
if (auto prefix = rootArgs.needsCompletion(*pos)) {
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
if (flag.completer) {
|
||||
rootArgs.deferredCompletions.push_back({
|
||||
.completer = flag.completer,
|
||||
.n = n,
|
||||
.prefix = *prefix,
|
||||
});
|
||||
}
|
||||
if (flag.completer)
|
||||
flag.completer(n, *prefix);
|
||||
}
|
||||
args.push_back(*pos++);
|
||||
}
|
||||
@@ -185,14 +159,14 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
};
|
||||
|
||||
if (std::string(*pos, 0, 2) == "--") {
|
||||
if (auto prefix = rootArgs.needsCompletion(*pos)) {
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
for (auto & [name, flag] : longFlags) {
|
||||
if (!hiddenCategories.count(flag->category)
|
||||
&& name.starts_with(std::string(*prefix, 2)))
|
||||
{
|
||||
if (auto & f = flag->experimentalFeature)
|
||||
rootArgs.flagExperimentalFeatures.insert(*f);
|
||||
rootArgs.completions->add("--" + name, flag->description);
|
||||
flagExperimentalFeatures.insert(*f);
|
||||
completions->add("--" + name, flag->description);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -209,12 +183,12 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
return process(std::string("-") + c, *i->second);
|
||||
}
|
||||
|
||||
if (auto prefix = rootArgs.needsCompletion(*pos)) {
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
if (prefix == "-") {
|
||||
rootArgs.completions->add("--");
|
||||
completions->add("--");
|
||||
for (auto & [flagName, flag] : shortFlags)
|
||||
if (experimentalFeatureSettings.isEnabled(flag->experimentalFeature))
|
||||
rootArgs.completions->add(std::string("-") + flagName, flag->description);
|
||||
completions->add(std::string("-") + flagName, flag->description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +203,6 @@ bool Args::processArgs(const Strings & args, bool finish)
|
||||
return true;
|
||||
}
|
||||
|
||||
auto & rootArgs = getRoot();
|
||||
|
||||
auto & exp = expectedArgs.front();
|
||||
|
||||
bool res = false;
|
||||
@@ -239,35 +211,16 @@ bool Args::processArgs(const Strings & args, bool finish)
|
||||
(exp.handler.arity != ArityAny && args.size() == exp.handler.arity))
|
||||
{
|
||||
std::vector<std::string> ss;
|
||||
bool anyCompleted = false;
|
||||
for (const auto &[n, s] : enumerate(args)) {
|
||||
if (auto prefix = rootArgs.needsCompletion(s)) {
|
||||
anyCompleted = true;
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
ss.push_back(*prefix);
|
||||
if (exp.completer) {
|
||||
rootArgs.deferredCompletions.push_back({
|
||||
.completer = exp.completer,
|
||||
.n = n,
|
||||
.prefix = *prefix,
|
||||
});
|
||||
}
|
||||
if (exp.completer)
|
||||
exp.completer(n, *prefix);
|
||||
} else
|
||||
ss.push_back(s);
|
||||
}
|
||||
if (!anyCompleted)
|
||||
exp.handler.fun(ss);
|
||||
|
||||
/* Move the list element to the processedArgs. This is almost the same as
|
||||
`processedArgs.push_back(expectedArgs.front()); expectedArgs.pop_front()`,
|
||||
except that it will only adjust the next and prev pointers of the list
|
||||
elements, meaning the actual contents don't move in memory. This is
|
||||
critical to prevent invalidating internal pointers! */
|
||||
processedArgs.splice(
|
||||
processedArgs.end(),
|
||||
expectedArgs,
|
||||
expectedArgs.begin(),
|
||||
++expectedArgs.begin());
|
||||
|
||||
exp.handler.fun(ss);
|
||||
expectedArgs.pop_front();
|
||||
res = true;
|
||||
}
|
||||
|
||||
@@ -318,11 +271,11 @@ nlohmann::json Args::toJSON()
|
||||
return res;
|
||||
}
|
||||
|
||||
static void hashTypeCompleter(AddCompletions & completions, size_t index, std::string_view prefix)
|
||||
static void hashTypeCompleter(size_t index, std::string_view prefix)
|
||||
{
|
||||
for (auto & type : hashTypes)
|
||||
if (type.starts_with(prefix))
|
||||
completions.add(type);
|
||||
completions->add(type);
|
||||
}
|
||||
|
||||
Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
|
||||
@@ -334,7 +287,7 @@ Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
|
||||
.handler = {[ht](std::string s) {
|
||||
*ht = parseHashType(s);
|
||||
}},
|
||||
.completer = hashTypeCompleter,
|
||||
.completer = hashTypeCompleter
|
||||
};
|
||||
}
|
||||
|
||||
@@ -347,13 +300,13 @@ Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional<
|
||||
.handler = {[oht](std::string s) {
|
||||
*oht = std::optional<HashType> { parseHashType(s) };
|
||||
}},
|
||||
.completer = hashTypeCompleter,
|
||||
.completer = hashTypeCompleter
|
||||
};
|
||||
}
|
||||
|
||||
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
|
||||
static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
completions.setType(Completions::Type::Filenames);
|
||||
completionType = ctFilenames;
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
@@ -367,20 +320,20 @@ static void _completePath(AddCompletions & completions, std::string_view prefix,
|
||||
auto st = stat(globbuf.gl_pathv[i]);
|
||||
if (!S_ISDIR(st.st_mode)) continue;
|
||||
}
|
||||
completions.add(globbuf.gl_pathv[i]);
|
||||
completions->add(globbuf.gl_pathv[i]);
|
||||
}
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
|
||||
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)
|
||||
void completePath(size_t, std::string_view prefix)
|
||||
{
|
||||
_completePath(completions, prefix, false);
|
||||
_completePath(prefix, false);
|
||||
}
|
||||
|
||||
void Args::completeDir(AddCompletions & completions, size_t, std::string_view prefix)
|
||||
void completeDir(size_t, std::string_view prefix)
|
||||
{
|
||||
_completePath(completions, prefix, true);
|
||||
_completePath(prefix, true);
|
||||
}
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv)
|
||||
@@ -415,10 +368,10 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||
command = {s, i->second()};
|
||||
command->second->parent = this;
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (name.starts_with(prefix))
|
||||
completions.add(name);
|
||||
completions->add(name);
|
||||
}}
|
||||
});
|
||||
|
||||
@@ -440,6 +393,14 @@ bool MultiCommand::processArgs(const Strings & args, bool finish)
|
||||
return Args::processArgs(args, finish);
|
||||
}
|
||||
|
||||
void MultiCommand::completionHook()
|
||||
{
|
||||
if (command)
|
||||
return command->second->completionHook();
|
||||
else
|
||||
return Args::completionHook();
|
||||
}
|
||||
|
||||
nlohmann::json MultiCommand::toJSON()
|
||||
{
|
||||
auto cmds = nlohmann::json::object();
|
||||
|
||||
+39
-84
@@ -15,14 +15,16 @@ enum HashType : char;
|
||||
|
||||
class MultiCommand;
|
||||
|
||||
class RootArgs;
|
||||
|
||||
class AddCompletions;
|
||||
|
||||
class Args
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Parse the command line, throwing a UsageError if something goes
|
||||
* wrong.
|
||||
*/
|
||||
void parseCmdline(const Strings & cmdline);
|
||||
|
||||
/**
|
||||
* Return a short one-line description of the command.
|
||||
*/
|
||||
@@ -121,25 +123,6 @@ protected:
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* The basic function type of the completion callback.
|
||||
*
|
||||
* Used to define `CompleterClosure` and some common case completers
|
||||
* that individual flags/arguments can use.
|
||||
*
|
||||
* The `AddCompletions` that is passed is an interface to the state
|
||||
* stored as part of the root command
|
||||
*/
|
||||
typedef void CompleterFun(AddCompletions &, size_t, std::string_view);
|
||||
|
||||
/**
|
||||
* The closure type of the completion callback.
|
||||
*
|
||||
* This is what is actually stored as part of each Flag / Expected
|
||||
* Arg.
|
||||
*/
|
||||
typedef std::function<CompleterFun> CompleterClosure;
|
||||
|
||||
/**
|
||||
* Description of flags / options
|
||||
*
|
||||
@@ -157,7 +140,7 @@ protected:
|
||||
std::string category;
|
||||
Strings labels;
|
||||
Handler handler;
|
||||
CompleterClosure completer;
|
||||
std::function<void(size_t, std::string_view)> completer;
|
||||
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
|
||||
@@ -194,31 +177,19 @@ protected:
|
||||
std::string label;
|
||||
bool optional = false;
|
||||
Handler handler;
|
||||
CompleterClosure completer;
|
||||
std::function<void(size_t, std::string_view)> completer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue of expected positional argument forms.
|
||||
*
|
||||
* Positional argument descriptions are inserted on the back.
|
||||
* Positional arugment descriptions are inserted on the back.
|
||||
*
|
||||
* As positional arguments are passed, these are popped from the
|
||||
* front, until there are hopefully none left as all args that were
|
||||
* expected in fact were passed.
|
||||
*/
|
||||
std::list<ExpectedArg> expectedArgs;
|
||||
/**
|
||||
* List of processed positional argument forms.
|
||||
*
|
||||
* All items removed from `expectedArgs` are added here. After all
|
||||
* arguments were processed, this list should be exactly the same as
|
||||
* `expectedArgs` was before.
|
||||
*
|
||||
* This list is used to extend the lifetime of the argument forms.
|
||||
* If this is not done, some closures that reference the command
|
||||
* itself will segfault.
|
||||
*/
|
||||
std::list<ExpectedArg> processedArgs;
|
||||
|
||||
/**
|
||||
* Process some positional arugments
|
||||
@@ -240,6 +211,13 @@ protected:
|
||||
*/
|
||||
virtual void initialFlagsProcessed() {}
|
||||
|
||||
/**
|
||||
* Called after the command line has been processed if we need to generate
|
||||
* completions. Useful for commands that need to know the whole command line
|
||||
* in order to know what completions to generate.
|
||||
*/
|
||||
virtual void completionHook() { }
|
||||
|
||||
public:
|
||||
|
||||
void addFlag(Flag && flag);
|
||||
@@ -274,30 +252,24 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
static CompleterFun completePath;
|
||||
|
||||
static CompleterFun completeDir;
|
||||
|
||||
virtual nlohmann::json toJSON();
|
||||
|
||||
friend class MultiCommand;
|
||||
|
||||
/**
|
||||
* The parent command, used if this is a subcommand.
|
||||
*
|
||||
* Invariant: An Args with a null parent must also be a RootArgs
|
||||
*
|
||||
* \todo this would probably be better in the CommandClass.
|
||||
* getRoot() could be an abstract method that peels off at most one
|
||||
* layer before recuring.
|
||||
*/
|
||||
MultiCommand * parent = nullptr;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Traverse parent pointers until we find the \ref RootArgs "root
|
||||
* arguments" object.
|
||||
* Experimental features needed when parsing args. These are checked
|
||||
* after flag parsing is completed in order to support enabling
|
||||
* experimental features coming after the flag that needs the
|
||||
* experimental feature.
|
||||
*/
|
||||
RootArgs & getRoot();
|
||||
std::set<ExperimentalFeature> flagExperimentalFeatures;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -348,6 +320,8 @@ public:
|
||||
|
||||
bool processArgs(const Strings & args, bool finish) override;
|
||||
|
||||
void completionHook() override;
|
||||
|
||||
nlohmann::json toJSON() override;
|
||||
};
|
||||
|
||||
@@ -359,40 +333,21 @@ struct Completion {
|
||||
|
||||
bool operator<(const Completion & other) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The abstract interface for completions callbacks
|
||||
*
|
||||
* The idea is to restrict the callback so it can only add additional
|
||||
* completions to the collection, or set the completion type. By making
|
||||
* it go through this interface, the callback cannot make any other
|
||||
* changes, or even view the completions / completion type that have
|
||||
* been set so far.
|
||||
*/
|
||||
class AddCompletions
|
||||
{
|
||||
class Completions : public std::set<Completion> {
|
||||
public:
|
||||
|
||||
/**
|
||||
* The type of completion we are collecting.
|
||||
*/
|
||||
enum class Type {
|
||||
Normal,
|
||||
Filenames,
|
||||
Attrs,
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the type of the completions being collected
|
||||
*
|
||||
* \todo it should not be possible to change the type after it has been set.
|
||||
*/
|
||||
virtual void setType(Type type) = 0;
|
||||
|
||||
/**
|
||||
* Add a single completion to the collection
|
||||
*/
|
||||
virtual void add(std::string completion, std::string description = "") = 0;
|
||||
void add(std::string completion, std::string description = "");
|
||||
};
|
||||
extern std::shared_ptr<Completions> completions;
|
||||
|
||||
enum CompletionType {
|
||||
ctNormal,
|
||||
ctFilenames,
|
||||
ctAttrs
|
||||
};
|
||||
extern CompletionType completionType;
|
||||
|
||||
void completePath(size_t, std::string_view prefix);
|
||||
|
||||
void completeDir(size_t, std::string_view prefix);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "args.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* The concrete implementation of a collection of completions.
|
||||
*
|
||||
* This is exposed so that the main entry point can print out the
|
||||
* collected completions.
|
||||
*/
|
||||
struct Completions final : AddCompletions
|
||||
{
|
||||
std::set<Completion> completions;
|
||||
Type type = Type::Normal;
|
||||
|
||||
void setType(Type type) override;
|
||||
void add(std::string completion, std::string description = "") override;
|
||||
};
|
||||
|
||||
/**
|
||||
* The outermost Args object. This is the one we will actually parse a command
|
||||
* line with, whereas the inner ones (if they exists) are subcommands (and this
|
||||
* is also a MultiCommand or something like it).
|
||||
*
|
||||
* This Args contains completions state shared between it and all of its
|
||||
* descendent Args.
|
||||
*/
|
||||
class RootArgs : virtual public Args
|
||||
{
|
||||
public:
|
||||
/** Parse the command line, throwing a UsageError if something goes
|
||||
* wrong.
|
||||
*/
|
||||
void parseCmdline(const Strings & cmdline);
|
||||
|
||||
std::shared_ptr<Completions> completions;
|
||||
|
||||
protected:
|
||||
|
||||
friend class Args;
|
||||
|
||||
/**
|
||||
* A pointer to the completion and its two arguments; a thunk;
|
||||
*/
|
||||
struct DeferredCompletion {
|
||||
const CompleterClosure & completer;
|
||||
size_t n;
|
||||
std::string prefix;
|
||||
};
|
||||
|
||||
/**
|
||||
* Completions are run after all args and flags are parsed, so completions
|
||||
* of earlier arguments can benefit from later arguments.
|
||||
*/
|
||||
std::vector<DeferredCompletion> deferredCompletions;
|
||||
|
||||
/**
|
||||
* Experimental features needed when parsing args. These are checked
|
||||
* after flag parsing is completed in order to support enabling
|
||||
* experimental features coming after the flag that needs the
|
||||
* experimental feature.
|
||||
*/
|
||||
std::set<ExperimentalFeature> flagExperimentalFeatures;
|
||||
|
||||
private:
|
||||
|
||||
std::optional<std::string> needsCompletion(std::string_view s);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -437,34 +437,6 @@ void OptionalPathSetting::operator =(const std::optional<Path> & v)
|
||||
this->assign(v);
|
||||
}
|
||||
|
||||
PathsSetting::PathsSetting(Config * options,
|
||||
const Paths & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases)
|
||||
: BaseSetting<Paths>(def, true, name, description, aliases)
|
||||
{
|
||||
options->addSetting(this);
|
||||
}
|
||||
|
||||
|
||||
Paths PathsSetting::parse(const std::string & str) const
|
||||
{
|
||||
auto strings = tokenizeString<Strings>(str);
|
||||
Paths parsed;
|
||||
|
||||
for (auto str : strings) {
|
||||
parsed.push_back(canonPath(str));
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
PathsSetting::operator bool() const noexcept
|
||||
{
|
||||
return !get().empty();
|
||||
}
|
||||
|
||||
bool GlobalConfig::set(const std::string & name, const std::string & value)
|
||||
{
|
||||
for (auto & config : *configRegistrations)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user