Merge remote-tracking branch 'upstream/master' into trustless-remote-builder-simple
This commit is contained in:
commit
b1343e8ad1
|
@ -184,7 +184,7 @@ fi
|
||||||
|
|
||||||
# Look for OpenSSL, a required dependency. FIXME: this is only (maybe)
|
# Look for OpenSSL, a required dependency. FIXME: this is only (maybe)
|
||||||
# used by S3BinaryCacheStore.
|
# used by S3BinaryCacheStore.
|
||||||
PKG_CHECK_MODULES([OPENSSL], [libcrypto], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([OPENSSL], [libcrypto >= 1.1.1], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
|
|
||||||
# Look for libarchive.
|
# Look for libarchive.
|
||||||
|
|
|
@ -10,7 +10,9 @@ let
|
||||||
|
|
||||||
result = ''
|
result = ''
|
||||||
> **Warning** \
|
> **Warning** \
|
||||||
> This program is **experimental** and its interface is subject to change.
|
> This program is
|
||||||
|
> [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command)
|
||||||
|
> and its interface is subject to change.
|
||||||
|
|
||||||
# Name
|
# Name
|
||||||
|
|
||||||
|
@ -29,19 +31,18 @@ let
|
||||||
|
|
||||||
showSynopsis = command: args:
|
showSynopsis = command: args:
|
||||||
let
|
let
|
||||||
showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "...");
|
showArgument = arg: "*${arg.label}*" + optionalString (! arg ? arity) "...";
|
||||||
arguments = concatStringsSep " " (map showArgument args);
|
arguments = concatStringsSep " " (map showArgument args);
|
||||||
in ''
|
in ''
|
||||||
`${command}` [*option*...] ${arguments}
|
`${command}` [*option*...] ${arguments}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
maybeSubcommands = if details ? commands && details.commands != {}
|
maybeSubcommands = optionalString (details ? commands && details.commands != {})
|
||||||
then ''
|
''
|
||||||
where *subcommand* is one of the following:
|
where *subcommand* is one of the following:
|
||||||
|
|
||||||
${subcommands}
|
${subcommands}
|
||||||
''
|
'';
|
||||||
else "";
|
|
||||||
|
|
||||||
subcommands = if length categories > 1
|
subcommands = if length categories > 1
|
||||||
then listCategories
|
then listCategories
|
||||||
|
@ -63,12 +64,11 @@ let
|
||||||
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
maybeDocumentation =
|
maybeDocumentation = optionalString
|
||||||
if details ? doc
|
(details ? doc)
|
||||||
then replaceStrings ["@stores@"] [storeDocs] details.doc
|
(replaceStrings ["@stores@"] [storeDocs] details.doc);
|
||||||
else "";
|
|
||||||
|
|
||||||
maybeOptions = if details.flags == {} then "" else ''
|
maybeOptions = optionalString (details.flags != {}) ''
|
||||||
# Options
|
# Options
|
||||||
|
|
||||||
${showOptions details.flags toplevel.flags}
|
${showOptions details.flags toplevel.flags}
|
||||||
|
@ -78,15 +78,19 @@ let
|
||||||
let
|
let
|
||||||
allOptions = options // commonOptions;
|
allOptions = options // commonOptions;
|
||||||
showCategory = cat: ''
|
showCategory = cat: ''
|
||||||
${if cat != "" then "**${cat}:**" else ""}
|
${optionalString (cat != "") "**${cat}:**"}
|
||||||
|
|
||||||
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
|
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
|
||||||
'';
|
'';
|
||||||
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
|
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
|
||||||
showOption = name: option:
|
showOption = name: option:
|
||||||
let
|
let
|
||||||
shortName = if option ? shortName then "/ `-${option.shortName}`" else "";
|
shortName = optionalString
|
||||||
labels = if option ? labels then (concatStringsSep " " (map (s: "*${s}*") option.labels)) else "";
|
(option ? shortName)
|
||||||
|
("/ `-${option.shortName}`");
|
||||||
|
labels = optionalString
|
||||||
|
(option ? labels)
|
||||||
|
(concatStringsSep " " (map (s: "*${s}*") option.labels));
|
||||||
in trim ''
|
in trim ''
|
||||||
- `--${name}` ${shortName} ${labels}
|
- `--${name}` ${shortName} ${labels}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Experimental Commands
|
# Experimental Commands
|
||||||
|
|
||||||
This section lists experimental commands.
|
This section lists [experimental commands](@docroot@/contributing/experimental-features.md#xp-feature-nix-command).
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
>
|
>
|
||||||
|
|
|
@ -225,3 +225,9 @@
|
||||||
[string]: ./language/values.md#type-string
|
[string]: ./language/values.md#type-string
|
||||||
[path]: ./language/values.md#type-path
|
[path]: ./language/values.md#type-path
|
||||||
[attribute name]: ./language/values.md#attribute-set
|
[attribute name]: ./language/values.md#attribute-set
|
||||||
|
|
||||||
|
- [experimental feature]{#gloss-experimental-feature}\
|
||||||
|
Not yet stabilized functionality guarded by named experimental feature flags.
|
||||||
|
These flags are enabled or disabled with the [`experimental-features`](./command-ref/conf-file.html#conf-experimental-features) setting.
|
||||||
|
|
||||||
|
See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/contributing/experimental-features.md).
|
||||||
|
|
|
@ -208,12 +208,26 @@ Derivations can declare some infrequently used optional attributes.
|
||||||
about converting to and from base-32 notation.)
|
about converting to and from base-32 notation.)
|
||||||
|
|
||||||
- [`__contentAddressed`]{#adv-attr-__contentAddressed}
|
- [`__contentAddressed`]{#adv-attr-__contentAddressed}
|
||||||
If this **experimental** attribute is set to true, then the derivation
|
> **Warning**
|
||||||
|
> This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||||
|
>
|
||||||
|
> To use this attribute, you must enable the
|
||||||
|
> [`ca-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) experimental feature.
|
||||||
|
> For example, in [nix.conf](../command-ref/conf-file.md) you could add:
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> extra-experimental-features = ca-derivations
|
||||||
|
> ```
|
||||||
|
|
||||||
|
If this attribute is set to `true`, then the derivation
|
||||||
outputs will be stored in a content-addressed location rather than the
|
outputs will be stored in a content-addressed location rather than the
|
||||||
traditional input-addressed one.
|
traditional input-addressed one.
|
||||||
This only has an effect if the `ca-derivations` experimental feature is enabled.
|
|
||||||
|
|
||||||
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
|
Setting this attribute also requires setting
|
||||||
|
[`outputHashMode`](#adv-attr-outputHashMode)
|
||||||
|
and
|
||||||
|
[`outputHashAlgo`](#adv-attr-outputHashAlgo)
|
||||||
|
like for *fixed-output derivations* (see above).
|
||||||
|
|
||||||
- [`passAsFile`]{#adv-attr-passAsFile}\
|
- [`passAsFile`]{#adv-attr-passAsFile}\
|
||||||
A list of names of attributes that should be passed via files rather
|
A list of names of attributes that should be passed via files rather
|
||||||
|
@ -307,9 +321,11 @@ Derivations can declare some infrequently used optional attributes.
|
||||||
|
|
||||||
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
|
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
|
||||||
> **Warning**
|
> **Warning**
|
||||||
> This is an experimental feature.
|
> This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||||
>
|
>
|
||||||
> To enable it, add the following to [nix.conf](../command-ref/conf-file.md):
|
> To use this attribute, you must enable the
|
||||||
|
> [`discard-references`](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) experimental feature.
|
||||||
|
> For example, in [nix.conf](../command-ref/conf-file.md) you could add:
|
||||||
>
|
>
|
||||||
> ```
|
> ```
|
||||||
> extra-experimental-features = discard-references
|
> extra-experimental-features = discard-references
|
||||||
|
|
|
@ -19,7 +19,7 @@ to subsequent chapters.
|
||||||
channel:
|
channel:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-env -qaP
|
$ nix-env --query --available --attr-path
|
||||||
nixpkgs.docbook_xml_dtd_43 docbook-xml-4.3
|
nixpkgs.docbook_xml_dtd_43 docbook-xml-4.3
|
||||||
nixpkgs.docbook_xml_dtd_45 docbook-xml-4.5
|
nixpkgs.docbook_xml_dtd_45 docbook-xml-4.5
|
||||||
nixpkgs.firefox firefox-33.0.2
|
nixpkgs.firefox firefox-33.0.2
|
||||||
|
@ -31,7 +31,7 @@ to subsequent chapters.
|
||||||
1. Install some packages from the channel:
|
1. Install some packages from the channel:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-env -iA nixpkgs.hello
|
$ nix-env --install --attr nixpkgs.hello
|
||||||
```
|
```
|
||||||
|
|
||||||
This should download pre-built packages; it should not build them
|
This should download pre-built packages; it should not build them
|
||||||
|
@ -49,13 +49,13 @@ to subsequent chapters.
|
||||||
1. Uninstall a package:
|
1. Uninstall a package:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-env -e hello
|
$ nix-env --uninstall hello
|
||||||
```
|
```
|
||||||
|
|
||||||
1. You can also test a package without installing it:
|
1. You can also test a package without installing it:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-shell -p hello
|
$ nix-shell --packages hello
|
||||||
```
|
```
|
||||||
|
|
||||||
This builds or downloads GNU Hello and its dependencies, then drops
|
This builds or downloads GNU Hello and its dependencies, then drops
|
||||||
|
@ -76,7 +76,7 @@ to subsequent chapters.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-channel --update nixpkgs
|
$ nix-channel --update nixpkgs
|
||||||
$ nix-env -u '*'
|
$ nix-env --upgrade '*'
|
||||||
```
|
```
|
||||||
|
|
||||||
The latter command will upgrade each installed package for which
|
The latter command will upgrade each installed package for which
|
||||||
|
@ -95,5 +95,5 @@ to subsequent chapters.
|
||||||
them:
|
them:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-collect-garbage -d
|
$ nix-collect-garbage --delete-old
|
||||||
```
|
```
|
||||||
|
|
|
@ -42,7 +42,9 @@ rec {
|
||||||
filterAttrs = pred: set:
|
filterAttrs = pred: set:
|
||||||
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
|
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
|
||||||
|
|
||||||
showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value }:
|
optionalString = cond: string: if cond then string else "";
|
||||||
|
|
||||||
|
showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
|
||||||
let
|
let
|
||||||
result = squash ''
|
result = squash ''
|
||||||
- ${if useAnchors
|
- ${if useAnchors
|
||||||
|
@ -52,10 +54,28 @@ rec {
|
||||||
${indent " " body}
|
${indent " " body}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
|
||||||
|
> **Warning**
|
||||||
|
> This setting is part of an
|
||||||
|
> [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||||
|
|
||||||
|
To change this setting, you need to make sure the corresponding experimental feature,
|
||||||
|
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
|
||||||
|
is enabled.
|
||||||
|
For example, include the following in [`nix.conf`](#):
|
||||||
|
|
||||||
|
```
|
||||||
|
extra-experimental-features = ${experimentalFeature}
|
||||||
|
${name} = ...
|
||||||
|
```
|
||||||
|
'';
|
||||||
|
|
||||||
# separate body to cleanly handle indentation
|
# separate body to cleanly handle indentation
|
||||||
body = ''
|
body = ''
|
||||||
${description}
|
${description}
|
||||||
|
|
||||||
|
${experimentalFeatureNote}
|
||||||
|
|
||||||
**Default:** ${showDefault documentDefault defaultValue}
|
**Default:** ${showDefault documentDefault defaultValue}
|
||||||
|
|
||||||
${showAliases aliases}
|
${showAliases aliases}
|
||||||
|
@ -74,7 +94,7 @@ rec {
|
||||||
else "*machine-specific*";
|
else "*machine-specific*";
|
||||||
|
|
||||||
showAliases = aliases:
|
showAliases = aliases:
|
||||||
if aliases == [] then "" else
|
optionalString (aliases != [])
|
||||||
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
|
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
|
||||||
|
|
||||||
in result;
|
in result;
|
||||||
|
|
|
@ -27,8 +27,6 @@ static ref<Store> store()
|
||||||
if (!_store) {
|
if (!_store) {
|
||||||
try {
|
try {
|
||||||
initLibStore();
|
initLibStore();
|
||||||
loadConfFile();
|
|
||||||
settings.lockCPU = false;
|
|
||||||
_store = openStore();
|
_store = openStore();
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
|
|
@ -40,6 +40,7 @@ extern "C" {
|
||||||
#include "markdown.hh"
|
#include "markdown.hh"
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
|
#include "print.hh"
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
#define GC_INCLUDE_NEW
|
#define GC_INCLUDE_NEW
|
||||||
|
@ -425,6 +426,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: DRY and match or use the parser
|
||||||
static bool isVarName(std::string_view s)
|
static bool isVarName(std::string_view s)
|
||||||
{
|
{
|
||||||
if (s.size() == 0) return false;
|
if (s.size() == 0) return false;
|
||||||
|
@ -894,17 +896,6 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FIXME: lot of cut&paste from Nix's eval.cc.
|
// FIXME: lot of cut&paste from Nix's eval.cc.
|
||||||
|
@ -922,12 +913,14 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nBool:
|
case nBool:
|
||||||
str << ANSI_CYAN << (v.boolean ? "true" : "false") << ANSI_NORMAL;
|
str << ANSI_CYAN;
|
||||||
|
printLiteralBool(str, v.boolean);
|
||||||
|
str << ANSI_NORMAL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nString:
|
case nString:
|
||||||
str << ANSI_WARNING;
|
str << ANSI_WARNING;
|
||||||
printStringValue(str, v.string.s);
|
printLiteralString(str, v.string.s);
|
||||||
str << ANSI_NORMAL;
|
str << ANSI_NORMAL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -964,10 +957,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
sorted.emplace(state->symbols[i.name], i.value);
|
sorted.emplace(state->symbols[i.name], i.value);
|
||||||
|
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
if (isVarName(i.first))
|
printAttributeName(str, i.first);
|
||||||
str << i.first;
|
|
||||||
else
|
|
||||||
printStringValue(str, i.first.c_str());
|
|
||||||
str << " = ";
|
str << " = ";
|
||||||
if (seen.count(i.second))
|
if (seen.count(i.second))
|
||||||
str << "«repeated»";
|
str << "«repeated»";
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "filetransfer.hh"
|
#include "filetransfer.hh"
|
||||||
#include "function-trace.hh"
|
#include "function-trace.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
|
#include "print.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -104,18 +105,10 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
|
||||||
str << integer;
|
str << integer;
|
||||||
break;
|
break;
|
||||||
case tBool:
|
case tBool:
|
||||||
str << (boolean ? "true" : "false");
|
printLiteralBool(str, boolean);
|
||||||
break;
|
break;
|
||||||
case tString:
|
case tString:
|
||||||
str << "\"";
|
printLiteralString(str, string.s);
|
||||||
for (const char * i = string.s; *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 if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
|
||||||
else str << *i;
|
|
||||||
str << "\"";
|
|
||||||
break;
|
break;
|
||||||
case tPath:
|
case tPath:
|
||||||
str << path; // !!! escaping?
|
str << path; // !!! escaping?
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "print.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
@ -60,45 +61,12 @@ Pos::operator std::shared_ptr<AbstractPos>() const
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Displaying abstract syntax trees. */
|
// FIXME: remove, because *symbols* are abstract and do not have a single
|
||||||
|
// textual representation; see printIdentifier()
|
||||||
static void showString(std::ostream & str, std::string_view s)
|
|
||||||
{
|
|
||||||
str << '"';
|
|
||||||
for (auto c : s)
|
|
||||||
if (c == '"' || c == '\\' || c == '$') str << "\\" << c;
|
|
||||||
else if (c == '\n') str << "\\n";
|
|
||||||
else if (c == '\r') str << "\\r";
|
|
||||||
else if (c == '\t') str << "\\t";
|
|
||||||
else str << c;
|
|
||||||
str << '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
|
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
|
||||||
{
|
{
|
||||||
std::string_view s = symbol;
|
std::string_view s = symbol;
|
||||||
|
return printIdentifier(str, s);
|
||||||
if (s.empty())
|
|
||||||
str << "\"\"";
|
|
||||||
else if (s == "if") // FIXME: handle other keywords
|
|
||||||
str << '"' << s << '"';
|
|
||||||
else {
|
|
||||||
char c = s[0];
|
|
||||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
|
||||||
showString(str, s);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
for (auto c : s)
|
|
||||||
if (!((c >= 'a' && c <= 'z') ||
|
|
||||||
(c >= 'A' && c <= 'Z') ||
|
|
||||||
(c >= '0' && c <= '9') ||
|
|
||||||
c == '_' || c == '\'' || c == '-')) {
|
|
||||||
showString(str, s);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
str << s;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
|
@ -118,7 +86,7 @@ void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
|
|
||||||
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
showString(str, s);
|
printLiteralString(str, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
|
|
78
src/libexpr/print.cc
Normal file
78
src/libexpr/print.cc
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#include "print.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
printLiteralString(std::ostream & str, const std::string_view string)
|
||||||
|
{
|
||||||
|
str << "\"";
|
||||||
|
for (auto i = string.begin(); i != string.end(); ++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 if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
||||||
|
else str << *i;
|
||||||
|
}
|
||||||
|
str << "\"";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
printLiteralBool(std::ostream & str, bool boolean)
|
||||||
|
{
|
||||||
|
str << (boolean ? "true" : "false");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
printIdentifier(std::ostream & str, std::string_view s) {
|
||||||
|
if (s.empty())
|
||||||
|
str << "\"\"";
|
||||||
|
else if (s == "if") // FIXME: handle other keywords
|
||||||
|
str << '"' << s << '"';
|
||||||
|
else {
|
||||||
|
char c = s[0];
|
||||||
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
||||||
|
printLiteralString(str, s);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
for (auto c : s)
|
||||||
|
if (!((c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') ||
|
||||||
|
c == '_' || c == '\'' || c == '-')) {
|
||||||
|
printLiteralString(str, s);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
str << s;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: keywords
|
||||||
|
static bool isVarName(std::string_view s)
|
||||||
|
{
|
||||||
|
if (s.size() == 0) return false;
|
||||||
|
char c = s[0];
|
||||||
|
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
|
||||||
|
for (auto & i : s)
|
||||||
|
if (!((i >= 'a' && i <= 'z') ||
|
||||||
|
(i >= 'A' && i <= 'Z') ||
|
||||||
|
(i >= '0' && i <= '9') ||
|
||||||
|
i == '_' || i == '-' || i == '\''))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
printAttributeName(std::ostream & str, std::string_view name) {
|
||||||
|
if (isVarName(name))
|
||||||
|
str << name;
|
||||||
|
else
|
||||||
|
printLiteralString(str, name);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
48
src/libexpr/print.hh
Normal file
48
src/libexpr/print.hh
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Common printing functions for the Nix language
|
||||||
|
*
|
||||||
|
* While most types come with their own methods for printing, they share some
|
||||||
|
* functions that are placed here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
/**
|
||||||
|
* Print a string as a Nix string literal.
|
||||||
|
*
|
||||||
|
* Quotes and fairly minimal escaping are added.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a string as an attribute name in the Nix expression language syntax.
|
||||||
|
*
|
||||||
|
* Prints a quoted string if necessary.
|
||||||
|
*/
|
||||||
|
std::ostream & printAttributeName(std::ostream & o, std::string_view s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a string as an identifier in the Nix expression language syntax.
|
||||||
|
*
|
||||||
|
* FIXME: "identifier" is ambiguous. Identifiers do not have a single
|
||||||
|
* textual representation. They can be used in variable references,
|
||||||
|
* let bindings, left-hand sides or attribute names in a select
|
||||||
|
* expression, or something else entirely, like JSON. Use one of the
|
||||||
|
* `print*` functions instead.
|
||||||
|
*/
|
||||||
|
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||||
|
}
|
|
@ -10,7 +10,6 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -20,16 +19,9 @@
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <features.h>
|
#include <features.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef __GLIBC__
|
|
||||||
#include <gnu/lib-names.h>
|
|
||||||
#include <nss.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
|
|
||||||
#include <sodium.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -115,57 +107,6 @@ std::string getArg(const std::string & opt,
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
|
||||||
/* OpenSSL is not thread-safe by default - it will randomly crash
|
|
||||||
unless the user supplies a mutex locking function. So let's do
|
|
||||||
that. */
|
|
||||||
static std::vector<std::mutex> opensslLocks;
|
|
||||||
|
|
||||||
static void opensslLockCallback(int mode, int type, const char * file, int line)
|
|
||||||
{
|
|
||||||
if (mode & CRYPTO_LOCK)
|
|
||||||
opensslLocks[type].lock();
|
|
||||||
else
|
|
||||||
opensslLocks[type].unlock();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static std::once_flag dns_resolve_flag;
|
|
||||||
|
|
||||||
static void preloadNSS() {
|
|
||||||
/* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
|
|
||||||
one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
|
|
||||||
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
|
|
||||||
load its lookup libraries in the parent before any child gets a chance to. */
|
|
||||||
std::call_once(dns_resolve_flag, []() {
|
|
||||||
#ifdef __GLIBC__
|
|
||||||
/* On linux, glibc will run every lookup through the nss layer.
|
|
||||||
* That means every lookup goes, by default, through nscd, which acts as a local
|
|
||||||
* cache.
|
|
||||||
* Because we run builds in a sandbox, we also remove access to nscd otherwise
|
|
||||||
* lookups would leak into the sandbox.
|
|
||||||
*
|
|
||||||
* But now we have a new problem, we need to make sure the nss_dns backend that
|
|
||||||
* does the dns lookups when nscd is not available is loaded or available.
|
|
||||||
*
|
|
||||||
* We can't make it available without leaking nix's environment, so instead we'll
|
|
||||||
* load the backend, and configure nss so it does not try to run dns lookups
|
|
||||||
* through nscd.
|
|
||||||
*
|
|
||||||
* This is technically only used for builtins:fetch* functions so we only care
|
|
||||||
* about dns.
|
|
||||||
*
|
|
||||||
* All other platforms are unaffected.
|
|
||||||
*/
|
|
||||||
if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
|
|
||||||
warn("unable to load nss_dns backend");
|
|
||||||
// FIXME: get hosts entry from nsswitch.conf.
|
|
||||||
__nss_configure_lookup("hosts", "files dns");
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sigHandler(int signo) { }
|
static void sigHandler(int signo) { }
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,16 +118,7 @@ void initNix()
|
||||||
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
initLibStore();
|
||||||
/* Initialise OpenSSL locking. */
|
|
||||||
opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
|
|
||||||
CRYPTO_set_locking_callback(opensslLockCallback);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
|
||||||
throw Error("could not initialise libsodium");
|
|
||||||
|
|
||||||
loadConfFile();
|
|
||||||
|
|
||||||
startSignalHandlerThread();
|
startSignalHandlerThread();
|
||||||
|
|
||||||
|
@ -223,7 +155,10 @@ void initNix()
|
||||||
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Register a SIGSEGV handler to detect stack overflows. */
|
/* Register a SIGSEGV handler to detect stack overflows.
|
||||||
|
Why not initLibExpr()? initGC() is essentially that, but
|
||||||
|
detectStackOverflow is not an instance of the init function concept, as
|
||||||
|
it may have to be invoked more than once per process. */
|
||||||
detectStackOverflow();
|
detectStackOverflow();
|
||||||
|
|
||||||
/* There is no privacy in the Nix system ;-) At least not for
|
/* There is no privacy in the Nix system ;-) At least not for
|
||||||
|
@ -236,16 +171,6 @@ void initNix()
|
||||||
gettimeofday(&tv, 0);
|
gettimeofday(&tv, 0);
|
||||||
srandom(tv.tv_usec);
|
srandom(tv.tv_usec);
|
||||||
|
|
||||||
/* On macOS, don't use the per-session TMPDIR (as set e.g. by
|
|
||||||
sshd). This breaks build users because they don't have access
|
|
||||||
to the TMPDIR, in particular in ‘nix-store --serve’. */
|
|
||||||
#if __APPLE__
|
|
||||||
if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
|
|
||||||
unsetenv("TMPDIR");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
preloadNSS();
|
|
||||||
initLibStore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,15 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a derivation string literal to an `std::string`.
|
||||||
|
*
|
||||||
|
* This syntax does not generalize to the expression language, which needs to
|
||||||
|
* escape `$`.
|
||||||
|
*
|
||||||
|
* @param res Where to print to
|
||||||
|
* @param s Which logical string to print
|
||||||
|
*/
|
||||||
static void printString(std::string & res, std::string_view s)
|
static void printString(std::string & res, std::string_view s)
|
||||||
{
|
{
|
||||||
boost::container::small_vector<char, 64 * 1024> buffer;
|
boost::container::small_vector<char, 64 * 1024> buffer;
|
||||||
|
|
|
@ -62,15 +62,31 @@ std::string DerivedPath::Opaque::to_string(const Store & store) const
|
||||||
std::string DerivedPath::Built::to_string(const Store & store) const
|
std::string DerivedPath::Built::to_string(const Store & store) const
|
||||||
{
|
{
|
||||||
return store.printStorePath(drvPath)
|
return store.printStorePath(drvPath)
|
||||||
+ "!"
|
+ '^'
|
||||||
|
+ outputs.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DerivedPath::Built::to_string_legacy(const Store & store) const
|
||||||
|
{
|
||||||
|
return store.printStorePath(drvPath)
|
||||||
|
+ '!'
|
||||||
+ outputs.to_string();
|
+ outputs.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DerivedPath::to_string(const Store & store) const
|
std::string DerivedPath::to_string(const Store & store) const
|
||||||
{
|
{
|
||||||
return std::visit(
|
return std::visit(overloaded {
|
||||||
[&](const auto & req) { return req.to_string(store); },
|
[&](const DerivedPath::Built & req) { return req.to_string(store); },
|
||||||
this->raw());
|
[&](const DerivedPath::Opaque & req) { return req.to_string(store); },
|
||||||
|
}, this->raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DerivedPath::to_string_legacy(const Store & store) const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & req) { return req.to_string_legacy(store); },
|
||||||
|
[&](const DerivedPath::Opaque & req) { return req.to_string(store); },
|
||||||
|
}, this->raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,14 +103,24 @@ DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_vi
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
static inline DerivedPath parseWith(const Store & store, std::string_view s, std::string_view separator)
|
||||||
{
|
{
|
||||||
size_t n = s.find("!");
|
size_t n = s.find(separator);
|
||||||
return n == s.npos
|
return n == s.npos
|
||||||
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
||||||
: (DerivedPath) DerivedPath::Built::parse(store, s.substr(0, n), s.substr(n + 1));
|
: (DerivedPath) DerivedPath::Built::parse(store, s.substr(0, n), s.substr(n + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
||||||
|
{
|
||||||
|
return parseWith(store, s, "^");
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPath DerivedPath::parseLegacy(const Store & store, std::string_view s)
|
||||||
|
{
|
||||||
|
return parseWith(store, s, "!");
|
||||||
|
}
|
||||||
|
|
||||||
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||||
{
|
{
|
||||||
RealisedPath::Set res;
|
RealisedPath::Set res;
|
||||||
|
|
|
@ -48,8 +48,18 @@ struct DerivedPathBuilt {
|
||||||
StorePath drvPath;
|
StorePath drvPath;
|
||||||
OutputsSpec outputs;
|
OutputsSpec outputs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses `^` as the separator
|
||||||
|
*/
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
/**
|
||||||
|
* Uses `!` as the separator
|
||||||
|
*/
|
||||||
|
std::string to_string_legacy(const Store & store) const;
|
||||||
|
/**
|
||||||
|
* The caller splits on the separator, so it works for both variants.
|
||||||
|
*/
|
||||||
|
static DerivedPathBuilt parse(const Store & store, std::string_view drvPath, std::string_view outputs);
|
||||||
nlohmann::json toJSON(ref<Store> store) const;
|
nlohmann::json toJSON(ref<Store> store) const;
|
||||||
|
|
||||||
GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs);
|
GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs);
|
||||||
|
@ -81,8 +91,22 @@ struct DerivedPath : _DerivedPathRaw {
|
||||||
return static_cast<const Raw &>(*this);
|
return static_cast<const Raw &>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses `^` as the separator
|
||||||
|
*/
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
|
/**
|
||||||
|
* Uses `!` as the separator
|
||||||
|
*/
|
||||||
|
std::string to_string_legacy(const Store & store) const;
|
||||||
|
/**
|
||||||
|
* Uses `^` as the separator
|
||||||
|
*/
|
||||||
static DerivedPath parse(const Store & store, std::string_view);
|
static DerivedPath parse(const Store & store, std::string_view);
|
||||||
|
/**
|
||||||
|
* Uses `!` as the separator
|
||||||
|
*/
|
||||||
|
static DerivedPath parseLegacy(const Store & store, std::string_view);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,6 +71,9 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
|
||||||
void queryRealisationUncached(const DrvOutput &,
|
void queryRealisationUncached(const DrvOutput &,
|
||||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
||||||
{ callback(nullptr); }
|
{ callback(nullptr); }
|
||||||
|
|
||||||
|
virtual ref<FSAccessor> getFSAccessor() override
|
||||||
|
{ unsupported("getFSAccessor"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;
|
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;
|
||||||
|
|
|
@ -7,12 +7,20 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <sodium/core.h>
|
||||||
|
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
#include <gnu/lib-names.h>
|
||||||
|
#include <nss.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -41,7 +49,6 @@ Settings::Settings()
|
||||||
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||||
{
|
{
|
||||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||||
lockCPU = getEnv("NIX_AFFINITY_HACK") == "1";
|
|
||||||
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||||
|
|
||||||
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||||
|
@ -281,6 +288,42 @@ void initPlugins()
|
||||||
settings.pluginFiles.pluginsLoaded = true;
|
settings.pluginFiles.pluginsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void preloadNSS()
|
||||||
|
{
|
||||||
|
/* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
|
||||||
|
one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
|
||||||
|
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
|
||||||
|
load its lookup libraries in the parent before any child gets a chance to. */
|
||||||
|
static std::once_flag dns_resolve_flag;
|
||||||
|
|
||||||
|
std::call_once(dns_resolve_flag, []() {
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
/* On linux, glibc will run every lookup through the nss layer.
|
||||||
|
* That means every lookup goes, by default, through nscd, which acts as a local
|
||||||
|
* cache.
|
||||||
|
* Because we run builds in a sandbox, we also remove access to nscd otherwise
|
||||||
|
* lookups would leak into the sandbox.
|
||||||
|
*
|
||||||
|
* But now we have a new problem, we need to make sure the nss_dns backend that
|
||||||
|
* does the dns lookups when nscd is not available is loaded or available.
|
||||||
|
*
|
||||||
|
* We can't make it available without leaking nix's environment, so instead we'll
|
||||||
|
* load the backend, and configure nss so it does not try to run dns lookups
|
||||||
|
* through nscd.
|
||||||
|
*
|
||||||
|
* This is technically only used for builtins:fetch* functions so we only care
|
||||||
|
* about dns.
|
||||||
|
*
|
||||||
|
* All other platforms are unaffected.
|
||||||
|
*/
|
||||||
|
if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
|
||||||
|
warn("unable to load nss_dns backend");
|
||||||
|
// FIXME: get hosts entry from nsswitch.conf.
|
||||||
|
__nss_configure_lookup("hosts", "files dns");
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static bool initLibStoreDone = false;
|
static bool initLibStoreDone = false;
|
||||||
|
|
||||||
void assertLibStoreInitialized() {
|
void assertLibStoreInitialized() {
|
||||||
|
@ -291,6 +334,24 @@ void assertLibStoreInitialized() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initLibStore() {
|
void initLibStore() {
|
||||||
|
|
||||||
|
initLibUtil();
|
||||||
|
|
||||||
|
if (sodium_init() == -1)
|
||||||
|
throw Error("could not initialise libsodium");
|
||||||
|
|
||||||
|
loadConfFile();
|
||||||
|
|
||||||
|
preloadNSS();
|
||||||
|
|
||||||
|
/* On macOS, don't use the per-session TMPDIR (as set e.g. by
|
||||||
|
sshd). This breaks build users because they don't have access
|
||||||
|
to the TMPDIR, in particular in ‘nix-store --serve’. */
|
||||||
|
#if __APPLE__
|
||||||
|
if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
|
||||||
|
unsetenv("TMPDIR");
|
||||||
|
#endif
|
||||||
|
|
||||||
initLibStoreDone = true;
|
initLibStoreDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -328,16 +328,6 @@ public:
|
||||||
users in `build-users-group`.
|
users in `build-users-group`.
|
||||||
|
|
||||||
UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS.
|
UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS.
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
> This is an experimental feature.
|
|
||||||
|
|
||||||
To enable it, add the following to [`nix.conf`](#):
|
|
||||||
|
|
||||||
```
|
|
||||||
extra-experimental-features = auto-allocate-uids
|
|
||||||
auto-allocate-uids = true
|
|
||||||
```
|
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
Setting<uint32_t> startId{this,
|
Setting<uint32_t> startId{this,
|
||||||
|
@ -367,16 +357,6 @@ public:
|
||||||
|
|
||||||
Cgroups are required and enabled automatically for derivations
|
Cgroups are required and enabled automatically for derivations
|
||||||
that require the `uid-range` system feature.
|
that require the `uid-range` system feature.
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
> This is an experimental feature.
|
|
||||||
|
|
||||||
To enable it, add the following to [`nix.conf`](#):
|
|
||||||
|
|
||||||
```
|
|
||||||
extra-experimental-features = cgroups
|
|
||||||
use-cgroups = true
|
|
||||||
```
|
|
||||||
)"};
|
)"};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -478,11 +458,6 @@ public:
|
||||||
)",
|
)",
|
||||||
{"env-keep-derivations"}};
|
{"env-keep-derivations"}};
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to lock the Nix client and worker to the same CPU.
|
|
||||||
*/
|
|
||||||
bool lockCPU;
|
|
||||||
|
|
||||||
Setting<SandboxMode> sandboxMode{
|
Setting<SandboxMode> sandboxMode{
|
||||||
this,
|
this,
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
|
@ -342,6 +342,9 @@ public:
|
||||||
void ensurePath(const StorePath & path) override
|
void ensurePath(const StorePath & path) override
|
||||||
{ unsupported("ensurePath"); }
|
{ unsupported("ensurePath"); }
|
||||||
|
|
||||||
|
virtual ref<FSAccessor> getFSAccessor() override
|
||||||
|
{ unsupported("getFSAccessor"); }
|
||||||
|
|
||||||
void computeFSClosure(const StorePathSet & paths,
|
void computeFSClosure(const StorePathSet & paths,
|
||||||
StorePathSet & out, bool flipDirection = false,
|
StorePathSet & out, bool flipDirection = false,
|
||||||
bool includeOutputs = false, bool includeDerivers = false) override
|
bool includeOutputs = false, bool includeDerivers = false) override
|
||||||
|
|
|
@ -90,12 +90,12 @@ void write(const Store & store, Sink & out, const ContentAddress & ca)
|
||||||
DerivedPath read(const Store & store, Source & from, Phantom<DerivedPath> _)
|
DerivedPath read(const Store & store, Source & from, Phantom<DerivedPath> _)
|
||||||
{
|
{
|
||||||
auto s = readString(from);
|
auto s = readString(from);
|
||||||
return DerivedPath::parse(store, s);
|
return DerivedPath::parseLegacy(store, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const Store & store, Sink & out, const DerivedPath & req)
|
void write(const Store & store, Sink & out, const DerivedPath & req)
|
||||||
{
|
{
|
||||||
out << req.to_string(store);
|
out << req.to_string_legacy(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -239,14 +239,11 @@ SQLiteTxn::~SQLiteTxn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSQLiteBusy(const SQLiteBusy & e)
|
void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning)
|
||||||
{
|
{
|
||||||
static std::atomic<time_t> lastWarned{0};
|
|
||||||
|
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
|
if (now > nextWarning) {
|
||||||
if (now > lastWarned + 10) {
|
nextWarning = now + 10;
|
||||||
lastWarned = now;
|
|
||||||
logWarning({
|
logWarning({
|
||||||
.msg = hintfmt(e.what())
|
.msg = hintfmt(e.what())
|
||||||
});
|
});
|
||||||
|
|
|
@ -139,7 +139,7 @@ protected:
|
||||||
|
|
||||||
MakeError(SQLiteBusy, SQLiteError);
|
MakeError(SQLiteBusy, SQLiteError);
|
||||||
|
|
||||||
void handleSQLiteBusy(const SQLiteBusy & e);
|
void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function for retrying a SQLite transaction when the
|
* Convenience function for retrying a SQLite transaction when the
|
||||||
|
@ -148,11 +148,13 @@ void handleSQLiteBusy(const SQLiteBusy & e);
|
||||||
template<typename T, typename F>
|
template<typename T, typename F>
|
||||||
T retrySQLite(F && fun)
|
T retrySQLite(F && fun)
|
||||||
{
|
{
|
||||||
|
time_t nextWarning = time(0) + 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
return fun();
|
return fun();
|
||||||
} catch (SQLiteBusy & e) {
|
} catch (SQLiteBusy & e) {
|
||||||
handleSQLiteBusy(e);
|
handleSQLiteBusy(e, nextWarning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -678,8 +678,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @return An object to access files in the Nix store.
|
* @return An object to access files in the Nix store.
|
||||||
*/
|
*/
|
||||||
virtual ref<FSAccessor> getFSAccessor()
|
virtual ref<FSAccessor> getFSAccessor() = 0;
|
||||||
{ unsupported("getFSAccessor"); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repair the contents of the given path by redownloading it using
|
* Repair the contents of the given path by redownloading it using
|
||||||
|
|
|
@ -51,6 +51,14 @@ TEST_F(DerivedPathTest, force_init)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RC_GTEST_FIXTURE_PROP(
|
||||||
|
DerivedPathTest,
|
||||||
|
prop_legacy_round_rip,
|
||||||
|
(const DerivedPath & o))
|
||||||
|
{
|
||||||
|
RC_ASSERT(o == DerivedPath::parseLegacy(*store, o.to_string_legacy(*store)));
|
||||||
|
}
|
||||||
|
|
||||||
RC_GTEST_FIXTURE_PROP(
|
RC_GTEST_FIXTURE_PROP(
|
||||||
DerivedPathTest,
|
DerivedPathTest,
|
||||||
prop_round_rip,
|
prop_round_rip,
|
||||||
|
|
|
@ -23,7 +23,7 @@ struct ChunkedCompressionSink : CompressionSink
|
||||||
{
|
{
|
||||||
uint8_t outbuf[32 * 1024];
|
uint8_t outbuf[32 * 1024];
|
||||||
|
|
||||||
void write(std::string_view data) override
|
void writeUnbuffered(std::string_view data) override
|
||||||
{
|
{
|
||||||
const size_t CHUNK_SIZE = sizeof(outbuf) << 2;
|
const size_t CHUNK_SIZE = sizeof(outbuf) << 2;
|
||||||
while (!data.empty()) {
|
while (!data.empty()) {
|
||||||
|
@ -103,7 +103,7 @@ struct ArchiveCompressionSink : CompressionSink
|
||||||
throw Error(reason, archive_error_string(this->archive));
|
throw Error(reason, archive_error_string(this->archive));
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(std::string_view data) override
|
void writeUnbuffered(std::string_view data) override
|
||||||
{
|
{
|
||||||
ssize_t result = archive_write_data(archive, data.data(), data.length());
|
ssize_t result = archive_write_data(archive, data.data(), data.length());
|
||||||
if (result <= 0) check(result);
|
if (result <= 0) check(result);
|
||||||
|
@ -136,7 +136,7 @@ struct NoneSink : CompressionSink
|
||||||
warn("requested compression level '%d' not supported by compression method 'none'", level);
|
warn("requested compression level '%d' not supported by compression method 'none'", level);
|
||||||
}
|
}
|
||||||
void finish() override { flush(); }
|
void finish() override { flush(); }
|
||||||
void write(std::string_view data) override { nextSink(data); }
|
void writeUnbuffered(std::string_view data) override { nextSink(data); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BrotliDecompressionSink : ChunkedCompressionSink
|
struct BrotliDecompressionSink : ChunkedCompressionSink
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace nix {
|
||||||
struct CompressionSink : BufferedSink, FinishSink
|
struct CompressionSink : BufferedSink, FinishSink
|
||||||
{
|
{
|
||||||
using BufferedSink::operator ();
|
using BufferedSink::operator ();
|
||||||
using BufferedSink::write;
|
using BufferedSink::writeUnbuffered;
|
||||||
using FinishSink::finish;
|
using FinishSink::finish;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,10 @@ std::map<std::string, nlohmann::json> AbstractSetting::toJSONObject()
|
||||||
std::map<std::string, nlohmann::json> obj;
|
std::map<std::string, nlohmann::json> obj;
|
||||||
obj.emplace("description", description);
|
obj.emplace("description", description);
|
||||||
obj.emplace("aliases", aliases);
|
obj.emplace("aliases", aliases);
|
||||||
|
if (experimentalFeature)
|
||||||
|
obj.emplace("experimentalFeature", *experimentalFeature);
|
||||||
|
else
|
||||||
|
obj.emplace("experimentalFeature", nullptr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <openssl/crypto.h>
|
||||||
#include <openssl/md5.h>
|
#include <openssl/md5.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
@ -16,7 +17,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static size_t regularHashSize(HashType type) {
|
static size_t regularHashSize(HashType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case htMD5: return md5HashSize;
|
case htMD5: return md5HashSize;
|
||||||
|
@ -343,7 +343,7 @@ HashSink::~HashSink()
|
||||||
delete ctx;
|
delete ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashSink::write(std::string_view data)
|
void HashSink::writeUnbuffered(std::string_view data)
|
||||||
{
|
{
|
||||||
bytes += data.size();
|
bytes += data.size();
|
||||||
update(ht, *ctx, data);
|
update(ht, *ctx, data);
|
||||||
|
|
|
@ -197,7 +197,7 @@ public:
|
||||||
HashSink(HashType ht);
|
HashSink(HashType ht);
|
||||||
HashSink(const HashSink & h);
|
HashSink(const HashSink & h);
|
||||||
~HashSink();
|
~HashSink();
|
||||||
void write(std::string_view data) override;
|
void writeUnbuffered(std::string_view data) override;
|
||||||
HashResult finish() override;
|
HashResult finish() override;
|
||||||
HashResult currentHash();
|
HashResult currentHash();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ void BufferedSink::operator () (std::string_view data)
|
||||||
buffer size. */
|
buffer size. */
|
||||||
if (bufPos + data.size() >= bufSize) {
|
if (bufPos + data.size() >= bufSize) {
|
||||||
flush();
|
flush();
|
||||||
write(data);
|
writeUnbuffered(data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Otherwise, copy the bytes to the buffer. Flush the buffer
|
/* Otherwise, copy the bytes to the buffer. Flush the buffer
|
||||||
|
@ -38,7 +38,7 @@ void BufferedSink::flush()
|
||||||
if (bufPos == 0) return;
|
if (bufPos == 0) return;
|
||||||
size_t n = bufPos;
|
size_t n = bufPos;
|
||||||
bufPos = 0; // don't trigger the assert() in ~BufferedSink()
|
bufPos = 0; // don't trigger the assert() in ~BufferedSink()
|
||||||
write({buffer.get(), n});
|
writeUnbuffered({buffer.get(), n});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ FdSink::~FdSink()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void FdSink::write(std::string_view data)
|
void FdSink::writeUnbuffered(std::string_view data)
|
||||||
{
|
{
|
||||||
written += data.size();
|
written += data.size();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -53,7 +53,9 @@ struct BufferedSink : virtual Sink
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
virtual void write(std::string_view data) = 0;
|
protected:
|
||||||
|
|
||||||
|
virtual void writeUnbuffered(std::string_view data) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ struct FdSink : BufferedSink
|
||||||
|
|
||||||
~FdSink();
|
~FdSink();
|
||||||
|
|
||||||
void write(std::string_view data) override;
|
void writeUnbuffered(std::string_view data) override;
|
||||||
|
|
||||||
bool good() override;
|
bool good() override;
|
||||||
|
|
||||||
|
@ -520,7 +522,7 @@ struct FramedSink : nix::BufferedSink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(std::string_view data) override
|
void writeUnbuffered(std::string_view data) override
|
||||||
{
|
{
|
||||||
/* Don't send more data if the remote has
|
/* Don't send more data if the remote has
|
||||||
encountered an error. */
|
encountered an error. */
|
||||||
|
|
|
@ -156,12 +156,54 @@ namespace nix {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Config, toJSONOnNonEmptyConfig) {
|
TEST(Config, toJSONOnNonEmptyConfig) {
|
||||||
|
using nlohmann::literals::operator "" _json;
|
||||||
Config config;
|
Config config;
|
||||||
std::map<std::string, Config::SettingInfo> settings;
|
Setting<std::string> setting{
|
||||||
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
&config,
|
||||||
|
"",
|
||||||
|
"name-of-the-setting",
|
||||||
|
"description",
|
||||||
|
};
|
||||||
setting.assign("value");
|
setting.assign("value");
|
||||||
|
|
||||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","documentDefault":true,"value":"value"}})#");
|
ASSERT_EQ(config.toJSON(),
|
||||||
|
R"#({
|
||||||
|
"name-of-the-setting": {
|
||||||
|
"aliases": [],
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "description\n",
|
||||||
|
"documentDefault": true,
|
||||||
|
"value": "value",
|
||||||
|
"experimentalFeature": null
|
||||||
|
}
|
||||||
|
})#"_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, toJSONOnNonEmptyConfigWithExperimentalSetting) {
|
||||||
|
using nlohmann::literals::operator "" _json;
|
||||||
|
Config config;
|
||||||
|
Setting<std::string> setting{
|
||||||
|
&config,
|
||||||
|
"",
|
||||||
|
"name-of-the-setting",
|
||||||
|
"description",
|
||||||
|
{},
|
||||||
|
true,
|
||||||
|
Xp::Flakes,
|
||||||
|
};
|
||||||
|
setting.assign("value");
|
||||||
|
|
||||||
|
ASSERT_EQ(config.toJSON(),
|
||||||
|
R"#({
|
||||||
|
"name-of-the-setting": {
|
||||||
|
"aliases": [],
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "description\n",
|
||||||
|
"documentDefault": true,
|
||||||
|
"value": "value",
|
||||||
|
"experimentalFeature": "flakes"
|
||||||
|
}
|
||||||
|
})#"_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Config, setSettingAlias) {
|
TEST(Config, setSettingAlias) {
|
||||||
|
|
|
@ -47,6 +47,9 @@ extern char * * environ __attribute__((weak));
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
void initLibUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> getEnv(const std::string & key)
|
std::optional<std::string> getEnv(const std::string & key)
|
||||||
{
|
{
|
||||||
char * value = getenv(key.c_str());
|
char * value = getenv(key.c_str());
|
||||||
|
@ -1744,13 +1747,39 @@ void triggerInterrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
static sigset_t savedSignalMask;
|
static sigset_t savedSignalMask;
|
||||||
|
static bool savedSignalMaskIsSet = false;
|
||||||
|
|
||||||
|
void setChildSignalMask(sigset_t * sigs)
|
||||||
|
{
|
||||||
|
assert(sigs); // C style function, but think of sigs as a reference
|
||||||
|
|
||||||
|
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||||
|
sigemptyset(&savedSignalMask);
|
||||||
|
// There's no "assign" or "copy" function, so we rely on (math) idempotence
|
||||||
|
// of the or operator: a or a = a.
|
||||||
|
sigorset(&savedSignalMask, sigs, sigs);
|
||||||
|
#else
|
||||||
|
// Without sigorset, our best bet is to assume that sigset_t is a type that
|
||||||
|
// can be assigned directly, such as is the case for a sigset_t defined as
|
||||||
|
// an integer type.
|
||||||
|
savedSignalMask = *sigs;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
savedSignalMaskIsSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveSignalMask() {
|
||||||
|
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
|
||||||
|
throw SysError("querying signal mask");
|
||||||
|
|
||||||
|
savedSignalMaskIsSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
void startSignalHandlerThread()
|
void startSignalHandlerThread()
|
||||||
{
|
{
|
||||||
updateWindowSize();
|
updateWindowSize();
|
||||||
|
|
||||||
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
|
saveSignalMask();
|
||||||
throw SysError("querying signal mask");
|
|
||||||
|
|
||||||
sigset_t set;
|
sigset_t set;
|
||||||
sigemptyset(&set);
|
sigemptyset(&set);
|
||||||
|
@ -1767,6 +1796,20 @@ void startSignalHandlerThread()
|
||||||
|
|
||||||
static void restoreSignals()
|
static void restoreSignals()
|
||||||
{
|
{
|
||||||
|
// If startSignalHandlerThread wasn't called, that means we're not running
|
||||||
|
// in a proper libmain process, but a process that presumably manages its
|
||||||
|
// own signal handlers. Such a process should call either
|
||||||
|
// - initNix(), to be a proper libmain process
|
||||||
|
// - startSignalHandlerThread(), to resemble libmain regarding signal
|
||||||
|
// handling only
|
||||||
|
// - saveSignalMask(), for processes that define their own signal handling
|
||||||
|
// thread
|
||||||
|
// TODO: Warn about this? Have a default signal mask? The latter depends on
|
||||||
|
// whether we should generally inherit signal masks from the caller.
|
||||||
|
// I don't know what the larger unix ecosystem expects from us here.
|
||||||
|
if (!savedSignalMaskIsSet)
|
||||||
|
return;
|
||||||
|
|
||||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||||
throw SysError("restoring signals");
|
throw SysError("restoring signals");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace nix {
|
||||||
struct Sink;
|
struct Sink;
|
||||||
struct Source;
|
struct Source;
|
||||||
|
|
||||||
|
void initLibUtil();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The system for which Nix is compiled.
|
* The system for which Nix is compiled.
|
||||||
|
@ -445,6 +446,8 @@ void setStackSize(size_t stackSize);
|
||||||
/**
|
/**
|
||||||
* Restore the original inherited Unix process context (such as signal
|
* Restore the original inherited Unix process context (such as signal
|
||||||
* masks, stack size).
|
* masks, stack size).
|
||||||
|
|
||||||
|
* See startSignalHandlerThread(), saveSignalMask().
|
||||||
*/
|
*/
|
||||||
void restoreProcessContext(bool restoreMounts = true);
|
void restoreProcessContext(bool restoreMounts = true);
|
||||||
|
|
||||||
|
@ -814,9 +817,26 @@ class Callback;
|
||||||
/**
|
/**
|
||||||
* Start a thread that handles various signals. Also block those signals
|
* Start a thread that handles various signals. Also block those signals
|
||||||
* on the current thread (and thus any threads created by it).
|
* on the current thread (and thus any threads created by it).
|
||||||
|
* Saves the signal mask before changing the mask to block those signals.
|
||||||
|
* See saveSignalMask().
|
||||||
*/
|
*/
|
||||||
void startSignalHandlerThread();
|
void startSignalHandlerThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the signal mask, which is the signal mask that nix will restore
|
||||||
|
* before creating child processes.
|
||||||
|
* See setChildSignalMask() to set an arbitrary signal mask instead of the
|
||||||
|
* current mask.
|
||||||
|
*/
|
||||||
|
void saveSignalMask();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't
|
||||||
|
* necessarily match the current thread's mask.
|
||||||
|
* See saveSignalMask() to set the saved mask to the current mask.
|
||||||
|
*/
|
||||||
|
void setChildSignalMask(sigset_t *sigs);
|
||||||
|
|
||||||
struct InterruptCallback
|
struct InterruptCallback
|
||||||
{
|
{
|
||||||
virtual ~InterruptCallback() { };
|
virtual ~InterruptCallback() { };
|
||||||
|
|
|
@ -48,12 +48,17 @@ manual](https://nixos.org/manual/nix/stable/).
|
||||||
|
|
||||||
# Installables
|
# Installables
|
||||||
|
|
||||||
|
> **Warning** \
|
||||||
|
> Installables are part of the unstable
|
||||||
|
> [`nix-command` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-nix-command),
|
||||||
|
> and subject to change without notice.
|
||||||
|
|
||||||
Many `nix` subcommands operate on one or more *installables*.
|
Many `nix` subcommands operate on one or more *installables*.
|
||||||
These are command line arguments that represent something that can be realised in the Nix store.
|
These are command line arguments that represent something that can be realised in the Nix store.
|
||||||
|
|
||||||
The following types of installable are supported by most commands:
|
The following types of installable are supported by most commands:
|
||||||
|
|
||||||
- [Flake output attribute](#flake-output-attribute)
|
- [Flake output attribute](#flake-output-attribute) (experimental)
|
||||||
- [Store path](#store-path)
|
- [Store path](#store-path)
|
||||||
- [Nix file](#nix-file), optionally qualified by an attribute path
|
- [Nix file](#nix-file), optionally qualified by an attribute path
|
||||||
- [Nix expression](#nix-expression), optionally qualified by an attribute path
|
- [Nix expression](#nix-expression), optionally qualified by an attribute path
|
||||||
|
@ -63,6 +68,13 @@ That is, Nix will operate on the default flake output attribute of the flake in
|
||||||
|
|
||||||
### Flake output attribute
|
### Flake output attribute
|
||||||
|
|
||||||
|
> **Warning** \
|
||||||
|
> Flake output attribute installables depend on both the
|
||||||
|
> [`flakes`](@docroot@/contributing/experimental-features.md#xp-feature-flakes)
|
||||||
|
> and
|
||||||
|
> [`nix-command`](@docroot@/contributing/experimental-features.md#xp-feature-nix-command)
|
||||||
|
> experimental features, and subject to change without notice.
|
||||||
|
|
||||||
Example: `nixpkgs#hello`
|
Example: `nixpkgs#hello`
|
||||||
|
|
||||||
These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a
|
These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a
|
||||||
|
|
|
@ -79,6 +79,14 @@ testReplResponse '
|
||||||
"result: ${a}"
|
"result: ${a}"
|
||||||
' "result: 2"
|
' "result: 2"
|
||||||
|
|
||||||
|
# check dollar escaping https://github.com/NixOS/nix/issues/4909
|
||||||
|
# note the escaped \,
|
||||||
|
# \\
|
||||||
|
# because the second argument is a regex
|
||||||
|
testReplResponse '
|
||||||
|
"$" + "{hi}"
|
||||||
|
' '"\\${hi}"'
|
||||||
|
|
||||||
testReplResponse '
|
testReplResponse '
|
||||||
drvPath
|
drvPath
|
||||||
' '".*-simple.drv"' \
|
' '".*-simple.drv"' \
|
||||||
|
|
Loading…
Reference in a new issue