nixos-option: fix build against nix 2.24

This commit is contained in:
Jörg Thalheim 2024-11-13 20:37:55 +01:00
parent d13c9db8c3
commit 5f9d2d9572
8 changed files with 115 additions and 157 deletions

View File

@ -1,24 +1,28 @@
{ lib {
, stdenv lib,
, boost stdenv,
, cmake boost,
, pkg-config meson,
, installShellFiles ninja,
, nix pkg-config,
installShellFiles,
nix,
}: }:
stdenv.mkDerivation { stdenv.mkDerivation {
name = "nixos-option"; name = "nixos-option";
src = ./src; src = ./.;
postInstall = '' postInstall = ''
installManPage ${./nixos-option.8} installManPage ../nixos-option.8
''; '';
strictDeps = true; strictDeps = true;
nativeBuildInputs = [ nativeBuildInputs = [
cmake meson
ninja
pkg-config pkg-config
installShellFiles installShellFiles
]; ];
@ -26,9 +30,6 @@ stdenv.mkDerivation {
boost boost
nix nix
]; ];
cmakeFlags = [
"-DNIX_DEV_INCLUDEPATH=${nix.dev}/include/nix"
];
meta = with lib; { meta = with lib; {
license = licenses.lgpl2Plus; license = licenses.lgpl2Plus;

View File

@ -0,0 +1,15 @@
project('nixos-option', 'cpp',
version : '0.1.6',
license : 'GPL-3.0',
)
nix_main_dep = dependency('nix-main', required: true)
nix_store_dep = dependency('nix-store', required: true)
nix_expr_dep = dependency('nix-expr', required: true)
nix_cmd_dep = dependency('nix-cmd', required: true)
nix_flake_dep = dependency('nix-flake', required: true)
threads_dep = dependency('threads', required: true)
nlohmann_json_dep = dependency('nlohmann_json', required: true)
boost_dep = dependency('boost', required: true)
subdir('src')

View File

@ -1,11 +0,0 @@
cmake_minimum_required (VERSION 2.6)
project (nixos-option)
set(NIX_DEV_INCLUDEPATH "" CACHE STRING "path to nix include directory")
add_executable(nixos-option nixos-option.cc libnix-copy-paste.cc)
target_include_directories(nixos-option PUBLIC "${NIX_DEV_INCLUDEPATH}")
target_link_libraries(nixos-option PRIVATE -lnixmain -lnixexpr -lnixstore -lnixutil -lnixcmd)
target_compile_features(nixos-option PRIVATE cxx_std_20)
install (TARGETS nixos-option DESTINATION bin)

View File

@ -2,79 +2,21 @@
// Since they are not, copy/paste them here. // Since they are not, copy/paste them here.
// TODO: Delete these and use the ones in the library as they become available. // TODO: Delete these and use the ones in the library as they become available.
#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
#include "libnix-copy-paste.hh" #include "libnix-copy-paste.hh"
#include <boost/format/alt_sstream.hpp> // for basic_altstringbuf... #include <nix/print.hh> // for Strings
#include <boost/format/alt_sstream_impl.hpp> // for basic_altstringbuf...
#include <boost/optional/optional.hpp> // for get_pointer
#include <iostream> // for operator<<, basic_...
#include <nix/types.hh> // for Strings
#include <nix/error.hh> // for Error
#include <string> // for string, basic_string
using nix::Error;
using nix::Strings;
using std::string;
// From nix/src/libexpr/attr-path.cc
Strings parseAttrPath(const string & s)
{
Strings res;
string cur;
string::const_iterator i = s.begin();
while (i != s.end()) {
if (*i == '.') {
res.push_back(cur);
cur.clear();
} else if (*i == '"') {
++i;
while (1) {
if (i == s.end())
throw Error("missing closing quote in selection path '%1%'", s);
if (*i == '"')
break;
cur.push_back(*i++);
}
} else
cur.push_back(*i);
++i;
}
if (!cur.empty())
res.push_back(cur);
return res;
}
// From nix/src/nix/repl.cc // From nix/src/nix/repl.cc
bool isVarName(const string & s) bool isVarName(const std::string_view & s)
{ {
if (s.size() == 0) if (s.size() == 0) return false;
return false; if (nix::isReservedKeyword(s)) return false;
char c = s[0]; char c = s[0];
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
return false;
for (auto & i : s) for (auto & i : s)
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-' || if (!((i >= 'a' && i <= 'z') ||
i == '\'')) (i >= 'A' && i <= 'Z') ||
(i >= '0' && i <= '9') ||
i == '_' || i == '-' || i == '\''))
return false; return false;
return true; return true;
} }
// From nix/src/nix/repl.cc
std::ostream & printStringValue(std::ostream & str, const char * string)
{
str << "\"";
for (const char * i = string; *i; i++)
if (*i == '\"' || *i == '\\')
str << "\\" << *i;
else if (*i == '\n')
str << "\\n";
else if (*i == '\r')
str << "\\r";
else if (*i == '\t')
str << "\\t";
else
str << *i;
str << "\"";
return str;
}

View File

@ -1,9 +1,5 @@
#pragma once #pragma once
#include <iostream>
#include <nix/types.hh> #include <nix/types.hh>
#include <string>
nix::Strings parseAttrPath(const std::string & s); bool isVarName(const std::string_view & s);
bool isVarName(const std::string & s);
std::ostream & printStringValue(std::ostream & str, const char * string);

View File

@ -0,0 +1,20 @@
src = [
'nixos-option.cc',
'libnix-copy-paste.cc',
]
cc = meson.get_compiler('cpp')
executable('nixos-option', src,
dependencies : [
nix_main_dep,
nix_store_dep,
nix_expr_dep,
nix_cmd_dep,
nix_flake_dep,
boost_dep,
nlohmann_json_dep,
threads_dep
],
install: true,
cpp_args: ['-std=c++2a', '--include', 'nix/config.h'])

View File

@ -1,16 +1,8 @@
#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
#include <exception> // for exception_ptr, current_exception
#include <functional> // for function
#include <iostream> // for operator<<, basic_ostream, ostrin...
#include <iterator> // for next
#include <list> // for _List_iterator
#include <memory> // for allocator, unique_ptr, make_unique
#include <new> // for operator new
#include <nix/args.hh> // for argvToStrings, UsageError #include <nix/args.hh> // for argvToStrings, UsageError
#include <nix/attr-path.hh> // for findAlongAttrPath #include <nix/attr-path.hh> // for findAlongAttrPath, parseAttrPath
#include <nix/attr-set.hh> // for Attr, Bindings, Bindings::iterator #include <nix/attr-set.hh> // for Attr, Bindings, Bindings::iterator
#include <nix/common-eval-args.hh> // for MixEvalArgs #include <nix/common-eval-args.hh> // for MixEvalArgs
#include <nix/eval-gc.hh> // for initGC, initNix
#include <nix/eval-inline.hh> // for EvalState::forceValue #include <nix/eval-inline.hh> // for EvalState::forceValue
#include <nix/eval.hh> // for EvalState, initGC, operator<< #include <nix/eval.hh> // for EvalState, initGC, operator<<
#include <nix/globals.hh> // for initPlugins, Settings, settings #include <nix/globals.hh> // for initPlugins, Settings, settings
@ -23,8 +15,6 @@
#include <nix/value.hh> // for Value, Value::(anonymous), Value:... #include <nix/value.hh> // for Value, Value::(anonymous), Value:...
#include <string> // for string, operator+, operator== #include <string> // for string, operator+, operator==
#include <utility> // for move #include <utility> // for move
#include <variant> // for get, holds_alternative, variant
#include <vector> // for vector<>::iterator, vector
#include "libnix-copy-paste.hh" #include "libnix-copy-paste.hh"
@ -119,7 +109,7 @@ Out::Out(Out & o, const std::string & start, const std::string & end, LinePolicy
Value evaluateValue(Context & ctx, Value & v) Value evaluateValue(Context & ctx, Value & v)
{ {
ctx.state.forceValue(v, [&]() { return v.determinePos(nix::noPos); }); ctx.state.forceValue(v, v.determinePos(nix::noPos));
if (ctx.autoArgs.empty()) { if (ctx.autoArgs.empty()) {
return v; return v;
} }
@ -133,8 +123,8 @@ bool isOption(Context & ctx, const Value & v)
if (v.type() != nAttrs) { if (v.type() != nAttrs) {
return false; return false;
} }
const auto & actualType = v.attrs->find(ctx.underscoreType); const auto & actualType = v.attrs()->find(ctx.underscoreType);
if (actualType == v.attrs->end()) { if (actualType == v.attrs()->end()) {
return false; return false;
} }
try { try {
@ -142,7 +132,7 @@ bool isOption(Context & ctx, const Value & v)
if (evaluatedType.type() != nString) { if (evaluatedType.type() != nString) {
return false; return false;
} }
return static_cast<std::string>(evaluatedType.string.s) == "option"; return evaluatedType.string_view() == "option";
} catch (Error &) { } catch (Error &) {
return false; return false;
} }
@ -152,17 +142,17 @@ bool isOption(Context & ctx, const Value & v)
// These are needed for paths like: // These are needed for paths like:
// fileSystems."/".fsType // fileSystems."/".fsType
// systemd.units."dbus.service".text // systemd.units."dbus.service".text
std::string quoteAttribute(const std::string & attribute) std::string quoteAttribute(const std::string_view & attribute)
{ {
if (isVarName(attribute)) { if (isVarName(attribute)) {
return attribute; return std::string(attribute);
} }
std::ostringstream buf; std::ostringstream buf;
printStringValue(buf, attribute.c_str()); nix::printLiteralString(buf, attribute);
return buf.str(); return buf.str();
} }
const std::string appendPath(const std::string & prefix, const std::string & suffix) const std::string appendPath(const std::string & prefix, const std::string_view & suffix)
{ {
if (prefix.empty()) { if (prefix.empty()) {
return quoteAttribute(suffix); return quoteAttribute(suffix);
@ -174,7 +164,7 @@ bool forbiddenRecursionName(const nix::Symbol symbol, const nix::SymbolTable & s
// note: this is created from a pointer // note: this is created from a pointer
// According to standard, it may never point to null, and hence attempts to check against nullptr are not allowed. // According to standard, it may never point to null, and hence attempts to check against nullptr are not allowed.
// However, at the time of writing, I am not certain about the full implications of the omission of a nullptr check here. // However, at the time of writing, I am not certain about the full implications of the omission of a nullptr check here.
const std::string & name = symbolTable[symbol]; const std::string_view & name = symbolTable[symbol];
// TODO: figure out why haskellPackages is not recursed here // TODO: figure out why haskellPackages is not recursed here
return (!name.empty() && name[0] == '_') || name == "haskellPackages"; return (!name.empty() && name[0] == '_') || name == "haskellPackages";
} }
@ -198,34 +188,35 @@ void recurse(const std::function<bool(const std::string & path, std::variant<Val
if (evaluated_value.type() != nAttrs) { if (evaluated_value.type() != nAttrs) {
return; return;
} }
for (const auto & child : evaluated_value.attrs->lexicographicOrder(ctx.state.symbols)) { for (const auto & child : evaluated_value.attrs()->lexicographicOrder(ctx.state.symbols)) {
if (forbiddenRecursionName(child->name, ctx.state.symbols)) { if (forbiddenRecursionName(child->name, ctx.state.symbols)) {
continue; continue;
} }
recurse(f, ctx, *child->value, appendPath(path, ctx.state.symbols[child->name])); std::string_view name = ctx.state.symbols[child->name];
recurse(f, ctx, *child->value, appendPath(path, name));
} }
} }
bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType) bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
{ {
try { try {
const auto & typeLookup = v.attrs->find(ctx.state.sType); const auto & typeLookup = v.attrs()->find(ctx.state.sType);
if (typeLookup == v.attrs->end()) { if (typeLookup == v.attrs()->end()) {
return false; return false;
} }
Value type = evaluateValue(ctx, *typeLookup->value); Value type = evaluateValue(ctx, *typeLookup->value);
if (type.type() != nAttrs) { if (type.type() != nAttrs) {
return false; return false;
} }
const auto & nameLookup = type.attrs->find(ctx.state.sName); const auto & nameLookup = type.attrs()->find(ctx.state.sName);
if (nameLookup == type.attrs->end()) { if (nameLookup == type.attrs()->end()) {
return false; return false;
} }
Value name = evaluateValue(ctx, *nameLookup->value); Value name = evaluateValue(ctx, *nameLookup->value);
if (name.type() != nString) { if (name.type() != nString) {
return false; return false;
} }
return name.string.s == soughtType; return name.string_view() == soughtType;
} catch (Error &) { } catch (Error &) {
return false; return false;
} }
@ -242,7 +233,7 @@ Value getSubOptions(Context & ctx, Value & option)
{ {
Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option).first); Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option).first);
if (getSubOptions.isLambda()) { if (getSubOptions.isLambda()) {
throw OptionPathError("Option's type.getSubOptions isn't a function"); throw OptionPathError(ctx.state, "Option's type.getSubOptions isn't a function");
} }
Value emptyString{}; Value emptyString{};
emptyString.mkString(""); emptyString.mkString("");
@ -260,40 +251,40 @@ struct FindAlongOptionPathRet
}; };
FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path) FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path)
{ {
Strings tokens = parseAttrPath(path); std::vector<Symbol> tokens = nix::parseAttrPath(ctx.state, path);
Value v = ctx.optionsRoot; Value v = ctx.optionsRoot;
std::string processedPath; std::string processedPath;
for (auto i = tokens.begin(); i != tokens.end(); i++) { for (auto i = tokens.begin(); i != tokens.end(); i++) {
const auto & attr = *i; const std::string_view attr = ctx.state.symbols[*i];
try { try {
bool lastAttribute = std::next(i) == tokens.end(); bool lastAttribute = std::next(i) == tokens.end();
v = evaluateValue(ctx, v); v = evaluateValue(ctx, v);
if (attr.empty()) { if (attr.empty()) {
throw OptionPathError("empty attribute name"); throw OptionPathError(ctx.state, "empty attribute name");
} }
if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) { if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
v = getSubOptions(ctx, v); v = getSubOptions(ctx, v);
} }
if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) { if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) {
auto subOptions = getSubOptions(ctx, v); auto subOptions = getSubOptions(ctx, v);
if (lastAttribute && subOptions.attrs->empty()) { if (lastAttribute && subOptions.attrs()->empty()) {
break; break;
} }
v = subOptions; v = subOptions;
// Note that we've consumed attr, but didn't actually use it. This is the path component that's looked // Note that we've consumed attr, but didn't actually use it. This is the path component that's looked
// up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name". // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
} else if (v.type() != nAttrs) { } else if (v.type() != nAttrs) {
throw OptionPathError("Value is %s while a set was expected", showType(v)); throw OptionPathError(ctx.state, "Value is %s while a set was expected", showType(v));
} else { } else {
const auto & next = v.attrs->find(ctx.state.symbols.create(attr)); const auto & next = v.attrs()->find(ctx.state.symbols.create(attr));
if (next == v.attrs->end()) { if (next == v.attrs()->end()) {
throw OptionPathError("Attribute not found", attr, path); throw OptionPathError(ctx.state, "Attribute not found", attr, path);
} }
v = *next->value; v = *next->value;
} }
processedPath = appendPath(processedPath, attr); processedPath = appendPath(processedPath, attr);
} catch (OptionPathError & e) { } catch (OptionPathError & e) {
throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg()); throw OptionPathError(ctx.state, "At '%s' in path '%s': %s", attr, path, e.msg());
} }
} }
return {v, processedPath}; return {v, processedPath};
@ -367,21 +358,23 @@ std::string describeError(const Error & e) { return "«error: " + e.msg() + "»"
void describeDerivation(Context & ctx, Out & out, Value v) void describeDerivation(Context & ctx, Out & out, Value v)
{ {
// Copy-pasted from nix/src/nix/repl.cc :( // Copy-pasted from nix/src/nix/repl.cc printDerivation() :(
std::optional<nix::StorePath> storePath = std::nullopt;
if (auto i = v.attrs()->get(ctx.state.sDrvPath)) {
nix::NixStringContext context;
storePath = ctx.state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
}
out << "«derivation "; out << "«derivation ";
Bindings::iterator i = v.attrs->find(ctx.state.sDrvPath); if (storePath) {
nix::NixStringContext strContext; out << " " << ctx.state.store->printStorePath(*storePath);
if (i != v.attrs->end()) }
out << ctx.state.store->printStorePath(ctx.state.coerceToStorePath(i->pos, *i->value, strContext, "while evaluating the drvPath of a derivation"));
else
out << "???";
out << "»"; out << "»";
} }
Value parseAndEval(EvalState & state, const std::string & expression, const std::string & path) Value parseAndEval(EvalState & state, const std::string & expression, const std::string & path)
{ {
Value v{}; Value v{};
state.eval(state.parseExprFromString(expression, nix::SourcePath(nix::CanonPath::fromCwd(path))), v); state.eval(state.parseExprFromString(expression, state.rootPath(".")), v);
return v; return v;
} }
@ -398,10 +391,10 @@ void printList(Context & ctx, Out & out, Value & v)
void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path) void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
{ {
Out attrsOut(out, "{", "}", v.attrs->size()); Out attrsOut(out, "{", "}", v.attrs()->size());
for (const auto & a : v.attrs->lexicographicOrder(ctx.state.symbols)) { for (const auto & a : v.attrs()->lexicographicOrder(ctx.state.symbols)) {
if (!forbiddenRecursionName(a->name, ctx.state.symbols)) { if (!forbiddenRecursionName(a->name, ctx.state.symbols)) {
const std::string name = ctx.state.symbols[a->name]; std::string_view name = ctx.state.symbols[a->name];
attrsOut << name << " = "; attrsOut << name << " = ";
printValue(ctx, attrsOut, *a->value, appendPath(path, name)); printValue(ctx, attrsOut, *a->value, appendPath(path, name));
attrsOut << ";" << Out::sep; attrsOut << ";" << Out::sep;
@ -409,9 +402,9 @@ void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
} }
} }
void multiLineStringEscape(Out & out, const std::string & s) void multiLineStringEscape(Out & out, const std::string_view & s)
{ {
int i; size_t i;
for (i = 1; i < s.size(); i++) { for (i = 1; i < s.size(); i++) {
if (s[i - 1] == '$' && s[i] == '{') { if (s[i - 1] == '$' && s[i] == '{') {
out << "''${"; out << "''${";
@ -430,7 +423,7 @@ void multiLineStringEscape(Out & out, const std::string & s)
void printMultiLineString(Out & out, const Value & v) void printMultiLineString(Out & out, const Value & v)
{ {
std::string s = v.string.s; std::string_view s = v.string_view();
Out strOut(out, "''", "''", Out::MULTI_LINE); Out strOut(out, "''", "''", Out::MULTI_LINE);
std::string::size_type begin = 0; std::string::size_type begin = 0;
while (begin < s.size()) { while (begin < s.size()) {
@ -458,11 +451,11 @@ void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr
printList(ctx, out, v); printList(ctx, out, v);
} else if (v.type() == nAttrs) { } else if (v.type() == nAttrs) {
printAttrs(ctx, out, v, path); printAttrs(ctx, out, v, path);
} else if (v.type() == nString && std::string(v.string.s).find('\n') != std::string::npos) { } else if (v.type() == nString && std::string(v.string_view()).find('\n') != std::string::npos) {
printMultiLineString(out, v); printMultiLineString(out, v);
} else { } else {
ctx.state.forceValueDeep(v); ctx.state.forceValueDeep(v);
v.print(ctx.state.symbols, out.ostream); v.print(ctx.state, out.ostream);
} }
} catch (ThrownError & e) { } catch (ThrownError & e) {
if (e.msg() == "The option `" + path + "' was accessed but has no value defined. Try setting the option.") { if (e.msg() == "The option `" + path + "' was accessed but has no value defined. Try setting the option.") {
@ -560,8 +553,8 @@ void printOption(Context & ctx, Out & out, const std::string & path, Value & opt
void printListing(Context & ctx, Out & out, Value & v) void printListing(Context & ctx, Out & out, Value & v)
{ {
out << "This attribute set contains:\n"; out << "This attribute set contains:\n";
for (const auto & a : v.attrs->lexicographicOrder(ctx.state.symbols)) { for (const auto & a : v.attrs()->lexicographicOrder(ctx.state.symbols)) {
const std::string & name = ctx.state.symbols[a->name]; const std::string_view & name = ctx.state.symbols[a->name];
if (!name.empty() && name[0] != '_') { if (!name.empty() && name[0] != '_') {
out << name << "\n"; out << name << "\n";
} }
@ -630,7 +623,11 @@ int main(int argc, char ** argv)
nix::initGC(); nix::initGC();
nix::settings.readOnlyMode = true; nix::settings.readOnlyMode = true;
auto store = nix::openStore(); auto store = nix::openStore();
auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
auto evalStore = myArgs.evalStoreUrl ? nix::openStore(*myArgs.evalStoreUrl)
: nix::openStore();
auto state = nix::make_ref<nix::EvalState>(
myArgs.lookupPath, evalStore, nix::fetchSettings, nix::evalSettings);
Value optionsRoot = parseAndEval(*state, optionsExpr, path); Value optionsRoot = parseAndEval(*state, optionsExpr, path);
Value configRoot = parseAndEval(*state, configExpr, path); Value configRoot = parseAndEval(*state, configExpr, path);
@ -646,7 +643,7 @@ int main(int argc, char ** argv)
print(ctx, out, arg); print(ctx, out, arg);
} }
ctx.state.printStats(); ctx.state.maybePrintStats();
return 0; return 0;
} }

View File

@ -18681,9 +18681,7 @@ with pkgs;
nix-linter = haskell.lib.compose.justStaticExecutables (haskellPackages.nix-linter); nix-linter = haskell.lib.compose.justStaticExecutables (haskellPackages.nix-linter);
nixos-option = callPackage ../tools/nix/nixos-option { nixos-option = callPackage ../tools/nix/nixos-option { };
nix = nixVersions.nix_2_18;
};
nix-pin = callPackage ../tools/package-management/nix-pin { }; nix-pin = callPackage ../tools/package-management/nix-pin { };