Compare commits

..

1 Commits

Author SHA1 Message Date
Domen Kožar
02ea337a36 Install from closure: wording and fallback using .bash_profile
- use empty ~/.bash_profile if it's not there
- use "source" instead of vague "."
- make it explicit how to start using Nix after installation
2017-01-25 19:21:57 +01:00
282 changed files with 6411 additions and 34696 deletions

View File

@@ -1,26 +0,0 @@
# EditorConfig configuration for nix
# http://EditorConfig.org
# Top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file, utf-8 charset
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
# Match nix files, set indent to spaces with width of two
[*.nix]
indent_style = space
indent_size = 2
# Match c++/shell/perl, set indent to spaces with width of four
[*.{hpp,cc,hh,sh,pl}]
indent_style = space
indent_size = 4
# Match diffs, avoid to trim trailing whitespace
[*.{diff,patch}]
trim_trailing_whitespace = false

View File

@@ -1,27 +0,0 @@
<!--
# Filing a Nix issue
*WAIT* Are you sure you're filing your issue in the right repository?
We appreciate you taking the time to tell us about issues you encounter, but routing the issue to the right place will get you help sooner and save everyone time.
This is the Nix repository, and issues here should be about Nix the build and package management *_tool_*.
If you have a problem with a specific package on NixOS or when using Nix, you probably want to file an issue with _nixpkgs_, whose issue tracker is over at https://github.com/NixOS/nixpkgs/issues.
Examples of _Nix_ issues:
- Nix segfaults when I run `nix-build -A blahblah`
- The Nix language needs a new builtin: `builtins.foobar`
- Regression in the behavior of `nix-env` in Nix 1.12
Examples of _nixpkgs_ issues:
- glibc is b0rked on aarch64
- chromium in NixOS doesn't support U2F but google-chrome does!
- The OpenJDK package on macOS is missing a key component
Chances are if you're a newcomer to the Nix world, you'll probably want the [nixpkgs tracker](https://github.com/NixOS/nixpkgs/issues). It also gets a lot more eyeball traffic so you'll probably get a response a lot more quickly.
-->

16
.gitignore vendored
View File

@@ -1,5 +1,4 @@
Makefile.config
perl/Makefile.config
# /
/aclocal.m4
@@ -36,6 +35,7 @@ perl/Makefile.config
# /scripts/
/scripts/nix-profile.sh
/scripts/nix-copy-closure
/scripts/build-remote.pl
/scripts/nix-reduce-build
/scripts/nix-http-export.cgi
@@ -48,7 +48,8 @@ perl/Makefile.config
/src/libexpr/nix.tbl
# /src/libstore/
/src/libstore/*.gen.hh
/src/libstore/schema.sql.hh
/src/libstore/sandbox-defaults.sb
/src/nix/nix
@@ -71,16 +72,15 @@ perl/Makefile.config
# /src/nix-channel/
/src/nix-channel/nix-channel
# /src/download-via-ssh/
/src/download-via-ssh/download-via-ssh
# /src/buildenv/
/src/buildenv/buildenv
# /src/nix-build/
/src/nix-build/nix-build
/src/nix-copy-closure/nix-copy-closure
/src/build-remote/build-remote
# /tests/
/tests/test-tmp
/tests/common.sh
@@ -99,20 +99,16 @@ perl/Makefile.config
/misc/systemd/nix-daemon.socket
/misc/upstart/nix-daemon.conf
/src/resolve-system-dependencies/resolve-system-dependencies
inst/
*.a
*.o
*.so
*.dylib
*.dll
*.exe
*.dep
*~
*.pc
*.plist
# GNU Global
GPATH

View File

@@ -1,2 +0,0 @@
os: osx
script: ./tests/install-darwin.sh

View File

@@ -11,22 +11,23 @@ makefiles = \
src/nix-env/local.mk \
src/nix-daemon/local.mk \
src/nix-collect-garbage/local.mk \
src/nix-copy-closure/local.mk \
src/nix-prefetch-url/local.mk \
src/buildenv/local.mk \
src/resolve-system-dependencies/local.mk \
src/nix-channel/local.mk \
src/nix-build/local.mk \
src/build-remote/local.mk \
perl/local.mk \
scripts/local.mk \
corepkgs/local.mk \
misc/systemd/local.mk \
misc/launchd/local.mk \
misc/upstart/local.mk \
misc/emacs/local.mk \
doc/manual/local.mk \
tests/local.mk
#src/download-via-ssh/local.mk \
GLOBAL_CXXFLAGS += -std=c++14 -g -Wall -include config.h
GLOBAL_CXXFLAGS += -std=c++11 -g -Wall
-include Makefile.config

View File

@@ -5,7 +5,6 @@ CXX = @CXX@
CXXFLAGS = @CXXFLAGS@
ENABLE_S3 = @ENABLE_S3@
HAVE_SODIUM = @HAVE_SODIUM@
HAVE_READLINE = @HAVE_READLINE@
LIBCURL_LIBS = @LIBCURL_LIBS@
OPENSSL_LIBS = @OPENSSL_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@
@@ -15,8 +14,8 @@ LIBLZMA_LIBS = @LIBLZMA_LIBS@
SQLITE3_LIBS = @SQLITE3_LIBS@
bash = @bash@
bindir = @bindir@
bro = @bro@
lsof = @lsof@
bsddiff_compat_include = @bsddiff_compat_include@
curl = @curl@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
@@ -26,9 +25,11 @@ libdir = @libdir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
mandir = @mandir@
perl = @perl@
perlbindings = @perlbindings@
perllibdir = @perllibdir@
pkglibdir = $(libdir)/$(PACKAGE_NAME)
prefix = @prefix@
sandbox_shell = @sandbox_shell@
storedir = @storedir@
sysconfdir = @sysconfdir@
doc_generate = @doc_generate@

View File

@@ -1,7 +1,7 @@
Nix, the purely functional package manager
------------------------------------------
Nix is a new take on package management that is fairly unique. Because of its
Nix is a new take on package management that is fairly unique. Because of it's
purity aspects, a lot of issues found in traditional package managers don't
appear with Nix.

View File

@@ -114,12 +114,14 @@ if test -z "$$1"; then
fi
])
NEED_PROG(curl, curl)
NEED_PROG(bash, bash)
NEED_PROG(patch, patch)
AC_PATH_PROG(xmllint, xmllint, false)
AC_PATH_PROG(xsltproc, xsltproc, false)
AC_PATH_PROG(flex, flex, false)
AC_PATH_PROG(bison, bison, false)
NEED_PROG(perl, perl)
NEED_PROG(sed, sed)
NEED_PROG(tar, tar)
NEED_PROG(bzip2, bzip2)
@@ -127,8 +129,23 @@ NEED_PROG(gzip, gzip)
NEED_PROG(xz, xz)
AC_PATH_PROG(dot, dot)
AC_PATH_PROG(pv, pv, pv)
AC_PATH_PROG(bro, bro, bro)
AC_PATH_PROG(lsof, lsof, lsof)
# Test that Perl has the open/fork feature (Perl 5.8.0 and beyond).
AC_MSG_CHECKING([whether Perl is recent enough])
if ! $perl -e 'open(FOO, "-|", "true"); while (<FOO>) { print; }; close FOO or die;'; then
AC_MSG_RESULT(no)
AC_MSG_ERROR([Your Perl version is too old. Nix requires Perl 5.8.0 or newer.])
fi
AC_MSG_RESULT(yes)
# Figure out where to install Perl modules.
AC_MSG_CHECKING([for the Perl installation prefix])
perlversion=$($perl -e 'use Config; print $Config{version};')
perlarchname=$($perl -e 'use Config; print $Config{archname};')
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
AC_MSG_RESULT($perllibdir)
NEED_PROG(cat, cat)
@@ -176,13 +193,6 @@ AC_SUBST(HAVE_SODIUM, [$have_sodium])
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
# Look for libseccomp, required for Linux sandboxing.
if test "$sys_name" = linux; then
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
fi
# Look for aws-cpp-sdk-s3.
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([aws/s3/S3Client.h],
@@ -203,6 +213,40 @@ if test "$gc" = yes; then
fi
# Check for the required Perl dependencies (DBI, DBD::SQLite).
perlFlags="-I$perllibdir"
AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH],
[prefix of the Perl DBI library]),
perlFlags="$perlFlags -I$withval")
AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH],
[prefix of the Perl DBD::SQLite library]),
perlFlags="$perlFlags -I$withval")
AC_MSG_CHECKING([whether DBD::SQLite works])
if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then
AC_MSG_RESULT(no)
AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.])
fi
AC_MSG_RESULT(yes)
AC_SUBST(perlFlags)
# Whether to build the Perl bindings
AC_MSG_CHECKING([whether to build the Perl bindings])
AC_ARG_ENABLE(perl-bindings, AC_HELP_STRING([--enable-perl-bindings],
[whether to build the Perl bindings (recommended) [default=yes]]),
perlbindings=$enableval, perlbindings=yes)
if test "$enable_shared" = no; then
# Perl bindings require shared libraries.
perlbindings=no
fi
AC_SUBST(perlbindings)
AC_MSG_RESULT($perlbindings)
AC_ARG_ENABLE(init-state, AC_HELP_STRING([--disable-init-state],
[do not initialise DB etc. in `make install']),
init_state=$enableval, init_state=yes)
@@ -221,7 +265,7 @@ AC_CHECK_FUNCS([setresuid setreuid lchown])
# Nice to have, but not essential.
AC_CHECK_FUNCS([strsignal posix_fallocate sysconf])
AC_CHECK_FUNCS([strsignal posix_fallocate nanosleep sysconf])
# This is needed if bzip2 is a static library, and the Nix libraries
@@ -247,12 +291,6 @@ fi
AC_SUBST(tarFlags)
AC_ARG_WITH(sandbox-shell, AC_HELP_STRING([--with-sandbox-shell=PATH],
[path of a statically-linked shell to use as /bin/sh in sandboxes]),
sandbox_shell=$withval)
AC_SUBST(sandbox_shell)
# Expand all variables in config.status.
test "$prefix" = NONE && prefix=$ac_default_prefix
test "$exec_prefix" = NONE && exec_prefix='${prefix}'

View File

@@ -14,9 +14,6 @@ in rec {
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
nixPrefix = "@prefix@";
nixLibexecDir = fromEnv "NIX_LIBEXEC_DIR" "@libexecdir@";
nixLocalstateDir = "@localstatedir@";
nixSysconfDir = "@sysconfdir@";
nixStoreDir = fromEnv "NIX_STORE_DIR" "@storedir@";
# If Nix is installed in the Nix store, then automatically add it as
# a dependency to the core packages. This ensures that they work

View File

@@ -1,20 +1,24 @@
{ system ? builtins.currentSystem
, url
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
, outputHash ?
if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
, outputHashAlgo ?
if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
, outputHash ? ""
, outputHashAlgo ? ""
, md5 ? "", sha1 ? "", sha256 ? ""
, executable ? false
, unpack ? false
, name ? baseNameOf (toString url)
}:
assert (outputHash != "" && outputHashAlgo != "")
|| md5 != "" || sha1 != "" || sha256 != "";
derivation {
builder = "builtin:fetchurl";
# New-style output content requirements.
inherit outputHashAlgo outputHash;
outputHashAlgo = if outputHashAlgo != "" then outputHashAlgo else
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
outputHash = if outputHash != "" then outputHash else
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
outputHashMode = if unpack || executable then "recursive" else "flat";
inherit name system url executable unpack;

View File

@@ -15,9 +15,7 @@ let
else
${bzip2} -d < $src | ${tar} xf - ${tarFlags}
fi
if [ * != $channelName ]; then
mv * $out/$channelName
fi
mv * $out/$channelName
if [ -n "$binaryCacheURL" ]; then
mkdir $out/binary-caches
echo -n "$binaryCacheURL" > $out/binary-caches/$channelName

View File

@@ -22,7 +22,10 @@ will call whenever it wants to build a derivation. The build hook
will perform it in the usual way if possible, or it can accept it, in
which case it is responsible for somehow getting the inputs of the
build to another machine, doing the build there, and getting the
results back.</para>
results back. The details of the build hook protocol are described in
the documentation of the <link
linkend="envar-build-hook"><envar>NIX_BUILD_HOOK</envar>
variable</link>.</para>
<example xml:id='ex-remote-systems'><title>Remote machine configuration:
<filename>remote-systems.conf</filename></title>
@@ -39,7 +42,7 @@ purposes. It uses <command>ssh</command> and
<command>nix-copy-closure</command> to copy the build inputs and
outputs and perform the remote build. To use it, you should set
<envar>NIX_BUILD_HOOK</envar> to
<filename><replaceable>prefix</replaceable>/libexec/nix/build-remote</filename>.
<filename><replaceable>prefix</replaceable>/libexec/nix/build-remote.pl</filename>.
You should also define a list of available build machines and point
the environment variable <envar>NIX_REMOTE_SYSTEMS</envar> to
it. <envar>NIX_REMOTE_SYSTEMS</envar> must be an absolute path. An
@@ -65,7 +68,7 @@ bits of information:
should not have a passphrase!</para></listitem>
<listitem><para>The maximum number of builds that
<filename>build-remote</filename> will execute in parallel on the
<filename>build-remote.pl</filename> will execute in parallel on the
machine. Typically this should be equal to the number of CPU cores.
For instance, the machine <literal>itchy</literal> in the example
will execute up to 8 builds in parallel.</para></listitem>
@@ -77,7 +80,7 @@ bits of information:
<listitem><para>A comma-separated list of <emphasis>supported
features</emphasis>. If a derivation has the
<varname>requiredSystemFeatures</varname> attribute, then
<filename>build-remote</filename> will only perform the
<filename>build-remote.pl</filename> will only perform the
derivation on a machine that has the specified features. For
instance, the attribute
@@ -100,6 +103,14 @@ requiredSystemFeatures = [ "kvm" ];
</orderedlist>
</para>
You should also set up the environment variable
<envar>NIX_CURRENT_LOAD</envar> to point at a directory (e.g.,
<filename>/var/run/nix/current-load</filename>) that
<filename>build-remote.pl</filename> uses to remember how many builds
it is currently executing remotely. It doesn't look at the actual
load on the remote machine, so if you have multiple instances of Nix
running, they should use the same <envar>NIX_CURRENT_LOAD</envar>
file. Maybe in the future <filename>build-remote.pl</filename> will
look at the actual remote load.</para>
</chapter>

View File

@@ -17,48 +17,29 @@
<refsection><title>Description</title>
<para>Nix reads settings from two configuration files:</para>
<itemizedlist>
<listitem>
<para>The system-wide configuration file
<filename><replaceable>sysconfdir</replaceable>/nix/nix.conf</filename>
(i.e. <filename>/etc/nix/nix.conf</filename> on most systems), or
<filename>$NIX_CONF_DIR/nix.conf</filename> if
<envar>NIX_CONF_DIR</envar> is set.</para>
</listitem>
<listitem>
<para>The user configuration file
<filename>$XDG_CONFIG_HOME/nix/nix.conf</filename>, or
<filename>~/.config/nix/nix.conf</filename> if
<envar>XDG_CONFIG_HOME</envar> is not set.</para>
</listitem>
</itemizedlist>
<para>The configuration files consist of
<literal><replaceable>name</replaceable> =
<para>A number of persistent settings of Nix are stored in the file
<filename><replaceable>sysconfdir</replaceable>/nix/nix.conf</filename> or
<filename>$NIX_CONF_DIR/nix.conf</filename> if <envar>NIX_CONF_DIR</envar> is set.
This file is a list of <literal><replaceable>name</replaceable> =
<replaceable>value</replaceable></literal> pairs, one per line.
Comments start with a <literal>#</literal> character. Here is an
example configuration file:</para>
Comments start with a <literal>#</literal> character. Here is an example
configuration file:</para>
<programlisting>
keep-outputs = true # Nice for developers
keep-derivations = true # Idem
gc-keep-outputs = true # Nice for developers
gc-keep-derivations = true # Idem
env-keep-derivations = false
</programlisting>
<para>You can override settings on the command line using the
<option>--option</option> flag, e.g. <literal>--option keep-outputs
false</literal>.</para>
<para>You can override settings using the <option>--option</option>
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<para>The following settings are currently available:
<variablelist>
<varlistentry xml:id="conf-keep-outputs"><term><literal>keep-outputs</literal></term>
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
<listitem><para>If <literal>true</literal>, the garbage collector
will keep the outputs of non-garbage derivations. If
@@ -75,7 +56,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-keep-derivations"><term><literal>keep-derivations</literal></term>
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
<listitem><para>If <literal>true</literal> (default), the garbage
collector will keep the derivations from which non-garbage store
@@ -87,12 +68,12 @@ false</literal>.</para>
traceability (e.g., it allows you to ask with what dependencies or
options a store path was built), so by default this option is on.
Turn it off to save a bit of disk space (or a lot if
<literal>keep-outputs</literal> is also turned on).</para></listitem>
<literal>gc-keep-outputs</literal> is also turned on).</para></listitem>
</varlistentry>
<varlistentry><term><literal>keep-env-derivations</literal></term>
<varlistentry><term><literal>env-keep-derivations</literal></term>
<listitem><para>If <literal>false</literal> (default), derivations
are not stored in Nix user environments. That is, the derivation
@@ -104,32 +85,32 @@ false</literal>.</para>
garbage-collected until the user environment generation is deleted
(<command>nix-env --delete-generations</command>). To prevent
build-time-only dependencies from being collected, you should also
turn on <literal>keep-outputs</literal>.</para>
turn on <literal>gc-keep-outputs</literal>.</para>
<para>The difference between this option and
<literal>keep-derivations</literal> is that this one is
<literal>gc-keep-derivations</literal> is that this one is
“sticky”: it applies to any user environment created while this
option was enabled, while <literal>keep-derivations</literal>
option was enabled, while <literal>gc-keep-derivations</literal>
only applies at the moment the garbage collector is
run.</para></listitem>
</varlistentry>
<varlistentry xml:id="conf-max-jobs"><term><literal>max-jobs</literal></term>
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
<listitem><para>This option defines the maximum number of jobs
that Nix will try to build in parallel. The default is
<literal>1</literal>. The special value <literal>auto</literal>
causes Nix to use the number of CPUs in your system. It can be
overridden using the <option
<literal>1</literal>. You should generally set it to the number
of CPUs in your system (e.g., <literal>2</literal> on an Athlon 64
X2). It can be overridden using the <option
linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>)
command line switch.</para></listitem>
</varlistentry>
<varlistentry xml:id="conf-cores"><term><literal>cores</literal></term>
<varlistentry xml:id="conf-build-cores"><term><literal>build-cores</literal></term>
<listitem><para>Sets the value of the
<envar>NIX_BUILD_CORES</envar> environment variable in the
@@ -148,7 +129,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-max-silent-time"><term><literal>max-silent-time</literal></term>
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
<listitem>
@@ -169,7 +150,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-timeout"><term><literal>timeout</literal></term>
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
<listitem>
@@ -189,7 +170,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-max-build-log-size"><term><literal>max-build-log-size</literal></term>
<varlistentry xml:id="conf-build-max-log-size"><term><literal>build-max-log-size</literal></term>
<listitem>
@@ -244,7 +225,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>sandbox</literal></term>
<varlistentry><term><literal>build-use-sandbox</literal></term>
<listitem><para>If set to <literal>true</literal>, builds will be
performed in a <emphasis>sandboxed environment</emphasis>, i.e.,
@@ -253,7 +234,7 @@ false</literal>.</para>
directory, private versions of <filename>/proc</filename>,
<filename>/dev</filename>, <filename>/dev/shm</filename> and
<filename>/dev/pts</filename> (on Linux), and the paths configured with the
<link linkend='conf-sandbox-paths'><literal>sandbox-paths</literal>
<link linkend='conf-build-sandbox-paths'><literal>build-sandbox-paths</literal>
option</link>. This is useful to prevent undeclared dependencies
on files in directories such as <filename>/usr/bin</filename>. In
addition, on Linux, builds run in private PID, mount, network, IPC
@@ -261,7 +242,7 @@ false</literal>.</para>
system (except that fixed-output derivations do not run in private
network namespace to ensure they can access the network).</para>
<para>Currently, sandboxing only work on Linux and macOS. The use
<para>Currently, sandboxing only work on Linux and Mac OS X. The use
of a sandbox requires that Nix is run as root (so you should use
the <link linkend='conf-build-users-group'>“build users”
feature</link> to perform the actual builds under different users
@@ -279,8 +260,8 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-sandbox-paths">
<term><literal>sandbox-paths</literal></term>
<varlistentry xml:id="conf-build-sandbox-paths">
<term><literal>build-sandbox-paths</literal></term>
<listitem><para>A list of paths bind-mounted into Nix sandbox
environments. You can use the syntax
@@ -302,17 +283,17 @@ false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-extra-sandbox-paths">
<varlistentry xml:id="conf-build-extra-sandbox-paths">
<term><literal>build-extra-sandbox-paths</literal></term>
<listitem><para>A list of additional paths appended to
<option>sandbox-paths</option>. Useful if you want to extend
<option>build-sandbox-paths</option>. Useful if you want to extend
its default value.</para></listitem>
</varlistentry>
<varlistentry><term><literal>use-substitutes</literal></term>
<varlistentry><term><literal>build-use-substitutes</literal></term>
<listitem><para>If set to <literal>true</literal> (default), Nix
will use binary substitutes if available. This option can be
@@ -321,7 +302,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>fallback</literal></term>
<varlistentry><term><literal>build-fallback</literal></term>
<listitem><para>If set to <literal>true</literal>, Nix will fall
back to building from source if a binary substitute fails. This
@@ -331,7 +312,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>keep-build-log</literal></term>
<varlistentry><term><literal>build-keep-log</literal></term>
<listitem><para>If set to <literal>true</literal> (the default),
Nix will write the build log of a derivation (i.e. the standard
@@ -343,7 +324,7 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>compress-build-log</literal></term>
<varlistentry><term><literal>build-compress-log</literal></term>
<listitem><para>If set to <literal>true</literal> (the default),
build logs written to <filename>/nix/var/log/nix/drvs</filename>
@@ -353,16 +334,25 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>substituters</literal></term>
<varlistentry><term><literal>use-binary-caches</literal></term>
<listitem><para>A list of URLs of substituters, separated by
<listitem><para>If set to <literal>true</literal> (the default),
Nix will check the binary caches specified by
<option>binary-caches</option> and related options to obtain
binary substitutes.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches</literal></term>
<listitem><para>A list of URLs of binary caches, separated by
whitespace. The default is
<literal>https://cache.nixos.org</literal>.</para></listitem>
</varlistentry>
<!--
<varlistentry><term><literal>binary-caches-files</literal></term>
<listitem><para>A list of names of files that will be read to
@@ -375,71 +365,54 @@ false</literal>.</para>
option to read files created by untrusted users!</para></listitem>
</varlistentry>
-->
<varlistentry><term><literal>trusted-substituters</literal></term>
<varlistentry><term><literal>trusted-binary-caches</literal></term>
<listitem><para>A list of URLs of substituters, separated by
<listitem><para>A list of URLs of binary caches, separated by
whitespace. These are not used by default, but can be enabled by
users of the Nix daemon by specifying <literal>--option
substituters <replaceable>urls</replaceable></literal> on the
binary-caches <replaceable>urls</replaceable></literal> on the
command line. Unprivileged users are only allowed to pass a
subset of the URLs listed in <literal>substituters</literal> and
<literal>trusted-substituters</literal>.</para></listitem>
subset of the URLs listed in <literal>binary-caches</literal> and
<literal>trusted-binary-caches</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>extra-substituters</literal></term>
<varlistentry><term><literal>extra-binary-caches</literal></term>
<listitem><para>Additional binary caches appended to those
specified in <option>substituters</option>. When used by
unprivileged users, untrusted substituters (i.e. those not listed
in <option>trusted-substituters</option>) are silently
specified in <option>binary-caches</option> and
<option>binary-caches-files</option>. When used by unprivileged
users, untrusted binary caches (i.e. those not listed in
<option>trusted-binary-caches</option>) are silently
ignored.</para></listitem>
</varlistentry>
<varlistentry><term><literal>require-sigs</literal></term>
<varlistentry><term><literal>signed-binary-caches</literal></term>
<listitem><para>If set to <literal>true</literal> (the default),
any non-content-addressed path added or copied to the Nix store
(e.g. when substituting from a binary cache) must have a valid
signature, that is, be signed using one of the keys listed in
<option>trusted-public-keys</option> or
<option>secret-key-files</option>. Set to <literal>false</literal>
to disable signature checking.</para></listitem>
<listitem><para>If set to <literal>*</literal>, Nix will only
download binaries if they are signed using one of the keys listed
in <option>binary-cache-public-keys</option>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>trusted-public-keys</literal></term>
<varlistentry><term><literal>binary-cache-public-keys</literal></term>
<listitem><para>A whitespace-separated list of public keys. When
paths are copied from another Nix store (such as a binary cache),
they must be signed with one of these keys. For example:
<listitem><para>A whitespace-separated list of public keys
corresponding to the secret keys trusted to sign binary
caches. For example:
<literal>cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>secret-key-files</literal></term>
<listitem><para>A whitespace-separated list of files containing
secret (private) keys. These are used to sign locally-built
paths. They can be generated using <command>nix-store
--generate-binary-cache-key</command>. The corresponding public
key can be distributed to other users, who can add it to
<option>trusted-public-keys</option> in their
<filename>nix.conf</filename>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>http-connections</literal></term>
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
<listitem><para>The maximum number of parallel TCP connections
used to fetch files from binary caches and by other downloads. It
@@ -448,25 +421,11 @@ false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>netrc-file</literal></term>
<varlistentry><term><literal>verify-https-binary-caches</literal></term>
<listitem><para>If set to an absolute path to a <filename>netrc</filename>
file, Nix will use the HTTP authentication credentials in this file when
trying to download from a remote host through HTTP or HTTPS. Defaults to
<filename>$NIX_CONF_DIR/netrc</filename>.</para>
<para>The <filename>netrc</filename> file consists of a list of
accounts in the following format:
<screen>
machine <replaceable>my-machine</replaceable>
login <replaceable>my-username</replaceable>
password <replaceable>my-password</replaceable>
</screen>
For the exact syntax, see <link
xlink:href="https://ec.haxx.se/usingcurl-netrc.html">the
<literal>curl</literal> documentation.</link></para></listitem>
<listitem><para>Whether HTTPS binary caches are required to have a
certificate that can be verified. Defaults to
<literal>true</literal>.</para></listitem>
</varlistentry>
@@ -530,6 +489,20 @@ password <replaceable>my-password</replaceable>
</varlistentry>
<varlistentry xml:id="conf-log-servers"><term><literal>log-servers</literal></term>
<listitem>
<para>A list of URL prefixes (such as
<literal>http://hydra.nixos.org/log</literal>) from which
<command>nix-store -l</command> will try to fetch build logs if
theyre not available locally.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-trusted-users"><term><literal>trusted-users</literal></term>
<listitem>
@@ -543,12 +516,11 @@ password <replaceable>my-password</replaceable>
<literal>wheel</literal> group. The default is
<literal>root</literal>.</para>
<warning><para>Adding a user to <option>trusted-users</option>
is essentially equivalent to giving that user root access to the
system. For example, the user can set
<option>sandbox-paths</option> and thereby obtain read access to
directories that are otherwise inacessible to
them.</para></warning>
<warning><para>The users listed here have the ability to
compromise the security of a multi-user Nix store. For instance,
they could install Trojan horses subsequently executed by other
users. So you should consider carefully whether to add users to
this list.</para></warning>
</listitem>
@@ -580,8 +552,7 @@ password <replaceable>my-password</replaceable>
<para>If set to <literal>true</literal>, the Nix evaluator will
not allow access to any files outside of the Nix search path (as
set via the <envar>NIX_PATH</envar> environment variable or the
<option>-I</option> option), or to URIs outside of
<option>allowed-uri</option>. The default is
<option>-I</option> option). The default is
<literal>false</literal>.</para>
</listitem>
@@ -589,21 +560,6 @@ password <replaceable>my-password</replaceable>
</varlistentry>
<varlistentry xml:id="conf-allowed-uris"><term><literal>allowed-uris</literal></term>
<listitem>
<para>A list of URI prefixes to which access is allowed in
restricted evaluation mode. For example, when set to
<literal>https://github.com/NixOS</literal>, builtin functions
such as <function>fetchGit</function> are allowed to access
<literal>https://github.com/NixOS/patchelf.git</literal>.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term>
<listitem>
@@ -629,7 +585,7 @@ password <replaceable>my-password</replaceable>
<para>Pass a list of files and directories to be included in the
sandbox for this build. One entry per line, terminated by an empty
line. Entries have the same format as
<literal>sandbox-paths</literal>.</para>
<literal>build-sandbox-paths</literal>.</para>
</listitem>
@@ -640,7 +596,7 @@ password <replaceable>my-password</replaceable>
</varlistentry>
<varlistentry xml:id="conf-repeat"><term><literal>repeat</literal></term>
<varlistentry xml:id="conf-build-repeat"><term><literal>build-repeat</literal></term>
<listitem><para>How many times to repeat builds to check whether
they are deterministic. The default value is 0. If the value is
@@ -665,61 +621,6 @@ password <replaceable>my-password</replaceable>
</varlistentry>
<varlistentry xml:id="conf-allow-import-from-derivation"><term><literal>allow-import-from-derivation</literal></term>
<listitem><para>By default, Nix allows you to <function>import</function> from a derivation,
allowing building at evaluation time. With this option set to false, Nix will throw an error
when evaluating an expression that uses this feature, allowing users to ensure their evaluation
will not require any builds to take place.</para></listitem>
</varlistentry>
<varlistentry xml:id="conf-allow-new-privileges"><term><literal>allow-new-privileges</literal></term>
<listitem><para>(Linux-specific.) By default, builders on Linux
cannot acquire new privileges by calling setuid/setgid programs or
programs that have file capabilities. For example, programs such
as <command>sudo</command> or <command>ping</command> will
fail. (Note that in sandbox builds, no such programs are available
unless you bind-mount them into the sandbox via the
<option>sandbox-paths</option> option.) You can allow the
use of such programs by enabling this option. This is impure and
usually undesirable, but may be useful in certain scenarios
(e.g. to spin up containers or set up userspace network interfaces
in tests).</para></listitem>
</varlistentry>
<varlistentry xml:id="conf-hashed-mirrors"><term><literal>hashed-mirrors</literal></term>
<listitem><para>A list of web servers used by
<function>builtins.fetchurl</function> to obtain files by
hash. The default is
<literal>http://tarballs.nixos.org/</literal>. Given a hash type
<replaceable>ht</replaceable> and a base-16 hash
<replaceable>h</replaceable>, Nix will try to download the file
from
<literal>hashed-mirror/<replaceable>ht</replaceable>/<replaceable>h</replaceable></literal>.
This allows files to be downloaded even if they have disappeared
from their original URI. For example, given the default mirror
<literal>http://tarballs.nixos.org/</literal>, when building the derivation
<programlisting>
builtins.fetchurl {
url = https://example.org/foo-1.2.3.tar.xz;
sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae";
}
</programlisting>
Nix will attempt to download this file from
<literal>http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae</literal>
first. If it is not available there, if will try the original URI.</para></listitem>
</varlistentry>
</variablelist>
</para>

View File

@@ -148,6 +148,139 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry xml:id="envar-build-hook"><term><envar>NIX_BUILD_HOOK</envar></term>
<listitem>
<para>Specifies the location of the <emphasis>build hook</emphasis>,
which is a program (typically some script) that Nix will call
whenever it wants to build a derivation. This is used to implement
distributed builds<phrase condition="manual"> (see <xref
linkend="chap-distributed-builds" />)</phrase>.</para>
<!--
The protocol by
which the calling Nix process and the build hook communicate is as
follows.
<para>The build hook is called with the following command-line
arguments:
<orderedlist>
<listitem><para>A boolean value <literal>0</literal> or
<literal>1</literal> specifying whether Nix can locally execute
more builds, as per the <link
linkend="opt-max-jobs"><option>- -max-jobs</option> option</link>.
The purpose of this argument is to allow the hook to not have to
maintain bookkeeping for the local machine.</para></listitem>
<listitem><para>The Nix platform identifier for the local machine
(e.g., <literal>i686-linux</literal>).</para></listitem>
<listitem><para>The Nix platform identifier for the derivation,
i.e., its <link linkend="attr-system"><varname>system</varname>
attribute</link>.</para></listitem>
<listitem><para>The store path of the derivation.</para></listitem>
</orderedlist>
</para>
<para>On the basis of this information, and whatever persistent
state the build hook keeps about other machines and their current
load, it has to decide what to do with the build. It should print
out on standard error one of the following responses (terminated by
a newline, <literal>"\n"</literal>):
<variablelist>
<varlistentry><term><literal># decline</literal></term>
<listitem><para>The build hook is not willing or able to perform
the build; the calling Nix process should do the build itself,
if possible.</para></listitem>
</varlistentry>
<varlistentry><term><literal># postpone</literal></term>
<listitem><para>The build hook cannot perform the build now, but
can do so in the future (e.g., because all available build slots
on remote machines are in use). The calling Nix process should
postpone this build until at least one currently running build
has terminated.</para></listitem>
</varlistentry>
<varlistentry><term><literal># accept</literal></term>
<listitem><para>The build hook has accepted the
build.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>After sending <literal># accept</literal>, the hook should
read one line from standard input, which will be the string
<literal>okay</literal>. It can then proceed with the build.
Before sending <literal>okay</literal>, Nix will store in the hooks
current directory a number of text files that contain information
about the derivation:
<variablelist>
<varlistentry><term><filename>inputs</filename></term>
<listitem><para>The set of store paths that are inputs to the
build process (one per line). These have to be copied
<emphasis>to</emphasis> the remote machine (in addition to the
store derivation itself).</para></listitem>
</varlistentry>
<varlistentry><term><filename>outputs</filename></term>
<listitem><para>The set of store paths that are outputs of the
derivation (one per line). These have to be copied
<emphasis>from</emphasis> the remote machine if the build
succeeds.</para></listitem>
</varlistentry>
<varlistentry><term><filename>references</filename></term>
<listitem><para>The reference graph of the inputs, in the format
accepted by the command <command>nix-store
- -register-validity</command>. It is necessary to run this
command on the remote machine after copying the inputs to inform
Nix on the remote machine that the inputs are valid
paths.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>The hook should copy the inputs to the remote machine,
register the validity of the inputs, perform the remote build, and
copy the outputs back to the local machine. An exit code other than
<literal>0</literal> indicates that the hook has failed. An exit
code equal to 100 means that the remote build failed (as opposed to,
e.g., a network error).</para>
-->
</listitem>
</varlistentry>
<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
<listitem><para>This variable should be set to

View File

@@ -27,10 +27,8 @@
<arg><option>- -show-progress</option></arg>
-->
<arg><option>--include-outputs</option></arg>
<group>
<arg choice='plain'><option>--use-substitutes</option></arg>
<arg choice='plain'><option>-s</option></arg>
</group>
<arg><option>--use-substitutes</option></arg>
<arg><option>-s</option></arg>
<arg><option>-v</option></arg>
<arg choice='plain'>
<replaceable>user@</replaceable><replaceable>machine</replaceable>

View File

@@ -146,7 +146,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<variablelist>
<varlistentry><term><option>--file</option> / <option>-f</option> <replaceable>path</replaceable></term>
<varlistentry><term><option>--file</option></term>
<term><option>-f</option></term>
<listitem><para>Specifies the Nix expression (designated below as
the <emphasis>active Nix expression</emphasis>) used by the
@@ -165,7 +166,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</varlistentry>
<varlistentry><term><option>--profile</option> / <option>-p</option> <replaceable>path</replaceable></term>
<varlistentry><term><option>--profile</option></term>
<term><option>-p</option></term>
<listitem><para>Specifies the profile to be used by those
operations that operate on a profile (designated below as the
@@ -1134,7 +1136,7 @@ user environment elements, etc. -->
<listitem><para>Print all of the meta-attributes of the
derivation. This option is only available with
<option>--xml</option> or <option>--json</option>.</para></listitem>
<option>--xml</option>.</para></listitem>
</varlistentry>

View File

@@ -44,9 +44,7 @@
cryptographic hash of the contents of each
<replaceable>path</replaceable> and prints it on standard output. By
default, it computes an MD5 hash, but other hash algorithms are
available as well. The hash is printed in hexadecimal. To generate
the same hash as <command>nix-prefetch-url</command> you have to
specify multiple arguments, see below for an example.</para>
available as well. The hash is printed in hexadecimal.</para>
<para>The hash is computed over a <emphasis>serialisation</emphasis>
of each path: a dump of the file system tree rooted at the path. This
@@ -124,15 +122,6 @@ cryptographic hash as <literal>nix-store --dump
<refsection><title>Examples</title>
<para>Computing the same hash as <command>nix-prefetch-url</command>:
<screen>
$ nix-prefetch-url file://&lt;(echo test)
1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
$ nix-hash --type sha256 --flat --base32 &lt;(echo test)
1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
</screen>
</para>
<para>Computing hashes:
<screen>

View File

@@ -24,7 +24,6 @@
<arg choice='plain'>
<option>--eval</option>
<arg><option>--strict</option></arg>
<arg><option>--json</option></arg>
<arg><option>--xml</option></arg>
</arg>
</group>
@@ -39,13 +38,12 @@
</arg>
<arg><option>--add-root</option> <replaceable>path</replaceable></arg>
<arg><option>--indirect</option></arg>
<group>
<group choice='req'>
<arg choice='plain'><option>--expr</option></arg>
<arg choice='plain'><option>-E</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<sbr/>
<command>nix-instantiate</command>
<arg choice='plain'><option>--find-file</option></arg>
<arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
@@ -117,6 +115,26 @@ input.</para>
</varlistentry>
<varlistentry><term><option>--xml</option></term>
<listitem><para>When used with <option>--parse</option> and
<option>--eval</option>, print the resulting expression as an
XML representation of the abstract syntax tree rather than as an
ATerm. The schema is the same as that used by the <link
linkend="builtin-toXML"><function>toXML</function>
built-in</link>.</para></listitem>
</varlistentry>
<varlistentry><term><option>--json</option></term>
<listitem><para>When used with <option>--parse</option> and
<option>--eval</option>, print the resulting expression as an
JSON representation of the abstract syntax tree rather than as an
ATerm.</para></listitem>
</varlistentry>
<varlistentry><term><option>--strict</option></term>
<listitem><para>When used with <option>--eval</option>,
@@ -131,24 +149,6 @@ input.</para>
</varlistentry>
<varlistentry><term><option>--json</option></term>
<listitem><para>When used with <option>--eval</option>, print the resulting
value as an JSON representation of the abstract syntax tree rather
than as an ATerm.</para></listitem>
</varlistentry>
<varlistentry><term><option>--xml</option></term>
<listitem><para>When used with <option>--eval</option>, print the resulting
value as an XML representation of the abstract syntax tree rather than as
an ATerm. The schema is the same as that used by the <link
linkend="builtin-toXML"><function>toXML</function> built-in</link>.
</para></listitem>
</varlistentry>
<varlistentry><term><option>--read-write-mode</option></term>
<listitem><para>When used with <option>--eval</option>, perform

View File

@@ -19,16 +19,14 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-prefetch-url</command>
<arg><option>--version</option></arg>
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
<arg><option>--print-path</option></arg>
<arg><option>--unpack</option></arg>
<arg><option>--name</option> <replaceable>name</replaceable></arg>
<arg choice='plain'><replaceable>url</replaceable></arg>
<arg><replaceable>hash</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>The command <command>nix-prefetch-url</command> downloads the
@@ -93,7 +91,7 @@ downloaded file in the Nix store is also printed.</para>
</varlistentry>
<varlistentry><term><option>--name</option> <replaceable>name</replaceable></term>
<varlistentry><term><option>--name</option></term>
<listitem><para>Override the name of the file in the Nix store. By
default, this is

View File

@@ -33,13 +33,13 @@
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
<arg><option>--pure</option></arg>
<group choice='req'>
<arg choice='plain'>
<group choice='req'>
<group choice='plain'>
<group>
<arg choice='plain'><option>--packages</option></arg>
<arg choice='plain'><option>-p</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>packages</replaceable></arg>
</arg>
<replaceable>packages</replaceable>
</group>
<arg><replaceable>path</replaceable></arg>
</group>
</cmdsynopsis>
@@ -144,7 +144,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</varlistentry>
<varlistentry><term><option>--packages</option> / <option>-p</option> <replaceable>packages</replaceable></term>
<varlistentry><term><option>--packages</option> / <option>-p</option></term>
<listitem><para>Set up an environment in which the specified
packages are present. The command line arguments are interpreted

View File

@@ -234,7 +234,7 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
<para>To test whether a previously-built derivation is deterministic:
<screen>
$ nix-build '&lt;nixpkgs>' -A hello --check -K
$ nix-build -r '&lt;nixpkgs>' -A hello --check -K
</screen>
</para>
@@ -397,9 +397,9 @@ options control what gets deleted and in what order:
</para>
<para>The behaviour of the collector is also influenced by the <link
linkend="conf-keep-outputs"><literal>keep-outputs</literal></link>
linkend="conf-gc-keep-outputs"><literal>gc-keep-outputs</literal></link>
and <link
linkend="conf-keep-derivations"><literal>keep-derivations</literal></link>
linkend="conf-gc-keep-derivations"><literal>gc-keep-derivations</literal></link>
variables in the Nix configuration file.</para>
<para>With <option>--delete</option>, the collector prints the total
@@ -1236,7 +1236,12 @@ the store path is used.</para>
<filename>/nix/var/log/nix/drvs</filename>. However, there is no
guarantee that a build log is available for any particular store path.
For instance, if the path was downloaded as a pre-built binary through
a substitute, then the log is unavailable.</para>
a substitute, then the log is unavailable. If the log is not available
locally, then <command>nix-store</command> will try to download the
log from the servers specified in the Nix option
<option>log-servers</option>. For example, if its set to
<literal>http://hydra.nixos.org/log</literal>, then Nix will check
<literal>http://hydra.nixos.org/log/<replaceable>base-name</replaceable></literal>.</para>
</refsection>

View File

@@ -2,18 +2,10 @@
<arg><option>--help</option></arg>
<arg><option>--version</option></arg>
<arg rep='repeat'>
<group choice='req'>
<arg choice='plain'><option>--verbose</option></arg>
<arg choice='plain'><option>-v</option></arg>
</group>
</arg>
<arg>
<group choice='plain'>
<arg choice='plain'><option>--no-build-output</option></arg>
<arg choice='plain'><option>-Q</option></arg>
</group>
</arg>
<arg rep='repeat'><option>--verbose</option></arg>
<arg rep='repeat'><option>-v</option></arg>
<arg><option>--no-build-output</option></arg>
<arg><option>-Q</option></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--max-jobs</option></arg>
@@ -33,18 +25,10 @@
<option>--timeout</option>
<replaceable>number</replaceable>
</arg>
<arg>
<group choice='plain'>
<arg choice='plain'><option>--keep-going</option></arg>
<arg choice='plain'><option>-k</option></arg>
</group>
</arg>
<arg>
<group choice='plain'>
<arg choice='plain'><option>--keep-failed</option></arg>
<arg choice='plain'><option>-K</option></arg>
</group>
</arg>
<arg><option>--keep-going</option></arg>
<arg><option>-k</option></arg>
<arg><option>--keep-failed</option></arg>
<arg><option>-K</option></arg>
<arg><option>--fallback</option></arg>
<arg><option>--readonly-mode</option></arg>
<arg><option>--show-trace</option></arg>

View File

@@ -22,7 +22,8 @@
</varlistentry>
<varlistentry><term><option>--verbose</option> / <option>-v</option></term>
<varlistentry><term><option>--verbose</option></term>
<term><option>-v</option></term>
<listitem>
@@ -75,7 +76,8 @@
</varlistentry>
<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term>
<varlistentry><term><option>--no-build-output</option></term>
<term><option>-Q</option></term>
<listitem><para>By default, output written by builders to standard
output and standard error is echoed to the Nix command's standard
@@ -87,14 +89,13 @@
</varlistentry>
<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option> / <option>-j</option>
<replaceable>number</replaceable></term>
<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option></term>
<term><option>-j</option></term>
<listitem><para>Sets the maximum number of build jobs that Nix will
perform in parallel to the specified number. Specify
<literal>auto</literal> to use the number of CPUs in the system.
The default is specified by the <link
linkend='conf-max-jobs'><literal>max-jobs</literal></link>
perform in parallel to the specified number. The default is
specified by the <link
linkend='conf-build-max-jobs'><literal>build-max-jobs</literal></link>
configuration setting, which itself defaults to
<literal>1</literal>. A higher value is useful on SMP systems or to
exploit I/O latency.</para></listitem>
@@ -112,7 +113,7 @@
<literal>true</literal>, the builder passes the
<option>-j<replaceable>N</replaceable></option> flag to GNU Make.
It defaults to the value of the <link
linkend='conf-cores'><literal>cores</literal></link>
linkend='conf-build-cores'><literal>build-cores</literal></link>
configuration setting, if set, or <literal>1</literal> otherwise.
The value <literal>0</literal> means that the builder should use all
available CPU cores in the system.</para></listitem>
@@ -125,7 +126,7 @@
<listitem><para>Sets the maximum number of seconds that a builder
can go without producing any data on standard output or standard
error. The default is specified by the <link
linkend='conf-max-silent-time'><literal>max-silent-time</literal></link>
linkend='conf-build-max-silent-time'><literal>build-max-silent-time</literal></link>
configuration setting. <literal>0</literal> means no
time-out.</para></listitem>
@@ -135,13 +136,14 @@
<listitem><para>Sets the maximum number of seconds that a builder
can run. The default is specified by the <link
linkend='conf-timeout'><literal>timeout</literal></link>
linkend='conf-build-timeout'><literal>build-timeout</literal></link>
configuration setting. <literal>0</literal> means no
timeout.</para></listitem>
</varlistentry>
<varlistentry><term><option>--keep-going</option> / <option>-k</option></term>
<varlistentry><term><option>--keep-going</option></term>
<term><option>-k</option></term>
<listitem><para>Keep going in case of failed builds, to the
greatest extent possible. That is, if building an input of some
@@ -153,7 +155,8 @@
</varlistentry>
<varlistentry><term><option>--keep-failed</option> / <option>-K</option></term>
<varlistentry><term><option>--keep-failed</option></term>
<term><option>-K</option></term>
<listitem><para>Specifies that in case of a build failure, the
temporary directory (usually in <filename>/tmp</filename>) in which

View File

@@ -139,7 +139,7 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
<programlisting>
fetchurl {
url = http://ftp.gnu.org/pub/gnu/hello/hello-2.1.1.tar.gz;
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
md5 = "70c9ccf9fac07f762c24f2df2290784d";
}
</programlisting>
@@ -150,7 +150,7 @@ fetchurl {
<programlisting>
fetchurl {
url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
md5 = "70c9ccf9fac07f762c24f2df2290784d";
}
</programlisting>

View File

@@ -65,7 +65,7 @@ available as <function>builtins.derivation</function>.</para>
<replaceable>set</replaceable></term>
<listitem><para>Return the names of the attributes in the set
<replaceable>set</replaceable> in an alphabetically sorted list. For instance,
<replaceable>set</replaceable> in a sorted list. For instance,
<literal>builtins.attrNames { y = 1; x = "foo"; }</literal>
evaluates to <literal>[ "x" "y" ]</literal>.</para></listitem>
@@ -134,14 +134,6 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.concatStringsSep</function>
<replaceable>separator</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Concatenate a list of strings with a separator
between each element, e.g. <literal>concatStringsSep "/"
["usr" "local" "bin"] == "usr/local/bin"</literal></para></listitem>
</varlistentry>
<varlistentry
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
@@ -218,6 +210,36 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.match</function>
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
<listitem><para>Returns a list if
<replaceable>regex</replaceable> matches
<replaceable>str</replaceable> precisely, otherwise returns <literal>null</literal>.
Each item in the list is a regex group.
<programlisting>
builtins.match "ab" "abc"
</programlisting>
Evaluates to <literal>null</literal>.
<programlisting>
builtins.match "abc" "abc"
</programlisting>
Evaluates to <literal>[ ]</literal>.
<programlisting>
builtins.match "a(b)(c)" "abc"
</programlisting>
Evaluates to <literal>[ "b" "c" ]</literal>.
</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.elem</function>
<replaceable>x</replaceable> <replaceable>xs</replaceable></term>
@@ -267,24 +289,6 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
<programlisting>
with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {};
stdenv.mkDerivation { … }
</programlisting>
Note that when obtaining the hash with <varname>nix-prefetch-url
</varname> the option <varname>--unpack</varname> is required.
</para>
<para>This function can also verify the contents against a hash.
In that case, the function takes a set instead of a URL. The set
requires the attribute <varname>url</varname> and the attribute
<varname>sha256</varname>, e.g.
<programlisting>
with import (fetchTarball {
url = https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz;
sha256 = "1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2";
}) {};
stdenv.mkDerivation { … }
</programlisting>
@@ -412,7 +416,7 @@ builtins.fromJSON ''{"x": [1, 2, 3], "y": null}''
<listitem><para>Generate list of size
<replaceable>length</replaceable>, with each element
<replaceable>i</replaceable> equal to the value returned by
<replaceable>i></replaceable> equal to the value returned by
<replaceable>generator</replaceable> <literal>i</literal>. For
example,
@@ -608,16 +612,6 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isFloat</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a float, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isBool</function>
<replaceable>e</replaceable></term>
@@ -708,42 +702,6 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.match</function>
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
<listitem><para>Returns a list if the <link
xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
POSIX regular expression</link> <replaceable>regex</replaceable>
matches <replaceable>str</replaceable> precisely, otherwise returns
<literal>null</literal>. Each item in the list is a regex group.
<programlisting>
builtins.match "ab" "abc"
</programlisting>
Evaluates to <literal>null</literal>.
<programlisting>
builtins.match "abc" "abc"
</programlisting>
Evaluates to <literal>[ ]</literal>.
<programlisting>
builtins.match "a(b)(c)" "abc"
</programlisting>
Evaluates to <literal>[ "b" "c" ]</literal>.
<programlisting>
builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO "
</programlisting>
Evaluates to <literal>[ "foo" ]</literal>.
</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.mul</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -891,43 +849,6 @@ builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
</varlistentry>
<varlistentry><term><function>builtins.split</function>
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
<listitem><para>Returns a list composed of non matched strings interleaved
with the lists of the <link
xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
POSIX regular expression</link> <replaceable>regex</replaceable> matches
of <replaceable>str</replaceable>. Each item in the lists of matched
sequences is a regex group.
<programlisting>
builtins.split "(a)b" "abc"
</programlisting>
Evaluates to <literal>[ "" [ "a" ] "c" ]</literal>.
<programlisting>
builtins.split "([ac])" "abc"
</programlisting>
Evaluates to <literal>[ "" [ "a" ] "b" [ "c" ] "" ]</literal>.
<programlisting>
builtins.split "(a)|(c)" "abc"
</programlisting>
Evaluates to <literal>[ "" [ "a" null ] "b" [ null "c" ] "" ]</literal>.
<programlisting>
builtins.split "([[:upper:]]+)" " FOO "
</programlisting>
Evaluates to <literal>[ " " [ "FOO" ] " " ]</literal>.
</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.stringLength</function>
<replaceable>e</replaceable></term>
@@ -1028,8 +949,8 @@ stdenv.mkDerivation {
";
src = fetchurl {
url = http://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl;
}</programlisting>
@@ -1102,17 +1023,10 @@ in foo</programlisting>
<listitem><para>Convert the expression
<replaceable>e</replaceable> to a string.
<replaceable>e</replaceable> can be:</para>
<itemizedlist>
<listitem><para>A string (in which case the string is returned unmodified).</para></listitem>
<listitem><para>A path (e.g., <literal>toString /foo/bar</literal> yields <literal>"/foo/bar"</literal>.</para></listitem>
<listitem><para>A set containing <literal>{ __toString = self: ...; }</literal>.</para></listitem>
<listitem><para>An integer.</para></listitem>
<listitem><para>A list, in which case the string representations of its elements are joined with spaces.</para></listitem>
<listitem><para>A Boolean (<literal>false</literal> yields <literal>""</literal>, <literal>true</literal> yields <literal>"1"</literal>.</para></listitem>
<listitem><para><literal>null</literal>, which yields the empty string.</para></listitem>
</itemizedlist>
</listitem>
<replaceable>e</replaceable> can be a string (in which case
<function>toString</function> is a no-op), a path (e.g.,
<literal>toString /foo/bar</literal> yields
<literal>"/foo/bar"</literal> or a set containing <literal>{ __toString = self: ...; }</literal>.</para></listitem>
</varlistentry>
@@ -1242,19 +1156,6 @@ stdenv.mkDerivation (rec {
</varlistentry>
<varlistentry><term><function>builtins.tryEval</function>
<replaceable>e</replaceable></term>
<listitem><para>Try to evaluate <replaceable>e</replaceable>.
Return a set containing the attributes <literal>success</literal>
(<literal>true</literal> if <replaceable>e</replaceable> evaluated
successfully, <literal>false</literal> if an error was thrown) and
<literal>value</literal>, equalling <replaceable>e</replaceable>
if successful and <literal>false</literal> otherwise.
</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.typeOf</function>
<replaceable>e</replaceable></term>
@@ -1263,9 +1164,8 @@ stdenv.mkDerivation (rec {
<replaceable>e</replaceable>, namely <literal>"int"</literal>,
<literal>"bool"</literal>, <literal>"string"</literal>,
<literal>"path"</literal>, <literal>"null"</literal>,
<literal>"set"</literal>, <literal>"list"</literal>,
<literal>"lambda"</literal> or
<literal>"float"</literal>.</para></listitem>
<literal>"set"</literal>, <literal>"list"</literal> or
<literal>"lambda"</literal>.</para></listitem>
</varlistentry>

View File

@@ -100,7 +100,7 @@ outputs = [ "lib" "headers" "doc" ];
<programlisting>
buildInputs = [ pkg.lib pkg.headers ];
</programlisting>
The first element of <varname>outputs</varname> determines the
The first element of <varname>output</varname> determines the
<emphasis>default output</emphasis>. Thus, you could also write
<programlisting>
buildInputs = [ pkg pkg.headers ];

View File

@@ -16,7 +16,7 @@ stdenv.mkDerivation { <co xml:id='ex-hello-nix-co-2' />
builder = ./builder.sh; <co xml:id='ex-hello-nix-co-4' />
src = fetchurl { <co xml:id='ex-hello-nix-co-5' />
url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl; <co xml:id='ex-hello-nix-co-6' />
}</programlisting>
@@ -108,7 +108,7 @@ the single Nix expression in that directory
<para>The builder has to know what the sources of the package
are. Here, the attribute <varname>src</varname> is bound to the
result of a call to the <command>fetchurl</command> function.
Given a URL and a SHA-256 hash of the expected contents of the file
Given a URL and an MD5 hash of the expected contents of the file
at that URL, this function builds a derivation that downloads the
file and checks its hash. So the sources are a dependency that
like all other dependencies is built before Hello itself is
@@ -145,4 +145,4 @@ perl = perl;</programlisting>
</para>
</section>
</section>

View File

@@ -333,20 +333,7 @@ with (import ./definitions.nix); ...</programlisting>
makes all attributes defined in the file
<filename>definitions.nix</filename> available as if they were defined
locally in a <literal>let</literal>-expression.</para>
<para>The bindings introduced by <literal>with</literal> do not shadow bindings
introduced by other means, e.g.
<programlisting>
let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...</programlisting>
establishes the same scope as
<programlisting>
let a = 1; in let a = 2; in let a = 3; in let a = 4; in ...</programlisting>
</para>
locally in a <literal>rec</literal>-expression.</para>
</simplesect>

View File

@@ -40,11 +40,6 @@ weakest binding).</para>
<entry>Call function <replaceable>e1</replaceable> with
argument <replaceable>e2</replaceable>.</entry>
</row>
<row>
<entry><literal>-</literal> <replaceable>e</replaceable></entry>
<entry>none</entry>
<entry>Arithmetic negation.</entry>
</row>
<row>
<entry><replaceable>e</replaceable> <literal>?</literal>
<replaceable>attrpath</replaceable></entry>
@@ -60,24 +55,13 @@ weakest binding).</para>
<entry>List concatenation.</entry>
</row>
<row>
<entry>
<replaceable>e1</replaceable> <literal>*</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>/</literal> <replaceable>e2</replaceable>
</entry>
<entry><replaceable>e1</replaceable> <literal>+</literal> <replaceable>e2</replaceable></entry>
<entry>left</entry>
<entry>Arithmetic multiplication and division.</entry>
</row>
<row>
<entry>
<replaceable>e1</replaceable> <literal>+</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>-</literal> <replaceable>e2</replaceable>
</entry>
<entry>left</entry>
<entry>Arithmetic addition and subtraction. String or path concatenation (only by <literal>+</literal>).</entry>
<entry>String or path concatenation.</entry>
</row>
<row>
<entry><literal>!</literal> <replaceable>e</replaceable></entry>
<entry>none</entry>
<entry>left</entry>
<entry>Boolean negation.</entry>
</row>
<row>
@@ -91,22 +75,16 @@ weakest binding).</para>
attributes).</entry>
</row>
<row>
<entry>
<replaceable>e1</replaceable> <literal>&lt;</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>&gt;</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>&lt;=</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>&gt;=</literal> <replaceable>e2</replaceable>
</entry>
<entry><replaceable>e1</replaceable> <literal>==</literal>
<replaceable>e2</replaceable></entry>
<entry>none</entry>
<entry>Arithmetic comparison.</entry>
<entry>Equality.</entry>
</row>
<row>
<entry>
<replaceable>e1</replaceable> <literal>==</literal> <replaceable>e2</replaceable>,
<replaceable>e1</replaceable> <literal>!=</literal> <replaceable>e2</replaceable>
</entry>
<entry><replaceable>e1</replaceable> <literal>!=</literal>
<replaceable>e2</replaceable></entry>
<entry>none</entry>
<entry>Equality and inequality.</entry>
<entry>Inequality.</entry>
</row>
<row>
<entry><replaceable>e1</replaceable> <literal>&amp;&amp;</literal>

View File

@@ -98,17 +98,13 @@ configureFlags = "
<para>Since <literal>${</literal> and <literal>''</literal> have
special meaning in indented strings, you need a way to quote them.
<literal>$</literal> can be escaped by prefixing it with
<literal>${</literal> can be escaped by prefixing it with
<literal>''</literal> (that is, two single quotes), i.e.,
<literal>''$</literal>. <literal>''</literal> can be escaped by
<literal>''${</literal>. <literal>''</literal> can be escaped by
prefixing it with <literal>'</literal>, i.e.,
<literal>'''</literal>. <literal>$</literal> removes any special meaning
from the following <literal>$</literal>. Linefeed, carriage-return and tab
characters can be written as <literal>''\n</literal>,
<literal>''\r</literal>, <literal>''\t</literal>, and <literal>''\</literal>
escapes any other character.
</para>
<literal>'''</literal>. Finally, linefeed, carriage-return and
tab characters can be written as <literal>''\n</literal>,
<literal>''\r</literal>, <literal>''\t</literal>.</para>
<para>Indented strings are primarily useful in that they allow
multi-line string literals to follow the indentation of the

View File

@@ -54,7 +54,7 @@
<glossdef><para>A substitute is a command invocation stored in the
Nix database that describes how to build a store object, bypassing
the normal build mechanism (i.e., derivations). Typically, the
normal the build mechanism (i.e., derivations). Typically, the
substitute builds the store object by downloading a pre-built
version of the store object from some server.</para></glossdef>

View File

@@ -6,7 +6,7 @@
<title>Installing a Binary Distribution</title>
<para>If you are using Linux or macOS, the easiest way to install
<para>If you are using Linux or Mac OS X, the easiest way to install
Nix is to run the following command:
<screen>
@@ -39,7 +39,7 @@ behaviour.
<!--
<para>You can also manually download and install a binary package.
Binary packages of the latest stable release are available for Fedora,
Debian, Ubuntu, macOS and various other systems from the <link
Debian, Ubuntu, Mac OS X and various other systems from the <link
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
You can also get builds of the latest development release from our
<link

View File

@@ -52,7 +52,7 @@ This creates 10 build users. There can never be more concurrent builds
than the number of build users, so you may want to increase this if
you expect to do many builds at the same time.</para>
<para>On macOS, you can create the required group and users by
<para>On Mac OS X, you can create the required group and users by
running the following script:
<programlisting>

View File

@@ -10,14 +10,16 @@
<listitem><para>GNU Make.</para></listitem>
<listitem><para>A version of GCC or Clang that supports C++14.</para></listitem>
<listitem><para>A version of GCC or Clang that supports C++11.</para></listitem>
<listitem><para>Perl 5.8 or higher.</para></listitem>
<listitem><para><command>pkg-config</command> to locate
dependencies. If your distribution does not provide it, you can get
it from <link
xlink:href="http://www.freedesktop.org/wiki/Software/pkg-config"
/>.</para></listitem>
<listitem><para>The OpenSSL library to calculate cryptographic hashes.
If your distribution does not provide it, you can get it from <link
xlink:href="https://www.openssl.org"/>.</para></listitem>
@@ -32,6 +34,11 @@
or higher. If your distribution does not provide it, please install
it from <link xlink:href="http://www.sqlite.org/" />.</para></listitem>
<listitem><para>The Perl DBI and DBD::SQLite libraries, which are
available from <link
xlink:href="http://search.cpan.org/">CPAN</link> if your
distribution does not provide them.</para></listitem>
<listitem><para>The <link
xlink:href="http://www.hboehm.info/gc/">Boehm
garbage collector</link> to reduce the evaluators memory

View File

@@ -12,7 +12,7 @@
<listitem><para>Linux (i686, x86_64).</para></listitem>
<listitem><para>macOS (x86_64).</para></listitem>
<listitem><para>Mac OS X (x86_64).</para></listitem>
<!--
<listitem><para>FreeBSD (only tested on Intel).</para></listitem>

View File

@@ -93,7 +93,7 @@ time window in which the package has some files from the old version
and some files from the new version — which would be bad because a
program might well crash if its started during that period.</para>
<para>And since packages arent overwritten, the old versions are still
<para>And since package arent overwritten, the old versions are still
there after an upgrade. This means that you can <emphasis>roll
back</emphasis> to the old version:</para>
@@ -236,7 +236,7 @@ href="[%root%]hydra">a continuous build system</a>.</para>
<simplesect><title>Portability</title>
<para>Nix runs on Linux and macOS.</para>
<para>Nix runs on Linux and Mac OS X.</para>
</simplesect>
@@ -261,12 +261,6 @@ xlink:href="http://nixos.org/">NixOS homepage</link>.</para>
xlink:href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">GNU
LGPLv2.1 or (at your option) any later version</link>.</para>
<para>Nix uses the <link
xlink:href="https://github.com/arangodb/linenoise-ng">linenoise-ng
library</link>, which has the following license:</para>
<programlisting><xi:include href="../../../src/linenoise/LICENSE" parse="text" /></programlisting>
</simplesect>

View File

@@ -40,7 +40,7 @@ libxslt-1.1.28
<step><para>Install some packages from the channel:
<screen>
$ nix-env -i hello</screen>
$ nix-env -i hello <replaceable>...</replaceable> </screen>
This should download pre-built packages; it should not build them
locally (if it does, something went wrong).</para></step>

View File

@@ -13,7 +13,6 @@
-->
<xi:include href="rl-1.12.xml" />
<xi:include href="rl-1.11.10.xml" />
<xi:include href="rl-1.11.xml" />
<xi:include href="rl-1.10.xml" />
<xi:include href="rl-1.9.xml" />

View File

@@ -1,31 +0,0 @@
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="ssec-relnotes-1.11.10">
<title>Release 1.11.10 (2017-06-12)</title>
<para>This release fixes a security bug in Nixs “build user” build
isolation mechanism. Previously, Nix builders had the ability to
create setuid binaries owned by a <literal>nixbld</literal>
user. Such a binary could then be used by an attacker to assume a
<literal>nixbld</literal> identity and interfere with subsequent
builds running under the same UID.</para>
<para>To prevent this issue, Nix now disallows builders to create
setuid and setgid binaries. On Linux, this is done using a seccomp BPF
filter. Note that this imposes a small performance penalty (e.g. 1%
when building GNU Hello). Using seccomp, we now also prevent the
creation of extended attributes and POSIX ACLs since these cannot be
represented in the NAR format and (in the case of POSIX ACLs) allow
bypassing regular Nix store permissions. On macOS, the restriction is
implemented using the existing sandbox mechanism, which now uses a
minimal “allow all except the creation of setuid/setgid binaries”
profile when regular sandboxing is disabled. On other platforms, the
“build user” mechanism is now disabled.</para>
<para>Thanks go to Linus Heckemann for discovering and reporting this
bug.</para>
</section>

View File

@@ -121,6 +121,13 @@ $ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zl
also improves performance.</para>
</listitem>
<listitem>
<para>The Nix language now supports floating point numbers. They are
based on regular C++ <literal>float</literal> and compatible with
existing integers and number-related operations. Export and import to and
from JSON and XML works, too.
</para>
</listitem>
<listitem>
<para>All "chroot"-containing strings got renamed to "sandbox".
In particular, some Nix options got renamed, but the old names

View File

@@ -10,97 +10,6 @@
<itemizedlist>
<listitem>
<para>Start of new <command>nix</command> command line
interface. This is a work in progress and the interface is subject
to change.</para>
<itemizedlist>
<listitem><para>Self-documenting: <option>--help</option> shows
all available command-line arguments.</para></listitem>
<listitem><para><option>--help-config</option> shows all
configuration options.</para></listitem>
<listitem><para><command>nix build</command>: Replacement for
<command>nix-build</command>.</para></listitem>
<listitem><para><command>nix ls-store</command> and <command>nix
ls-nar</command> allow listing the contents of a store path or
NAR file.</para></listitem>
<listitem><para><command>nix cat-store</command> and
<command>nix cat-nar</command> allow extracting a file from a
store path or NAR file.</para></listitem>
<listitem><para><command>nix verify</command> checks whether a
store path is unmodified and/or is trusted.</para></listitem>
<listitem><para><command>nix copy-sigs</command> copies
signatures from one store to another.</para></listitem>
<listitem><para><command>nix sign-paths</command> signs store
paths.</para></listitem>
<listitem><para><command>nix copy</command> copies paths between
arbitrary Nix stores, generalising
<command>nix-copy-closure</command> and
<command>nix-push</command>.</para></listitem>
<listitem><para><command>nix path-info</command> shows
information about store paths.</para></listitem>
<listitem><para><command>nix run</command> starts a shell in
which the specified packages are available.</para></listitem>
<listitem><para><command>nix log</command> shows the build log
of a package or path. If the build log is not available locally,
it will try to obtain it from a binary cache.</para></listitem>
<listitem><para><command>nix eval</command> replaces
<command>nix-instantiate --eval</command>.</para></listitem>
<listitem><para><command>nix dump-path</command> to get a NAR
from a store path.</para></listitem>
<listitem><para><command>nix edit</command> opens the source
code of a package in an editor.</para></listitem>
<listitem><para><command>nix search</command> replaces
<command>nix-env -qa</command>. It searches the available
packages for occurences of a search string in the attribute
name, package name or description. It caches available packages
to speed up searches.</para></listitem>
<listitem><para><command>nix why-depends</command> (d41c5eb13f4f3a37d80dbc6d3888644170c3b44a).</para></listitem>
<listitem><para><command>nix show-derivation</command> (e8d6ee7c1b90a2fe6d824f1a875acc56799ae6e2).</para></listitem>
<listitem><para><command>nix add-to-store</command> (970366266b8df712f5f9cedb45af183ef5a8357f).</para></listitem>
<listitem><para>Progress indicator.</para></listitem>
<listitem><para>All options are available as flags now
(b8283773bd64d7da6859ed520ee19867742a03ba).</para></listitem>
</itemizedlist>
</listitem>
<listitem>
<para>The external program <command>nix-repl</command> has been
integrated into Nix as <command>nix repl</command>.</para>
</listitem>
<listitem>
<para>New build mode <command>nix-build --hash</command> that
builds a derivation, computes the hash of the output, and moves
the output to the store path corresponding to what a fixed-output
derivation with that hash would produce.
(Add docs and examples; see d367b8e7875161e655deaa96bf8a5dd0bcf8229e)</para>
</listitem>
<listitem>
<para>It is no longer necessary to set the
<envar>NIX_REMOTE</envar> environment variable if you need to use
@@ -108,317 +17,6 @@
have write access to the Nix database.</para>
</listitem>
<listitem>
<para>The Nix language now supports floating point numbers. They are
based on regular C++ <literal>float</literal> and compatible with
existing integers and number-related operations. Export and import to and
from JSON and XML works, too.</para>
</listitem>
<listitem>
<para><command>nix-shell</command> now sets the
<varname>IN_NIX_SHELL</varname> environment variable during
evaluation and in the shell itself. This can be used to perform
different actions depending on whether youre in a Nix shell or in
a regular build. Nixpkgs provides
<varname>lib.inNixShell</varname> to check this variable during
evaluation. (bb36a1a3cf3fbe6bc9d0afcc5fa0f928bed03170)</para>
</listitem>
<listitem>
<para>Internal: all <classname>Store</classname> classes are now
thread-safe. <classname>RemoteStore</classname> supports multiple
concurrent connections to the daemon. This is primarily useful in
multi-threaded programs such as
<command>hydra-queue-runner</command>.</para>
</listitem>
<listitem>
<para>The dependency on Perl has been removed. As a result, some
(obsolete) programs have been removed: <command>nix-push</command>
(replaced by <command>nix copy</command>),
<command>nix-pull</command> (obsoleted by binary caches),
<command>nix-generate-patches</command>,
<command>bsdiff</command>, <command>bspatch</command>.</para>
</listitem>
<listitem>
<para>Improved store abstraction. Substituters
eliminated. BinaryCacheStore, LocalBinaryCacheStore,
HttpBinaryCacheStore, S3BinaryCacheStore (compile-time
optional), SSHStore. Add docs + examples?
</para>
</listitem>
<listitem>
<para>Nix now stores signatures for local store
paths. Locally-built paths are now signed automatically using the
secret keys specified by the <option>secret-key-files</option>
store option.</para>
<para>In addition, store paths that have been built locally are
marked as “ultimately trusted”, and content-addressable store
paths carry a “content-addressability assertion” that allow them
to be trusted without any signatures.</para>
</listitem>
<listitem>
<para><envar>NIX_PATH</envar> is now lazy, so URIs in the path are
only downloaded if they are needed for evaluation.</para>
</listitem>
<listitem>
<para>You can now use
<uri>channel:<replaceable>channel-name</replaceable></uri> as a
short-hand for
<uri>https://nixos.org/channels/<replaceable>channel-name</replaceable>/nixexprs.tar.xz</uri>. For
example, <literal>nix-build channel:nixos-15.09 -A hello</literal>
will build the GNU Hello package from the
<literal>nixos-15.09</literal> channel.</para>
</listitem>
<listitem>
<para>When <option>--no-build-output</option> is given, the last
10 lines of the build log will be shown if a build
fails.</para>
</listitem>
<listitem>
<para><function>builtins.fetchGit</function>.
(38539b943a060d9cdfc24d6e5d997c0885b8aa2f)</para>
</listitem>
<listitem>
<para><literal>&lt;nix/fetchurl.nix&gt;</literal> now uses the
content-addressable tarball cache at
<uri>http://tarballs.nixos.org/</uri>, just like
<function>fetchurl</function> in
Nixpkgs. (f2682e6e18a76ecbfb8a12c17e3a0ca15c084197)</para>
</listitem>
<listitem>
<para>Chroot Nix stores: allow the “physical” location of the Nix
store (e.g. <filename>/home/alice/nix/store</filename>) to differ
from its “logical” location (typically
<filename>/nix/store</filename>). This allows non-root users to
use Nix while still getting the benefits from prebuilt binaries
from
<uri>cache.nixos.org</uri>. (4494000e04122f24558e1436e66d20d89028b4bd,
3eb621750848e0e6b30e5a79f76afbb096bb6c8a)</para>
</listitem>
<listitem>
<para>On Linux, builds are now executed in a user
namespace with uid 1000 and gid 100.</para>
</listitem>
<listitem>
<para><function>builtins.fetchurl</function> and
<function>builtins.fetchTarball</function> now support
<varname>sha256</varname> and <varname>name</varname>
attributes.</para>
</listitem>
<listitem>
<para><literal>HttpBinaryCacheStore</literal> (the replacement of
<command>download-from-binary-cache</command>) now retries
automatically on certain HTTP error codes.</para>
</listitem>
<listitem>
<para>Derivation attributes can now reference the outputs of the
derivation using the <function>placeholder</function> builtin
function. For example, the attribute
<programlisting>
configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"}";
</programlisting>
will cause the <envar>configureFlags</envar> environment variable
to contain the actual store paths corresponding to the
<literal>out</literal> and <literal>dev</literal> outputs. TODO:
add docs.</para>
</listitem>
<listitem>
<para>Support for HTTP/2. This makes binary cache lookups much
more efficient. (90ad02bf626b885a5dd8967894e2eafc953bdf92)</para>
</listitem>
<listitem>
<para>The <option>build-sandbox-paths</option> configuration
option can now specify optional paths by appending a
<literal>?</literal>, e.g. <literal>/dev/nvidiactl?</literal> will
bind-mount <varname>/dev/nvidiactl</varname> only if it
exists.</para>
</listitem>
<listitem>
<para>More support for testing build reproducibility: when
<option>enforce-determinism</option> is set to
<literal>false</literal>, its no longer a fatal error build
rounds produce different output
(8bdf83f936adae6f2c907a6d2541e80d4120f051); add a hook to run
diffoscope when build rounds produce different output
(9a313469a4bdea2d1e8df24d16289dc2a172a169w).</para>
</listitem>
<listitem>
<para>Kill builds as soon as stdout/stderr is closed. This fixes a
bug that allowed builds to hang Nix indefinitely (regardless of
timeouts). (21948deed99a3295e4d5666e027a6ca42dc00b40)</para>
</listitem>
<listitem>
<para>Add support for passing structured data to builders. TODO:
document. (6de33a9c675b187437a2e1abbcb290981a89ecb1)</para>
</listitem>
<listitem>
<para><varname>exportReferencesGraph</varname>: Export more
complete info in JSON
format. (c2b0d8749f7e77afc1c4b3e8dd36b7ee9720af4a)</para>
</listitem>
<listitem>
<para>Support for
netrc. (e6e74f987f0fa284d220432d426eb965269a97d6,
302386f775eea309679654e5ea7c972fb6e7b9af)</para>
</listitem>
<listitem>
<para>Support <uri>s3://</uri> URIs in all places where Nix allows
URIs. (9ff9c3f2f80ba4108e9c945bbfda2c64735f987b)</para>
</listitem>
<listitem>
<para>The <option>build-max-jobs</option> option can be set to
<literal>auto</literal> to use the number of CPUs in the
system. (7251d048fa812d2551b7003bc9f13a8f5d4c95a5)</para>
</listitem>
<listitem>
<para>Add support for Brotli compression.
<uri>cache.nixos.org</uri> compresses build logs using
Brotli.</para>
</listitem>
<listitem>
<para>Substitutions from binary caches now require signatures by
default. This was already the case on
NixOS. (ecbc3fedd3d5bdc5a0e1a0a51b29062f2874ac8b)</para>
</listitem>
<listitem>
<para><command>nix-env</command> now ignores packages with bad
derivation names (in particular those starting with a digit or
containing a
dot). (b0cb11722626e906a73f10dd9a0c9eea29faf43a)</para>
</listitem>
<listitem>
<para>Renamed various configuration options. (TODO: in progress)</para>
</listitem>
<listitem>
<para>Remote machines can now be specified on the command
line. TODO:
document. (1a68710d4dff609bbaf61db3e17a2573f0aadf17)</para>
</listitem>
<listitem>
<para>In Linux sandbox builds, we now use
<filename>/build</filename> instead of <filename>/tmp</filename>
as the temporary build directory. This fixes potential security
problems when a build accidentally stores its
<envar>TMPDIR</envar> in some critical place, such as an
RPATH. (eba840c8a13b465ace90172ff76a0db2899ab11b)</para>
</listitem>
<listitem>
<para>In Linux sandbox builds, we now provide a default
<filename>/bin/sh</filename> (namely <filename>ash</filename> from
BusyBox). (a2d92bb20e82a0957067ede60e91fab256948b41)</para>
</listitem>
<listitem>
<para>Make all configuration options available as command line
flags (b8283773bd64d7da6859ed520ee19867742a03ba).</para>
</listitem>
<listitem>
<para>Support base-64
hashes. (c0015e87af70f539f24d2aa2bc224a9d8b84276b)</para>
</listitem>
<listitem>
<para><command>nix-shell</command> now uses
<varname>bashInteractive</varname> from Nixpkgs, rather than the
<command>bash</command> command that happens to be in the callers
<envar>PATH</envar>. This is especially important on macOS where
the <command>bash</command> provided by the system is seriously
outdated and cannot execute <literal>stdenv</literal>s setup
script.</para>
</listitem>
<listitem>
<para>New builtin functions: <function>builtins.split</function>
(b8867a0239b1930a16f9ef3f7f3e864b01416dff),
<function>builtins.partition</function>.</para>
</listitem>
<listitem>
<para>Automatic garbage collection.</para>
</listitem>
<listitem>
<para><command>nix-store -q --roots</command> and
<command>nix-store --gc --print-roots</command> now show temporary
and in-memory roots.</para>
</listitem>
<listitem>
<para>Builders can now communicate what build phase they are in by
writing messages to the file descriptor specified in
<envar>NIX_LOG_FD</envar>. (88e6bb76de5564b3217be9688677d1c89101b2a3)
</para>
</listitem>
</itemizedlist>
<para>Some features were removed:</para>
<itemizedlist>
<listitem>
<para>“Nested” log output. As a result,
<command>nix-log2xml</command> was also removed.</para>
</listitem>
<listitem>
<para>OpenSSL-based signing. (f435f8247553656774dd1b2c88e9de5d59cab203)</para>
</listitem>
<listitem>
<para>Caching of failed
builds. (8cffec84859cec8b610a2a22ab0c4d462a9351ff)</para>
</listitem>
<listitem>
<para><filename>nix-mode.el</filename> has been removed from
Nix. It is now a separate repository in
<uri>https://github.com/NixOS/nix-mode</uri> and can be installed
through the MELPA package repository.</para>
</listitem>
<listitem>
<para>In restricted evaluation mode
(<option>--restrict-eval</option>), builtin functions that
download from the network (such as <function>fetchGit</function>)
are permitted to fetch underneath the list of URI prefixes
specified in the option <option>allowed-uris</option>.</para>
</listitem>
</itemizedlist>
<para>This release has contributions from TBD.</para>

View File

@@ -40,7 +40,7 @@ $ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
<para>Binary caches are created using <command>nix-push</command>.
For details on the operation and format of binary caches, see the
<command>nix-push</command> manpage. More details are provided in
<link xlink:href="https://nixos.org/nix-dev/2012-September/009826.html">this
<link xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2012-September/009826.html">this
nix-dev posting</link>.</para>
</listitem>

View File

@@ -83,8 +83,8 @@ $ nix-store -l $(which xterm)
caches).</para></listitem>
<listitem><para>The configuration option
<option>build-cores</option> now defaults to the number of available
CPU cores.</para></listitem>
<option>build-max-jobs</option> now defaults to the number of
available CPU cores.</para></listitem>
<listitem><para>Build users are now used by default when Nix is
invoked as root. This prevents builds from accidentally running as

View File

@@ -3,13 +3,14 @@ ifeq ($(MAKECMDGOALS), dist)
dist-files += $(shell git --git-dir=.git ls-files || find * -type f)
endif
dist-files += configure config.h.in nix.spec perl/configure
dist-files += configure config.h.in nix.spec
clean-files += Makefile.config
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr -D_FILE_OFFSET_BITS=64
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr \
-Wno-unneeded-internal-declaration
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh), \
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh) src/nix-store/serve-protocol.hh, \
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
$(foreach i, $(call rwildcard, src/boost, *.hpp), $(eval $(call install-file-in, $(i), $(includedir)/nix/$(patsubst src/%/,%,$(dir $(i))), 0644)))

View File

@@ -83,7 +83,6 @@ downloadFile("tarball", "3"); # .tar.bz2
my ($tarball, $tarballHash) = downloadFile("tarball", "4"); # .tar.xz
my ($tarball_i686_linux, $tarball_i686_linux_hash) = downloadFile("binaryTarball.i686-linux", "1");
my ($tarball_x86_64_linux, $tarball_x86_64_linux_hash) = downloadFile("binaryTarball.x86_64-linux", "1");
my ($tarball_aarch64_linux, $tarball_aarch64_linux_hash) = downloadFile("binaryTarball.aarch64-linux", "1");
my ($tarball_x86_64_darwin, $tarball_x86_64_darwin_hash) = downloadFile("binaryTarball.x86_64-darwin", "1");
# Update Nixpkgs in a very hacky way.
@@ -112,7 +111,6 @@ write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
"{\n" .
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
"}\n");
@@ -146,7 +144,6 @@ write_file("$siteDir/nix-release.tt",
"latestNixVersion = \"$version\"\n" .
"nix_hash_i686_linux = \"$tarball_i686_linux_hash\"\n" .
"nix_hash_x86_64_linux = \"$tarball_x86_64_linux_hash\"\n" .
"nix_hash_aarch64_linux = \"$tarball_aarch64_linux_hash\"\n" .
"nix_hash_x86_64_darwin = \"$tarball_x86_64_darwin_hash\"\n" .
"-%]\n");

View File

@@ -1,26 +1,23 @@
FROM alpine
# Enable HTTPS support in wget.
RUN apk add --update openssl
# Download Nix and install it into the system.
RUN wget -O- https://nixos.org/releases/nix/nix-1.11.14/nix-1.11.14-x86_64-linux.tar.bz2 | bzcat - | tar xf - \
&& addgroup -g 30000 -S nixbld \
&& for i in $(seq 1 30); do adduser -S -D -h /var/empty -g "Nix build user $i" -u $((30000 + i)) -G nixbld nixbld$i ; done \
&& mkdir -m 0755 /nix && USER=root sh nix-*-x86_64-linux/install \
&& ln -s /nix/var/nix/profiles/default/etc/profile.d/nix.sh /etc/profile.d/ \
&& rm -r /nix-*-x86_64-linux \
&& rm -r /var/cache/apk/*
RUN wget -O- http://nixos.org/releases/nix/nix-1.11.2/nix-1.11.2-x86_64-linux.tar.bz2 | bzcat - | tar xf - \
&& echo "nixbld:x:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10,nixbld11,nixbld12,nixbld13,nixbld14,nixbld15,nixbld16,nixbld17,nixbld18,nixbld19,nixbld20,nixbld21,nixbld22,nixbld23,nixbld24,nixbld25,nixbld26,nixbld27,nixbld28,nixbld29,nixbld30" >> /etc/group \
&& for i in $(seq 1 30); do echo "nixbld$i:x:$((30000 + $i)):30000:::" >> /etc/passwd; done \
&& mkdir -m 0755 /nix && USER=root sh nix-*-x86_64-linux/install \
&& echo ". /root/.nix-profile/etc/profile.d/nix.sh" >> /etc/profile \
&& rm -r /nix-*-x86_64-linux \
&& apk --update add bash tar \
&& rm -rf /var/cache/apk/*
ONBUILD ENV \
ENV=/etc/profile \
PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
NIX_SSL_CERT_FILE=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt
ENV \
ENV=/etc/profile \
PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
NIX_PATH=/nix/var/nix/profiles/per-user/root/channels
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
NIX_SSL_CERT_FILE=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
NIX_PATH=/nix/var/nix/profiles/per-user/root/channels/

10
misc/emacs/README Normal file
View File

@@ -0,0 +1,10 @@
The Nix Emacs mode supports syntax highlighting, somewhat sensible
indenting, and refilling of comments.
To enable Nix mode in Emacs, add something like this to your ~/.emacs
file:
(load "/nix/share/emacs/site-lisp/nix-mode.el")
This automatically causes Nix mode to be activated for all files with
extension `.nix'.

1
misc/emacs/local.mk Normal file
View File

@@ -0,0 +1 @@
$(eval $(call install-data-in,$(d)/nix-mode.el,$(datadir)/emacs/site-lisp))

177
misc/emacs/nix-mode.el Normal file
View File

@@ -0,0 +1,177 @@
;;; nix-mode.el --- Major mode for editing Nix expressions
;; Author: Eelco Dolstra
;; URL: https://github.com/NixOS/nix/tree/master/misc/emacs
;; Version: 1.0
;;; Commentary:
;;; Code:
(defun nix-syntax-match-antiquote (limit)
(let ((pos (next-single-char-property-change (point) 'nix-syntax-antiquote
nil limit)))
(when (and pos (> pos (point)))
(goto-char pos)
(let ((char (char-after pos)))
(pcase char
(`?$
(forward-char 2))
(`?}
(forward-char 1)))
(set-match-data (list pos (point)))
t))))
(defconst nix-font-lock-keywords
'("\\_<if\\_>" "\\_<then\\_>" "\\_<else\\_>" "\\_<assert\\_>" "\\_<with\\_>"
"\\_<let\\_>" "\\_<in\\_>" "\\_<rec\\_>" "\\_<inherit\\_>" "\\_<or\\_>"
("\\_<true\\_>" . font-lock-builtin-face)
("\\_<false\\_>" . font-lock-builtin-face)
("\\_<null\\_>" . font-lock-builtin-face)
("\\_<import\\_>" . font-lock-builtin-face)
("\\_<derivation\\_>" . font-lock-builtin-face)
("\\_<baseNameOf\\_>" . font-lock-builtin-face)
("\\_<toString\\_>" . font-lock-builtin-face)
("\\_<isNull\\_>" . font-lock-builtin-face)
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
. font-lock-constant-face)
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
(1 font-lock-variable-name-face nil nil))
("<[a-zA-Z0-9._\\+-]+\\(/[a-zA-Z0-9._\\+-]+\\)*>"
. font-lock-constant-face)
("[a-zA-Z0-9._\\+-]*\\(/[a-zA-Z0-9._\\+-]+\\)+"
. font-lock-constant-face)
(nix-syntax-match-antiquote 0 font-lock-preprocessor-face t))
"Font lock keywords for nix.")
(defvar nix-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?/ ". 14" table)
(modify-syntax-entry ?* ". 23" table)
(modify-syntax-entry ?# "< b" table)
(modify-syntax-entry ?\n "> b" table)
table)
"Syntax table for Nix mode.")
(defun nix-syntax-propertize-escaped-antiquote ()
"Set syntax properies for escaped antiquote marks."
nil)
(defun nix-syntax-propertize-multiline-string ()
"Set syntax properies for multiline string delimiters."
(let* ((start (match-beginning 0))
(end (match-end 0))
(context (save-excursion (save-match-data (syntax-ppss start))))
(string-type (nth 3 context)))
(pcase string-type
(`t
;; inside a multiline string
;; ending multi-line string delimiter
(put-text-property (1- end) end
'syntax-table (string-to-syntax "|")))
(`nil
;; beginning multi-line string delimiter
(put-text-property start (1+ start)
'syntax-table (string-to-syntax "|"))))))
(defun nix-syntax-propertize-antiquote ()
"Set syntax properties for antiquote marks."
(let* ((start (match-beginning 0)))
(put-text-property start (1+ start)
'syntax-table (string-to-syntax "|"))
(put-text-property start (+ start 2)
'nix-syntax-antiquote t)))
(defun nix-syntax-propertize-close-brace ()
"Set syntax properties for close braces.
If a close brace `}' ends an antiquote, the next character begins a string."
(let* ((start (match-beginning 0))
(end (match-end 0))
(context (save-excursion (save-match-data (syntax-ppss start))))
(open (nth 1 context)))
(when open ;; a corresponding open-brace was found
(let* ((antiquote (get-text-property open 'nix-syntax-antiquote)))
(when antiquote
(put-text-property (+ start 1) (+ start 2)
'syntax-table (string-to-syntax "|"))
(put-text-property start (1+ start)
'nix-syntax-antiquote t))))))
(defun nix-syntax-propertize (start end)
"Special syntax properties for Nix."
;; search for multi-line string delimiters
(goto-char start)
(remove-text-properties start end '(syntax-table nil nix-syntax-antiquote nil))
(funcall
(syntax-propertize-rules
("''\\${"
(0 (ignore (nix-syntax-propertize-escaped-antiquote))))
("''"
(0 (ignore (nix-syntax-propertize-multiline-string))))
("\\${"
(0 (ignore (nix-syntax-propertize-antiquote))))
("}"
(0 (ignore (nix-syntax-propertize-close-brace)))))
start end))
(defun nix-indent-line ()
"Indent current line in a Nix expression."
(interactive)
(indent-relative-maybe))
;;;###autoload
(define-derived-mode nix-mode prog-mode "Nix"
"Major mode for editing Nix expressions.
The following commands may be useful:
'\\[newline-and-indent]'
Insert a newline and move the cursor to align with the previous
non-empty line.
'\\[fill-paragraph]'
Refill a paragraph so that all lines are at most `fill-column'
lines long. This should do the right thing for comments beginning
with `#'. However, this command doesn't work properly yet if the
comment is adjacent to code (i.e., no intervening empty lines).
In that case, select the text to be refilled and use
`\\[fill-region]' instead.
The hook `nix-mode-hook' is run when Nix mode is started.
\\{nix-mode-map}
"
(set-syntax-table nix-mode-syntax-table)
;; Font lock support.
(setq-local font-lock-defaults '(nix-font-lock-keywords nil nil nil nil))
;; Special syntax properties for Nix
(setq-local syntax-propertize-function 'nix-syntax-propertize)
;; Look at text properties when parsing
(setq-local parse-sexp-lookup-properties t)
;; Automatic indentation [C-j].
(set (make-local-variable 'indent-line-function) 'nix-indent-line)
;; Indenting of comments.
(set (make-local-variable 'comment-start) "# ")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'comment-start-skip) "\\(^\\|\\s-\\);?#+ *")
;; Filling of comments.
(set (make-local-variable 'adaptive-fill-mode) t)
(set (make-local-variable 'paragraph-start) "[ \t]*\\(#+[ \t]*\\)?$")
(set (make-local-variable 'paragraph-separate) paragraph-start))
;;;###autoload
(progn
(add-to-list 'auto-mode-alist '("\\.nix\\'" . nix-mode))
(add-to-list 'auto-mode-alist '("\\.nix.in\\'" . nix-mode)))
(provide 'nix-mode)
;;; nix-mode.el ends here

View File

@@ -12,5 +12,10 @@
<string>/var/log/nix-daemon.log</string>
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>EnvironmentVariables</key>
<dict>
<key>NIX_SSL_CERT_FILE</key>
<string>/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt</string>
</dict>
</dict>
</plist>

View File

@@ -7,3 +7,4 @@ ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
[Service]
ExecStart=@@bindir@/nix-daemon nix-daemon --daemon
KillMode=process
Environment=XDG_CACHE_HOME=/root/.cache

View File

@@ -53,8 +53,8 @@ BUILD_SHARED_LIBS ?= 1
ifeq ($(BUILD_SHARED_LIBS), 1)
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
GLOBAL_CFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
GLOBAL_CFLAGS += -U__STRICT_ANSI__
GLOBAL_CXXFLAGS += -U__STRICT_ANSI__
else
GLOBAL_CFLAGS += -fPIC
GLOBAL_CXXFLAGS += -fPIC

View File

@@ -7,39 +7,20 @@ define run-install-test
endef
# Color code from https://unix.stackexchange.com/a/10065
installcheck:
@total=0; failed=0; \
red=""; \
green=""; \
yellow=""; \
normal=""; \
if [ -t 1 ]; then \
red=""; \
green=""; \
yellow=""; \
normal=""; \
fi; \
for i in $(_installcheck-list); do \
@total=0; failed=0; for i in $(_installcheck-list); do \
total=$$((total + 1)); \
printf "running test $$i..."; \
log="$$(cd $$(dirname $$i) && $(tests-environment) $$(basename $$i) 2>&1)"; \
status=$$?; \
if [ $$status -eq 0 ]; then \
echo " [$${green}PASS$$normal]"; \
elif [ $$status -eq 99 ]; then \
echo " [$${yellow}SKIP$$normal]"; \
echo "running test $$i"; \
if (cd $$(dirname $$i) && $(tests-environment) $$(basename $$i)); then \
echo "PASS: $$i"; \
else \
echo " [$${red}FAIL$$normal]"; \
echo "$$log" | sed 's/^/ /'; \
echo "FAIL: $$i"; \
failed=$$((failed + 1)); \
fi; \
done; \
if [ "$$failed" != 0 ]; then \
echo "$${red}$$failed out of $$total tests failed $$normal"; \
echo "$$failed out of $$total tests failed "; \
exit 1; \
else \
echo "$${green}All tests succeeded"; \
fi
.PHONY: check installcheck

View File

@@ -1,5 +1,3 @@
%undefine _hardened_build
%global nixbld_user "nix-builder-"
%global nixbld_group "nixbld"
@@ -16,15 +14,18 @@ Source0: %{name}-%{version}.tar.bz2
%if 0%{?el5}
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
%endif
BuildRequires: perl(DBD::SQLite)
BuildRequires: perl(DBI)
BuildRequires: perl(ExtUtils::ParseXS)
Requires: /usr/bin/perl
Requires: curl
Requires: perl-DBD-SQLite
Requires: bzip2
Requires: gzip
Requires: xz
Requires: libseccomp
BuildRequires: bzip2-devel
BuildRequires: sqlite-devel
BuildRequires: libcurl-devel
BuildRequires: libseccomp-devel
# Hack to make that shitty RPM scanning hack shut up.
Provides: perl(Nix::SSH)
@@ -60,8 +61,40 @@ Requires: %{name} = %{version}-%{release}
%description doc
The %{name}-doc package contains documentation files for %{name}.
%package -n emacs-%{name}
Summary: Nix mode for Emacs
%if 0%{?rhel} && 0%{?rhel} < 7
Group: Applications/Editors
%endif
BuildArch: noarch
BuildRequires: emacs
Requires: emacs(bin) >= %{_emacs_version}
%description -n emacs-%{name}
This package provides a major mode for editing Nix expressions.
%package -n emacs-%{name}-el
Summary: Elisp source files for emacs-%{name}
%if 0%{?rhel} && 0%{?rhel} < 7
Group: Applications/Editors
%endif
BuildArch: noarch
Requires: emacs-%{name} = %{version}-%{release}
%description -n emacs-%{name}-el
This package contains the elisp source file for the Nix major mode for
GNU Emacs. You do not need to install this package to run Nix. Install
the emacs-%{name} package to edit Nix expressions with GNU Emacs.
%prep
%setup -q
# Install Perl modules to vendor_perl
# configure.ac need to be changed to make this global; however, this will
# also affect NixOS. Use discretion.
%{__sed} -i 's|perl5/site_perl/$perlversion/$perlarchname|perl5/vendor_perl|' \
configure
%build
@@ -72,7 +105,8 @@ extraFlags=
%configure --localstatedir=/nix/var \
--docdir=%{_defaultdocdir}/%{name}-doc-%{version} \
$extraFlags
make -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
make %{?_smp_flags}
%{_emacs_bytecompile} misc/emacs/nix-mode.el
%install
@@ -98,6 +132,9 @@ done
# (until this is fixed in the relevant Makefile)
chmod -x $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/nix.sh
# Copy the byte-compiled mode file by hand
cp -p misc/emacs/nix-mode.elc $RPM_BUILD_ROOT%{_emacs_sitelispdir}/
# we ship this file in the base package
rm -f $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}-doc-%{version}/README
@@ -130,17 +167,19 @@ systemctl start nix-daemon.socket
%files
%{_bindir}/nix*
%{_libdir}/*.so
%{perl_vendorarch}/*
%exclude %dir %{perl_vendorarch}/auto/
%{_prefix}/libexec/*
%if ! 0%{?rhel} || 0%{?rhel} >= 7
%{_prefix}/lib/systemd/system/nix-daemon.socket
%{_prefix}/lib/systemd/system/nix-daemon.service
%endif
%{_datadir}/emacs/site-lisp/nix-mode.el
%{_datadir}/nix
%{_mandir}/man1/*.1*
%{_mandir}/man5/*.5*
%{_mandir}/man8/*.8*
%config(noreplace) %{_sysconfdir}/profile.d/nix.sh
%config(noreplace) %{_sysconfdir}/profile.d/nix-daemon.sh
/nix
%files devel
@@ -150,3 +189,10 @@ systemctl start nix-daemon.socket
%files doc
%docdir %{_defaultdocdir}/%{name}-doc-%{version}
%{_defaultdocdir}/%{name}-doc-%{version}
%files -n emacs-%{name}
%{_emacs_sitelispdir}/*.elc
#{_emacs_sitestartdir}/*.el
%files -n emacs-%{name}-el
%{_emacs_sitelispdir}/*.el

View File

@@ -1,14 +0,0 @@
makefiles = local.mk
GLOBAL_CXXFLAGS += -std=c++14 -g -Wall
-include Makefile.config
OPTIMIZE = 1
ifeq ($(OPTIMIZE), 1)
GLOBAL_CFLAGS += -O3
GLOBAL_CXXFLAGS += -O3
endif
include mk/lib.mk

View File

@@ -1,18 +0,0 @@
CC = @CC@
CFLAGS = @CFLAGS@
CXX = @CXX@
CXXFLAGS = @CXXFLAGS@
HAVE_SODIUM = @HAVE_SODIUM@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
SODIUM_LIBS = @SODIUM_LIBS@
NIX_CFLAGS = @NIX_CFLAGS@
NIX_LIBS = @NIX_LIBS@
nixbindir = @nixbindir@
curl = @curl@
nixlibexecdir = @nixlibexecdir@
nixlocalstatedir = @nixlocalstatedir@
perl = @perl@
perllibdir = @perllibdir@
nixstoredir = @nixstoredir@
nixsysconfdir = @nixsysconfdir@

View File

@@ -1,99 +0,0 @@
AC_INIT(nix-perl, m4_esyscmd([bash -c "echo -n $(cat ../version)$VERSION_SUFFIX"]))
AC_CONFIG_SRCDIR(MANIFEST)
AC_CONFIG_AUX_DIR(../config)
CFLAGS=
CXXFLAGS=
AC_PROG_CC
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX_11
# Use 64-bit file system calls so that we can support files > 2 GiB.
AC_SYS_LARGEFILE
AC_DEFUN([NEED_PROG],
[
AC_PATH_PROG($1, $2)
if test -z "$$1"; then
AC_MSG_ERROR([$2 is required])
fi
])
NEED_PROG(perl, perl)
NEED_PROG(curl, curl)
NEED_PROG(bzip2, bzip2)
NEED_PROG(xz, xz)
# Test that Perl has the open/fork feature (Perl 5.8.0 and beyond).
AC_MSG_CHECKING([whether Perl is recent enough])
if ! $perl -e 'open(FOO, "-|", "true"); while (<FOO>) { print; }; close FOO or die;'; then
AC_MSG_RESULT(no)
AC_MSG_ERROR([Your Perl version is too old. Nix requires Perl 5.8.0 or newer.])
fi
AC_MSG_RESULT(yes)
# Figure out where to install Perl modules.
AC_MSG_CHECKING([for the Perl installation prefix])
perlversion=$($perl -e 'use Config; print $Config{version};')
perlarchname=$($perl -e 'use Config; print $Config{archname};')
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
AC_MSG_RESULT($perllibdir)
# Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium],
[AC_DEFINE([HAVE_SODIUM], [1], [Whether to use libsodium for cryptography.])
CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"
have_sodium=1], [have_sodium=])
AC_SUBST(HAVE_SODIUM, [$have_sodium])
# Check for the required Perl dependencies (DBI and DBD::SQLite).
perlFlags="-I$perllibdir"
AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH],
[prefix of the Perl DBI library]),
perlFlags="$perlFlags -I$withval")
AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH],
[prefix of the Perl DBD::SQLite library]),
perlFlags="$perlFlags -I$withval")
AC_MSG_CHECKING([whether DBD::SQLite works])
if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then
AC_MSG_RESULT(no)
AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.])
fi
AC_MSG_RESULT(yes)
AC_SUBST(perlFlags)
PKG_CHECK_MODULES([NIX], [nix-store])
NEED_PROG([NIX_INSTANTIATE_PROGRAM], [nix-instantiate])
# Get nix configure values
nixbindir=$("$NIX_INSTANTIATE_PROGRAM" --eval '<nix/config.nix>' -A nixBinDir | tr -d \")
nixlibexecdir=$("$NIX_INSTANTIATE_PROGRAM" --eval '<nix/config.nix>' -A nixLibexecDir | tr -d \")
nixlocalstatedir=$("$NIX_INSTANTIATE_PROGRAM" --eval '<nix/config.nix>' -A nixLocalstateDir | tr -d \")
nixsysconfdir=$("$NIX_INSTANTIATE_PROGRAM" --eval '<nix/config.nix>' -A nixSysconfDir | tr -d \")
nixstoredir=$("$NIX_INSTANTIATE_PROGRAM" --eval '<nix/config.nix>' -A nixStoreDir | tr -d \")
AC_SUBST(nixbindir)
AC_SUBST(nixlibexecdir)
AC_SUBST(nixlocalstatedir)
AC_SUBST(nixsysconfdir)
AC_SUBST(nixstoredir)
# Expand all variables in config.status.
test "$prefix" = NONE && prefix=$ac_default_prefix
test "$exec_prefix" = NONE && exec_prefix='${prefix}'
for name in $ac_subst_vars; do
declare $name="$(eval echo "${!name}")"
declare $name="$(eval echo "${!name}")"
declare $name="$(eval echo "${!name}")"
done
rm -f Makefile.config
ln -sfn ../mk mk
AC_CONFIG_FILES([])
AC_OUTPUT

View File

@@ -4,31 +4,48 @@ use MIME::Base64;
$version = "@PACKAGE_VERSION@";
$binDir = $ENV{"NIX_BIN_DIR"} || "@nixbindir@";
$libexecDir = $ENV{"NIX_LIBEXEC_DIR"} || "@nixlibexecdir@";
$stateDir = $ENV{"NIX_STATE_DIR"} || "@nixlocalstatedir@/nix";
$logDir = $ENV{"NIX_LOG_DIR"} || "@nixlocalstatedir@/log/nix";
$confDir = $ENV{"NIX_CONF_DIR"} || "@nixsysconfdir@/nix";
$storeDir = $ENV{"NIX_STORE_DIR"} || "@nixstoredir@";
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
$libexecDir = $ENV{"NIX_LIBEXEC_DIR"} || "@libexecdir@";
$stateDir = $ENV{"NIX_STATE_DIR"} || "@localstatedir@/nix";
$logDir = $ENV{"NIX_LOG_DIR"} || "@localstatedir@/log/nix";
$confDir = $ENV{"NIX_CONF_DIR"} || "@sysconfdir@/nix";
$storeDir = $ENV{"NIX_STORE_DIR"} || "@storedir@";
$bzip2 = "@bzip2@";
$xz = "@xz@";
$curl = "@curl@";
$useBindings = 1;
$useBindings = "@perlbindings@" eq "yes";
%config = ();
sub readConfig {
my $config = "$confDir/nix.conf";
return unless -f $config;
%binaryCachePublicKeys = ();
open CONFIG, "<$config" or die "cannot open '$config'";
while (<CONFIG>) {
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
$config{$1} = $2;
$defaultPublicKeys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=";
sub readConfig {
if (defined $ENV{'_NIX_OPTIONS'}) {
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
my ($n, $v) = split '=', $s, 2;
$config{$n} = $v;
}
} else {
my $config = "$confDir/nix.conf";
return unless -f $config;
open CONFIG, "<$config" or die "cannot open $config";
while (<CONFIG>) {
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
$config{$1} = $2;
}
close CONFIG;
}
foreach my $s (split(/ /, $config{"binary-cache-public-keys"} // $defaultPublicKeys)) {
my ($keyName, $publicKey) = split ":", $s;
next unless defined $keyName && defined $publicKey;
$binaryCachePublicKeys{$keyName} = decode_base64($publicKey);
}
close CONFIG;
}
return 1;

View File

@@ -35,14 +35,14 @@ sub copyToOpen {
my $missingSize = 0;
$missingSize += (queryPathInfo($_, 1))[3] foreach @missing;
printf STDERR "copying %d missing paths (%.2f MiB) to '$sshHost'...\n",
printf STDERR "copying %d missing paths (%.2f MiB) to $sshHost...\n",
scalar(@missing), $missingSize / (1024**2);
return if $dryRun;
# Send the "import paths" command.
syswrite($to, pack("L<x4", 4)) or die;
exportPaths(fileno($to), @missing);
readInt($from) == 1 or die "remote machine '$sshHost' failed to import closure\n";
readInt($from) == 1 or die "remote machine $sshHost failed to import closure\n";
}

View File

@@ -60,10 +60,10 @@ sub readManifest_ {
# Decompress the manifest if necessary.
if ($manifest =~ /\.bz2$/) {
open MANIFEST, "$Nix::Config::bzip2 -d < $manifest |"
or die "cannot decompress '$manifest': $!";
or die "cannot decompress $manifest: $!";
} else {
open MANIFEST, "<$manifest"
or die "cannot open '$manifest': $!";
or die "cannot open $manifest: $!";
}
my $inside = 0;
@@ -287,7 +287,7 @@ sub parseNARInfo {
# FIXME: might be useful to support multiple signatures per .narinfo.
if (!defined $sig) {
warn "NAR info file '$location' lacks a signature; ignoring\n";
warn "NAR info file $location lacks a signature; ignoring\n";
return undef;
}
my ($keyName, $sig64) = split ":", $sig;
@@ -295,7 +295,7 @@ sub parseNARInfo {
my $publicKey = $Nix::Config::binaryCachePublicKeys{$keyName};
if (!defined $publicKey) {
warn "NAR info file '$location' is signed by unknown key '$keyName'; ignoring\n";
warn "NAR info file $location is signed by unknown key $keyName; ignoring\n";
return undef;
}
@@ -306,12 +306,12 @@ sub parseNARInfo {
[ map { "$Nix::Config::storeDir/$_" } @refs ]);
};
if ($@) {
warn "cannot compute fingerprint of '$location'; ignoring\n";
warn "cannot compute fingerprint of $location; ignoring\n";
return undef;
}
if (!checkSignature($publicKey, decode_base64($sig64), $fingerprint)) {
warn "NAR info file '$location' has an incorrect signature; ignoring\n";
warn "NAR info file $location has an incorrect signature; ignoring\n";
return undef;
}

View File

@@ -97,7 +97,7 @@ sub connectToRemoteNix {
syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion)) or die;
$magic = readInt($from);
};
die "unable to connect to '$sshHost'\n" if $@;
die "unable to connect to $sshHost\n" if $@;
die "did not get valid handshake from remote host\n" if $magic != 0x5452eecb;
my $serverVersion = readInt($from);

View File

@@ -1,5 +1,3 @@
#include "config.h"
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
@@ -27,7 +25,10 @@ static ref<Store> store()
static std::shared_ptr<Store> _store;
if (!_store) {
try {
logger = makeDefaultLogger();
settings.processEnvironment();
settings.loadConfFile();
settings.update();
settings.lockCPU = false;
_store = openStore();
} catch (Error & e) {
@@ -81,7 +82,8 @@ SV * queryReferences(char * path)
SV * queryPathHash(char * path)
PPCODE:
try {
auto s = store()->queryPathInfo(path)->narHash.to_string();
auto hash = store()->queryPathInfo(path)->narHash;
string s = "sha256:" + printHash32(hash);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -107,7 +109,7 @@ SV * queryPathInfo(char * path, int base32)
XPUSHs(&PL_sv_undef);
else
XPUSHs(sv_2mortal(newSVpv(info->deriver.c_str(), 0)));
auto s = info->narHash.to_string(base32 ? Base32 : Base16);
string s = "sha256:" + (base32 ? printHash32(info->narHash) : printHash(info->narHash));
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
mXPUSHi(info->registrationTime);
mXPUSHi(info->narSize);
@@ -183,7 +185,7 @@ void importPaths(int fd, int dontCheckSigs)
PPCODE:
try {
FdSource source(fd);
store()->importPaths(source, nullptr, dontCheckSigs ? NoCheckSigs : CheckSigs);
store()->importPaths(source, 0, dontCheckSigs);
} catch (Error & e) {
croak("%s", e.what());
}
@@ -193,7 +195,7 @@ SV * hashPath(char * algo, int base32, char * path)
PPCODE:
try {
Hash h = hashPath(parseHashType(algo), path).first;
auto s = h.to_string(base32 ? Base32 : Base16, false);
string s = base32 ? printHash32(h) : printHash(h);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -204,7 +206,7 @@ SV * hashFile(char * algo, int base32, char * path)
PPCODE:
try {
Hash h = hashFile(parseHashType(algo), path);
auto s = h.to_string(base32 ? Base32 : Base16, false);
string s = base32 ? printHash32(h) : printHash(h);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -215,7 +217,7 @@ SV * hashString(char * algo, int base32, char * s)
PPCODE:
try {
Hash h = hashString(parseHashType(algo), s);
auto s = h.to_string(base32 ? Base32 : Base16, false);
string s = base32 ? printHash32(h) : printHash(h);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -225,8 +227,8 @@ SV * hashString(char * algo, int base32, char * s)
SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
Hash h(s, parseHashType(algo));
string s = h.to_string(toBase32 ? Base32 : Base16, false);
Hash h = parseHash16or32(parseHashType(algo), s);
string s = toBase32 ? printHash32(h) : printHash(h);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -285,7 +287,8 @@ SV * addToStore(char * srcPath, int recursive, char * algo)
SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
PPCODE:
try {
Hash h(hash, parseHashType(algo));
HashType ht = parseHashType(algo);
Hash h = parseHash16or32(ht, hash);
Path path = store()->makeFixedOutputPath(recursive, h, name);
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
} catch (Error & e) {

View File

@@ -10,7 +10,7 @@ $urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!
sub checkURL {
my ($url) = @_;
die "invalid URL '$url'\n" unless $url =~ /^ $urlRE $ /x;
die "invalid URL $url\n" unless $url =~ /^ $urlRE $ /x;
}
sub uniq {
@@ -26,7 +26,7 @@ sub uniq {
sub writeFile {
my ($fn, $s) = @_;
open TMP, ">$fn" or die "cannot create file '$fn': $!";
open TMP, ">$fn" or die "cannot create file $fn: $!";
print TMP "$s" or die;
close TMP or die;
}
@@ -34,7 +34,7 @@ sub writeFile {
sub readFile {
local $/ = undef;
my ($fn) = @_;
open TMP, "<$fn" or die "cannot open file '$fn': $!";
open TMP, "<$fn" or die "cannot open file $fn: $!";
my $s = <TMP>;
close TMP or die;
return $s;

View File

@@ -1,43 +1,48 @@
nix_perl_sources := \
lib/Nix/Store.pm \
lib/Nix/Manifest.pm \
lib/Nix/SSH.pm \
lib/Nix/CopyClosure.pm \
lib/Nix/Config.pm.in \
lib/Nix/Utils.pm
$(d)/lib/Nix/Store.pm \
$(d)/lib/Nix/Manifest.pm \
$(d)/lib/Nix/SSH.pm \
$(d)/lib/Nix/CopyClosure.pm \
$(d)/lib/Nix/Config.pm.in \
$(d)/lib/Nix/Utils.pm
nix_perl_modules := $(nix_perl_sources:.in=)
$(foreach x, $(nix_perl_modules), $(eval $(call install-data-in, $(x), $(perllibdir)/Nix)))
lib/Nix/Store.cc: lib/Nix/Store.xs
ifeq ($(perlbindings), yes)
$(d)/lib/Nix/Store.cc: $(d)/lib/Nix/Store.xs
$(trace-gen) xsubpp $^ -output $@
libraries += Store
libraries += Store
Store_DIR := lib/Nix
Store_DIR := $(d)/lib/Nix
Store_SOURCES := $(Store_DIR)/Store.cc
Store_SOURCES := $(Store_DIR)/Store.cc
Store_CXXFLAGS = \
$(NIX_CFLAGS) \
-I$(shell perl -e 'use Config; print $$Config{archlibexp};')/CORE \
-D_FILE_OFFSET_BITS=64 \
-Wno-unknown-warning-option -Wno-unused-variable -Wno-literal-suffix \
-Wno-reserved-user-defined-literal -Wno-duplicate-decl-specifier -Wno-pointer-bool-conversion
Store_CXXFLAGS = \
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
-D_FILE_OFFSET_BITS=64 \
-Wno-unknown-warning-option -Wno-unused-variable -Wno-literal-suffix \
-Wno-reserved-user-defined-literal -Wno-duplicate-decl-specifier -Wno-pointer-bool-conversion
Store_LDFLAGS := $(SODIUM_LIBS) $(NIX_LIBS)
Store_LIBS = libstore libutil
Store_LDFLAGS := $(SODIUM_LIBS)
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
endif
Store_ALLOW_UNDEFINED = 1
Store_FORCE_INSTALL = 1
Store_INSTALL_DIR = $(perllibdir)/auto/Nix/Store
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
endif
Store_ALLOW_UNDEFINED = 1
Store_FORCE_INSTALL = 1
Store_INSTALL_DIR = $(perllibdir)/auto/Nix/Store
clean-files += lib/Nix/Config.pm lib/Nix/Store.cc Makefile.config
clean-files += $(d)/lib/Nix/Config.pm $(d)/lib/Nix/Store.cc

View File

@@ -1,22 +0,0 @@
{ pkgs }:
rec {
sh = pkgs.busybox.override {
useMusl = true;
enableStatic = true;
enableMinimal = true;
extraConfig = ''
CONFIG_ASH y
CONFIG_ASH_ECHO y
CONFIG_ASH_TEST y
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
'';
};
configureFlags =
[ "--disable-init-state"
"--enable-gc"
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
"--with-sandbox-shell=${sh}/bin/busybox"
];
}

View File

@@ -1,13 +1,15 @@
{ nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
, nixpkgs ? { outPath = <nixpkgs>; revCount = 1234; shortRev = "abcdef"; }
, officialRelease ? false
, systems ? [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]
}:
let
pkgs = import <nixpkgs> {};
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" /* "x86_64-freebsd" "i686-freebsd" */ ];
jobs = rec {
@@ -18,19 +20,21 @@ let
name = "nix-tarball";
version = builtins.readFile ./version;
versionSuffix = if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}";
src = nix;
src = if lib.inNixShell then null else nix;
inherit officialRelease;
buildInputs =
[ curl bison flex libxml2 libxslt
bzip2 xz brotli
[ curl bison flex perl libxml2 libxslt bzip2 xz
pkgconfig sqlite libsodium boehmgc
docbook5 docbook5_xsl
autoconf-archive
git
] ++ lib.optional stdenv.isLinux libseccomp;
] ++ lib.optional (!lib.inNixShell) git;
configureFlags = "--enable-gc";
configureFlags = ''
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--enable-gc
'';
postUnpack = ''
# Clean up when building from a working tree.
@@ -40,7 +44,6 @@ let
'';
preConfigure = ''
(cd perl ; autoreconf --install --force --verbose)
# TeX needs a writable font cache.
export VARTEXFONTS=$TMPDIR/texfonts
'';
@@ -64,31 +67,26 @@ let
with import <nixpkgs> { inherit system; };
with import ./release-common.nix { inherit pkgs; };
releaseTools.nixBuild {
name = "nix";
src = tarball;
buildInputs =
[ curl
bzip2 xz brotli
openssl pkgconfig sqlite boehmgc
# Tests
git
mercurial
]
++ lib.optionals stdenv.isLinux [ libseccomp fuse ]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional (stdenv.isLinux || stdenv.isDarwin)
[ curl perl bzip2 xz openssl pkgconfig sqlite boehmgc ]
++ lib.optional stdenv.isLinux libsodium
++ lib.optional stdenv.isLinux
(aws-sdk-cpp.override {
apis = ["s3"];
customMemoryManagement = false;
});
configureFlags = configureFlags ++
[ "--sysconfdir=/etc" ];
configureFlags = ''
--disable-init-state
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--enable-gc
--sysconfdir=/etc
'';
enableParallelBuilding = true;
@@ -103,31 +101,6 @@ let
});
perlBindings = pkgs.lib.genAttrs systems (system:
let pkgs = import <nixpkgs> { inherit system; }; in with pkgs;
releaseTools.nixBuild {
name = "nix-perl";
src = tarball;
buildInputs =
[ (builtins.getAttr system jobs.build) curl bzip2 xz pkgconfig pkgs.perl ]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium;
configureFlags = ''
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}
'';
enableParallelBuilding = true;
postUnpack = "sourceRoot=$sourceRoot/perl";
preBuild = "unset NIX_INDENT_MAKE";
});
binaryTarball = pkgs.lib.genAttrs systems (system:
# FIXME: temporarily use a different branch for the Darwin build.
@@ -140,7 +113,7 @@ let
runCommand "nix-binary-tarball-${version}"
{ exportReferencesGraph = [ "closure1" toplevel "closure2" cacert ];
buildInputs = [ perl shellcheck ];
buildInputs = [ perl ];
meta.description = "Distribution-independent Nix bootstrap binaries for ${system}";
}
''
@@ -149,15 +122,7 @@ let
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
--subst-var-by nix ${toplevel} \
--subst-var-by cacert ${cacert}
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user \
--subst-var-by nix ${toplevel} \
--subst-var-by cacert ${cacert}
shellcheck -e SC1090 $TMPDIR/install
shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user
chmod +x $TMPDIR/install
chmod +x $TMPDIR/install-darwin-multi-user
dir=nix-${version}-${system}
fn=$out/$dir.tar.bz2
mkdir -p $out/nix-support
@@ -169,7 +134,7 @@ let
--transform "s,$TMPDIR/install,$dir/install," \
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
--transform "s,$NIX_STORE,$dir/store,S" \
$TMPDIR/install $TMPDIR/install-darwin-multi-user $TMPDIR/reginfo $storePaths
$TMPDIR/install $TMPDIR/reginfo $storePaths
'');
@@ -181,20 +146,22 @@ let
src = tarball;
buildInputs =
[ curl bzip2 openssl pkgconfig sqlite xz libsodium libseccomp
[ curl perl bzip2 openssl pkgconfig sqlite xz libsodium
# These are for "make check" only:
graphviz libxml2 libxslt git mercurial
graphviz libxml2 libxslt
];
configureFlags = ''
--disable-init-state
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
'';
dontInstall = false;
doInstallCheck = true;
lcovFilter = [ "*/boost/*" "*-tab.*" "*/nlohmann/*" "*/linenoise/*" ];
lcovFilter = [ "*/boost/*" "*-tab.*" ];
# We call `dot', and even though we just use it to
# syntax-check generated dot files, it still requires some
@@ -203,17 +170,21 @@ let
};
rpm_fedora25i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora25i386) [ "libsodium-devel" ];
rpm_fedora25x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora25x86_64) [ "libsodium-devel" ];
rpm_fedora21i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora21i386) [ "libsodium-devel" ];
rpm_fedora21x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora21x86_64) [ "libsodium-devel" ];
#deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ] [ "libsodium13" ];
#deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ] [ "libsodium13" ];
deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386) [] [];
deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1410x86_64) [] [];
deb_ubuntu1504i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1504i386) [ "libsodium-dev" ] [ "libsodium13" ];
deb_ubuntu1504x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1504x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
deb_ubuntu1510i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1510i386) [ "libsodium-dev" ] [ "libsodium13"];
deb_ubuntu1510x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1510x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
deb_ubuntu1604i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1604i386) [ "libsodium-dev" ] [ "libsodium18" ];
deb_ubuntu1604x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1604x86_64) [ "libsodium-dev" ] [ "libsodium18" ];
deb_ubuntu1610i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1610i386) [ "libsodium-dev" ] [ "libsodium18" ];
deb_ubuntu1610x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1610x86_64) [ "libsodium-dev" ] [ "libsodium18" ];
# System tests.
@@ -225,11 +196,6 @@ let
nix = build.x86_64-linux; system = "x86_64-linux";
});
tests.setuid = pkgs.lib.genAttrs (pkgs.lib.filter (pkgs.lib.hasSuffix "-linux") systems) (system:
import ./tests/setuid.nix rec {
nix = build.${system}; inherit system;
});
tests.binaryTarball =
with import <nixpkgs> { system = "x86_64-linux"; };
vmTools.runInLinuxImage (runCommand "nix-binary-tarball-test"
@@ -275,18 +241,22 @@ let
meta.description = "Release-critical builds";
constituents =
[ tarball
#build.i686-freebsd
build.i686-linux
build.x86_64-darwin
#build.x86_64-freebsd
build.x86_64-linux
#binaryTarball.i686-freebsd
binaryTarball.i686-linux
binaryTarball.x86_64-darwin
#binaryTarball.x86_64-freebsd
binaryTarball.x86_64-linux
#deb_debian8i386
#deb_debian8x86_64
deb_ubuntu1604i386
deb_ubuntu1604x86_64
rpm_fedora25i386
rpm_fedora25x86_64
deb_debian8i386
deb_debian8x86_64
deb_ubuntu1504i386
deb_ubuntu1504x86_64
rpm_fedora21i386
rpm_fedora21x86_64
tests.remoteBuilds
tests.nix-copy-closure
tests.binaryTarball
@@ -311,12 +281,11 @@ let
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages =
[ "sqlite" "sqlite-devel" "bzip2-devel" "libcurl-devel" "openssl-devel" "xz-devel" "libseccomp-devel" ]
[ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "libcurl-devel" "openssl-devel" "xz-devel" ]
++ extraPackages; };
memSize = 1024;
meta.schedulingPriority = 50;
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
#enableParallelBuilding = true;
};
@@ -333,18 +302,17 @@ let
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages =
[ "libsqlite3-dev" "libbz2-dev" "libcurl-dev" "libcurl3-nss" "libssl-dev" "liblzma-dev" "libseccomp-dev" ]
[ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" "libcurl3-nss" "libssl-dev" "liblzma-dev" ]
++ extraPackages; };
memSize = 1024;
meta.schedulingPriority = 50;
postInstall = "make installcheck";
configureFlags = "--sysconfdir=/etc";
debRequires =
[ "curl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libssl1.0.0" "liblzma5" "libseccomp2" ]
[ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" "libssl1.0.0" "liblzma5" ]
++ extraDebPackages;
debMaintainer = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
doInstallCheck = true;
#enableParallelBuilding = true;
};

275
scripts/build-remote.pl.in Executable file
View File

@@ -0,0 +1,275 @@
#! @perl@ -w @perlFlags@
use utf8;
use Fcntl qw(:DEFAULT :flock);
use English '-no_match_vars';
use IO::Handle;
use Nix::Config;
use Nix::SSH;
use Nix::CopyClosure;
use Nix::Store;
use Encode;
no warnings('once');
STDERR->autoflush(1);
binmode STDERR, ":encoding(utf8)";
my $debug = defined $ENV{NIX_DEBUG_HOOK};
# General operation:
#
# Try to find a free machine of type $neededSystem. We do this as
# follows:
# - We acquire an exclusive lock on $currentLoad/main-lock.
# - For each machine $machine of type $neededSystem and for each $slot
# less than the maximum load for that machine, we try to get an
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
# If we get such a lock, we send "accept" to the caller. Otherwise,
# we send "postpone" and exit.
# - We release the exclusive lock on $currentLoad/main-lock.
# - We perform the build on $neededSystem.
# - We release the exclusive lock on $currentLoad/$machine-$slot.
#
# The nice thing about this scheme is that if we die prematurely, the
# locks are released automatically.
# Make sure that we don't get any SSH passphrase or host key popups -
# if there is any problem it should fail, not do something
# interactive.
$ENV{"DISPLAY"} = "";
$ENV{"SSH_ASKPASS"} = "";
sub sendReply {
my $reply = shift;
print STDERR "# $reply\n";
}
sub all { $_ || return 0 for @_; 1 }
# Initialisation.
my $loadIncreased = 0;
my ($localSystem, $maxSilentTime, $buildTimeout) = @ARGV;
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"} // "/run/nix/current-load";
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"} // "@sysconfdir@/nix/machines";
sub openSlotLock {
my ($machine, $slot) = @_;
my $slotLockFn = "$currentLoad/" . (join '+', @{$machine->{systemTypes}}) . "-" . $machine->{hostName} . "-$slot";
my $slotLock = new IO::Handle;
sysopen $slotLock, "$slotLockFn", O_RDWR|O_CREAT, 0600 or die;
return $slotLock;
}
# Read the list of machines.
my @machines;
if (defined $conf && -e $conf) {
open CONF, "<$conf" or die;
while (<CONF>) {
chomp;
s/\#.*$//g;
next if /^\s*$/;
my @tokens = split /\s/, $_;
my @supportedFeatures = split(/,/, $tokens[5] || "");
my @mandatoryFeatures = split(/,/, $tokens[6] || "");
push @machines,
{ hostName => $tokens[0]
, systemTypes => [ split(/,/, $tokens[1]) ]
, sshKey => $tokens[2]
, maxJobs => int($tokens[3])
, speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1)
, supportedFeatures => [ @supportedFeatures, @mandatoryFeatures ]
, mandatoryFeatures => [ @mandatoryFeatures ]
, enabled => 1
};
}
close CONF;
}
# Wait for the calling process to ask us whether we can build some derivation.
my ($drvPath, $hostName, $slotLock);
my ($from, $to);
REQ: while (1) {
$_ = <STDIN> || exit 0;
(my $amWilling, my $neededSystem, $drvPath, my $requiredFeatures) = split;
my @requiredFeatures = split /,/, $requiredFeatures;
my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem);
if (!defined $currentLoad) {
sendReply "decline";
next;
}
# Acquire the exclusive lock on $currentLoad/main-lock.
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
my $mainLock = "$currentLoad/main-lock";
sysopen MAINLOCK, "$mainLock", O_RDWR|O_CREAT, 0600 or die;
flock(MAINLOCK, LOCK_EX) or die;
while (1) {
# Find all machine that can execute this build, i.e., that
# support builds for the given platform and features, and are
# not at their job limit.
my $rightType = 0;
my @available = ();
LOOP: foreach my $cur (@machines) {
if ($cur->{enabled}
&& (grep { $neededSystem eq $_ } @{$cur->{systemTypes}})
&& all(map { my $f = $_; 0 != grep { $f eq $_ } @{$cur->{supportedFeatures}} } (@requiredFeatures, @mandatoryFeatures))
&& all(map { my $f = $_; 0 != grep { $f eq $_ } @requiredFeatures } @{$cur->{mandatoryFeatures}})
)
{
$rightType = 1;
# We have a machine of the right type. Determine the load on
# the machine.
my $slot = 0;
my $load = 0;
my $free;
while ($slot < $cur->{maxJobs}) {
my $slotLock = openSlotLock($cur, $slot);
if (flock($slotLock, LOCK_EX | LOCK_NB)) {
$free = $slot unless defined $free;
flock($slotLock, LOCK_UN) or die;
} else {
$load++;
}
close $slotLock;
$slot++;
}
push @available, { machine => $cur, load => $load, free => $free }
if $load < $cur->{maxJobs};
}
}
if ($debug) {
print STDERR "load on " . $_->{machine}->{hostName} . " = " . $_->{load} . "\n"
foreach @available;
}
# Didn't find any available machine? Then decline or postpone.
if (scalar @available == 0) {
# Postpone if we have a machine of the right type, except
# if the local system can and wants to do the build.
if ($rightType && !$canBuildLocally) {
sendReply "postpone";
} else {
sendReply "decline";
}
close MAINLOCK;
next REQ;
}
# Prioritise the available machines as follows:
# - First by load divided by speed factor, rounded to the nearest
# integer. This causes fast machines to be preferred over slow
# machines with similar loads.
# - Then by speed factor.
# - Finally by load.
sub lf { my $x = shift; return int($x->{load} / $x->{machine}->{speedFactor} + 0.4999); }
@available = sort
{ lf($a) <=> lf($b)
|| $b->{machine}->{speedFactor} <=> $a->{machine}->{speedFactor}
|| $a->{load} <=> $b->{load}
} @available;
# Select the best available machine and lock a free slot.
my $selected = $available[0];
my $machine = $selected->{machine};
$slotLock = openSlotLock($machine, $selected->{free});
flock($slotLock, LOCK_EX | LOCK_NB) or die;
utime undef, undef, $slotLock;
close MAINLOCK;
# Connect to the selected machine.
my @sshOpts = ("-i", $machine->{sshKey});
$hostName = $machine->{hostName};
eval {
($from, $to) = connectToRemoteNix($hostName, \@sshOpts, "2>&4");
# FIXME: check if builds are inhibited.
};
last REQ unless $@;
print STDERR "$@";
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
$from = undef;
$to = undef;
$machine->{enabled} = 0;
}
}
# Tell Nix we've accepted the build.
sendReply "accept";
my @inputs = split /\s/, readline(STDIN);
my @outputs = split /\s/, readline(STDIN);
# Copy the derivation and its dependencies to the build machine. This
# is guarded by an exclusive lock per machine to prevent multiple
# build-remote instances from copying to a machine simultaneously.
# That's undesirable because we may end up with N instances uploading
# the same missing path simultaneously, causing the effective network
# bandwidth and target disk speed to be divided by N.
my $uploadLock = "$currentLoad/$hostName.upload-lock";
sysopen UPLOADLOCK, "$uploadLock", O_RDWR|O_CREAT, 0600 or die;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
# Don't wait forever, so that a process that gets stuck while
# holding the lock doesn't block everybody else indefinitely.
# It's safe to continue after a timeout, just (potentially)
# inefficient.
alarm 15 * 60;
flock(UPLOADLOCK, LOCK_EX);
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n";
print STDERR "somebody is hogging $uploadLock, continuing...\n";
unlink $uploadLock;
}
Nix::CopyClosure::copyToOpen($from, $to, $hostName, [ $drvPath, @inputs ], 0, 0);
close UPLOADLOCK;
# Perform the build.
print STDERR "building $drvPath on $hostName\n";
writeInt(6, $to) or die; # == cmdBuildPaths
writeStrings([$drvPath], $to);
writeInt($maxSilentTime, $to);
writeInt($buildTimeout, $to);
my $res = readInt($from);
if ($res != 0) {
my $msg = decode("utf-8", readString($from));
print STDERR "error: $msg on $hostName\n";
exit $res;
}
# Copy the output from the build machine.
my @outputs2 = grep { !isValidPath($_) } @outputs;
if (scalar @outputs2 > 0) {
writeInt(5, $to); # == cmdExportPaths
writeInt(0, $to); # don't sign
writeStrings(\@outputs2, $to);
$ENV{'NIX_HELD_LOCKS'} = "@outputs2"; # FIXME: ugly
importPaths(fileno($from), 1);
}

View File

@@ -1,819 +0,0 @@
#!/bin/bash
set -eu
set -o pipefail
# Sourced from:
# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh
# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0
# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh
# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh
# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a
#
# however tracking which bits came from which would be impossible.
readonly ESC='\033[0m'
readonly BOLD='\033[38;1m'
readonly BLUE='\033[38;34m'
readonly BLUE_UL='\033[38;4;34m'
readonly GREEN='\033[38;32m'
readonly GREEN_UL='\033[38;4;32m'
readonly RED='\033[38;31m'
readonly RED_UL='\033[38;4;31m'
readonly YELLOW='\033[38;33m'
readonly YELLOW_UL='\033[38;4;33m'
readonly CORES=$(sysctl -n hw.ncpu)
readonly NIX_USER_COUNT="$CORES"
readonly NIX_BUILD_GROUP_ID="30000"
readonly NIX_BUILD_GROUP_NAME="nixbld"
readonly NIX_FIRST_BUILD_UID="30001"
# Please don't change this. We don't support it, because the
# default shell profile that comes with Nix doesn't support it.
readonly NIX_ROOT="/nix"
readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
readonly PROFILE_TARGETS=("/etc/profile" "/etc/bashrc" "/etc/zshrc")
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
readonly NIX_INSTALLED_NIX="@nix@"
readonly NIX_INSTALLED_CACERT="@cacert@"
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
readonly ROOT_HOME="/var/root"
if [ -t 0 ]; then
readonly IS_HEADLESS='no'
else
readonly IS_HEADLESS='yes'
fi
headless() {
if [ "$IS_HEADLESS" = "yes" ]; then
return 0
else
return 1
fi
}
contactme() {
echo "We'd love to help if you need it."
echo ""
echo "If you can, open an issue at https://github.com/nixos/nix/issues"
echo ""
echo "Or feel free to contact the team,"
echo " - on IRC #nixos on irc.freenode.net"
echo " - on twitter @nixos_org"
}
uninstall_directions() {
subheader "Uninstalling nix:"
local step=0
if [ -e "$PLIST_DEST" ]; then
step=$((step + 1))
cat <<EOF
$step. Delete $PLIST_DEST
sudo launchctl unload $PLIST_DEST
sudo rm $PLIST_DEST
EOF
fi
for profile_target in "${PROFILE_TARGETS[@]}"; do
if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
step=$((step + 1))
cat <<EOF
$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
(after this one, you may need to re-open any terminals that were
opened while it existed.)
EOF
fi
done
step=$((step + 1))
cat <<EOF
$step. Delete the files Nix added to your system:
sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
and that is it.
EOF
}
nix_user_for_core() {
printf "nixbld%d" "$1"
}
nix_uid_for_core() {
echo $((NIX_FIRST_BUILD_UID + $1 - 1))
}
dsclattr() {
/usr/bin/dscl . -read "$1" \
| awk "/$2/ { print \$2 }"
}
_textout() {
echo -en "$1"
shift
if [ "$*" = "" ]; then
cat
else
echo "$@"
fi
echo -en "$ESC"
}
header() {
follow="---------------------------------------------------------"
header=$(echo "---- $* $follow$follow$follow" | head -c 80)
echo ""
_textout "$BLUE" "$header"
}
warningheader() {
follow="---------------------------------------------------------"
header=$(echo "---- $* $follow$follow$follow" | head -c 80)
echo ""
_textout "$RED" "$header"
}
subheader() {
echo ""
_textout "$BLUE_UL" "$*"
}
row() {
printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2"
}
task() {
echo ""
ok "~~> $1"
}
bold() {
echo "$BOLD$*$ESC"
}
ok() {
_textout "$GREEN" "$@"
}
warning() {
warningheader "warning!"
cat
echo ""
}
failure() {
header "oh no!"
_textout "$RED" "$@"
echo ""
_textout "$RED" "$(contactme)"
trap finish_cleanup EXIT
exit 1
}
ui_confirm() {
_textout "$GREEN$GREEN_UL" "$1"
if headless; then
echo "No TTY, assuming you would say yes :)"
return 0
fi
local prompt="[y/n] "
echo -n "$prompt"
while read -r y; do
if [ "$y" = "y" ]; then
echo ""
return 0
elif [ "$y" = "n" ]; then
echo ""
return 1
else
_textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n"
echo -n "$prompt"
fi
done
echo ""
return 1
}
__sudo() {
local expl="$1"
local cmd="$2"
shift
header "sudo execution"
echo "I am executing:"
echo ""
printf " $ sudo %s\\n" "$cmd"
echo ""
echo "$expl"
echo ""
return 0
}
_sudo() {
local expl="$1"
shift
if ! headless; then
__sudo "$expl" "$*"
fi
sudo "$@"
}
readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
function finish_cleanup {
rm -rf "$SCRATCH"
}
function finish_fail {
finish_cleanup
failure <<EOF
Jeeze, something went wrong. If you can take all the output and open
an issue, we'd love to fix the problem so nobody else has this issue.
:(
EOF
}
trap finish_fail EXIT
function finish_success {
finish_cleanup
ok "Alright! We're done!"
cat <<EOF
Before Nix will work in your existing shells, you'll need to close
them and open them again. Other than that, you should be ready to go.
Try it! Open a new terminal, and type:
$ nix-shell -p nix-info --run "nix-info -m"
Thank you for using this installer. If you have any feedback, don't
hesitate:
$(contactme)
EOF
}
validate_starting_assumptions() {
if [ "$(uname -s)" != "Darwin" ]; then
failure "This script is for use with macOS!"
fi
if [ $EUID -eq 0 ]; then
failure <<EOF
Please do not run this script with root privileges. We will call sudo
when we need to.
EOF
fi
if type nix-env 2> /dev/null >&2; then
failure <<EOF
Nix already appears to be installed, and this tool assumes it is
_not_ yet installed.
$(uninstall_directions)
EOF
fi
if [ "${NIX_REMOTE:-}" != "" ]; then
failure <<EOF
For some reason, \$NIX_REMOTE is set. It really should not be set
before this installer runs, and it hints that Nix is currently
installed. Please delete the old Nix installation and start again.
Note: You might need to close your shell window and open a new shell
to clear the variable.
EOF
fi
if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then
failure <<EOF
It looks like \$SSL_CERT_FILE is set to a path that used to be part of
the old Nix installation. Please unset that variable and try again:
$ unset SSL_CERT_FILE
EOF
fi
for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
if [ -f "$file" ]; then
if grep -l "^[^#].*.nix-profile" "$file"; then
failure <<EOF
I found a reference to a ".nix-profile" in $file.
This has a high chance of breaking a new nix installation. It was most
likely put there by a previous Nix installer.
Please remove this reference and try running this again. You should
also look for similar references in:
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
or other shell init files that you may have.
$(uninstall_directions)
EOF
fi
fi
done
if [ -d /nix ]; then
failure <<EOF
There are some relics of a previous installation of Nix at /nix, and
this scripts assumes Nix is _not_ yet installed. Please delete the old
Nix installation and start again.
$(uninstall_directions)
EOF
fi
if [ -d /etc/nix ]; then
failure <<EOF
There are some relics of a previous installation of Nix at /etc/nix, and
this scripts assumes Nix is _not_ yet installed. Please delete the old
Nix installation and start again.
$(uninstall_directions)
EOF
fi
for profile_target in "${PROFILE_TARGETS[@]}"; do
if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
failure <<EOF
When this script runs, it backs up the current $profile_target to
$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
Please follow these instructions to clean up the old backup file:
1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
in case.
2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
it has anything nix-related in it. If it does, something is probably
quite wrong. Please open an issue or get in touch immediately.
3. Take care to make sure that $profile_target doesn't look like it has
anything nix-related in it. If it does, and $profile_target _did not_,
run:
$ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
and try again.
EOF
fi
if grep -qi "nix" "$profile_target"; then
failure <<EOF
It looks like $profile_target already has some Nix configuration in
there. There should be no reason to run this again. If you're having
trouble, please open an issue.
EOF
fi
done
danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile")
for danger_path in "${danger_paths[@]}"; do
if _sudo "making sure that $danger_path doesn't exist" \
test -e "$danger_path"; then
failure <<EOF
I found a file at $danger_path, which is a relic of a previous
installation. You must first delete this file before continuing.
$(uninstall_directions)
EOF
fi
done
}
setup_report() {
header "hardware report"
row " Cores" "$CORES"
header "Nix config report"
row " Temp Dir" "$SCRATCH"
row " Nix Root" "$NIX_ROOT"
row " Build Users" "$NIX_USER_COUNT"
row " Build Group ID" "$NIX_BUILD_GROUP_ID"
row "Build Group Name" "$NIX_BUILD_GROUP_NAME"
if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then
row "Preexisting Install" "Allowed"
fi
subheader "build users:"
row " Username" "UID"
for i in $(seq 1 "$NIX_USER_COUNT"); do
row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")"
done
echo ""
}
create_build_group() {
local primary_group_id
task "Setting up the build group $NIX_BUILD_GROUP_NAME"
if ! /usr/bin/dscl . -read "/Groups/$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1; then
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
/usr/sbin/dseditgroup -o create \
-r "Nix build group for nix-daemon" \
-i "$NIX_BUILD_GROUP_ID" \
"$NIX_BUILD_GROUP_NAME" >&2
row " Created" "Yes"
else
primary_group_id=$(dsclattr "/Groups/$NIX_BUILD_GROUP_NAME" "PrimaryGroupID")
if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then
failure <<EOF
It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
with the UID $primary_group_id. This script can't really handle
that right now, so I'm going to give up.
You can fix this by editing this script and changing the
NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
to $primary_group_id and re-run.
EOF
else
row " Exists" "Yes"
fi
fi
}
create_build_user_for_core() {
local coreid
local username
local uid
coreid="$1"
username=$(nix_user_for_core "$coreid")
uid=$(nix_uid_for_core "$coreid")
dsclpath="/Users/$username"
task "Setting up the build user $username"
if ! /usr/bin/dscl . -read "$dsclpath" > /dev/null 2>&1; then
_sudo "Creating the Nix build user, $username" \
/usr/bin/dscl . create "$dsclpath" \
UniqueID "${uid}"
row " Created" "Yes"
else
actual_uid=$(dsclattr "$dsclpath" "UniqueID")
if [ "$actual_uid" -ne "$uid" ]; then
failure <<EOF
It seems the build user $username already exists, but with the UID
with the UID $actual_uid. This script can't really handle that right
now, so I'm going to give up.
If you already created the users and you know they start from
$actual_uid and go up from there, you can edit this script and change
NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try
again.
EOF
else
row " Exists" "Yes"
fi
fi
if [ "$(dsclattr "$dsclpath" "IsHidden")" = "1" ]; then
row " IsHidden" "Yes"
else
_sudo "in order to make $username a hidden user" \
/usr/bin/dscl . -create "$dsclpath" "IsHidden" "1"
row " IsHidden" "Yes"
fi
if [ "$(dsclattr "$dsclpath" "NFSHomeDirectory")" = "/var/empty" ]; then
row " NFSHomeDirectory" "/var/empty"
else
_sudo "in order to give $username a safe home directory" \
/usr/bin/dscl . -create "$dsclpath" "NFSHomeDirectory" "/var/empty"
row " NFSHomeDirectory" "/var/empty"
fi
if [ "$(dsclattr "$dsclpath" "RealName")" = "Nix build user $coreid" ]; then
row " RealName" "Nix build user $coreid"
else
_sudo "in order to give $username a useful name" \
/usr/bin/dscl . -create "$dsclpath" "RealName" "Nix build user $coreid"
row " RealName" "Nix build user $coreid"
fi
if [ "$(dsclattr "$dsclpath" "UserShell")" = "/sbin/nologin" ]; then
row " Logins Disabled" "Yes"
else
_sudo "in order to prevent $username from logging in" \
/usr/bin/dscl . -create "$dsclpath" "UserShell" "/sbin/nologin"
row " Logins Disabled" "Yes"
fi
if dseditgroup -o checkmember -m "$username" "$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1 ; then
row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
else
_sudo "Add $username to the $NIX_BUILD_GROUP_NAME group"\
/usr/sbin/dseditgroup -o edit -t user \
-a "$username" "$NIX_BUILD_GROUP_NAME"
row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
fi
if [ "$(dsclattr "$dsclpath" "PrimaryGroupID")" = "$NIX_BUILD_GROUP_ID" ]; then
row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
else
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
/usr/bin/dscl . -create "$dsclpath" "PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
fi
}
create_build_users() {
for i in $(seq 1 "$NIX_USER_COUNT"); do
create_build_user_for_core "$i"
done
}
create_directories() {
_sudo "to make the basic directory structure of Nix (part 1)" \
mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool}
_sudo "to make the basic directory structure of Nix (part 2)" \
mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user
_sudo "to make the basic directory structure of Nix (part 3)" \
mkdir -pv -m 1775 /nix/store
_sudo "to make the basic directory structure of Nix (part 4)" \
chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
_sudo "to set up the root user's profile (part 1)" \
mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root
_sudo "to set up the root user's profile (part 2)" \
mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr"
_sudo "to place the default nix daemon configuration (part 1)" \
mkdir -pv -m 0555 /etc/nix
}
place_channel_configuration() {
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
_sudo "to set up the default system channel (part 1)" \
install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
}
welcome_to_nix() {
ok "Welcome to the Multi-User Nix Installation"
cat <<EOF
This installation tool will set up your computer with the Nix package
manager. This will happen in a few stages:
1. Make sure your computer doesn't already have Nix. If it does, I
will show you instructions on how to clean up your old one.
2. Show you what we are going to install and where. Then we will ask
if you are ready to continue.
3. Create the system users and groups that the Nix daemon uses to run
builds.
4. Perform the basic installation of the Nix files daemon.
5. Configure your shell to import special Nix Profile files, so you
can use Nix.
6. Start the Nix daemon.
EOF
if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
cat <<EOF
We will:
- make sure your computer doesn't already have Nix files
(if it does, I will tell you how to clean them up.)
- create local users (see the list above for the users we'll make)
- create a local group ($NIX_BUILD_GROUP_NAME)
- install Nix in to $NIX_ROOT
- create a configuration file in /etc/nix
- set up the "default profile" by creating some Nix-related files in
$ROOT_HOME
EOF
for profile_target in "${PROFILE_TARGETS[@]}"; do
if [ -e "$profile_target" ]; then
cat <<EOF
- back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX
- update $profile_target to include some Nix configuration
EOF
fi
done
cat <<EOF
- load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
EOF
if ! ui_confirm "Ready to continue?"; then
failure <<EOF
Okay, maybe you would like to talk to the team.
EOF
fi
fi
}
chat_about_sudo() {
header "let's talk about sudo"
if headless; then
cat <<EOF
This script is going to call sudo a lot. Normally, it would show you
exactly what commands it is running and why. However, the script is
run in a headless fashion, like this:
$ curl https://nixos.org/nix/install | sh
or maybe in a CI pipeline. Because of that, we're going to skip the
verbose output in the interest of brevity.
If you would like to
see the output, try like this:
$ curl -o install-nix https://nixos.org/nix/install
$ sh ./install-nix
EOF
return 0
fi
cat <<EOF
This script is going to call sudo a lot. Every time we do, it'll
output exactly what it'll do, and why.
Just like this:
EOF
__sudo "to demonstrate how our sudo prompts look" \
echo "this is a sudo prompt"
cat <<EOF
This might look scary, but everything can be undone by running just a
few commands. We used to ask you to confirm each time sudo ran, but it
was too many times. Instead, I'll just ask you this one time:
EOF
if ui_confirm "Can we use sudo?"; then
ok "Yay! Thanks! Let's get going!"
else
failure <<EOF
That is okay, but we can't install.
EOF
fi
}
install_from_extracted_nix() {
(
cd "$EXTRACTED_NIX_PATH"
_sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
rsync -rlpt "$(pwd)/store/" "$NIX_ROOT/store/"
if [ -d "$NIX_INSTALLED_NIX" ]; then
echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
else
failure <<EOF
Something went wrong, and I didn't find Nix installed at
$NIX_INSTALLED_NIX.
EOF
fi
_sudo "to initialize the Nix Database" \
$NIX_INSTALLED_NIX/bin/nix-store --init
cat ./.reginfo \
| _sudo "to load data for the first time in to the Nix Database" \
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db
echo " Just finished getting the nix database ready."
)
}
shell_source_lines() {
cat <<EOF
# Nix
if [ -e '$PROFILE_NIX_FILE' ]; then
. '$PROFILE_NIX_FILE'
fi
# End Nix
EOF
}
configure_shell_profile() {
for profile_target in "${PROFILE_TARGETS[@]}"; do
if [ -e "$profile_target" ]; then
_sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
shell_source_lines \
| _sudo "extend your $profile_target with nix-daemon settings" \
tee -a "$profile_target"
fi
done
}
setup_default_profile() {
_sudo "to installing a bootstrapping Nix in to the default Profile" \
HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
_sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
_sudo "to update the default channel in the default profile" \
HOME=$ROOT_HOME NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs
}
place_nix_configuration() {
cat <<EOF > "$SCRATCH/nix.conf"
build-users-group = $NIX_BUILD_GROUP_NAME
max-jobs = $NIX_USER_COUNT
cores = 1
sandbox = false
EOF
_sudo "to place the default nix daemon configuration (part 2)" \
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
}
configure_nix_daemon_plist() {
_sudo "to set up the nix-daemon as a LaunchDaemon" \
ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
_sudo "to load the LaunchDaemon plist for nix-daemon" \
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
_sudo "to start the nix-daemon" \
launchctl start org.nixos.nix-daemon
}
main() {
welcome_to_nix
chat_about_sudo
if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then
validate_starting_assumptions
fi
setup_report
if ! ui_confirm "Ready to continue?"; then
ok "Alright, no changes have been made :)"
contactme
trap finish_cleanup EXIT
exit 1
fi
create_build_group
create_build_users
create_directories
place_channel_configuration
install_from_extracted_nix
configure_shell_profile
set +eu
. /etc/profile
set -eu
setup_default_profile
place_nix_configuration
configure_nix_daemon_plist
trap finish_success EXIT
}
main

View File

@@ -7,9 +7,9 @@ self="$(dirname "$0")"
nix="@nix@"
cacert="@cacert@"
if ! [ -e "$self/.reginfo" ]; then
echo "$0: incomplete installer (.reginfo is missing)" >&2
exit 1
fi
if [ -z "$USER" ]; then
@@ -17,23 +17,6 @@ if [ -z "$USER" ]; then
exit 1
fi
if [ -z "$HOME" ]; then
echo "$0: \$HOME is not set" >&2
exit 1
fi
# macOS support for 10.10 or higher
if [ "$(uname -s)" = "Darwin" ]; then
if [ $(($(sw_vers -productVersion | cut -d '.' -f 2))) -lt 10 ]; then
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.10 or higher"
exit 1
fi
printf '\e[1;31mSwitching to the Multi-User Darwin Installer\e[0m\n'
exec "$self/install-darwin-multi-user"
exit 0
fi
if [ "$(id -u)" -eq 0 ]; then
printf '\e[1;31mwarning: installing Nix as root is not supported by this script!\e[0m\n'
fi
@@ -44,13 +27,13 @@ if ! [ -e $dest ]; then
cmd="mkdir -m 0755 $dest && chown $USER $dest"
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
if ! sudo sh -c "$cmd"; then
echo "$0: please manually run '$cmd' as root to create $dest" >&2
echo "$0: please manually run $cmd as root to create $dest" >&2
exit 1
fi
fi
if ! [ -w $dest ]; then
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see http://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see http://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run chown -R $USER $dest as root." >&2
exit 1
fi
@@ -107,43 +90,37 @@ if [ -z "$_NIX_INSTALLER_TEST" ]; then
fi
added=
# Make the shell source nix.sh during login.
p=$HOME/.nix-profile/etc/profile.d/nix.sh
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
# Make the shell source nix.sh during login.
p=$HOME/.nix-profile/etc/profile.d/nix.sh
for i in .bash_profile .bash_login .profile; do
fn="$HOME/$i"
if [ -w "$fn" ]; then
if ! grep -q "$p" "$fn"; then
echo "modifying $fn..." >&2
echo "$0: modifying $fn..." >&2
echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> "$fn"
fi
added=1
break
fi
done
fi
if [ -z "$added" ]; then
cat >&2 <<EOF
Installation finished! To ensure that the necessary environment
variables are set, please add the line
. $p
to your shell profile (e.g. ~/.profile).
EOF
else
cat >&2 <<EOF
Installation finished! To ensure that the necessary environment
variables are set, either log in again, or type
. $p
in your shell.
EOF
echo "$0: creating ~/.bash_profile ..." >&2
touch ~/.bash_profile
chmod +x ~/.bash_profile
echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> /.bash_profile
fi
cat >&2 <<EOF
Installation has finished successfully.
To start using Nix, execute in your shell:
$ source $p
EOF

View File

@@ -1,4 +1,10 @@
nix_bin_scripts := \
$(d)/nix-copy-closure \
bin-scripts += $(nix_bin_scripts)
nix_noinst_scripts := \
$(d)/build-remote.pl \
$(d)/nix-http-export.cgi \
$(d)/nix-profile.sh \
$(d)/nix-reduce-build
@@ -8,6 +14,6 @@ noinst-scripts += $(nix_noinst_scripts)
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-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
$(eval $(call install-program-in, $(d)/build-remote.pl, $(libexecdir)/nix))
clean-files += $(nix_noinst_scripts)
clean-files += $(nix_bin_scripts) $(nix_noinst_scripts)

103
scripts/nix-copy-closure.in Executable file
View File

@@ -0,0 +1,103 @@
#! @perl@ -w @perlFlags@
use utf8;
use strict;
use Nix::SSH;
use Nix::Config;
use Nix::Store;
use Nix::CopyClosure;
use List::Util qw(sum);
binmode STDERR, ":encoding(utf8)";
if (scalar @ARGV < 1) {
print STDERR <<EOF
Usage: nix-copy-closure [--from | --to] HOSTNAME [--gzip] [--bzip2] [--xz] PATHS...
EOF
;
exit 1;
}
# Get the target host.
my $sshHost;
my $toMode = 1;
my $includeOutputs = 0;
my $dryRun = 0;
my $useSubstitutes = 0;
my $verbosity = 1;
# !!! Copied from nix-pack-closure, should put this in a module.
my @storePaths = ();
while (@ARGV) {
my $arg = shift @ARGV;
if ($arg eq "--help") {
exec "man nix-copy-closure" or die;
}
elsif ($arg eq "--gzip" || $arg eq "--bzip2" || $arg eq "--xz") {
warn "$0: $arg is not implemented\n" if $arg ne "--gzip";
push @globalSshOpts, "-C";
}
elsif ($arg eq "--from") {
$toMode = 0;
}
elsif ($arg eq "--to") {
$toMode = 1;
}
elsif ($arg eq "--include-outputs") {
$includeOutputs = 1;
}
elsif ($arg eq "--show-progress") {
warn "$0: $arg is not implemented\n";
}
elsif ($arg eq "--dry-run") {
$dryRun = 1;
}
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
$useSubstitutes = 1;
}
elsif ($arg eq "-v") {
$verbosity++;
setVerbosity($verbosity);
}
elsif (!defined $sshHost) {
$sshHost = $arg;
}
else {
push @storePaths, $arg;
}
}
die "$0: you did not specify a host name\n" unless defined $sshHost;
if ($toMode) { # Copy TO the remote machine.
Nix::CopyClosure::copyTo(
$sshHost, [ @storePaths ],
$includeOutputs, $dryRun, $useSubstitutes);
}
else { # Copy FROM the remote machine.
my ($from, $to) = connectToRemoteNix($sshHost, []);
# Query the closure of the given store paths on the remote
# machine. Paths are assumed to be store paths; there is no
# resolution (following of symlinks).
syswrite($to, pack("L<x4L<x4", 7, $includeOutputs ? 1 : 0)) or die;
writeStrings(\@storePaths, $to);
my @missing = grep { !isValidPath($_) } readStrings($from);
# Export the store paths on the remote machine and import them locally.
if (scalar @missing > 0) {
print STDERR "copying ", scalar @missing, " missing paths from $sshHost...\n";
writeInt(5, $to); # == cmdExportPaths
writeInt(0, $to); # obsolete
writeStrings(\@missing, $to);
importPaths(fileno($from), 1);
}
}

View File

@@ -1,54 +0,0 @@
# Only execute this file once per shell.
if [ -n "$__ETC_PROFILE_NIX_SOURCED" ]; then return; fi
__ETC_PROFILE_NIX_SOURCED=1
# Set up secure multi-user builds: non-root users build through the
# Nix daemon.
if [ "$USER" != root -o ! -w @localstatedir@/nix/db ]; then
export NIX_REMOTE=daemon
fi
export NIX_USER_PROFILE_DIR="@localstatedir@/nix/profiles/per-user/$USER"
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set up the per-user profile.
mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
if ! test -O "$NIX_USER_PROFILE_DIR"; then
echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
fi
if test -w $HOME; then
if ! test -L $HOME/.nix-profile; then
if test "$USER" != root; then
ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
else
# Root installs in the system-wide profile by default.
ln -s @localstatedir@/nix/profiles/default $HOME/.nix-profile
fi
fi
# Subscribe the root user to the NixOS channel by default.
if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
fi
# Create the per-user garbage collector roots directory.
NIX_USER_GCROOTS_DIR=@localstatedir@/nix/gcroots/per-user/$USER
mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
if ! test -O "$NIX_USER_GCROOTS_DIR"; then
echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
fi
# Set up a default Nix expression from which to install stuff.
if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
rm -f $HOME/.nix-defexpr
mkdir -p $HOME/.nix-defexpr
if [ "$USER" != root ]; then
ln -s @localstatedir@/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
fi
fi
fi
export NIX_SSL_CERT_FILE="@localstatedir@/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
export NIX_PATH="@localstatedir@/nix/profiles/per-user/root/channels"
export PATH="$HOME/.nix-profile/bin:$HOME/.nix-profile/lib/kde4/libexec:@localstatedir@/nix/profiles/default/bin:@localstatedir@/nix/profiles/default:@localstatedir@/nix/profiles/default/lib/kde4/libexec:$PATH"

View File

@@ -85,6 +85,6 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
export MANPATH="$NIX_LINK/share/man:$MANPATH"
fi
export PATH="$NIX_LINK/bin:$__savedpath"
export PATH="$NIX_LINK/bin:$NIX_LINK/sbin:$__savedpath"
unset __savedpath NIX_LINK NIX_USER_PROFILE_DIR NIX_PROFILES
fi

View File

@@ -1,15 +1,10 @@
{ useClang ? false }:
with import <nixpkgs> {};
with import ./release-common.nix { inherit pkgs; };
(if useClang then clangStdenv else stdenv).mkDerivation {
stdenv.mkDerivation {
name = "nix";
buildInputs =
[ curl bison flex libxml2 libxslt
bzip2 xz brotli
[ curl bison flex perl libxml2 libxslt bzip2 xz
pkgconfig sqlite libsodium boehmgc
docbook5 docbook5_xsl
autoconf-archive
@@ -18,18 +13,15 @@ with import ./release-common.nix { inherit pkgs; };
customMemoryManagement = false;
})
autoreconfHook
# For nix-perl
perl
perlPackages.DBDSQLite
];
# Tests
git
mercurial
]
++ lib.optionals stdenv.isLinux [ libseccomp fuse ];
inherit configureFlags;
configureFlags =
[ "--disable-init-state"
"--enable-gc"
"--with-dbi=${perlPackages.DBI}/${perl.libPrefix}"
"--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}"
];
enableParallelBuilding = true;
@@ -37,9 +29,6 @@ with import ./release-common.nix { inherit pkgs; };
shellHook =
''
export prefix=$(pwd)/inst
configureFlags+=" --prefix=$prefix"
PKG_CONFIG_PATH=$prefix/lib/pkgconfig:$PKG_CONFIG_PATH
PATH=$prefix/bin:$PATH
configureFlags+=" --prefix=$(pwd)/inst"
'';
}

View File

@@ -37,13 +37,6 @@ namespace {
os.str(emptyStr);
}
void do_pad( std::string & s,
std::streamsize w,
const char c,
std::ios::fmtflags f,
bool center)
__attribute__ ((unused));
void do_pad( std::string & s,
std::streamsize w,
const char c,

View File

@@ -1,248 +0,0 @@
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <set>
#include <memory>
#include <tuple>
#include <iomanip>
#if __APPLE__
#include <sys/time.h>
#endif
#include "machines.hh"
#include "shared.hh"
#include "pathlocks.hh"
#include "globals.hh"
#include "serialise.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "local-store.hh"
using namespace nix;
using std::cin;
static void handleAlarm(int sig) {
}
std::string escapeUri(std::string uri)
{
std::replace(uri.begin(), uri.end(), '/', '_');
return uri;
}
static string currentLoad;
static AutoCloseFD openSlotLock(const Machine & m, unsigned long long slot)
{
return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true);
}
int main (int argc, char * * argv)
{
return handleExceptions(argv[0], [&]() {
initNix();
logger = makeJSONLogger(*logger);
/* Ensure we don't get any SSH passphrase or host key popups. */
unsetenv("DISPLAY");
unsetenv("SSH_ASKPASS");
if (argc != 2)
throw UsageError("called without required arguments");
verbosity = (Verbosity) std::stoll(argv[1]);
FdSource source(STDIN_FILENO);
/* Read the parent's settings. */
while (readInt(source)) {
auto name = readString(source);
auto value = readString(source);
settings.set(name, value);
}
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
auto store = openStore().cast<LocalStore>();
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
that gets cleared on reboot, but it wouldn't work on macOS. */
currentLoad = store->stateDir + "/current-load";
std::shared_ptr<Store> sshStore;
AutoCloseFD bestSlotLock;
auto machines = getMachines();
debug("got %d remote builders", machines.size());
if (machines.empty()) {
std::cerr << "# decline-permanently\n";
return;
}
string drvPath;
string storeUri;
while (true) {
try {
auto s = readString(source);
if (s != "try") return;
} catch (EndOfFile &) { return; }
auto amWilling = readInt(source);
auto neededSystem = readString(source);
source >> drvPath;
auto requiredFeatures = readStrings<std::set<std::string>>(source);
auto canBuildLocally = amWilling && (neededSystem == settings.thisSystem);
/* Error ignored here, will be caught later */
mkdir(currentLoad.c_str(), 0777);
while (true) {
bestSlotLock = -1;
AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true);
lockFile(lock.get(), ltWrite, true);
bool rightType = false;
Machine * bestMachine = nullptr;
unsigned long long bestLoad = 0;
for (auto & m : machines) {
debug("considering building on remote machine '%s'", m.storeUri);
if (m.enabled && std::find(m.systemTypes.begin(),
m.systemTypes.end(),
neededSystem) != m.systemTypes.end() &&
m.allSupported(requiredFeatures) &&
m.mandatoryMet(requiredFeatures)) {
rightType = true;
AutoCloseFD free;
unsigned long long load = 0;
for (unsigned long long slot = 0; slot < m.maxJobs; ++slot) {
auto slotLock = openSlotLock(m, slot);
if (lockFile(slotLock.get(), ltWrite, false)) {
if (!free) {
free = std::move(slotLock);
}
} else {
++load;
}
}
if (!free) {
continue;
}
bool best = false;
if (!bestSlotLock) {
best = true;
} else if (load / m.speedFactor < bestLoad / bestMachine->speedFactor) {
best = true;
} else if (load / m.speedFactor == bestLoad / bestMachine->speedFactor) {
if (m.speedFactor > bestMachine->speedFactor) {
best = true;
} else if (m.speedFactor == bestMachine->speedFactor) {
if (load < bestLoad) {
best = true;
}
}
}
if (best) {
bestLoad = load;
bestSlotLock = std::move(free);
bestMachine = &m;
}
}
}
if (!bestSlotLock) {
if (rightType && !canBuildLocally)
std::cerr << "# postpone\n";
else
std::cerr << "# decline\n";
break;
}
#if __APPLE__
futimes(bestSlotLock.get(), NULL);
#else
futimens(bestSlotLock.get(), NULL);
#endif
lock = -1;
try {
Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri));
Store::Params storeParams;
if (hasPrefix(bestMachine->storeUri, "ssh://")) {
storeParams["max-connections"] ="1";
storeParams["log-fd"] = "4";
if (bestMachine->sshKey != "")
storeParams["ssh-key"] = bestMachine->sshKey;
}
sshStore = openStore(bestMachine->storeUri, storeParams);
sshStore->connect();
storeUri = bestMachine->storeUri;
} catch (std::exception & e) {
printError("unable to open SSH connection to '%s': %s; trying other available machines...",
bestMachine->storeUri, e.what());
bestMachine->enabled = false;
continue;
}
goto connected;
}
}
connected:
std::cerr << "# accept\n" << storeUri << "\n";
auto inputs = readStrings<PathSet>(source);
auto outputs = readStrings<PathSet>(source);
AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri));
auto old = signal(SIGALRM, handleAlarm);
alarm(15 * 60);
if (!lockFile(uploadLock.get(), ltWrite, true))
printError("somebody is hogging the upload lock for '%s', continuing...");
alarm(0);
signal(SIGALRM, old);
}
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
copyPaths(store, ref<Store>(sshStore), inputs, NoRepair, NoCheckSigs);
}
uploadLock = -1;
BasicDerivation drv(readDerivation(store->realStoreDir + "/" + baseNameOf(drvPath)));
drv.inputSrcs = inputs;
auto result = sshStore->buildDerivation(drvPath, drv);
if (!result.success())
throw Error("build of '%s' on '%s' failed: %s", drvPath, storeUri, result.errorMsg);
PathSet missing;
for (auto & path : outputs)
if (!store->isValidPath(path)) missing.insert(path);
if (!missing.empty()) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
setenv("NIX_HELD_LOCKS", concatStringsSep(" ", missing).c_str(), 1); /* FIXME: ugly */
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs);
}
return;
});
}

View File

@@ -1,9 +0,0 @@
programs += build-remote
build-remote_DIR := $(d)
build-remote_INSTALL_DIR := $(libexecdir)/nix
build-remote_LIBS = libmain libutil libformat libstore
build-remote_SOURCES := $(d)/build-remote.cc

View File

@@ -12,7 +12,7 @@ static bool isDirectory (const Path & path)
{
struct stat st;
if (stat(path.c_str(), &st) == -1)
throw SysError(format("getting status of '%1%'") % path);
throw SysError(format("getting status of %1%") % path);
return S_ISDIR(st.st_mode);
}
@@ -53,18 +53,18 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
} else if (S_ISLNK(dstSt.st_mode)) {
auto target = readLink(dstFile);
if (!isDirectory(target))
throw Error(format("collision between '%1%' and non-directory '%2%'")
throw Error(format("collision between %1% and non-directory %2%")
% srcFile % target);
if (unlink(dstFile.c_str()) == -1)
throw SysError(format("unlinking '%1%'") % dstFile);
throw SysError(format("unlinking %1%") % dstFile);
if (mkdir(dstFile.c_str(), 0755) == -1)
throw SysError(format("creating directory '%1%'"));
throw SysError(format("creating directory %1%"));
createLinks(target, dstFile, priorities[dstFile]);
createLinks(srcFile, dstFile, priority);
continue;
}
} else if (errno != ENOENT)
throw SysError(format("getting status of '%1%'") % dstFile);
throw SysError(format("getting status of %1%") % dstFile);
} else {
struct stat dstSt;
auto res = lstat(dstFile.c_str(), &dstSt);
@@ -74,18 +74,17 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
auto prevPriority = priorities[dstFile];
if (prevPriority == priority)
throw Error(format(
"Packages '%1%' and '%2%' have the same priority '%3%'"
"use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' "
"collision between %1% and %2%; "
"use nix-env --set-flag priority NUMBER PKGNAME "
"to change the priority of one of the conflicting packages"
" ('0' being the highest priority)"
) % srcFile % target % priority);
) % srcFile % target);
if (prevPriority < priority)
continue;
if (unlink(dstFile.c_str()) == -1)
throw SysError(format("unlinking '%1%'") % dstFile);
throw SysError(format("unlinking %1%") % dstFile);
}
} else if (errno != ENOENT)
throw SysError(format("getting status of '%1%'") % dstFile);
throw SysError(format("getting status of %1%") % dstFile);
}
createSymlink(srcFile, dstFile);
priorities[dstFile] = priority;
@@ -113,11 +112,11 @@ static void addPkg(const Path & pkgDir, int priority)
if (!fd) {
if (errno == ENOENT)
return;
throw SysError(format("opening '%1%'") % propagatedFN);
throw SysError(format("opening %1%") % propagatedFN);
}
propagated = readFile(fd.get());
propagated = readLine(fd.get());
}
for (const auto & p : tokenizeString<std::vector<string>>(propagated, " \n"))
for (const auto & p : tokenizeString<std::vector<string>>(propagated, " "))
if (done.find(p) == done.end())
postponed.insert(p);
}

View File

@@ -0,0 +1,141 @@
#include "shared.hh"
#include "util.hh"
#include "serialise.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include "serve-protocol.hh"
#include "worker-protocol.hh"
#include "store-api.hh"
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace nix;
// !!! TODO:
// * Respect more than the first host
// * use a database
// * show progress
static std::pair<FdSink, FdSource> connect(const string & conn)
{
Pipe to, from;
to.create();
from.create();
startProcess([&]() {
if (dup2(to.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (dup2(from.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL);
throw SysError("executing ssh");
});
// If child exits unexpectedly, we'll EPIPE or EOF early.
// If we exit unexpectedly, child will EPIPE or EOF early.
// So no need to keep track of it.
return std::pair<FdSink, FdSource>(to.writeSide.borrow(), from.readSide.borrow());
}
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
{
pipes.first << cmdDumpStorePath << storePath;
pipes.first.flush();
restorePath(destPath, pipes.second);
std::cout << std::endl;
}
static void query(std::pair<FdSink, FdSource> & pipes)
{
for (string line; getline(std::cin, line);) {
Strings tokenized = tokenizeString<Strings>(line);
string cmd = tokenized.front();
tokenized.pop_front();
if (cmd == "have") {
pipes.first
<< cmdQueryValidPaths
<< 0 // don't lock
<< 0 // don't substitute
<< tokenized;
pipes.first.flush();
PathSet paths = readStrings<PathSet>(pipes.second);
for (auto & i : paths)
std::cout << i << std::endl;
} else if (cmd == "info") {
pipes.first << cmdQueryPathInfos << tokenized;
pipes.first.flush();
while (1) {
Path path = readString(pipes.second);
if (path.empty()) break;
assertStorePath(path);
std::cout << path << std::endl;
string deriver = readString(pipes.second);
if (!deriver.empty()) assertStorePath(deriver);
std::cout << deriver << std::endl;
PathSet references = readStorePaths<PathSet>(pipes.second);
std::cout << references.size() << std::endl;
for (auto & i : references)
std::cout << i << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
}
} else
throw Error(format("unknown substituter query %1%") % cmd);
std::cout << std::endl;
}
}
int main(int argc, char * * argv)
{
return handleExceptions(argv[0], [&]() {
if (argc < 2)
throw UsageError("download-via-ssh requires an argument");
initNix();
settings.update();
if (settings.sshSubstituterHosts.empty())
return;
std::cout << std::endl;
/* Pass on the location of the daemon client's SSH
authentication socket. */
string sshAuthSock = settings.get("ssh-auth-sock", string(""));
if (sshAuthSock != "") setenv("SSH_AUTH_SOCK", sshAuthSock.c_str(), 1);
string host = settings.sshSubstituterHosts.front();
std::pair<FdSink, FdSource> pipes = connect(host);
/* Exchange the greeting */
pipes.first << SERVE_MAGIC_1;
pipes.first.flush();
unsigned int magic = readInt(pipes.second);
if (magic != SERVE_MAGIC_2)
throw Error("protocol mismatch");
readInt(pipes.second); // Server version, unused for now
pipes.first << SERVE_PROTOCOL_VERSION;
pipes.first.flush();
string arg = argv[1];
if (arg == "--query")
query(pipes);
else if (arg == "--substitute") {
if (argc != 4)
throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
Path storePath = argv[2];
Path destPath = argv[3];
printError(format("downloading %1% via SSH from %2%...") % storePath % host);
substitute(pipes, storePath, destPath);
}
else
throw UsageError(format("download-via-ssh: unknown command %1%") % arg);
});
}

View File

@@ -0,0 +1,11 @@
programs += download-via-ssh
download-via-ssh_DIR := $(d)
download-via-ssh_SOURCES := $(d)/download-via-ssh.cc
download-via-ssh_INSTALL_DIR := $(libexecdir)/nix/substituters
download-via-ssh_CXXFLAGS = -Isrc/nix-store
download-via-ssh_LIBS = libmain libstore libutil libformat

View File

@@ -19,7 +19,7 @@ static Strings parseAttrPath(const string & s)
++i;
while (1) {
if (i == s.end())
throw Error(format("missing closing quote in selection path '%1%'") % s);
throw Error(format("missing closing quote in selection path %1%") % s);
if (*i == '"') break;
cur.push_back(*i++);
}
@@ -38,7 +38,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Strings tokens = parseAttrPath(attrPath);
Error attrError =
Error(format("attribute selection path '%1%' does not match expression") % attrPath);
Error(format("attribute selection path %1% does not match expression") % attrPath);
Value * v = &vIn;
@@ -62,15 +62,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
if (v->type != tAttrs)
throw TypeError(
format("the expression selected by the selection path '%1%' should be a set but is %2%")
format("the expression selected by the selection path %1% should be a set but is %2%")
% attrPath % showType(*v));
if (attr.empty())
throw Error(format("empty attribute name in selection path '%1%'") % attrPath);
throw Error(format("empty attribute name in selection path %1%") % attrPath);
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
if (a == v->attrs->end())
throw Error(format("attribute '%1%' in selection path '%2%' not found") % attr % attrPath);
throw Error(format("attribute %1% in selection path %2% not found") % attr % attrPath);
v = &*a->value;
}
@@ -78,11 +78,11 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
if (!v->isList())
throw TypeError(
format("the expression selected by the selection path '%1%' should be a list but is %2%")
format("the expression selected by the selection path %1% should be a list but is %2%")
% attrPath % showType(*v));
if (attrIndex >= v->listSize())
throw Error(format("list index %1% in selection path '%2%' is out of range") % attrIndex % attrPath);
throw Error(format("list index %1% in selection path %2% is out of range") % attrIndex % attrPath);
v = v->listElems()[attrIndex];
}

View File

@@ -75,19 +75,6 @@ public:
size_t capacity() { return capacity_; }
/* Returns the attributes in lexicographically sorted order. */
std::vector<const Attr *> lexicographicOrder() const
{
std::vector<const Attr *> res;
res.reserve(size_);
for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]);
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
return (string) a->name < (string) b->name;
});
return res;
}
friend class EvalState;
};

View File

@@ -1,57 +0,0 @@
#include "common-eval-args.hh"
#include "shared.hh"
#include "download.hh"
#include "util.hh"
#include "eval.hh"
namespace nix {
MixEvalArgs::MixEvalArgs()
{
mkFlag()
.longName("arg")
.description("argument to be passed to Nix functions")
.labels({"name", "expr"})
.handler([&](std::vector<std::string> ss) { autoArgs[ss[0]] = 'E' + ss[1]; });
mkFlag()
.longName("argstr")
.description("string-valued argument to be passed to Nix functions")
.labels({"name", "string"})
.handler([&](std::vector<std::string> ss) { autoArgs[ss[0]] = 'S' + ss[1]; });
mkFlag()
.shortName('I')
.longName("include")
.description("add a path to the list of locations used to look up <...> file names")
.label("path")
.handler([&](std::string s) { searchPath.push_back(s); });
}
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
{
Bindings * res = state.allocBindings(autoArgs.size());
for (auto & i : autoArgs) {
Value * v = state.allocValue();
if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
else
mkString(*v, string(i.second, 1));
res->push_back(Attr(state.symbols.create(i.first), v));
}
res->sort();
return res;
}
Path lookupFileArg(EvalState & state, string s)
{
if (isUri(s))
return getDownloader()->downloadCached(state.store, s, true);
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
Path p = s.substr(1, s.size() - 2);
return state.findFile(p);
} else
return absPath(s);
}
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include "args.hh"
namespace nix {
class Store;
class EvalState;
class Bindings;
struct MixEvalArgs : virtual Args
{
MixEvalArgs();
Bindings * getAutoArgs(EvalState & state);
Strings searchPath;
private:
std::map<std::string, std::string> autoArgs;
};
Path lookupFileArg(EvalState & state, string s);
}

View File

@@ -0,0 +1,67 @@
#include "common-opts.hh"
#include "shared.hh"
#include "download.hh"
#include "util.hh"
namespace nix {
bool parseAutoArgs(Strings::iterator & i,
const Strings::iterator & argsEnd, std::map<string, string> & res)
{
string arg = *i;
if (arg != "--arg" && arg != "--argstr") return false;
UsageError error(format("%1% requires two arguments") % arg);
if (++i == argsEnd) throw error;
string name = *i;
if (++i == argsEnd) throw error;
string value = *i;
res[name] = (arg == "--arg" ? 'E' : 'S') + value;
return true;
}
Bindings * evalAutoArgs(EvalState & state, std::map<string, string> & in)
{
Bindings * res = state.allocBindings(in.size());
for (auto & i : in) {
Value * v = state.allocValue();
if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
else
mkString(*v, string(i.second, 1));
res->push_back(Attr(state.symbols.create(i.first), v));
}
res->sort();
return res;
}
bool parseSearchPathArg(Strings::iterator & i,
const Strings::iterator & argsEnd, Strings & searchPath)
{
if (*i != "-I") return false;
if (++i == argsEnd) throw UsageError("-I requires an argument");
searchPath.push_back(*i);
return true;
}
Path lookupFileArg(EvalState & state, string s)
{
if (isUri(s))
return getDownloader()->downloadCached(state.store, s, true);
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
Path p = s.substr(1, s.size() - 2);
return state.findFile(p);
} else
return absPath(s);
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "eval.hh"
namespace nix {
class Store;
/* Some common option parsing between nix-env and nix-instantiate. */
bool parseAutoArgs(Strings::iterator & i,
const Strings::iterator & argsEnd, std::map<string, string> & res);
Bindings * evalAutoArgs(EvalState & state, std::map<string, string> & in);
bool parseSearchPathArg(Strings::iterator & i,
const Strings::iterator & argsEnd, Strings & searchPath);
Path lookupFileArg(EvalState & state, string s);
}

View File

@@ -33,7 +33,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
v.type = tBlackhole;
//checkInterrupt();
expr->eval(*this, *env, v);
} catch (...) {
} catch (Error & e) {
v.type = tThunk;
v.thunk.env = env;
v.thunk.expr = expr;

View File

@@ -91,9 +91,13 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
break;
case tAttrs: {
str << "{ ";
for (auto & i : v.attrs->lexicographicOrder()) {
str << i->name << " = ";
printValue(str, active, *i->value);
typedef std::map<string, Value *> Sorted;
Sorted sorted;
for (auto & i : *v.attrs)
sorted[i.name] = i.value;
for (auto & i : sorted) {
str << i.first << " = ";
printValue(str, active, *i.second);
str << "; ";
}
str << "}";
@@ -149,7 +153,7 @@ string showType(const Value & v)
switch (v.type) {
case tInt: return "an integer";
case tBool: return "a boolean";
case tString: return v.string.context ? "a string with context" : "a string";
case tString: return "a string";
case tPath: return "a path";
case tNull: return "null";
case tAttrs: return "a set";
@@ -202,7 +206,7 @@ void initGC()
GC_INIT();
GC_set_oom_fn(oomHandler);
GC_oom_fn = oomHandler;
/* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 384 MiB) so that in most cases
@@ -291,16 +295,13 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
, sToString(symbols.create("__toString"))
, sRight(symbols.create("right"))
, sWrong(symbols.create("wrong"))
, sStructuredAttrs(symbols.create("__structuredAttrs"))
, sBuilder(symbols.create("builder"))
, repair(NoRepair)
, store(store)
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
{
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
restricted = settings.restrictEval;
restricted = settings.get("restrict-eval", false);
assert(gcInitialised);
@@ -329,7 +330,7 @@ Path EvalState::checkSourcePath(const Path & path_)
if (!restricted) return path_;
/* Resolve symlinks. */
debug(format("checking access to '%s'") % path_);
debug(format("checking access to %s") % path_);
Path path = canonPath(path_, true);
for (auto & i : searchPath) {
@@ -351,27 +352,7 @@ Path EvalState::checkSourcePath(const Path & path_)
return path;
#endif
throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_);
}
void EvalState::checkURI(const std::string & uri)
{
if (!restricted) return;
/* 'uri' should be equal to a prefix, or in a subdirectory of a
prefix. Thus, the prefix https://github.co does not permit
access to https://github.com. Note: this allows 'http://' and
'https://' as prefixes for any http/https URI. */
for (auto & prefix : settings.allowedUris.get())
if (uri == prefix ||
(uri.size() > prefix.size()
&& prefix.size() > 0
&& hasPrefix(uri, prefix)
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
return;
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
throw RestrictedPathError(format("access to path %1% is forbidden in restricted mode") % path_);
}
@@ -524,7 +505,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value;
}
if (!env->prevWith)
throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos);
throwUndefinedVarError("undefined variable %1% at %2%", var.name, var.pos);
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
}
}
@@ -663,12 +644,12 @@ void EvalState::evalFile(const Path & path, Value & v)
return;
}
printTalkative("evaluating file '%1%'", path2);
Activity act(*logger, lvlTalkative, format("evaluating file %1%") % path2);
Expr * e = parseExprFromFile(checkSourcePath(path2));
try {
eval(e, v);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
addErrorPrefix(e, "while evaluating the file %1%:\n", path2);
throw;
}
@@ -818,7 +799,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
throwEvalError("dynamic attribute %1% at %2% already defined at %3%", nameSym, i.pos, *j->pos);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
@@ -906,7 +887,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} else {
state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError("attribute '%1%' missing, at %2%", name, pos);
throwEvalError("attribute %1% missing, at %2%", name, pos);
}
vAttrs = j->value;
pos2 = j->pos;
@@ -917,7 +898,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} catch (Error & e) {
if (pos2 && pos2->file != state.sDerivationNix)
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
addErrorPrefix(e, "while evaluating the attribute %1% at %2%:\n",
showAttrPath(state, env, attrPath), *pos2);
throw;
}
@@ -1059,7 +1040,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
for (auto & i : lambda.formals->formals) {
Bindings::iterator j = arg.attrs->find(i.name);
if (j == arg.attrs->end()) {
if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
if (!i.def) throwTypeError("%1% called without required argument %2%, at %3%",
lambda, i.name, pos);
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
@@ -1075,7 +1056,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
user. */
for (auto & i : *arg.attrs)
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
throwTypeError("%1% called with unexpected argument '%2%', at %3%", lambda, i.name, pos);
throwTypeError("%1% called with unexpected argument %2%, at %3%", lambda, i.name, pos);
abort(); // can't happen
}
}
@@ -1133,7 +1114,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
if (j != args.end())
actualArgs->attrs->push_back(*j);
else if (!i.def)
throwTypeError("cannot auto-call a function that has an argument without a default value ('%1%')", i.name);
throwTypeError("cannot auto-call a function that has an argument without a default value (%1%)", i.name);
}
actualArgs->attrs->sort();
@@ -1362,7 +1343,7 @@ void EvalState::forceValueDeep(Value & v)
try {
recurse(*i.value);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n", i.name, *i.pos);
addErrorPrefix(e, "while evaluating the attribute %1% at %2%:\n", i.name, *i.pos);
throw;
}
}
@@ -1454,10 +1435,10 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
string s = forceString(v, pos);
if (v.string.context) {
if (pos)
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
throwEvalError("the string %1% is not allowed to refer to a store path (such as %2%), at %3%",
v.string.s, v.string.context[0], pos);
else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
throwEvalError("the string %1% is not allowed to refer to a store path (such as %2%)",
v.string.s, v.string.context[0]);
}
return s;
@@ -1539,7 +1520,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
string EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
throwEvalError("file names are not allowed to end in %1%", drvExtension);
Path dstPath;
if (srcToStore[path] != "")
@@ -1549,7 +1530,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
? store->computeStorePathForPath(checkSourcePath(path)).first
: store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source '%1%' -> '%2%'")
printMsg(lvlChatty, format("copied source %1% -> %2%")
% path % dstPath);
}
@@ -1562,7 +1543,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
string path = coerceToString(pos, v, context, false, false);
if (path == "" || path[0] != '/')
throwEvalError("string '%1%' doesn't represent an absolute path, at %2%", path, pos);
throwEvalError("string %1% doesn't represent an absolute path, at %2%", path, pos);
return path;
}

View File

@@ -14,7 +14,6 @@ namespace nix {
class Store;
class EvalState;
enum RepairFlag : bool;
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
@@ -69,12 +68,12 @@ public:
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder;
sRight, sWrong;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
already exist there. */
RepairFlag repair;
bool repair = false;
/* If set, don't allow access to files outside of the Nix search
path or to environment variables. */
@@ -110,8 +109,6 @@ public:
Path checkSourcePath(const Path & path);
void checkURI(const std::string & uri);
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
@@ -120,8 +117,6 @@ public:
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(const string & s, const Path & basePath);
Expr * parseStdin();
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);

View File

@@ -1,70 +1,14 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
#include "derivations.hh"
#include <cstring>
#include <regex>
namespace nix {
DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
: state(&state), attrs(attrs), attrPath(attrPath)
{
}
DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
: state(&state), attrs(nullptr), attrPath("")
{
auto spec = parseDrvPathWithOutputs(drvPathWithOutputs);
drvPath = spec.first;
auto drv = store->derivationFromPath(drvPath);
name = storePathToName(drvPath);
if (spec.second.size() > 1)
throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs);
outputName =
spec.second.empty()
? get(drv.env, "outputName", "out")
: *spec.second.begin();
auto i = drv.outputs.find(outputName);
if (i == drv.outputs.end())
throw Error("derivation '%s' does not have output '%s'", drvPath, outputName);
outPath = i->second.path;
}
string DrvInfo::queryName() const
{
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
if (i == attrs->end()) throw TypeError("derivation name missing");
name = state->forceStringNoCtx(*i->value);
}
return name;
}
string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
}
return system;
}
string DrvInfo::queryDrvPath() const
string DrvInfo::queryDrvPath()
{
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
@@ -75,7 +19,7 @@ string DrvInfo::queryDrvPath() const
}
string DrvInfo::queryOutPath() const
string DrvInfo::queryOutPath()
{
if (outPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
@@ -117,7 +61,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
const Value * outTI = queryMeta("outputsToInstall");
if (!outTI) return outputs;
const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
const auto errMsg = Error("this derivation has bad meta.outputsToInstall");
/* ^ this shows during `nix-env -i` right under the bad derivation */
if (!outTI->isList()) throw errMsg;
Outputs result;
@@ -131,7 +75,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
}
string DrvInfo::queryOutputName() const
string DrvInfo::queryOutputName()
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
@@ -280,12 +224,17 @@ static bool getDerivation(EvalState & state, Value & v,
if (done.find(v.attrs) != done.end()) return false;
done.insert(v.attrs);
DrvInfo drv(state, attrPath, v.attrs);
Bindings::iterator i = v.attrs->find(state.sName);
/* !!! We really would like to have a decent back trace here. */
if (i == v.attrs->end()) throw TypeError("derivation name missing");
drv.queryName();
Bindings::iterator i2 = v.attrs->find(state.sSystem);
DrvInfo drv(state, state.forceStringNoCtx(*i->value), attrPath,
i2 == v.attrs->end() ? "unknown" : state.forceStringNoCtx(*i2->value, *i2->pos),
v.attrs);
drvs.push_back(drv);
return false;
} catch (AssertionError & e) {
@@ -295,14 +244,15 @@ static bool getDerivation(EvalState & state, Value & v,
}
std::experimental::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
bool ignoreAssertionFailures)
{
Done done;
DrvInfos drvs;
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
if (drvs.size() != 1) return {};
return std::move(drvs.front());
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
}
@@ -312,9 +262,6 @@ static string addToPath(const string & s1, const string & s2)
}
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
static void getDerivations(EvalState & state, Value & vIn,
const string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
@@ -337,21 +284,25 @@ static void getDerivations(EvalState & state, Value & vIn,
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
for (auto & i : v.attrs->lexicographicOrder()) {
debug("evaluating attribute '%1%'", i->name);
if (!std::regex_match(std::string(i->name), attrRegex))
continue;
string pathPrefix2 = addToPath(pathPrefix, i->name);
typedef std::map<string, Symbol> SortedSymbols;
SortedSymbols attrs;
for (auto & i : *v.attrs)
attrs.insert(std::pair<string, Symbol>(i.name, i.name));
for (auto & i : attrs) {
Activity act(*logger, lvlDebug, format("evaluating attribute %1%") % i.first);
string pathPrefix2 = addToPath(pathPrefix, i.first);
Value & v2(*v.attrs->find(i.second)->value);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
/* If the value of this attribute is itself a set,
should we recurse into it? => Only if it has a
`recurseForDerivations = true' attribute. */
if (i->value->type == tAttrs) {
Bindings::iterator j = i->value->attrs->find(state.symbols.create("recurseForDerivations"));
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(*j->value, *j->pos))
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
}
@@ -359,6 +310,7 @@ static void getDerivations(EvalState & state, Value & vIn,
else if (v.isList()) {
for (unsigned int n = 0; n < v.listSize(); ++n) {
Activity act(*logger, lvlDebug, "evaluating list element");
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);

View File

@@ -17,33 +17,31 @@ public:
private:
EvalState * state;
mutable string name;
mutable string system;
mutable string drvPath;
mutable string outPath;
mutable string outputName;
string drvPath;
string outPath;
string outputName;
Outputs outputs;
bool failed = false; // set if we get an AssertionError
bool failed; // set if we get an AssertionError
Bindings * attrs = nullptr, * meta = nullptr;
Bindings * attrs, * meta;
Bindings * getMeta();
bool checkMeta(Value & v);
public:
string name;
string attrPath; /* path towards the derivation */
string system;
DrvInfo(EvalState & state) : state(&state) { };
DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs);
DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
DrvInfo(EvalState & state) : state(&state), failed(false), attrs(0), meta(0) { };
DrvInfo(EvalState & state, const string & name, const string & attrPath, const string & system, Bindings * attrs)
: state(&state), failed(false), attrs(attrs), meta(0), name(name), attrPath(attrPath), system(system) { };
string queryName() const;
string querySystem() const;
string queryDrvPath() const;
string queryOutPath() const;
string queryOutputName() const;
string queryDrvPath();
string queryOutPath();
string queryOutputName();
/** Return the list of outputs. The "outputs to install" are determined by `mesa.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false);
@@ -60,9 +58,15 @@ public:
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
*/
void setName(const string & s) { name = s; }
void setDrvPath(const string & s) { drvPath = s; }
void setOutPath(const string & s) { outPath = s; }
void setDrvPath(const string & s)
{
drvPath = s;
}
void setOutPath(const string & s)
{
outPath = s;
}
void setFailed() { failed = true; };
bool hasFailed() { return failed; };
@@ -76,10 +80,10 @@ typedef list<DrvInfo> DrvInfos;
#endif
/* If value `v' denotes a derivation, return a DrvInfo object
describing it. Otherwise return nothing. */
std::experimental::optional<DrvInfo> getDerivation(EvalState & state,
Value & v, bool ignoreAssertionFailures);
/* If value `v' denotes a derivation, store information about the
derivation in `drv' and return true. Otherwise, return false. */
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
bool ignoreAssertionFailures);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs,

View File

@@ -1,3 +1,4 @@
#include "config.h"
#include "json-to-value.hh"
#include <cstring>
@@ -57,7 +58,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
values.push_back(v2);
skipWhitespace(s);
if (*s == ']') break;
if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element");
if (*s != ',') throw JSONParseError("expected , or ] after JSON array element");
s++;
}
s++;
@@ -74,14 +75,14 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
if (attrs.empty() && *s == '}') break;
string name = parseJSONString(s);
skipWhitespace(s);
if (*s != ':') throw JSONParseError("expected ':' in JSON object");
if (*s != ':') throw JSONParseError("expected : in JSON object");
s++;
Value * v2 = state.allocValue();
parseJSON(state, s, *v2);
attrs[state.symbols.create(name)] = v2;
skipWhitespace(s);
if (*s == '}') break;
if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member");
if (*s != ',') throw JSONParseError("expected , or } after JSON member");
s++;
}
state.mkAttrs(v, attrs.size());
@@ -106,16 +107,10 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
tmp_number += *s++;
}
try {
if (number_type == tFloat)
mkFloat(v, stod(tmp_number));
else
mkInt(v, stoi(tmp_number));
} catch (std::invalid_argument e) {
throw JSONParseError("invalid JSON number");
} catch (std::out_of_range e) {
throw JSONParseError("out-of-range JSON number");
}
if (number_type == tFloat)
mkFloat(v, stod(tmp_number));
else
mkInt(v, stoi(tmp_number));
}
else if (strncmp(s, "true", 4) == 0) {

View File

@@ -124,13 +124,13 @@ or { return OR_KW; }
{INT} { errno = 0;
yylval->n = strtol(yytext, 0, 10);
if (errno != 0)
throw ParseError(format("invalid integer '%1%'") % yytext);
throw ParseError(format("invalid integer %1%") % yytext);
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
throw ParseError(format("invalid float '%1%'") % yytext);
throw ParseError(format("invalid float %1%") % yytext);
return FLOAT;
}
@@ -142,34 +142,25 @@ or { return OR_KW; }
\{ { return '{'; }
<INSIDE_DOLLAR_CURLY>\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; }
<INITIAL,INSIDE_DOLLAR_CURLY>\" {
PUSH_STATE(STRING); return '"';
}
<INITIAL,INSIDE_DOLLAR_CURLY>\" { PUSH_STATE(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
yylval->e = unescapeStr(data->symbols, yytext);
return STR;
}
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
yylval->e = unescapeStr(data->symbols, yytext);
return STR;
}
<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
<STRING>\" { POP_STATE(); return '"'; }
<STRING>\$|\\|\$\\ {
/* This can only occur when we reach EOF, otherwise the above
(...|\$[^\{\"\\]|\\.|\$\\.)+ would have triggered.
This is technically invalid, but we leave the problem to the
parser who fails with exact location. */
return STR;
}
<STRING>\" { POP_STATE(); return '"'; }
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->e = new ExprIndStr(yytext);
return IND_STR;
}
<IND_STRING>\'\'\$ |
<IND_STRING>\$ {
<IND_STRING>\'\'\$ {
yylval->e = new ExprIndStr("$");
return IND_STR;
}
@@ -187,16 +178,17 @@ or { return OR_KW; }
yylval->e = new ExprIndStr("'");
return IND_STR;
}
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
<INITIAL,INSIDE_DOLLAR_CURLY>{
{PATH} { if (yytext[yyleng-1] == '/')
throw ParseError("path '%s' has a trailing slash", yytext);
throw ParseError("path %s has a trailing slash", yytext);
yylval->path = strdup(yytext);
return PATH;
}
{HPATH} { if (yytext[yyleng-1] == '/')
throw ParseError("path '%s' has a trailing slash", yytext);
throw ParseError("path %s has a trailing slash", yytext);
yylval->path = strdup(yytext);
return HPATH;
}

View File

@@ -6,6 +6,8 @@ libexpr_DIR := $(d)
libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
libexpr_CXXFLAGS := -Wno-deprecated-register
libexpr_LIBS = libutil libstore libformat
libexpr_LDFLAGS =

View File

@@ -267,7 +267,7 @@ void ExprVar::bindVars(const StaticEnv & env)
/* Otherwise, the variable must be obtained from the nearest
enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */
if (withLevel == -1) throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name % pos);
if (withLevel == -1) throw UndefinedVarError(format("undefined variable %1% at %2%") % name % pos);
fromWith = true;
this->level = withLevel;
@@ -419,7 +419,7 @@ void ExprLambda::setName(Symbol & name)
string ExprLambda::showNamePos() const
{
return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str();
return (format("%1% at %2%") % (name.set() ? "" + (string) name + "" : "anonymous function") % pos).str();
}

View File

@@ -235,7 +235,7 @@ struct ExprLambda : Expr
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
{
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
throw ParseError(format("duplicate formal function argument %1% at %2%")
% arg % pos);
};
void setName(Symbol & name);

View File

@@ -65,14 +65,14 @@ namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
throw ParseError(format("attribute %1% at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
}
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
throw ParseError(format("attribute %1% at %2% already defined at %3%")
% attr % pos % prevPos);
}
@@ -121,7 +121,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{
if (formals->argNames.find(formal.name) != formals->argNames.end())
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
throw ParseError(format("duplicate formal function argument %1% at %2%")
% formal.name % pos);
formals->formals.push_front(formal);
formals->argNames.insert(formal.name);
@@ -376,7 +376,7 @@ expr_simple
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
}
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| HPATH { $$ = new ExprPath(getHome() + string{$1 + 1}); }
| HPATH { $$ = new ExprPath(getEnv("HOME", "") + string{$1 + 1}); }
| SPATH {
string path($1 + 1, strlen($1) - 2);
$$ = new ExprApp(CUR_POS,
@@ -523,6 +523,7 @@ formal
#include "eval.hh"
#include "download.hh"
#include "store-api.hh"
#include "primops/fetchgit.hh"
namespace nix {
@@ -563,7 +564,7 @@ Path resolveExprPath(Path path)
struct stat st;
while (true) {
if (lstat(path.c_str(), &st))
throw SysError(format("getting status of '%1%'") % path);
throw SysError(format("getting status of %1%") % path);
if (!S_ISLNK(st.st_mode)) break;
path = absPath(readLink(path), dirOf(path));
}
@@ -600,13 +601,6 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
}
Expr * EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
return parseExprFromString(drainFD(0), absPath("."));
}
void EvalState::addToSearchPath(const string & s)
{
size_t pos = s.find('=');
@@ -648,7 +642,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
if (pathExists(res)) return canonPath(res);
}
format f = format(
"file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)"
"file %1% was not found in the Nix search path (add it using $NIX_PATH or -I)"
+ string(pos ? ", at %2%" : ""));
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
throw ThrownError(f % path % pos);
@@ -664,9 +658,13 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
if (isUri(elem.second)) {
try {
res = { true, getDownloader()->downloadCached(store, elem.second, true) };
if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git"))
// FIXME: support specifying revision/branch
res = { true, exportGit(store, elem.second, "master") };
else
res = { true, getDownloader()->downloadCached(store, elem.second, true) };
} catch (DownloadError & e) {
printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
printError(format("warning: Nix search path entry %1% cannot be downloaded, ignoring") % elem.second);
res = { false, "" };
}
} else {
@@ -674,12 +672,12 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
if (pathExists(path))
res = { true, path };
else {
printError(format("warning: Nix search path entry '%1%' does not exist, ignoring") % elem.second);
printError(format("warning: Nix search path entry %1% does not exist, ignoring") % elem.second);
res = { false, "" };
}
}
debug(format("resolved search path element '%s' to '%s'") % elem.second % res.second);
debug(format("resolved search path element %s to %s") % elem.second % res.second);
searchPathResolved[elem.second] = res;
return res;

View File

@@ -8,7 +8,6 @@
#include "names.hh"
#include "store-api.hh"
#include "util.hh"
#include "json.hh"
#include "value-to-json.hh"
#include "value-to-xml.hh"
#include "primops.hh"
@@ -44,7 +43,7 @@ std::pair<string, string> decodeContext(const string & s)
InvalidPathError::InvalidPathError(const Path & path) :
EvalError(format("path '%1%' is not valid") % path), path(path) {}
EvalError(format("path %1% is not valid") % path), path(path) {}
void EvalState::realiseContext(const PathSet & context)
{
@@ -59,8 +58,6 @@ void EvalState::realiseContext(const PathSet & context)
drvs.insert(decoded.first + "!" + decoded.second);
}
if (!drvs.empty()) {
if (!settings.enableImportFromDerivation)
throw EvalError(format("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false") % *(drvs.begin()));
/* For performance, prefetch all substitute info. */
PathSet willBuild, willSubstitute, unknown;
unsigned long long downloadSize, narSize;
@@ -80,7 +77,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
throw EvalError(format("cannot import %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
@@ -127,7 +124,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
env->values[displ++] = attr.value;
}
printTalkative("evaluating file '%1%'", path);
Activity act(*logger, lvlTalkative, format("evaluating file %1%") % path);
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
e->eval(state, *env, v);
@@ -149,7 +146,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
throw EvalError(format("cannot import %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
@@ -159,16 +156,16 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
throw EvalError(format("could not open '%1%': %2%") % path % dlerror());
throw EvalError(format("could not open %1%: %2%") % path % dlerror());
dlerror();
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
if(!func) {
char *message = dlerror();
if (message)
throw EvalError(format("could not load symbol '%1%' from '%2%': %3%") % sym % path % message);
throw EvalError(format("could not load symbol %1% from %2%: %3%") % sym % path % message);
else
throw EvalError(format("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected")
throw EvalError(format("symbol %1% from %2% resolved to NULL when a function pointer was expected")
% sym % path);
}
@@ -178,45 +175,6 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
}
/* Execute a program and parse its output */
static void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
auto elems = args[0]->listElems();
auto count = args[0]->listSize();
if (count == 0) {
throw EvalError(format("at least one argument to 'exec' required, at %1%") % pos);
}
PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false);
Strings commandArgs;
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
}
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot execute '%1%', since path '%2%' is not valid, at %3%")
% program % e.path % pos);
}
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
parsed = state.parseExprFromString(output, pos.file);
} catch (Error & e) {
e.addPrefix(format("While parsing the output from '%1%', at %2%\n") % program % pos);
throw;
}
try {
state.eval(parsed, v);
} catch (Error & e) {
e.addPrefix(format("While evaluating the output from '%1%', at %2%\n") % program % pos);
throw;
}
}
/* Return a string representing the type of the expression. */
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -326,13 +284,15 @@ typedef list<Value *> ValueList;
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
Activity act(*logger, lvlDebug, "finding dependencies");
state.forceAttrs(*args[0], pos);
/* Get the start set. */
Bindings::iterator startSet =
args[0]->attrs->find(state.symbols.create("startSet"));
if (startSet == args[0]->attrs->end())
throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
throw EvalError(format("attribute startSet required, at %1%") % pos);
state.forceList(*startSet->value, pos);
ValueList workSet;
@@ -343,7 +303,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator op =
args[0]->attrs->find(state.symbols.create("operator"));
if (op == args[0]->attrs->end())
throw EvalError(format("attribute 'operator' required, at %1%") % pos);
throw EvalError(format("attribute operator required, at %1%") % pos);
state.forceValue(*op->value);
/* Construct the closure by applying the operator to element of
@@ -362,7 +322,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator key =
e->attrs->find(state.symbols.create("key"));
if (key == e->attrs->end())
throw EvalError(format("attribute 'key' required, at %1%") % pos);
throw EvalError(format("attribute key required, at %1%") % pos);
state.forceValue(*key->value);
if (doneKeys.find(key->value) != doneKeys.end()) continue;
@@ -393,7 +353,7 @@ static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
throw Abort(format("evaluation aborted with the following error message: '%1%'") % s);
throw Abort(format("evaluation aborted with the following error message: %1%") % s);
}
@@ -497,28 +457,23 @@ void prim_valueSize(EvalState & state, const Pos & pos, Value * * args, Value &
derivation. */
static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
Activity act(*logger, lvlVomit, "evaluating derivation");
state.forceAttrs(*args[0], pos);
/* Figure out the name first (for stack backtraces). */
Bindings::iterator attr = args[0]->attrs->find(state.sName);
if (attr == args[0]->attrs->end())
throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
throw EvalError(format("required attribute name missing, at %1%") % pos);
string drvName;
Pos & posDrvName(*attr->pos);
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
} catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute 'name' at %1%:\n") % posDrvName);
e.addPrefix(format("while evaluating the derivation attribute name at %1%:\n") % posDrvName);
throw;
}
/* Check whether attributes should be passed as a JSON file. */
std::ostringstream jsonBuf;
std::unique_ptr<JSONObject> jsonObject;
attr = args[0]->attrs->find(state.sStructuredAttrs);
if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos))
jsonObject = std::make_unique<JSONObject>(jsonBuf);
/* Check whether null attributes should be ignored. */
bool ignoreNulls = false;
attr = args[0]->attrs->find(state.sIgnoreNulls);
@@ -530,55 +485,30 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
PathSet context;
std::experimental::optional<std::string> outputHash;
std::string outputHashAlgo;
string outputHash, outputHashAlgo;
bool outputHashRecursive = false;
StringSet outputs;
outputs.insert("out");
for (auto & i : args[0]->attrs->lexicographicOrder()) {
if (i->name == state.sIgnoreNulls) continue;
string key = i->name;
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string & s) {
if (s == "recursive") outputHashRecursive = true;
else if (s == "flat") outputHashRecursive = false;
else throw EvalError("invalid value '%s' for 'outputHashMode' attribute, at %s", s, posDrvName);
};
auto handleOutputs = [&](const Strings & ss) {
outputs.clear();
for (auto & j : ss) {
if (outputs.find(j) != outputs.end())
throw EvalError(format("duplicate derivation output '%1%', at %2%") % j % posDrvName);
/* !!! Check whether j is a valid attribute
name. */
/* Derivations cannot be named drv, because
then we'd have an attribute drvPath in
the resulting set. */
if (j == "drv")
throw EvalError(format("invalid derivation output name 'drv', at %1%") % posDrvName);
outputs.insert(j);
}
if (outputs.empty())
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
};
for (auto & i : *args[0]->attrs) {
if (i.name == state.sIgnoreNulls) continue;
string key = i.name;
Activity act(*logger, lvlVomit, format("processing attribute %1%") % key);
try {
if (ignoreNulls) {
state.forceValue(*i->value);
if (i->value->type == tNull) continue;
state.forceValue(*i.value);
if (i.value->type == tNull) continue;
}
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
if (key == "args") {
state.forceList(*i->value, pos);
for (unsigned int n = 0; n < i->value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i->value->listElems()[n], context, true);
state.forceList(*i.value, pos);
for (unsigned int n = 0; n < i.value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
drv.args.push_back(s);
}
}
@@ -586,65 +516,48 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* All other attributes are passed to the builder through
the environment. */
else {
if (jsonObject) {
if (i->name == state.sStructuredAttrs) continue;
auto placeholder(jsonObject->placeholder(key));
printValueAsJSON(state, true, *i->value, placeholder, context);
if (i->name == state.sBuilder)
drv.builder = state.forceString(*i->value, context, posDrvName);
else if (i->name == state.sSystem)
drv.platform = state.forceStringNoCtx(*i->value, posDrvName);
else if (i->name == state.sName)
drvName = state.forceStringNoCtx(*i->value, posDrvName);
else if (key == "outputHash")
outputHash = state.forceStringNoCtx(*i->value, posDrvName);
else if (key == "outputHashAlgo")
outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName);
else if (key == "outputHashMode")
handleHashMode(state.forceStringNoCtx(*i->value, posDrvName));
else if (key == "outputs") {
/* Require outputs to be a list of strings. */
state.forceList(*i->value, posDrvName);
Strings ss;
for (unsigned int n = 0; n < i->value->listSize(); ++n)
ss.emplace_back(state.forceStringNoCtx(*i->value->listElems()[n], posDrvName));
handleOutputs(ss);
}
} else {
auto s = state.coerceToString(posDrvName, *i->value, context, true);
drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = s;
else if (i->name == state.sSystem) drv.platform = s;
else if (i->name == state.sName) {
drvName = s;
printMsg(lvlVomit, format("derivation name is '%1%'") % drvName);
}
else if (key == "outputHash") outputHash = s;
else if (key == "outputHashAlgo") outputHashAlgo = s;
else if (key == "outputHashMode") handleHashMode(s);
else if (key == "outputs")
handleOutputs(tokenizeString<Strings>(s));
string s = state.coerceToString(posDrvName, *i.value, context, true);
drv.env[key] = s;
if (key == "builder") drv.builder = s;
else if (i.name == state.sSystem) drv.platform = s;
else if (i.name == state.sName) {
drvName = s;
printMsg(lvlVomit, format("derivation name is %1%") % drvName);
}
else if (key == "outputHash") outputHash = s;
else if (key == "outputHashAlgo") outputHashAlgo = s;
else if (key == "outputHashMode") {
if (s == "recursive") outputHashRecursive = true;
else if (s == "flat") outputHashRecursive = false;
else throw EvalError(format("invalid value %1% for outputHashMode attribute, at %2%") % s % posDrvName);
}
else if (key == "outputs") {
Strings tmp = tokenizeString<Strings>(s);
outputs.clear();
for (auto & j : tmp) {
if (outputs.find(j) != outputs.end())
throw EvalError(format("duplicate derivation output %1%, at %2%") % j % posDrvName);
/* !!! Check whether j is a valid attribute
name. */
/* Derivations cannot be named drv, because
then we'd have an attribute drvPath in
the resulting set. */
if (j == "drv")
throw EvalError(format("invalid derivation output name drv, at %1%") % posDrvName);
outputs.insert(j);
}
if (outputs.empty())
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
}
}
} catch (Error & e) {
e.addPrefix(format("while evaluating the attribute '%1%' of the derivation '%2%' at %3%:\n")
e.addPrefix(format("while evaluating the attribute %1% of the derivation %2% at %3%:\n")
% key % drvName % posDrvName);
throw;
}
}
if (jsonObject) {
jsonObject.reset();
drv.env.emplace("__json", jsonBuf.str());
}
/* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting
derivation. */
@@ -690,31 +603,31 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Do we have all required attributes? */
if (drv.builder == "")
throw EvalError(format("required attribute 'builder' missing, at %1%") % posDrvName);
throw EvalError(format("required attribute builder missing, at %1%") % posDrvName);
if (drv.platform == "")
throw EvalError(format("required attribute 'system' missing, at %1%") % posDrvName);
throw EvalError(format("required attribute system missing, at %1%") % posDrvName);
/* Check whether the derivation name is valid. */
checkStoreName(drvName);
if (isDerivation(drvName))
throw EvalError(format("derivation names are not allowed to end in '%1%', at %2%")
throw EvalError(format("derivation names are not allowed to end in %1%, at %2%")
% drvExtension % posDrvName);
if (outputHash) {
if (outputHash != "") {
/* Handle fixed-output derivations. */
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
HashType ht = parseHashType(outputHashAlgo);
if (ht == htUnknown)
throw EvalError(format("unknown hash algorithm '%1%', at %2%") % outputHashAlgo % posDrvName);
Hash h(*outputHash, ht);
outputHash = h.to_string(Base16, false);
throw EvalError(format("unknown hash algorithm %1%, at %2%") % outputHashAlgo % posDrvName);
Hash h = parseHash16or32(ht, outputHash);
outputHash = printHash(h);
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
if (!jsonObject) drv.env["out"] = outPath;
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, *outputHash);
drv.env["out"] = outPath;
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
}
else {
@@ -724,7 +637,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
an empty value. This ensures that changes in the set of
output names do get reflected in the hash. */
for (auto & i : outputs) {
if (!jsonObject) drv.env[i] = "";
drv.env[i] = "";
drv.outputs[i] = DerivationOutput("", "", "");
}
@@ -735,7 +648,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
for (auto & i : drv.outputs)
if (i.second.path == "") {
Path outPath = state.store->makeOutputPath(i.first, h, drvName);
if (!jsonObject) drv.env[i.first] = outPath;
drv.env[i.first] = outPath;
i.second.path = outPath;
}
}
@@ -743,7 +656,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Write the resulting term into the Nix store directory. */
Path drvPath = writeDerivation(state.store, drv, drvName, state.repair);
printMsg(lvlChatty, format("instantiated '%1%' -> '%2%'")
printMsg(lvlChatty, format("instantiated %1% -> %2%")
% drvName % drvPath);
/* Optimisation, but required in read-only mode! because in that
@@ -763,7 +676,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Return a placeholder string for the specified output that will be
substituted by the corresponding output path at build time. For
example, 'placeholder "out"' returns the string
example, placeholder "out" returns the string
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
time, any occurence of this string in an derivation attribute will
be replaced with the concrete path in the Nix store of the output
@@ -805,7 +718,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path))
throw EvalError(format("path '%1%' is not in the Nix store, at %2%") % path % pos);
throw EvalError(format("path %1% is not in the Nix store, at %2%") % path % pos);
Path path2 = state.store->toStorePath(path);
if (!settings.readOnlyMode)
state.store->ensurePath(path2);
@@ -819,7 +732,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
if (!context.empty())
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
throw EvalError(format("string %1% cannot refer to other paths, at %2%") % path % pos);
try {
mkBool(v, pathExists(state.checkSourcePath(path)));
} catch (SysError & e) {
@@ -860,18 +773,18 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
throw EvalError(format("cannot read %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
string s = readFile(state.checkSourcePath(path));
if (s.find((char) 0) != string::npos)
throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
mkString(v, s.c_str());
throw Error(format("the contents of the file %1% cannot be represented as a Nix string") % path);
mkString(v, s.c_str(), context);
}
/* Find a file in the Nix search path. Used to implement <x> paths,
which are desugared to 'findFile __nixPath "x"'. */
which are desugared to findFile __nixPath "x". */
static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
@@ -889,7 +802,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
i = v2.attrs->find(state.symbols.create("path"));
if (i == v2.attrs->end())
throw EvalError(format("attribute 'path' missing, at %1%") % pos);
throw EvalError(format("attribute path missing, at %1%") % pos);
PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false);
@@ -897,7 +810,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot find '%1%', since path '%2%' is not valid, at %3%")
throw EvalError(format("cannot find %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
@@ -917,7 +830,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
try {
state.realiseContext(ctx);
} catch (InvalidPathError & e) {
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
throw EvalError(format("cannot read %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
@@ -991,7 +904,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
if (isDerivation(path)) {
/* See prim_unsafeDiscardOutputDependency. */
if (path.at(0) != '~')
throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos);
throw EvalError(format("in toFile: the file %1% cannot refer to derivation outputs, at %2%") % name % pos);
path = string(path, 1);
}
refs.insert(path);
@@ -1009,21 +922,22 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
}
static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
struct FilterFromExpr : PathFilter
{
PathSet context;
Path path = state.coerceToPath(pos, *args[1], context);
if (!context.empty())
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
EvalState & state;
Value & filter;
Pos pos;
state.forceValue(*args[0]);
if (args[0]->type != tLambda)
throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
FilterFromExpr(EvalState & state, Value & filter, const Pos & pos)
: state(state), filter(filter), pos(pos)
{
}
path = state.checkSourcePath(path);
PathFilter filter = [&](const Path & path) {
auto st = lstat(path);
bool operator () (const Path & path)
{
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path %1%") % path);
/* Call the filter function. The first argument is the path,
the second is a string indicating the type of the file. */
@@ -1031,7 +945,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
mkString(arg1, path);
Value fun2;
state.callFunction(*args[0], arg1, fun2, noPos);
state.callFunction(filter, arg1, fun2, noPos);
Value arg2;
mkString(arg2,
@@ -1044,7 +958,24 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
state.callFunction(fun2, arg2, res, noPos);
return state.forceBool(res, pos);
};
}
};
static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[1], context);
if (!context.empty())
throw EvalError(format("string %1% cannot refer to other paths, at %2%") % path % pos);
state.forceValue(*args[0]);
if (args[0]->type != tLambda)
throw TypeError(format("first argument in call to filterSource is not a function but %1%, at %2%") % showType(*args[0]) % pos);
FilterFromExpr filter(state, *args[0], pos);
path = state.checkSourcePath(path);
Path dstPath = settings.readOnlyMode
? state.store->computeStorePathForPath(path, true, htSHA256, filter).first
@@ -1067,9 +998,12 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
state.mkList(v, args[0]->attrs->size());
size_t n = 0;
for (auto & i : args[0]->attrs->lexicographicOrder())
mkString(*(v.listElems()[n++] = state.allocValue()), i->name);
unsigned int n = 0;
for (auto & i : *args[0]->attrs)
mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
}
@@ -1101,7 +1035,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
// !!! Should we create a symbol here or just do a lookup?
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end())
throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
throw EvalError(format("attribute %1% missing, at %2%") % attr % pos);
// !!! add to stack trace?
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
state.forceValue(*i->value);
@@ -1181,14 +1115,14 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
Bindings::iterator j = v2.attrs->find(state.sName);
if (j == v2.attrs->end())
throw TypeError(format("'name' attribute missing in a call to 'listToAttrs', at %1%") % pos);
throw TypeError(format("name attribute missing in a call to listToAttrs, at %1%") % pos);
string name = state.forceStringNoCtx(*j->value, pos);
Symbol sym = state.symbols.create(name);
if (seen.find(sym) == seen.end()) {
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
if (j2 == v2.attrs->end())
throw TypeError(format("'value' attribute missing in a call to 'listToAttrs', at %1%") % pos);
throw TypeError(format("value attribute missing in a call to listToAttrs, at %1%") % pos);
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
seen.insert(sym);
@@ -1263,7 +1197,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
{
state.forceValue(*args[0]);
if (args[0]->type != tLambda)
throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
throw TypeError(format("functionArgs requires a function, at %1%") % pos);
if (!args[0]->lambda.fun->matchAttrs) {
state.mkAttrs(v, 0);
@@ -1322,7 +1256,7 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
{
state.forceList(*args[0], pos);
if (args[0]->listSize() == 0)
throw Error(format("'tail' called on an empty list, at %1%") % pos);
throw Error(format("tail called on an empty list, at %1%") % pos);
state.mkList(v, args[0]->listSize() - 1);
for (unsigned int n = 0; n < v.listSize(); ++n)
v.listElems()[n] = args[0]->listElems()[n + 1];
@@ -1631,7 +1565,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
PathSet context;
string s = state.coerceToString(pos, *args[2], context);
if (start < 0) throw EvalError(format("negative start position in 'substring', at %1%") % pos);
if (start < 0) throw EvalError(format("negative start position in substring, at %1%") % pos);
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
}
@@ -1678,12 +1612,12 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
string type = state.forceStringNoCtx(*args[0], pos);
HashType ht = parseHashType(type);
if (ht == htUnknown)
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
throw Error(format("unknown hash type %1%, at %2%") % type % pos);
PathSet context; // discarded
string s = state.forceString(*args[1], context, pos);
mkString(v, hashString(ht, s).to_string(Base16, false), context);
mkString(v, printHash(hashString(ht, s)), context);
}
@@ -1691,105 +1625,26 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
null or a list containing substring matches. */
static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);
std::regex regex(state.forceStringNoCtx(*args[0], pos), std::regex::extended);
try {
PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
std::regex regex(re, std::regex::extended);
PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
std::smatch match;
if (!std::regex_match(str, match, regex)) {
mkNull(v);
return;
}
// the first match is the whole string
const size_t len = match.size() - 1;
state.mkList(v, len);
for (size_t i = 0; i < len; ++i) {
if (!match[i+1].matched)
mkNull(*(v.listElems()[i] = state.allocValue()));
else
mkString(*(v.listElems()[i] = state.allocValue()), match[i + 1].str().c_str());
}
} catch (std::regex_error &e) {
if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
} else {
throw EvalError("invalid regular expression '%s', at %s", re, pos);
}
std::smatch match;
if (!std::regex_match(str, match, regex)) {
mkNull(v);
return;
}
}
/* Split a string with a regular expression, and return a list of the
non-matching parts interleaved by the lists of the matching groups. */
static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);
try {
std::regex regex(re, std::regex::extended);
PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
auto begin = std::sregex_iterator(str.begin(), str.end(), regex);
auto end = std::sregex_iterator();
// Any matches results are surrounded by non-matching results.
const size_t len = std::distance(begin, end);
state.mkList(v, 2 * len + 1);
size_t idx = 0;
Value * elem;
if (len == 0) {
v.listElems()[idx++] = args[1];
return;
}
for (std::sregex_iterator i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3);
std::smatch match = *i;
// Add a string for non-matched characters.
elem = v.listElems()[idx++] = state.allocValue();
mkString(*elem, match.prefix().str().c_str());
// Add a list for matched substrings.
const size_t slen = match.size() - 1;
elem = v.listElems()[idx++] = state.allocValue();
// Start at 1, beacause the first match is the whole string.
state.mkList(*elem, slen);
for (size_t si = 0; si < slen; ++si) {
if (!match[si + 1].matched)
mkNull(*(elem->listElems()[si] = state.allocValue()));
else
mkString(*(elem->listElems()[si] = state.allocValue()), match[si + 1].str().c_str());
}
// Add a string for non-matched suffix characters.
if (idx == 2 * len) {
elem = v.listElems()[idx++] = state.allocValue();
mkString(*elem, match.suffix().str().c_str());
}
}
assert(idx == 2 * len + 1);
} catch (std::regex_error &e) {
if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
} else {
throw EvalError("invalid regular expression '%s', at %s", re, pos);
}
// the first match is the whole string
const size_t len = match.size() - 1;
state.mkList(v, len);
for (size_t i = 0; i < len; ++i) {
if (!match[i+1].matched)
mkNull(*(v.listElems()[i] = state.allocValue()));
else
mkString(*(v.listElems()[i] = state.allocValue()), match[i + 1].str().c_str());
}
}
@@ -1819,7 +1674,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*args[0], pos);
state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize())
throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have different lengths, at %1%") % pos);
throw EvalError(format("from and to arguments to replaceStrings have different lengths, at %1%") % pos);
vector<string> from;
from.reserve(args[0]->listSize());
@@ -1889,11 +1744,11 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
const string & who, bool unpack, const std::string & defaultName)
const string & who, bool unpack)
{
string url;
Hash expectedHash;
string name = defaultName;
string name;
state.forceValue(*args[0]);
@@ -1906,20 +1761,21 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
if (n == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "sha256")
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
expectedHash = parseHash16or32(htSHA256, state.forceStringNoCtx(*attr.value, *attr.pos));
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError(format("unsupported argument '%1%' to '%2%', at %3%") % attr.name % who % attr.pos);
throw EvalError(format("unsupported argument %1% to %2%, at %3%") % attr.name % who % attr.pos);
}
if (url.empty())
throw EvalError(format("'url' argument required, at %1%") % pos);
throw EvalError(format("url argument required, at %1%") % pos);
} else
url = state.forceStringNoCtx(*args[0], pos);
state.checkURI(url);
if (state.restricted && !expectedHash)
throw Error(format("%1% is not allowed in restricted mode") % who);
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
mkString(v, res, PathSet({res}));
@@ -1928,13 +1784,13 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchurl", false, "");
fetch(state, pos, args, v, "fetchurl", false);
}
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchTarball", true, "source");
fetch(state, pos, args, v, "fetchTarball", true);
}
@@ -1989,7 +1845,7 @@ void EvalState::createBaseEnv()
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
mkInt(v, 5);
mkInt(v, 4);
addConstant("__langVersion", v);
// Miscellaneous
@@ -1999,10 +1855,8 @@ void EvalState::createBaseEnv()
mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2);
forceValue(v);
addConstant("import", v);
if (settings.enableNativeCode) {
if (settings.enableImportNative)
addPrimOp("__importNative", 2, prim_importNative);
addPrimOp("__exec", 1, prim_exec);
}
addPrimOp("__typeOf", 1, prim_typeOf);
addPrimOp("isNull", 1, prim_isNull);
addPrimOp("__isFunction", 1, prim_isFunction);
@@ -2087,7 +1941,6 @@ void EvalState::createBaseEnv()
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString);
addPrimOp("__match", 2, prim_match);
addPrimOp("__split", 2, prim_split);
addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
addPrimOp("__replaceStrings", 3, prim_replaceStrings);

View File

@@ -1,235 +0,0 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "download.hh"
#include "store-api.hh"
#include "pathlocks.hh"
#include <sys/time.h>
#include <regex>
#include <nlohmann/json.hpp>
using namespace std::string_literals;
namespace nix {
struct GitInfo
{
Path storePath;
std::string rev;
std::string shortRev;
uint64_t revCount = 0;
};
GitInfo exportGit(ref<Store> store, const std::string & uri,
std::experimental::optional<std::string> ref, const std::string & rev,
const std::string & name)
{
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
bool clean = true;
try {
runProgram("git", true, { "-C", uri, "diff-index", "--quiet", "HEAD", "--" });
} catch (ExecError e) {
if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw;
clean = false;
}
if (!clean) {
/* This is an unclean working tree. So copy all tracked
files. */
GitInfo gitInfo;
gitInfo.rev = "0000000000000000000000000000000000000000";
gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
auto files = tokenizeString<std::set<std::string>>(
runProgram("git", true, { "-C", uri, "ls-files", "-z" }), "\0"s);
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, uri));
std::string file(p, uri.size() + 1);
auto st = lstat(p);
if (S_ISDIR(st.st_mode)) {
auto prefix = file + "/";
auto i = files.lower_bound(prefix);
return i != files.end() && hasPrefix(*i, prefix);
}
return files.count(file);
};
gitInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
return gitInfo;
}
}
if (!ref) ref = "master"s;
if (rev != "") {
std::regex revRegex("^[0-9a-fA-F]{40}$");
if (!std::regex_match(rev, revRegex))
throw Error("invalid Git revision '%s'", rev);
}
Path cacheDir = getCacheDir() + "/nix/git";
if (!pathExists(cacheDir)) {
createDirs(cacheDir);
runProgram("git", true, { "init", "--bare", cacheDir });
}
std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, *ref)).to_string(Base32, false);
Path localRefFile = cacheDir + "/refs/heads/" + localRef;
bool doFetch;
time_t now = time(0);
/* If a rev was specified, we need to fetch if it's not in the
repo. */
if (rev != "") {
try {
runProgram("git", true, { "-C", cacheDir, "cat-file", "-e", rev });
doFetch = false;
} catch (ExecError & e) {
if (WIFEXITED(e.status)) {
doFetch = true;
} else {
throw;
}
}
} else {
/* If the local ref is older than tarball-ttl seconds, do a
git fetch to update the local ref to the remote ref. */
struct stat st;
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
st.st_mtime <= now - settings.tarballTtl;
}
if (doFetch)
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Git repository '%s'", uri));
// FIXME: git stderr messes up our progress indicator, so
// we're using --quiet for now. Should process its stderr.
runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, *ref + ":" + localRef });
struct timeval times[2];
times[0].tv_sec = now;
times[0].tv_usec = 0;
times[1].tv_sec = now;
times[1].tv_usec = 0;
utimes(localRefFile.c_str(), times);
}
// FIXME: check whether rev is an ancestor of ref.
GitInfo gitInfo;
gitInfo.rev = rev != "" ? rev : chomp(readFile(localRefFile));
gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
printTalkative("using revision %s of repo '%s'", uri, gitInfo.rev);
std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + gitInfo.rev).to_string(Base32, false);
Path storeLink = cacheDir + "/" + storeLinkName + ".link";
PathLocks storeLinkLock({storeLink}, fmt("waiting for lock on '%1%'...", storeLink)); // FIXME: broken
try {
auto json = nlohmann::json::parse(readFile(storeLink));
assert(json["name"] == name && json["rev"] == gitInfo.rev);
gitInfo.storePath = json["storePath"];
if (store->isValidPath(gitInfo.storePath)) {
gitInfo.revCount = json["revCount"];
return gitInfo;
}
} catch (SysError & e) {
if (e.errNo != ENOENT) throw;
}
// FIXME: should pipe this, or find some better way to extract a
// revision.
auto tar = runProgram("git", true, { "-C", cacheDir, "archive", gitInfo.rev });
Path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runProgram("tar", true, { "x", "-C", tmpDir }, tar);
gitInfo.storePath = store->addToStore(name, tmpDir);
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", cacheDir, "rev-list", "--count", gitInfo.rev }));
nlohmann::json json;
json["storePath"] = gitInfo.storePath;
json["uri"] = uri;
json["name"] = name;
json["rev"] = gitInfo.rev;
json["revCount"] = gitInfo.revCount;
writeFile(storeLink, json.dump());
return gitInfo;
}
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
std::string url;
std::experimental::optional<std::string> ref;
std::string rev;
std::string name = "source";
PathSet context;
state.forceValue(*args[0]);
if (args[0]->type == tAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
string n(attr.name);
if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
else if (n == "ref")
ref = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "rev")
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError("unsupported argument '%s' to 'fetchGit', at %s", attr.name, *attr.pos);
}
if (url.empty())
throw EvalError(format("'url' argument required, at %1%") % pos);
} else
url = state.coerceToString(pos, *args[0], context, false, false);
if (!isUri(url)) url = absPath(url);
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
state.checkURI(url);
auto gitInfo = exportGit(state.store, url, ref, rev, name);
state.mkAttrs(v, 8);
mkString(*state.allocAttr(v, state.sOutPath), gitInfo.storePath, PathSet({gitInfo.storePath}));
mkString(*state.allocAttr(v, state.symbols.create("rev")), gitInfo.rev);
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
v.attrs->sort();
}
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
}

View File

@@ -1,203 +0,0 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "download.hh"
#include "store-api.hh"
#include "pathlocks.hh"
#include <sys/time.h>
#include <regex>
#include <nlohmann/json.hpp>
using namespace std::string_literals;
namespace nix {
struct HgInfo
{
Path storePath;
std::string branch;
std::string rev;
uint64_t revCount = 0;
};
std::regex commitHashRegex("^[0-9a-fA-F]{40}$");
HgInfo exportMercurial(ref<Store> store, const std::string & uri,
std::string rev, const std::string & name)
{
if (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
if (!clean) {
/* This is an unclean working tree. So copy all tracked
files. */
printTalkative("copying unclean Mercurial working tree '%s'", uri);
HgInfo hgInfo;
hgInfo.rev = "0000000000000000000000000000000000000000";
hgInfo.branch = chomp(runProgram("hg", true, { "branch", "-R", uri }));
auto files = tokenizeString<std::set<std::string>>(
runProgram("hg", true, { "status", "-R", uri, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, uri));
std::string file(p, uri.size() + 1);
auto st = lstat(p);
if (S_ISDIR(st.st_mode)) {
auto prefix = file + "/";
auto i = files.lower_bound(prefix);
return i != files.end() && hasPrefix(*i, prefix);
}
return files.count(file);
};
hgInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
return hgInfo;
}
}
if (rev == "") rev = "default";
Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, uri).to_string(Base32, false));
Path stampFile = fmt("%s/.hg/%s.stamp", cacheDir, hashString(htSHA512, rev).to_string(Base32, false));
/* If we haven't pulled this repo less than tarball-ttl seconds,
do so now. */
time_t now = time(0);
struct stat st;
if (stat(stampFile.c_str(), &st) != 0 ||
st.st_mtime <= now - settings.tarballTtl)
{
/* Except that if this is a commit hash that we already have,
we don't have to pull again. */
if (!(std::regex_match(rev, commitHashRegex)
&& pathExists(cacheDir)
&& runProgram(
RunOptions("hg", { "log", "-R", cacheDir, "-r", rev, "--template", "1" })
.killStderr(true)).second == "1"))
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", uri));
if (pathExists(cacheDir)) {
runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri });
} else {
createDirs(dirOf(cacheDir));
runProgram("hg", true, { "clone", "--noupdate", "--", uri, cacheDir });
}
}
writeFile(stampFile, "");
}
auto tokens = tokenizeString<std::vector<std::string>>(
runProgram("hg", true, { "log", "-R", cacheDir, "-r", rev, "--template", "{node} {rev} {branch}" }));
assert(tokens.size() == 3);
HgInfo hgInfo;
hgInfo.rev = tokens[0];
hgInfo.revCount = std::stoull(tokens[1]);
hgInfo.branch = tokens[2];
std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + hgInfo.rev).to_string(Base32, false);
Path storeLink = fmt("%s/.hg/%s.link", cacheDir, storeLinkName);
try {
auto json = nlohmann::json::parse(readFile(storeLink));
assert(json["name"] == name && json["rev"] == hgInfo.rev);
hgInfo.storePath = json["storePath"];
if (store->isValidPath(hgInfo.storePath)) {
printTalkative("using cached Mercurial store path '%s'", hgInfo.storePath);
return hgInfo;
}
} catch (SysError & e) {
if (e.errNo != ENOENT) throw;
}
Path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runProgram("hg", true, { "archive", "-R", cacheDir, "-r", rev, tmpDir });
deletePath(tmpDir + "/.hg_archival.txt");
hgInfo.storePath = store->addToStore(name, tmpDir);
nlohmann::json json;
json["storePath"] = hgInfo.storePath;
json["uri"] = uri;
json["name"] = name;
json["branch"] = hgInfo.branch;
json["rev"] = hgInfo.rev;
json["revCount"] = hgInfo.revCount;
writeFile(storeLink, json.dump());
return hgInfo;
}
static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
std::string url;
std::string rev;
std::string name = "source";
PathSet context;
state.forceValue(*args[0]);
if (args[0]->type == tAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
string n(attr.name);
if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
else if (n == "rev")
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s", attr.name, *attr.pos);
}
if (url.empty())
throw EvalError(format("'url' argument required, at %1%") % pos);
} else
url = state.coerceToString(pos, *args[0], context, false, false);
if (!isUri(url)) url = absPath(url);
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
state.checkURI(url);
auto hgInfo = exportMercurial(state.store, url, rev, name);
state.mkAttrs(v, 8);
mkString(*state.allocAttr(v, state.sOutPath), hgInfo.storePath, PathSet({hgInfo.storePath}));
mkString(*state.allocAttr(v, state.symbols.create("branch")), hgInfo.branch);
mkString(*state.allocAttr(v, state.symbols.create("rev")), hgInfo.rev);
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(hgInfo.rev, 0, 12));
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), hgInfo.revCount);
v.attrs->sort();
}
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
}

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