Compare commits

..

10 Commits

Author SHA1 Message Date
Eelco Dolstra
a10da8466f * Option to turn off position information to test the impact on
maximal sharing.
2007-10-17 12:36:37 +00:00
Eelco Dolstra
e23d134b85 * Memoize the substitution function.
* Print some substitution statistics.
* Option to turn off the closed term optimization.
2007-10-15 12:08:31 +00:00
Eelco Dolstra
c3a79daaf3 * Short-circuiting of function call evaluation.
With maximal laziness, you would expect that a function like this

    fib = n:
      if n == 0 then 0 else
      if n == 1 then 1 else
      builtins.add (fib (builtins.sub n 1)) (fib (builtins.sub n 2));

  can be evaluated efficiently, because maximal laziness should
  implictly memoize the recursive calls to "fib".  However, non-strictness
  interferes with this: the argument "n" is generally not in a form
  that allows the memoization to work (e.g., it will be something like
  (20 - 1 - 2 - 2) rather than 15).  By the time that "n" is
  evaluated (in "if n == 0 ..."), we're already deep in the evaluation
  of the call.

  (Strictness solves this:

      builtins.add (strict fib (builtins.sub n 1)) (strict fib (builtins.sub n 2));

  but that's not a very nice approach.)

  With short-circuiting, the evaluator will check after evaluating a
  term, whether that term is the argument of a function call that
  we're currently evaluating.  If so, it will check to see if the same
  call but with the evaluated argument is in the normal form cache.

  For instance, after evaluating (20 - 1 - 2 - 2) to 15, if we see
  that "fib (20 - 1 - 2 - 2)" is currently being evaluated, we check
  to see if "fib 15" is in the normal form cache.  If so, we unwind
  the stack (by throwing an exception) up to the evalExpr call
  responsible for "fib (20 - 1 - 2 - 2)", which can then immediately
  return the normal form for "fib 15".  And indeed this makes "fib"
  run in O(n) time.

  The overhead for checking the active function calls (which isn't
  very smart yet) seems to be modest, about 2% for "nix-env -qa
  --drv-path --out-path" on Nixpkgs.
2007-10-12 17:53:47 +00:00
Eelco Dolstra
74ce938e18 * Simulate conventional laziness a bit better still by "allocating"
all local variables when entering a new scope.  I.e., don't do
  implicit let-floating.
2007-10-11 22:42:09 +00:00
Eelco Dolstra
981afe821c * Some hacks to simulate conventional laziness. 2007-10-11 21:58:37 +00:00
Eelco Dolstra
cd9d10d4e3 * Caching of parse results for fairer comparisons. 2007-10-11 20:02:08 +00:00
Eelco Dolstra
c1179badd5 * Playing with strictness. 2007-10-11 14:07:00 +00:00
Eelco Dolstra
3d14ed9270 * A primop for calling functions strictly (i.e. forcing evaluation of
argument).  Necessary to actually get memoisation of functions like
  "fib" with maximal laziness.
2007-10-11 14:06:43 +00:00
Eelco Dolstra
8e0488370d * Environment variable to disable normal form caching. 2007-10-11 12:09:06 +00:00
Eelco Dolstra
b57f8bd38d * PEPM related hacks. 2007-10-11 12:07:49 +00:00
283 changed files with 9857 additions and 13909 deletions

262
.gitignore vendored
View File

@@ -1,262 +0,0 @@
# START "git svn show-ignore"
# /
/Makefile
/Makefile.in
/aclocal.m4
/autom4te.cache
/config.*
/configure
/nix.spec
/stamp-h1
/svn-revision
/NEWS
/libtool
# /config/
/config/config.guess
/config/config.sub
/config/depcomp
/config/install-sh
/config/missing
/config/mkinstalldirs
/config/ltmain.sh
# /corepkgs/
/corepkgs/Makefile
/corepkgs/Makefile.in
# /corepkgs/buildenv/
/corepkgs/buildenv/Makefile.in
/corepkgs/buildenv/Makefile
/corepkgs/buildenv/builder.pl
# /corepkgs/channels/
/corepkgs/channels/Makefile.in
/corepkgs/channels/Makefile
/corepkgs/channels/unpack.sh
# /corepkgs/nar/
/corepkgs/nar/Makefile
/corepkgs/nar/Makefile.in
/corepkgs/nar/nar.sh
/corepkgs/nar/unnar.sh
# /doc/
/doc/Makefile
/doc/Makefile.in
# /doc/manual/
/doc/manual/Makefile
/doc/manual/Makefile.in
/doc/manual/manual.html
/doc/manual/manual.is-valid
/doc/manual/*.1
/doc/manual/*.8
/doc/manual/images
/doc/manual/version.txt
/doc/manual/NEWS.html
/doc/manual/NEWS.txt
# /externals/
/externals/Makefile
/externals/Makefile.in
/externals/aterm-*
/externals/have-aterm
/externals/build-aterm
/externals/inst-aterm
/externals/bzip2-*
/externals/have-bzip2
/externals/build-bzip2
/externals/inst-bzip2
# /make/examples/aterm/
/make/examples/aterm/result*
# /make/examples/aterm/aterm/
/make/examples/aterm/aterm/*
# /make/examples/aterm/test/
/make/examples/aterm/test/*
# /misc/
/misc/Makefile.in
/misc/Makefile
# /misc/emacs/
/misc/emacs/Makefile.in
/misc/emacs/Makefile
# /scripts/
/scripts/Makefile
/scripts/Makefile.in
/scripts/nix-profile.sh
/scripts/nix-pull
/scripts/nix-push
/scripts/nix-switch
/scripts/nix-collect-garbage
/scripts/nix-prefetch-url
/scripts/nix-install-package
/scripts/nix-channel
/scripts/nix-build
/scripts/nix-copy-closure
/scripts/readmanifest.pm
/scripts/readconfig.pm
/scripts/download-using-manifests.pl
/scripts/copy-from-other-stores.pl
/scripts/generate-patches.pl
/scripts/find-runtime-roots.pl
/scripts/build-remote.pl
# /src/
/src/Makefile
/src/Makefile.in
# /src/bin2c/
/src/bin2c/Makefile.in
/src/bin2c/Makefile
/src/bin2c/bin2c
/src/bin2c/.deps
/src/bin2c/.libs
# /src/boost/
/src/boost/Makefile
/src/boost/Makefile.in
# /src/boost/format/
/src/boost/format/Makefile
/src/boost/format/Makefile.in
/src/boost/format/.deps
/src/boost/format/libformat.a
/src/boost/format/.libs
# /src/bsdiff-4.3/
/src/bsdiff-4.3/Makefile
/src/bsdiff-4.3/Makefile.in
/src/bsdiff-4.3/bsdiff
/src/bsdiff-4.3/bspatch
/src/bsdiff-4.3/.deps
/src/bsdiff-4.3/.libs
# /src/libexpr/
/src/libexpr/Makefile
/src/libexpr/Makefile.in
/src/libexpr/.deps
/src/libexpr/libexpr.a
/src/libexpr/lexer-tab.cc
/src/libexpr/lexer-tab.hh
/src/libexpr/parser-tab.cc
/src/libexpr/parser-tab.hh
/src/libexpr/parser-tab.output
/src/libexpr/nixexpr-ast.hh
/src/libexpr/nixexpr-ast.cc
/src/libexpr/.libs
/src/libexpr/nix.tbl
# /src/libmain/
/src/libmain/Makefile
/src/libmain/Makefile.in
/src/libmain/.deps
/src/libmain/libmain.a
/src/libmain/.libs
# /src/libstore/
/src/libstore/Makefile
/src/libstore/Makefile.in
/src/libstore/.deps
/src/libstore/libstore.a
/src/libstore/derivations-ast.cc
/src/libstore/derivations-ast.hh
/src/libstore/.libs
# /src/libutil/
/src/libutil/Makefile
/src/libutil/Makefile.in
/src/libutil/.deps
/src/libutil/libutil.a
/src/libutil/.libs
# /src/nix-env/
/src/nix-env/Makefile.in
/src/nix-env/Makefile
/src/nix-env/.deps
/src/nix-env/nix-env
/src/nix-env/help.txt.hh
/src/nix-env/.libs
# /src/nix-hash/
/src/nix-hash/Makefile
/src/nix-hash/Makefile.in
/src/nix-hash/.deps
/src/nix-hash/.libs
/src/nix-hash/nix-hash
/src/nix-hash/help.txt.hh
# /src/nix-instantiate/
/src/nix-instantiate/Makefile.in
/src/nix-instantiate/Makefile
/src/nix-instantiate/.deps
/src/nix-instantiate/nix-instantiate
/src/nix-instantiate/help.txt.hh
/src/nix-instantiate/.libs
# /src/nix-log2xml/
/src/nix-log2xml/Makefile.in
/src/nix-log2xml/Makefile
/src/nix-log2xml/.deps
/src/nix-log2xml/nix-log2xml
/src/nix-log2xml/test*.*
/src/nix-log2xml/.libs
/src/nix-log2xml/*.log
/src/nix-log2xml/*.xml
/src/nix-log2xml/*.html
# /src/nix-setuid-helper/
/src/nix-setuid-helper/Makefile.in
/src/nix-setuid-helper/Makefile
/src/nix-setuid-helper/.deps
/src/nix-setuid-helper/nix-setuid-helper
/src/nix-setuid-helper/help.txt.hh
/src/nix-setuid-helper/.libs
# /src/nix-store/
/src/nix-store/Makefile
/src/nix-store/Makefile.in
/src/nix-store/.deps
/src/nix-store/help.txt.hh
/src/nix-store/nix-store
/src/nix-store/.libs
# /src/nix-worker/
/src/nix-worker/Makefile.in
/src/nix-worker/Makefile
/src/nix-worker/.deps
/src/nix-worker/nix-worker
/src/nix-worker/help.txt.hh
/src/nix-worker/.libs
# /tests/
/tests/Makefile
/tests/Makefile.in
/tests/test-tmp
/tests/config.nix
/tests/common.sh
/tests/dummy
# /tests/lang/
/tests/lang/*.out
/tests/lang/*.out.xml
/tests/lang/*.ast
# END "git svn show-ignore"
*.lo
*.la
*.o
*~
# GNU Global
GPATH
GRTAGS
GSYMS
GTAGS

View File

@@ -1,19 +1,23 @@
SUBDIRS = externals src scripts corepkgs doc misc tests
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
nix.conf.example NEWS version
svn-revision nix.conf.example NEWS
include ./substitute.mk
nix.spec: nix.spec.in
rpm: nix.spec dist
rpm $(EXTRA_RPM_FLAGS) -ta $(distdir).tar.gz
relname:
echo -n $(distdir) > relname
install-data-local: init-state
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix
if ! test -e $(DESTDIR)$(sysconfdir)/nix/nix.conf; then \
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix/nix.conf; \
fi
$(INSTALL) -d $(DESTDIR)$(docdir)
$(INSTALL_DATA) README $(DESTDIR)$(docdir)/
if INIT_STATE
@@ -29,18 +33,23 @@ init-state:
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/gcroots/tmp
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/gcroots/channels
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
-$(INSTALL) $(INIT_FLAGS) -m 1777 -d $(DESTDIR)$(storedir)
$(INSTALL) $(INIT_FLAGS) -m 1777 -d $(DESTDIR)$(prefix)/store
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
# $(bindir)/nix-store --init
else
init-state:
endif
NEWS:
$(MAKE) -C doc/manual NEWS.txt
svn-revision:
svnversion . > svn-revision
all-local: NEWS
NEWS: doc/manual/NEWS.txt
cp $(srcdir)/doc/manual/NEWS.txt NEWS

9
README
View File

@@ -1,10 +1,9 @@
Nix is a purely functional package manager. For installation and
usage instructions, please read the manual, which can be found in
`docs/manual/manual.html', and additionally at the Nix website at
<http://nixos.org/>.
For installation and usage instructions, please read the manual, which
can be found in `docs/manual/manual.html', and additionally at the Nix
website at <http://www.cs.uu.nl/groups/ST/Trace/Nix>.
Acknowledgments
This product includes software developed by the OpenSSL Project for
use in the OpenSSL Toolkit (http://www.OpenSSL.org/).
use in the OpenSSL Toolkit (http://www.OpenSSL.org/)

View File

@@ -1,117 +1,184 @@
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:*
fun:AT_collect_minor
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:*
fun:*
fun:AT_isValidSymbol
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_isValidSymbol
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_isInsideValidTerm
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:AT_isInsideValidTerm
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_markTerm_young
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:AT_markTerm_young
fun:mark_memory_young
fun:mark_phase_young
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:AT_isValidSymbol
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_isValidSymbol
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_isInsideValidTerm
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:AT_isInsideValidTerm
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:AT_markTerm
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_markTerm
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:AT_markTerm
fun:mark_memory
fun:mark_phase
}
{
ATerm library conservatively scans for GC roots
Memcheck:Cond
fun:mark_phase_young
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:*
fun:mark_phase_young
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value8
fun:*
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value4
fun:*
fun:*
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value8
fun:*
fun:*
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Addr4
fun:*
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Addr8
fun:*
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
<insert a suppression name here>
Memcheck:Cond
fun:*
fun:AT_collect
fun:AT_isValidSymbol
fun:mark_phase_young
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
<insert a suppression name here>
Memcheck:Value4
fun:*
fun:AT_collect
fun:AT_isValidSymbol
fun:mark_phase_young
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value8
fun:*
fun:AT_collect
}
{
ATerm library conservatively scans for GC roots
Memcheck:Addr4
fun:*
fun:AT_collect
}
{
ATerm library conservatively scans for GC roots
Memcheck:Addr8
fun:*
fun:AT_collect
}
{
ATerm library conservatively scans for GC roots
<insert a suppression name here>
Memcheck:Value4
fun:*
fun:*
fun:AT_collect
fun:AT_isInsideValidTerm
fun:mark_phase_young
fun:AT_collect_minor
}
{
ATerm library conservatively scans for GC roots
Memcheck:Value8
fun:*
fun:*
fun:AT_collect
}
{
ATerm library conservatively scans for GC roots
<insert a suppression name here>
Memcheck:Cond
fun:*
fun:*
fun:AT_collect
fun:AT_isInsideValidTerm
fun:mark_phase_young
fun:AT_collect_minor
}

View File

@@ -1,9 +1,24 @@
AC_INIT(nix, m4_esyscmd([echo -n $(cat ./version)$VERSION_SUFFIX]))
AC_INIT(nix, 0.11)
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(config)
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
# Change to `1' to produce a `stable' release (i.e., the `preREVISION'
# suffix is not added).
STABLE=0
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.])
# Put the revision number in the version.
if test "$STABLE" != "1"; then
if REVISION=`test -d $srcdir/.svn && svnversion -n $srcdir 2> /dev/null`; then
VERSION=${VERSION}pre${REVISION}
elif REVISION=`cat $srcdir/svn-revision 2> /dev/null`; then
VERSION=${VERSION}pre${REVISION}
fi
fi
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [version])
AC_PREFIX_DEFAULT(/nix)
AC_CANONICAL_HOST
@@ -39,62 +54,27 @@ case $sys_name in
esac
AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
[Platform identifier (e.g., `i686-linux').]),
[platform identifier (e.g., `i686-linux')]),
system=$withval, system="${machine_name}-${sys_name}")
AC_MSG_RESULT($system)
AC_SUBST(system)
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
# State should be stored in /nix/var, unless the user overrides it explicitly.
test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
# Windows-specific stuff.
if test "$sys_name" = "cygwin"; then
# We cannot delete open files.
AC_DEFINE(CANNOT_DELETE_OPEN_FILES, 1, [Whether it is impossible to delete open files.])
# Whether to produce a statically linked binary. On Cygwin, this is
# the default: dynamically linking against the ATerm DLL does work,
# except that it requires the ATerm "lib" directory to be in $PATH, as
# Windows doesn't have anything like an RPATH embedded in executable.
# Since this is kind of annoying, we use static libraries for now.
AC_ARG_ENABLE(static-nix, AC_HELP_STRING([--enable-static-nix],
[produce statically linked binaries]),
static_nix=$enableval, static_nix=no)
if test "$sys_name" = cygwin; then
static_nix=yes
fi
if test "$static_nix" = yes; then
# Shared libraries don't work, currently.
AC_DISABLE_SHARED
AC_ENABLE_STATIC
fi
# Windows-specific stuff.
if test "$sys_name" = "cygwin"; then
# We cannot delete open files.
AC_DEFINE(CANNOT_DELETE_OPEN_FILES, 1, [Whether it is impossible to delete open files.])
fi
# Solaris-specific stuff.
if test "$sys_name" = "sunos"; then
# Solaris requires -lsocket -lnsl for network functions
ADDITIONAL_NETWORK_LIBS="-lsocket -lnsl"
AC_SUBST(ADDITIONAL_NETWORK_LIBS)
fi
AC_PROG_CC
AC_PROG_CXX
# To build programs to be run in the build machine
if test "$CC_FOR_BUILD" = ""; then
if test "$cross_compiling" = "yes"; then
AC_CHECK_PROGS(CC_FOR_BUILD, gcc cc)
else
CC_FOR_BUILD="$CC"
fi
fi
AC_SUBST([CC_FOR_BUILD])
# We are going to use libtool.
AC_DISABLE_STATIC
@@ -103,7 +83,8 @@ AC_PROG_LIBTOOL
# Use 64-bit file system calls so that we can support files > 2 GiB.
AC_SYS_LARGEFILE
CFLAGS="-D_FILE_OFFSET_BITS=64 $CFLAGS"
CXXFLAGS="-D_FILE_OFFSET_BITS=64 $CXXFLAGS"
# Check for pubsetbuf.
@@ -113,34 +94,17 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <iostream>
using namespace std;
static char buf[1024];]],
[[cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));]])],
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [Whether pubsetbuf is available.])],
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [whether pubsetbuf is available])],
AC_MSG_RESULT(no))
AC_LANG_POP(C++)
# Check for chroot support (requires chroot() and bind mounts).
AC_CHECK_FUNCS([chroot])
AC_CHECK_FUNCS([unshare])
AC_CHECK_HEADERS([sched.h], [], [], [])
AC_CHECK_HEADERS([sys/param.h], [], [], [])
AC_CHECK_HEADERS([sys/mount.h], [], [],
[#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
# endif
])
# Check for <locale>.
# Check for <locale>
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([locale], [], [], [])
AC_CHECK_HEADERS([locale])
AC_LANG_POP(C++)
# Check whether we have the personality() syscall, which allows us to
# do i686-linux builds on x86_64-linux machines.
AC_CHECK_HEADERS([sys/personality.h])
AC_DEFUN([NEED_PROG],
[
AC_PATH_PROG($1, $2)
@@ -150,19 +114,17 @@ fi
])
NEED_PROG(curl, curl)
NEED_PROG(bash, bash)
NEED_PROG(shell, sh)
NEED_PROG(patch, patch)
AC_PATH_PROG(xmllint, xmllint, false)
AC_PATH_PROG(xsltproc, xsltproc, false)
AC_PATH_PROG(jing, jing, false) # needed because xmllint --relaxng seems broken
AC_PATH_PROG(w3m, w3m, 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)
AC_PATH_PROG(dot, dot)
AC_PATH_PROG(dblatex, dblatex)
AC_PATH_PROG(gzip, gzip)
AC_PATH_PROG(openssl_prog, openssl, openssl) # if not found, call openssl in $PATH
AC_SUBST(openssl_prog)
@@ -200,9 +162,40 @@ AC_SUBST(xmlflags)
AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
[path of the Nix store]),
storedir=$withval, storedir='/nix/store')
storedir=$withval, storedir='${prefix}/store')
AC_SUBST(storedir)
AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH],
[prefix of Berkeley DB]),
bdb=$withval, bdb=)
AM_CONDITIONAL(HAVE_BDB, test -n "$bdb")
if test -z "$bdb"; then
bdb_lib='-L${top_builddir}/externals/inst-bdb/lib -ldb_cxx'
bdb_include='-I${top_builddir}/externals/inst-bdb/include'
else
bdb_lib="-L$bdb/lib -ldb_cxx"
bdb_include="-I$bdb/include"
fi
AC_SUBST(bdb_lib)
AC_SUBST(bdb_include)
AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH],
[prefix of CWI ATerm library]),
aterm=$withval, aterm=)
AM_CONDITIONAL(HAVE_ATERM, test -n "$aterm")
if test -z "$aterm"; then
aterm_lib='-L${top_builddir}/externals/inst-aterm/lib -lATerm'
aterm_include='-I${top_builddir}/externals/inst-aterm/include'
aterm_bin='${top_builddir}/externals/inst-aterm/bin'
else
aterm_lib="-L$aterm/lib -lATerm"
aterm_include="-I$aterm/include"
aterm_bin="$aterm/bin"
fi
AC_SUBST(aterm_lib)
AC_SUBST(aterm_include)
AC_SUBST(aterm_bin)
AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH],
[prefix of the OpenSSL library]),
openssl=$withval, openssl=)
@@ -211,7 +204,7 @@ if test -n "$openssl"; then
LDFLAGS="-L$openssl/lib -lcrypto $LDFLAGS"
CFLAGS="-I$openssl/include $CFLAGS"
CXXFLAGS="-I$openssl/include $CXXFLAGS"
AC_DEFINE(HAVE_OPENSSL, 1, [Whether to use OpenSSL.])
AC_DEFINE(HAVE_OPENSSL, 1, [whether to use OpenSSL])
fi
AC_ARG_WITH(bzip2, AC_HELP_STRING([--with-bzip2=PATH],
@@ -224,7 +217,7 @@ if test -z "$bzip2"; then
bzip2_lib='-L${top_builddir}/externals/inst-bzip2/lib -lbz2'
bzip2_include='-I${top_builddir}/externals/inst-bzip2/include'
# The binary will be copied to $libexecdir.
bzip2_bin='${libexecdir}/nix'
bzip2_bin='${libexecdir}'
# But for testing, we have to use the temporary copy :-(
bzip2_bin_test='${top_builddir}/externals/inst-bzip2/bin'
else
@@ -232,7 +225,7 @@ else
bzip2_include="-I$bzip2/include"
bzip2_bin="$bzip2/bin"
bzip2_bin_test="$bzip2/bin"
fi
fi
AC_SUBST(bzip2_lib)
AC_SUBST(bzip2_include)
AC_SUBST(bzip2_bin)
@@ -252,26 +245,13 @@ AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes")
AC_CHECK_FUNCS([setresuid setreuid lchown])
# Nice to have, but not essential.
AC_CHECK_FUNCS([strsignal])
AC_CHECK_FUNCS([posix_fallocate])
# This is needed if ATerm or bzip2 are static libraries,
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
# and the Nix libraries are dynamic.
if test "$(uname)" = "Darwin"; then
LDFLAGS="-all_load $LDFLAGS"
fi
if test "$static_nix" = yes; then
# `-all-static' has to be added at the end of configure, because
# the C compiler doesn't know about -all-static (it's filtered out
# by libtool, but configure doesn't use libtool).
LDFLAGS="-all-static $LDFLAGS"
fi
AM_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile
externals/Makefile

View File

@@ -29,18 +29,10 @@ sub createLinks {
$baseName =~ s/^.*\///g; # strip directory
my $dstFile = "$dstDir/$baseName";
# The files below are special-cased so that they don't show up
# in user profiles, either because they are useless, or
# because they would cause pointless collisions (e.g., each
# Python package brings its own
# `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
# Urgh, hacky...
if ($srcFile =~ /\/propagated-build-inputs$/ ||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
$srcFile =~ /\/nix-support$/ ||
$srcFile =~ /\/perllocal.pod$/ ||
$srcFile =~ /\/easy-install.pth$/ ||
$srcFile =~ /\/site.py$/ ||
$srcFile =~ /\/site.pyc$/ ||
$srcFile =~ /\/info\/dir$/ ||
$srcFile =~ /\/log$/)
{
@@ -168,4 +160,4 @@ while (scalar(keys %postponed) > 0) {
print STDERR "created $symlinks symlinks in user environment\n";
symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest";
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";

View File

@@ -1,8 +1,5 @@
#! @shell@ -e
# Cygwin compatibility hack: bunzip2 expects cygwin.dll in $PATH.
export PATH=@coreutils@
@coreutils@/mkdir $out
@coreutils@/mkdir $out/tmp
cd $out/tmp
@@ -11,15 +8,9 @@ inputs=($inputs)
for ((n = 0; n < ${#inputs[*]}; n += 2)); do
channelName=${inputs[n]}
channelTarball=${inputs[n+1]}
echo "unpacking channel $channelName"
@bunzip2@ < $channelTarball | @tar@ xf -
if test -e */channel-name; then
channelName="$(@coreutils@/cat */channel-name)"
fi
nr=1
attrName=$(echo $channelName | @tr@ -- '- ' '__')
dirName=$attrName

View File

@@ -6,12 +6,7 @@ XSLTPROC = $(xsltproc) $(xmlflags) \
--param xref.with.number.and.title 1 \
--param toc.section.depth 3 \
--param admon.style \'\' \
--param callout.graphics.extension \'.gif\' \
--param contrib.inline.enabled 0
dblatex_opts = \
-P doc.collab.show=0 \
-P latex.output.revhistory=0
--param callout.graphics.extension \'.gif\'
# Note: we use GIF for now, since the PNGs shipped with Docbook aren't
# transparent.
@@ -19,24 +14,27 @@ dblatex_opts = \
man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
nix-collect-garbage.1 nix-push.1 nix-pull.1 \
nix-prefetch-url.1 nix-channel.1 \
nix-pack-closure.1 nix-unpack-closure.1 \
nix-install-package.1 nix-hash.1 nix-copy-closure.1
man8_MANS = nix-worker.8
FIGURES = figures/user-environments.png
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
package-management.xml writing-nix-expressions.xml builtins.xml \
package-management.xml writing-nix-expressions.xml \
build-farm.xml \
$(man1_MANS:.1=.xml) $(man8_MANS:.8=.xml) \
$(man1_MANS:.1=.xml) \
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
env-common.xml quick-start.xml nix-lang-ref.xml glossary.xml \
conf-file.xml release-notes.xml \
style.css images
# Note: RelaxNG validation requires xmllint >= 2.7.4.
manual.is-valid: $(MANUAL_SRCS) version.txt
$(XMLLINT) --noout --nonet --xinclude --noxincludenode --relaxng $(docbookrng)/docbook.rng $<
# $(XMLLINT) --xinclude $< | $(XMLLINT) --noout --nonet --relaxng $(docbookrng)/docbook.rng -
if test "$(jing)" != "false"; then \
$(XMLLINT) --xinclude $< | $(jing) $(docbookrng)/docbook.rng /dev/fd/0; \
else \
echo "Not validating."; \
fi
touch $@
version.txt:
@@ -49,14 +47,6 @@ manual.html: $(MANUAL_SRCS) manual.is-valid images
$(XSLTPROC) --nonet --xinclude --output manual.html \
$(docbookxsl)/html/docbook.xsl manual.xml
manual.pdf: $(MANUAL_SRCS) manual.is-valid images
if test "$(dblatex)" != ""; then \
$(dblatex) $(dblatex_opts) manual.xml; \
else \
echo "Please install dblatex and rerun configure."; \
exit 1; \
fi
NEWS_OPTS = \
--stringparam generate.toc "article nop" \
@@ -78,16 +68,12 @@ NEWS.txt: release-notes.xml
all-local: manual.html NEWS.html NEWS.txt
install-data-local: manual.html
$(INSTALL) -d $(DESTDIR)$(docdir)/manual
$(INSTALL_DATA) manual.html $(DESTDIR)$(docdir)/manual
ln -sf manual.html $(DESTDIR)$(docdir)/manual/index.html
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/manual
cp -r images $(DESTDIR)$(docdir)/manual/images
$(INSTALL) -d $(DESTDIR)$(docdir)/manual/figures
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(docdir)/manual/figures
$(INSTALL) -d $(DESTDIR)$(docdir)/release-notes
$(INSTALL_DATA) NEWS.html $(DESTDIR)$(docdir)/release-notes/index.html
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/release-notes/
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/manual
$(INSTALL_DATA) manual.html $(DESTDIR)$(datadir)/nix/manual
$(INSTALL_DATA) style.css $(DESTDIR)$(datadir)/nix/manual
cp -r images $(DESTDIR)$(datadir)/nix/manual/images
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/manual/figures
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(datadir)/nix/manual/figures
images:
mkdir images

View File

@@ -36,10 +36,10 @@ build farm, since:
builds, and Nix expressions are self-contained.</para></listitem>
<listitem><para>Nix will only rebuild things that have actually
changed. For instance, if the sources of a package haven't changed
between runs of the build farm, the package won't be rebuilt (unless
it was garbage-collected). Also, dependencies typically don't
change very often, so they only need to be built
changed. For instance, if the sources of a component haven't
changed between runs of the build farm, the component won't be
rebuild (unless it was garbage-collected). Also, dependencies
typically don't change very often, so they only need to be built
once.</para></listitem>
<listitem><para>The results of a Nix build farm can be made
@@ -56,7 +56,7 @@ build farm, since:
<para>TODO</para>
<para>The sources of the Nix build farm are at <link
xlink:href='https://svn.nixos.org/repos/nix/release/trunk'/>.</para>
xlink:href='https://svn.cs.uu.nl:12443/repos/trace/release/trunk'/>.</para>
</section>
@@ -85,7 +85,7 @@ nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto
<para>An example build hook can be found in the Nix build farm
sources: <link
xlink:href='https://svn.nixos.org/repos/nix/release/trunk/common/distributed/build-remote.pl'
xlink:href='https://svn.cs.uu.nl:12443/repos/trace/release/trunk/common/distributed/build-remote.pl'
/>. It should be suitable for most purposes, with maybe some minor
adjustments. It uses <command>ssh</command> and
<command>rsync</command> to copy the build inputs and outputs and

View File

@@ -1,851 +0,0 @@
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id='ssec-builtins'>
<title>Built-in functions</title>
<para>This section lists the functions and constants built into the
Nix expression evaluator. (The built-in function
<function>derivation</function> is discussed above.) Some built-ins,
such as <function>derivation</function>, are always in scope of every
Nix expression; you can just access them right away. But to prevent
polluting the namespace too much, most built-ins are not in scope.
Instead, you can access them through the <varname>builtins</varname>
built-in value, which is an attribute set that contains all built-in
functions and values. For instance, <function>derivation</function>
is also available as <function>builtins.derivation</function>.</para>
<variablelist>
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
<listitem><para>Abort Nix expression evaluation, print error
message <replaceable>s</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.add</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the sum of the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.attrNames</function>
<replaceable>attrs</replaceable></term>
<listitem><para>Return the names of the attributes in the
attribute set <replaceable>attrs</replaceable> in a sorted list.
For instance, <literal>builtins.attrNames {y = 1; x =
"foo";}</literal> evaluates to <literal>["x" "y"]</literal>.
There is no built-in function <function>attrValues</function>, but
you can easily define it yourself:
<programlisting>
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
</para></listitem>
</varlistentry>
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the <emphasis>base name</emphasis> of the
string <replaceable>s</replaceable>, that is, everything following
the final slash in the string. This is similar to the GNU
<command>basename</command> command.</para></listitem>
</varlistentry>
<varlistentry><term><varname>builtins</varname></term>
<listitem><para>The attribute set <varname>builtins</varname>
contains all the built-in functions and values. You can use
<varname>builtins</varname> to test for the availability of
features in the Nix installation, e.g.,
<programlisting>
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
This allows a Nix expression to fall back gracefully on older Nix
installations that dont have the desired built-in
function.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.compareVersions</function>
<replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
<listitem><para>Compare two strings representing versions and
return <literal>-1</literal> if version
<replaceable>s1</replaceable> is older than version
<replaceable>s2</replaceable>, <literal>0</literal> if they are
the same, and <literal>1</literal> if
<replaceable>s1</replaceable> is newer than
<replaceable>s2</replaceable>. The version comparison algorithm
is the same as the one used by <link
linkend="ssec-version-comparisons"><command>nix-env
-u</command></link>.</para></listitem>
</varlistentry>
<varlistentry
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
<listitem><para>The built-in value <varname>currentSystem</varname>
evaluates to the Nix platform identifier for the Nix installation
on which the expression is being evaluated, such as
<literal>"i686-linux"</literal> or
<literal>"powerpc-darwin"</literal>.</para></listitem>
</varlistentry>
<!--
<varlistentry><term><function>currentTime</function></term>
<listitem><para>The built-in value <varname>currentTime</varname>
returns the current system time in seconds since 00:00:00 1/1/1970
UTC. Due to the evaluation model of Nix expressions
(<emphasis>maximal laziness</emphasis>), it always yields the same
value within an execution of Nix.</para></listitem>
</varlistentry>
-->
<!--
<varlistentry><term><function>dependencyClosure</function></term>
<listitem><para>TODO</para></listitem>
</varlistentry>
-->
<varlistentry><term><function>derivation</function>
<replaceable>attrs</replaceable></term>
<listitem><para><function>derivation</function> is described in
<xref linkend='ssec-derivation' />.</para></listitem>
</varlistentry>
<varlistentry><term><function>dirOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the directory part of the string
<replaceable>s</replaceable>, that is, everything before the final
slash in the string. This is similar to the GNU
<command>dirname</command> command.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.div</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the quotient of the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.filterSource</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem>
<para>This function allows you to copy sources into the Nix
store while filtering certain files. For instance, suppose that
you want to use the directory <filename>source-dir</filename> as
an input to a Nix expression, e.g.
<programlisting>
stdenv.mkDerivation {
...
src = ./source-dir;
}
</programlisting>
However, if <filename>source-dir</filename> is a Subversion
working copy, then all those annoying <filename>.svn</filename>
subdirectories will also be copied to the store. Worse, the
contents of those directories may change a lot, causing lots of
spurious rebuilds. With <function>filterSource</function> you
can filter out the <filename>.svn</filename> directories:
<programlisting>
src = builtins.filterSource
(path: type: type != "directory" || baseNameOf path != ".svn")
./source-dir;
</programlisting>
</para>
<para>Thus, the first argument <replaceable>e1</replaceable>
must be a predicate function that is called for each regular
file, directory or symlink in the source tree
<replaceable>e2</replaceable>. If the function returns
<literal>true</literal>, the file is copied to the Nix store,
otherwise it is omitted. The function is called with two
arguments. The first is the full path of the file. The second
is a string that identifies the type of the file, which is
either <literal>"regular"</literal>,
<literal>"directory"</literal>, <literal>"symlink"</literal> or
<literal>"unknown"</literal> (for other kinds of files such as
device nodes or fifos — but note that those cannot be copied to
the Nix store, so if the predicate returns
<literal>true</literal> for them, the copy will fail).</para>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.getAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
<listitem><para><function>getAttr</function> returns the attribute
named <replaceable>s</replaceable> from the attribute set
<replaceable>attrs</replaceable>. Evaluation aborts if the
attribute doesnt exist. This is a dynamic version of the
<literal>.</literal> operator, since <replaceable>s</replaceable>
is an expression rather than an identifier.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.getEnv</function>
<replaceable>s</replaceable></term>
<listitem><para><function>getEnv</function> returns the value of
the environment variable <replaceable>s</replaceable>, or an empty
string if the variable doesnt exist. This function should be
used with care, as it can introduce all sorts of nasty environment
dependencies in your Nix expression.</para>
<para><function>getEnv</function> is used in Nix Packages to
locate the file <filename>~/.nixpkgs/config.nix</filename>, which
contains user-local settings for Nix Packages. (That is, it does
a <literal>getEnv "HOME"</literal> to locate the users home
directory.)</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.hasAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
<listitem><para><function>hasAttr</function> returns
<literal>true</literal> if the attribute set
<replaceable>attrs</replaceable> has an attribute named
<replaceable>s</replaceable>, and <literal>false</literal>
otherwise. This is a dynamic version of the <literal>?</literal>
operator, since <replaceable>s</replaceable> is an expression
rather than an identifier.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.head</function>
<replaceable>list</replaceable></term>
<listitem><para>Return the first element of a list; abort
evaluation if the argument isnt a list or is an empty list. You
can test whether a list is empty by comparing it with
<literal>[]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>import</function>
<replaceable>path</replaceable></term>
<listitem><para>Load, parse and return the Nix expression in the
file <replaceable>path</replaceable>. Evaluation aborts if the
file doesnt exist or contains an incorrect Nix
expression. <function>import</function> implements Nixs module
system: you can put any Nix expression (such as an attribute set
or a function) in a separate file, and use it from Nix expressions
in other files.</para>
<para>A Nix expression loaded by <function>import</function> must
not contain any <emphasis>free variables</emphasis> (identifiers
that are not defined in the Nix expression itself and are not
built-in). Therefore, it cannot refer to variables that are in
scope at the call site. For instance, if you have a calling
expression
<programlisting>
rec {
x = 123;
y = import ./foo.nix;
}</programlisting>
then the following <filename>foo.nix</filename> will give an
error:
<programlisting>
x + 456</programlisting>
since <varname>x</varname> is not in scope in
<filename>foo.nix</filename>. If you want <varname>x</varname>
to be available in <filename>foo.nix</filename>, you should pass
it as a function argument:
<programlisting>
rec {
x = 123;
y = import ./foo.nix x;
}</programlisting>
and
<programlisting>
x: x + 456</programlisting>
(The function argument doesnt have to be called
<varname>x</varname> in <filename>foo.nix</filename>; any name
would work.)</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.intersectAttrs</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return an attribute set consisting of the
attributes in the set <replaceable>e2</replaceable> that also
exist in the set <replaceable>e1</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isAttrs</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to an attribute set, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isList</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a list, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isFunction</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a function, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isString</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a string, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isInt</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a int, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isBool</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a bool, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>isNull</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to <literal>null</literal>,
and <literal>false</literal> otherwise.</para>
<warning><para>This function is <emphasis>deprecated</emphasis>;
just write <literal>e == null</literal> instead.</para></warning>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.length</function>
<replaceable>e</replaceable></term>
<listitem><para>Return the length of the list
<replaceable>e</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.lessThan</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return <literal>true</literal> if the integer
<replaceable>e1</replaceable> is less than the integer
<replaceable>e2</replaceable>, and <literal>false</literal>
otherwise. Evaluation aborts if either
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
does not evaluate to an integer.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.listToAttrs</function>
<replaceable>e</replaceable></term>
<listitem><para>Construct an attribute set from a list specifying
the names and values of each attribute. Each element of the list
should be an attribute set consisting of a string-valued attribute
<varname>name</varname> specifying the name of the attribute, and
an attribute <varname>value</varname> specifying its value.
Example:
<programlisting>
builtins.listToAttrs [
{name = "foo"; value = 123;}
{name = "bar"; value = 456;}
]
</programlisting>
evaluates to
<programlisting>
{ foo = 123; bar = 456; }
</programlisting>
</para></listitem>
</varlistentry>
<varlistentry><term><function>map</function>
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Apply the function <replaceable>f</replaceable> to
each element in the list <replaceable>list</replaceable>. For
example,
<programlisting>
map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
evaluates to <literal>["foobar" "foobla"
"fooabc"]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.mul</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the product of the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.parseDrvName</function>
<replaceable>s</replaceable></term>
<listitem><para>Split the string <replaceable>s</replaceable> into
a package name and version. The package name is everything up to
but not including the first dash followed by a digit, and the
version is everything following that dash. The result is returned
in an attribute set <literal>{name, version}</literal>. Thus,
<literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
returns <literal>{name = "nix"; version =
"0.12pre12876";}</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.pathExists</function>
<replaceable>path</replaceable></term>
<listitem><para>Return <literal>true</literal> if the path
<replaceable>path</replaceable> exists, and
<literal>false</literal> otherwise. One application of this
function is to conditionally include a Nix expression containing
user configuration:
<programlisting>
let
fileName = builtins.getEnv "CONFIG_FILE";
config =
if fileName != "" &amp;&amp; builtins.pathExists (builtins.toPath fileName)
then import (builtins.toPath fileName)
else { someSetting = false; }; <lineannotation># default configuration</lineannotation>
in config.someSetting</programlisting>
(Note that <envar>CONFIG_FILE</envar> must be an absolute path for
this to work.)</para></listitem>
</varlistentry>
<!--
<varlistentry><term><function>relativise</function></term>
<listitem><para>TODO</para></listitem>
</varlistentry>
-->
<varlistentry><term><function>builtins.readFile</function>
<replaceable>path</replaceable></term>
<listitem><para>Return the contents of the file
<replaceable>path</replaceable> as a string.</para></listitem>
</varlistentry>
<varlistentry><term><function>removeAttrs</function>
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Remove the attributes listed in
<replaceable>list</replaceable> from the attribute set
<replaceable>attrs</replaceable>. The attributes dont have to
exist in <replaceable>attrs</replaceable>. For instance,
<screen>
removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"]</screen>
evaluates to <literal>{y = 2;}</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.stringLength</function>
<replaceable>e</replaceable></term>
<listitem><para>Return the length of the string
<replaceable>e</replaceable>. If <replaceable>e</replaceable> is
not a string, evaluation is aborted.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.sub</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the difference between the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.substring</function>
<replaceable>start</replaceable> <replaceable>len</replaceable>
<replaceable>s</replaceable></term>
<listitem><para>Return the substring of
<replaceable>s</replaceable> from character position
<replaceable>start</replaceable> (zero-based) up to but not
including <replaceable>start + len</replaceable>. If
<replaceable>start</replaceable> is greater than the length of the
string, an empty string is returned, and if <replaceable>start +
len</replaceable> lies beyond the end of the string, only the
substring up to the end of the string is returned.
<replaceable>start</replaceable> must be
non-negative.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.tail</function>
<replaceable>list</replaceable></term>
<listitem><para>Return the second to last elements of a list;
abort evaluation if the argument isnt a list or is an empty
list.</para></listitem>
</varlistentry>
<varlistentry><term><function>throw</function>
<replaceable>s</replaceable></term>
<listitem><para>Throw an error message
<replaceable>s</replaceable>. This usually aborts Nix expression
evaluation, but in <command>nix-env -qa</command> and other
commands that try to evaluate a set of derivations to get
information about those derivations, a derivation that throws an
error is silently skipped (which is not the case for
<function>abort</function>).</para></listitem>
</varlistentry>
<varlistentry
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
<listitem><para>Store the string <replaceable>s</replaceable> in a
file in the Nix store and return its path. The file has suffix
<replaceable>name</replaceable>. This file can be used as an
input to derivations. One application is to write builders
“inline”. For instance, the following Nix expression combines
<xref linkend='ex-hello-nix' /> and <xref
linkend='ex-hello-builder' /> into one file:
<programlisting>
{stdenv, fetchurl, perl}:
stdenv.mkDerivation {
name = "hello-2.1.1";
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
PATH=$perl/bin:$PATH
tar xvfz $src
cd hello-*
./configure --prefix=$out
make
make install
";
src = fetchurl {
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl;
}</programlisting>
</para>
<para>It is even possible for one file to refer to another, e.g.,
<programlisting>
builder = let
configFile = builtins.toFile "foo.conf" "
# This is some dummy configuration file.
<replaceable>...</replaceable>
";
in builtins.toFile "builder.sh" "
source $stdenv/setup
<replaceable>...</replaceable>
cp ${configFile} $out/etc/foo.conf
";</programlisting>
Note that <literal>${configFile}</literal> is an antiquotation
(see <xref linkend='ssec-values' />), so the result of the
expression <literal>configFile</literal> (i.e., a path like
<filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
spliced into the resulting string.</para>
<para>It is however <emphasis>not</emphasis> allowed to have files
mutually referring to each other, like so:
<programlisting>
let
foo = builtins.toFile "foo" "...${bar}...";
bar = builtins.toFile "bar" "...${foo}...";
in foo</programlisting>
This is not allowed because it would cause a cyclic dependency in
the computation of the cryptographic hashes for
<varname>foo</varname> and <varname>bar</varname>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
<listitem><para>Convert the string value
<replaceable>s</replaceable> into a path value. The string
<replaceable>s</replaceable> must represent an absolute path
(i.e., must start with <literal>/</literal>). The path need not
exist. The resulting path is canonicalised, e.g.,
<literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns
<literal>/foo/bar</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
<listitem><para>Convert the expression
<replaceable>e</replaceable> to a string.
<replaceable>e</replaceable> can be a string (in which case
<function>toString</function> is a no-op) or a path (e.g.,
<literal>toString /foo/bar</literal> yields
<literal>"/foo/bar"</literal>.</para></listitem>
</varlistentry>
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
<listitem><para>Return a string containing an XML representation
of <replaceable>e</replaceable>. The main application for
<function>toXML</function> is to communicate information with the
builder in a more structured format than plain environment
variables.</para>
<!-- TODO: more formally describe the schema of the XML
representation -->
<para><xref linkend='ex-toxml' /> shows an example where this is
the case. The builder is supposed to generate the configuration
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
servlet container</link>. A servlet container contains a number
of servlets (<filename>*.war</filename> files) each exported under
a specific URI prefix. So the servlet configuration is a list of
attribute sets containing the <varname>path</varname> and
<varname>war</varname> of the servlet (<xref
linkend='ex-toxml-co-servlets' />). This kind of information is
difficult to communicate with the normal method of passing
information through an environment variable, which just
concatenates everything together into a string (which might just
work in this case, but wouldnt work if fields are optional or
contain lists themselves). Instead the Nix expression is
converted to an XML representation with
<function>toXML</function>, which is unambiguous and can easily be
processed with the appropriate tools. For instance, in the
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
generate the XML configuration file for the Jetty server. The XML
representation produced from <xref linkend='ex-toxml-co-servlets'
/> by <function>toXML</function> is shown in <xref
linkend='ex-toxml-result' />.</para>
<para>Note that <xref linkend='ex-toxml' /> uses the <function
linkend='builtin-toFile'>toFile</function> built-in to write the
builder and the stylesheet “inline” in the Nix expression. The
path of the stylesheet is spliced into the builder at
<literal>xsltproc ${stylesheet}
<replaceable>...</replaceable></literal>.</para>
<example xml:id='ex-toxml'><title>Passing information to a builder
using <function>toXML</function></title>
<programlisting><![CDATA[
{stdenv, fetchurl, libxslt, jira, uberwiki}:
stdenv.mkDerivation (rec {
name = "web-server";
buildInputs = [libxslt];
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
mkdir $out
echo $servlets | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
";
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
"<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:template match='/'>
<Configure>
<xsl:for-each select='/expr/list/attrs'>
<Call name='addWebApplication'>
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
</Call>
</xsl:for-each>
</Configure>
</xsl:template>
</xsl:stylesheet>
";
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
];
})]]></programlisting>
</example>
<example xml:id='ex-toxml-result'><title>XML representation produced by
<function>toXML</function></title>
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
<expr>
<list>
<attrs>
<attr name="path">
<string value="/bugtracker" />
</attr>
<attr name="war">
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
</attr>
</attrs>
<attrs>
<attr name="path">
<string value="/wiki" />
</attr>
<attr name="war">
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
</attr>
</attrs>
</list>
</expr>]]></programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.trace</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Evaluate <replaceable>e1</replaceable> and print its
abstract syntax representation on standard error. Then return
<replaceable>e2</replaceable>. This function is useful for
debugging.</para></listitem>
</varlistentry>
</variablelist>
</section>

View File

@@ -60,6 +60,27 @@ env-keep-derivations = false
</varlistentry>
<varlistentry xml:id="conf-gc-reserved-space"><term><literal>gc-reserved-space</literal></term>
<listitem><para>This option specifies how much space should be
reserved in normal use so that the garbage collector can run
succesfully. Since the garbage collector must perform Berkeley DB
transactions, it needs some disk space for itself. However, when
the disk is full, this space is not available, so the collector
would not be able to run precisely when it is most needed.</para>
<para>For this reason, when Nix is run, it allocates a file
<filename>/nix/var/nix/db/reserved</filename> of the size
specified by this option. When the garbage collector is run, this
file is deleted before the Berkeley DB environment is opened.
This should give it enough room to proceed.</para>
<para>The default is <literal>1048576</literal> (1
MiB).</para></listitem>
</varlistentry>
<varlistentry><term><literal>env-keep-derivations</literal></term>
<listitem><para>If <literal>false</literal> (default), derivations
@@ -97,123 +118,6 @@ env-keep-derivations = false
</varlistentry>
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
<listitem>
<para>This option defines the maximum number of seconds that a
builder can go without producing any data on standard output or
standard error. This is useful (for instance in a automated
build system) to catch builds that are stuck in an infinite
loop, or to catch remote builds that are hanging due to network
problems. It can be overriden using the <option
linkend="opt-max-silent-time">--max-silent-time</option> command
line switch.</para>
<para>The value <literal>0</literal> means that there is no
timeout. This is also the default.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
<listitem><para>This options specifies the Unix group containing
the Nix build user accounts. In multi-user Nix installations,
builds should not be performed by the Nix account since that would
allow users to arbitrarily modify the Nix store and database by
supplying specially crafted builders; and they cannot be performed
by the calling user since that would allow him/her to influence
the build result.</para>
<para>Therefore, if this option is non-empty and specifies a valid
group, builds will be performed under the user accounts that are a
member of the group specified here (as listed in
<filename>/etc/group</filename>). Those user accounts should not
be used for any other purpose!</para>
<para>Nix will never run two builds under the same user account at
the same time. This is to prevent an obvious security hole: a
malicious user writing a Nix expression that modifies the build
result of a legitimate Nix expression being built by another user.
Therefore it is good to have as many Nix build user accounts as
you can spare. (Remember: uids are cheap.)</para>
<para>The build users should have permission to create files in
the Nix store, but not delete them. Therefore,
<filename>/nix/store</filename> should be owned by the Nix
account, its group should be the group specified here, and its
mode should be <literal>1775</literal>.</para>
<para>If the build users group is empty, builds will be performed
under the uid of the Nix process (that is, the uid of the caller
if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
daemon runs if <envar>NIX_REMOTE</envar> is
<literal>daemon</literal>, or the uid that owns the setuid
<command>nix-worker</command> program if <envar>NIX_REMOTE</envar>
is <literal>slave</literal>). Obviously, this should not be used
in multi-user settings with untrusted users.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>build-use-chroot</literal></term>
<listitem><para>If set to <literal>true</literal>, builds will be
performed in a <emphasis>chroot environment</emphasis>, i.e., the
build will be isolated from the normal file system hierarchy and
will only see the Nix store, the temporary build directory, and
the directories configured with the <link
linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
option</link> (such as <filename>/proc</filename> and
<filename>/dev</filename>). This is useful to prevent undeclared
dependencies on files in directories such as
<filename>/usr/bin</filename>.</para>
<para>The use of a chroot requires that Nix is run as root (but
you can still use the <link
linkend='conf-build-users-group'>“build users” feature</link> to
perform builds under different users than root). Currently,
chroot builds only work on Linux because Nix uses “bind mounts” to
make the Nix store and other directories available inside the
chroot.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
<listitem><para>When builds are performed in a chroot environment,
Nix will mount (using <command>mount --bind</command> on Linux)
some directories from the normal file system hierarchy inside the
chroot. These are the Nix store, the temporary build directory
(usually
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
and the directories listed here. The default is <literal>dev
/proc</literal>. Files in <filename>/dev</filename> (such as
<filename>/dev/null</filename>) are needed by many builds, and
some files in <filename>/proc</filename> may also be needed
occasionally.</para>
<para>The value used on NixOS is
<programlisting>
build-use-chroot = /dev /proc /bin</programlisting>
to make the <filename>/bin/sh</filename> symlink available (which
is still needed by many builders).</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>system</literal></term>
<listitem><para>This option specifies the canonical Nix system
@@ -233,17 +137,7 @@ build-use-chroot = /dev /proc /bin</programlisting>
<filename>configure</filename> at build time.</para></listitem>
</varlistentry>
<varlistentry><term><literal>fsync-metadata</literal></term>
<listitem><para>If set to <literal>true</literal>, changes to the
Nix store metadata (in <filename>/nix/var/nix/db</filename>) are
synchronously flushed to disk. This improves robustness in case
of system crashes, but reduces performance. The default is
<literal>false</literal>.</para></listitem>
</varlistentry>
</variablelist>

View File

@@ -151,12 +151,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<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>):
out on file descriptor 3 one of the following responses (terminated
by a newline, <literal>"\n"</literal>):
<variablelist>
<varlistentry><term><literal># decline</literal></term>
<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,
@@ -164,7 +164,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry><term><literal># postpone</literal></term>
<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
@@ -174,7 +174,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry><term><literal># accept</literal></term>
<varlistentry><term><literal>accept</literal></term>
<listitem><para>The build hook has accepted the
build.</para></listitem>
@@ -185,12 +185,37 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</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:
<para>If the build hook accepts the build, it is possible that it is
no longer necessary to do the build because some other process has
performed the build in the meantime. To prevent races, the hook
must read from file descriptor 4 a single line that tells it whether
to continue:
<variablelist>
<varlistentry><term><literal>cancel</literal></term>
<listitem><para>The build has already been done, so the hook
should exit.</para></listitem>
</varlistentry>
<varlistentry><term><literal>okay</literal></term>
<listitem><para>The hook should proceed with the build. At this
point, the calling Nix process has acquired locks on the output
path, so no other Nix process will perform the
build.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>If the hook has been told to proceed, Nix will store in the
hooks current directory a number of text files that contain
information about the derivation:
<variablelist>
@@ -230,9 +255,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<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>
<literal>0</literal> indicates that the hook has failed.</para>
</listitem>
@@ -240,38 +263,6 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
<listitem><para>This variable should be set to
<literal>daemon</literal> if you want to use the Nix daemon to
executed Nix operations, which is necessary in <link
linkend="ssec-multi-user">multi-user Nix installations</link>.
Otherwise, it should be left unset.</para></listitem>
</varlistentry>
<varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term>
<listitem><para>This variable contains the paths of remote Nix
installations from whichs paths can be copied, separated by colons.
See <xref linkend="sec-sharing-packages" /> for details. Each path
should be the <filename>/nix</filename> directory of a remote Nix
installation (i.e., not the <filename>/nix/store</filename>
directory). The paths are subject to globbing, so you can set it so
something like <literal>/var/run/nix/remote-stores/*/nix</literal>
and mount multiple remote filesystems in
<literal>/var/run/nix/remote-stores</literal>.</para>
<para>Note that if youre building through the <link
linkend="sec-nix-worker">Nix daemon</link>, the only setting for
this variable that matters is the one that the
<command>nix-worker</command> process uses. So if you want to
change it, you have to restart the daemon.</para></listitem>
</varlistentry>
</variablelist>

View File

@@ -74,9 +74,9 @@
<glossentry><glossterm>Nix expression</glossterm>
<glossdef><para>A high-level description of software packages and
<glossdef><para>A high-level description of software components and
compositions thereof. Deploying software using Nix entails writing
Nix expressions for your packages. Nix expressions are translated
Nix expressions for your components. Nix expressions are translated
to derivations that are stored in the Nix store. These derivations
can then be built.</para></glossdef>

View File

@@ -42,22 +42,26 @@ platforms as well.</para>
<section><title>Obtaining Nix</title>
<para>The easiest way to obtain Nix is to download a <link
xlink:href="http://nixos.org/">source distribution</link>. RPMs
for Red Hat, SuSE, and Fedora Core are also available.</para>
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">source
distribution</link>. RPMs for Red Hat, SuSE, and Fedora Core are also
available.</para>
<para>Alternatively, the most recent sources of Nix can be obtained
from its <link
xlink:href="https://svn.nixos.org/repos/nix/nix/trunk">Subversion
xlink:href="https://svn.cs.uu.nl:12443/repos/trace/nix/trunk">Subversion
repository</link>. For example, the following command will check out
the latest revision into a directory called
<filename>nix</filename>:</para>
<screen>
$ svn checkout https://svn.nixos.org/repos/nix/nix/trunk nix</screen>
$ svn checkout https://svn.cs.uu.nl:12443/repos/trace/nix/trunk nix</screen>
<para>Likewise, specific releases can be obtained from the <link
xlink:href="https://svn.nixos.org/repos/nix/nix/tags">tags
directory</link> of the repository.</para>
xlink:href="https://svn.cs.uu.nl:12443/repos/trace/nix/tags">tags
directory</link> of the repository. If you don't have Subversion, you
can also download an automatically generated <link
xlink:href="https://svn.cs.uu.nl:12443/dist/trace/">compressed
tar-file</link> of the head revision of the trunk.</para>
</section>
@@ -96,14 +100,16 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
modify the parser or when you are building from the Subversion
repository.</para>
<para>Nix uses the bzip2 compressor (including the bzip2 library). It
is included in the Nix source distribution. If you build from the
Subversion repository, you must download it yourself and place it in
the <filename>externals/</filename> directory. See
<para>Nix uses Sleepycat's Berkeley DB and CWI's ATerm library. These
are included in the Nix source distribution. If you build from the
Subversion repository, you must download them yourself and place them
in the <filename>externals/</filename> directory. See
<filename>externals/Makefile.am</filename> for the precise URLs of
this packages. Alternatively, if you already have it installed, you
can use <command>configure</command>'s <option>--with-bzip2</option>
options to point to their respective locations.</para>
these packages. Alternatively, if you already have them installed,
you can use <command>configure</command>'s <option>--with-bdb</option>
and <option>--with-aterm</option> options to point to their respective
locations. Note that Berkeley DB <emphasis>must</emphasis> be version
4.5; other versions may not have compatible database formats.</para>
</section>
@@ -112,44 +118,33 @@ options to point to their respective locations.</para>
<para>After unpacking or checking out the Nix sources, issue the
following commands:
</para>
<screen>
$ ./configure <replaceable>options...</replaceable>
$ make
$ make install</screen>
</para>
<para>When building from the Subversion repository, these should be
preceded by the command:
</para>
<screen>
$ ./bootstrap.sh</screen>
</para>
$ ./boostrap</screen>
<para>The installation path can be specified by passing the
<option>--prefix=<replaceable>prefix</replaceable></option> to
<command>configure</command>. The default installation directory is
<filename>/usr/local</filename>. You can change this to any location
you like. You must have write permission to the
<filename>/nix</filename>. You can change this to any location you
like. You must have write permission to the
<replaceable>prefix</replaceable> path.</para>
<para>Nix keeps its <emphasis>store</emphasis> (the place where
packages are stored) in <filename>/nix/store</filename> by default.
This can be changed using
<option>--with-store-dir=<replaceable>path</replaceable></option>.</para>
<warning><para>It is best <emphasis>not</emphasis> to change the
installation prefix from its default, since doing so makes it
impossible to use pre-built binaries from the standard Nixpkgs
channels.</para></warning>
<warning><para>It is best <emphasis>not</emphasis> to change the Nix
store from its default, since doing so makes it impossible to use
pre-built binaries from the standard Nixpkgs channels — that is, all
packages will need to be built from source.</para></warning>
<para>Nix keeps state (such as its database and log files) in
<filename>/nix/var</filename> by default. This can be changed using
<option>--localstatedir=<replaceable>path</replaceable></option>.</para>
<para>If you want to rebuild the documentation, pass the full path to
<para>If you want to rebuilt the documentation, pass the full path to
the DocBook RELAX NG schemas and to the DocBook XSL stylesheets using
the
<option>--with-docbook-rng=<replaceable>path</replaceable></option>
@@ -160,50 +155,45 @@ options.</para>
</section>
<section><title>Installing a binary distribution</title>
<section><title>Installing from RPMs</title>
<para>RPM and Deb packages of Nix for a number of different versions
of Fedora, openSUSE, Debian and Ubuntu can be downloaded from <link
xlink:href="http://nixos.org/" />. Once downloaded, the RPMs can be
installed or upgraded using <command>rpm -U</command>. For example,
<para>RPM packages of Nix can be downloaded from <uri
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">http://www.cs.uu.nl/groups/ST/Trace/Nix</uri>.
These RPMs should work for most fairly recent releases of SuSE and Red
Hat Linux. They have been known to work work on SuSE Linux 8.1 and
9.0, and Red Hat 9.0. In fact, it should work on any RPM-based Linux
distribution based on <literal>glibc</literal> 2.3 or later.</para>
<para>Once downloaded, the RPMs can be installed or upgraded using
<command>rpm -U</command>. For example,</para>
<screen>
$ rpm -U nix-0.13pre18104-1.i386.rpm</screen>
$ rpm -U nix-0.5pre664-1.i386.rpm</screen>
Likewise, for a Deb package:
<screen>
$ dpkg -i nix_0.13pre18104-1_amd64.deb</screen>
</para>
<para>Nix can be uninstalled using <command>rpm -e nix</command> or
<command>dpkg -r nix</command>. After this you should manually remove
the Nix store and other auxiliary data, if desired:
<para>The RPMs install into the directory <filename>/nix</filename>.
Nix can be uninstalled using <command>rpm -e nix</command>. After
this it will be necessary to manually remove the Nix store and other
auxiliary data:</para>
<screen>
$ rm -rf /nix/store
$ rm -rf /nix/var</screen>
</para>
</section>
<!-- TODO: should be updated
<section><title>Upgrading Nix through Nix</title>
<para>You can install the latest stable version of Nix through Nix
itself by subscribing to the channel <link
xlink:href="http://nixos.org/releases/nix/channels/nix-stable" />,
or the latest unstable version by subscribing to the channel <link
xlink:href="http://nixos.org/releases/nix/channels/nix-unstable" />.
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable" />,
or the latest unstable version by subscribing to the channel<link
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable" />.
You can also do a <link linkend="sec-one-click">one-click
installation</link> by clicking on the package links at <link
xlink:href="http://nixos.org/releases/full-index-nix.html" />.</para>
xlink:href="http://nix.cs.uu.nl/dist/nix/" />.</para>
</section>
-->
<section><title>Security</title>
@@ -241,215 +231,33 @@ class="username">root</systemitem> all the time.</para>
</section>
<section xml:id="ssec-multi-user"><title>Multi-user mode</title>
<section><title>Multi-user mode</title>
<para>To allow a Nix store to be shared safely among multiple users,
it is important that users are not able to run builders that modify
the Nix store or database in arbitrary ways, or that interfere with
builds started by other users. If they could do so, they could
install a Trojan horse in some package and compromise the accounts of
other users.</para>
<para></para>
<para>To prevent this, the Nix store and database are owned by some
privileged user (usually <literal>root</literal>) and builders are
executed under special user accounts (usually named
<literal>nixbld1</literal>, <literal>nixbld2</literal>, etc.). When a
unprivileged user runs a Nix command, actions that operate on the Nix
store (such as builds) are forwarded to a <emphasis>Nix
daemon</emphasis> running under the owner of the Nix store/database
that performs the operation.</para>
<!--
warning: the nix-builders group should contain *only* the Nix
builders, and nothing else. If the Nix account is compromised, you
can execute programs under the accounts in the nix-builders group, so
it obviously shouldnt contain any “real” user accounts. So dont use
an existing group like <literal>users</literal> — just create a new
one.
-->
<note><para>Multi-user mode has one important limitation: only
<systemitem class="username">root</systemitem> can run <command
linkend="sec-nix-pull">nix-pull</command> to register the availability
of pre-built binaries. However, those registrations are shared by all
users, so they still get the benefit from <command>nix-pull</command>s
done by <systemitem class="username">root</systemitem>.</para></note>
<section><title>Setting up the build users</title>
<para>The <emphasis>build users</emphasis> are the special UIDs under
which builds are performed. They should all be members of the
<emphasis>build users group</emphasis> (usually called
<literal>nixbld</literal>). This group should have no other members.
The build users should not be members of any other group.</para>
<para>Here is a typical <filename>/etc/group</filename> definition of
the build users group with 10 build users:
<programlisting>
nixbld:!:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
</programlisting>
In this example the <literal>nixbld</literal> group has UID 30000, but
of course it can be anything that doesnt collide with an existing
group.</para>
<para>Here is the corresponding part of
<filename>/etc/passwd</filename>:
<programlisting>
nixbld1:x:30001:65534:Nix build user 1:/var/empty:/noshell
nixbld2:x:30002:65534:Nix build user 2:/var/empty:/noshell
nixbld3:x:30003:65534:Nix build user 3:/var/empty:/noshell
...
nixbld10:x:30010:65534:Nix build user 10:/var/empty:/noshell
</programlisting>
The home directory of the build users should not exist or should be an
empty directory to which they do not have write access.</para>
<para>The build users should have write access to the Nix store, but
they should not have the right to delete files. Thus the Nix stores
group should be the build users group, and it should have the sticky
bit turned on (like <filename>/tmp</filename>):
<screen>
$ chgrp nixbld /nix/store
$ chmod 1777 /nix/store
</screen>
</para>
<para>Finally, you should tell Nix to use the build users by
specifying the build users group in the <link
linkend="conf-build-users-group"><literal>build-users-group</literal>
option</link> in the <link linkend="sec-conf-file">Nix configuration
file</link> (<literal>/nix/etc/nix/nix.conf</literal>):
<programlisting>
build-users-group = nixbld
</programlisting>
</para>
of pre-built binaries. However, those registrations
<emphasis>are</emphasis> used by all users to speed up
builds.</para></note>
</section>
<section><title>Nix store/database owned by root</title>
<para>The simplest setup is to let <literal>root</literal> own the Nix
store and database. I.e.,
<screen>
$ chown -R root /nix/store /nix/var/nix</screen>
</para>
<para>The <link linkend="sec-nix-worker">Nix daemon</link> should be
started as follows (as <literal>root</literal>):
<screen>
$ nix-worker --daemon</screen>
Youll want to put that line somewhere in your systems boot
scripts.</para>
<para>To let unprivileged users use the daemon, they should set the
<link linkend="envar-remote"><envar>NIX_REMOTE</envar> environment
variable</link> to <literal>daemon</literal>. So you should put a
line like
<programlisting>
export NIX_REMOTE=daemon</programlisting>
into the users login scripts.</para>
</section>
<section><title>Nix store/database not owned by root</title>
<para>It is also possible to let the Nix store and database be owned
by a non-root user, which should be more secure<footnote><para>Note
however that even when the Nix daemon runs as root, not
<emphasis>that</emphasis> much code is executed as root: Nix
expression evaluation is performed by the calling (unprivileged) user,
and builds are performed under the special build user accounts. So
only the code that accesses the database and starts builds is executed
as <literal>root</literal>.</para></footnote>. Typically, this user
is a special account called <literal>nix</literal>, but it can be
named anything. It should own the Nix store and database:
<screen>
$ chown -R root /nix/store /nix/var/nix</screen>
and of course <command>nix-worker --daemon</command> should be started
under that user, e.g.,
<screen>
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
</para>
<para>There is a catch, though: non-<literal>root</literal> users
cannot start builds under the build user accounts, since the
<function>setuid</function> system call is obviously privileged. To
allow a non-<literal>root</literal> Nix daemon to use the build user
feature, it calls a setuid-root helper program,
<command>nix-setuid-helper</command>. This program is installed in
<filename><replaceable>prefix</replaceable>/libexec/nix-setuid-helper</filename>.
To set the permissions properly (Nixs <command>make install</command>
doesnt do this, since we dont want to ship setuid-root programs
out-of-the-box):
<screen>
$ chown root.root /nix/libexec/nix-setuid-helper
$ chmod 4755 /nix/libexec/nix-setuid-helper
</screen>
(This example assumes that the Nix binaries are installed in
<filename>/nix</filename>.)</para>
<para>Of course, the <command>nix-setuid-helper</command> command
should not be usable by just anybody, since then anybody could run
commands under the Nix build user accounts. For that reason there is
a configuration file <filename>/etc/nix-setuid.conf</filename> that
restricts the use of the helper. This file should be a text file
containing precisely two lines, the first being the Nix daemon user
and the second being the build users group, e.g.,
<programlisting>
nix
nixbld
</programlisting>
The setuid-helper barfs if it is called by a user other than the one
specified on the first line, or if it is asked to execute a build
under a user who is not a member of the group specified on the second
line. The file <filename>/etc/nix-setuid.conf</filename> must be
owned by root, and must not be group- or world-writable. The
setuid-helper barfs if this is not the case.</para>
</section>
<section><title>Restricting access</title>
<para>To limit which users can perform Nix operations, you can use the
permissions on the directory
<filename>/nix/var/nix/daemon-socket</filename>. For instance, if you
want to restrict the use of Nix to the members of a group called
<literal>nix-users</literal>, do
<screen>
$ chgrp nix-users /nix/var/nix/daemon-socket
$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
</screen>
This way, users who are not in the <literal>nix-users</literal> group
cannot connect to the Unix domain socket
<filename>/nix/var/nix/daemon-socket/socket</filename>, so they cannot
perform Nix operations.</para>
</section>
</section> <!-- end of multi-user -->
</section> <!-- end of security -->
</section> <!-- end of permissions section -->
<section><title>Using Nix</title>

View File

@@ -1,337 +1,150 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="chap-introduction">
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Introduction</title>
<section><title>About Nix</title>
<para>Nix is a <emphasis>purely functional package manager</emphasis>.
This means that it treats packages like values in purely functional
programming languages such as Haskell — they are built by functions
that dont have side-effects, and they never change after they have
been built. Nix stores packages in the <emphasis>Nix
store</emphasis>, usually the directory
<filename>/nix/store</filename>, where each package has its own unique
subdirectory such as
<programlisting>
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
</programlisting>
where <literal>r8vvq9kq…</literal> is a unique identifier for the
package that captures all its dependencies (its a cryptographic hash
of the packages build dependency graph). This enables many powerful
features.</para>
<simplesect><title>Multiple versions</title>
<para>You can have multiple versions or variants of a package
installed at the same time. This is especially important when
different applications have dependencies on different versions of the
same package — it prevents the “DLL hell”. Because of the hashing
scheme, different versions of a package end up in different paths in
the Nix store, so they dont interfere with each other.</para>
<para>An important consequence is that operations like upgrading or
uninstalling an application cannot break other applications, since
these operations never “destructively” update or delete files that are
used by other packages.</para>
</simplesect>
<simplesect><title>Complete dependencies</title>
<para>Nix helps you make sure that package dependency specifications
are complete. In general, when youre making a package for a package
management system like RPM, you have to specify for each package what
its dependencies are, but there are no guarantees that this
specification is complete. If you forget a dependency, then the
package will build and work correctly on <emphasis>your</emphasis>
machine if you have the dependency installed, but not on the end
user's machine if it's not there.</para>
<para>Since Nix on the other hand doesnt install packages in “global”
locations like <filename>/usr/bin</filename> but in package-specific
directories, the risk of incomplete dependencies is greatly reduced.
This is because tools such as compilers dont search in per-packages
directories such as
<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
so if a package builds correctly on your system, this is because you
specified the dependency explicitly.</para>
<para>Runtime dependencies are found by scanning binaries for the hash
parts of Nix store paths (such as <literal>r8vvq9kq…</literal>). This
sounds risky, but it works extremely well.</para>
</simplesect>
<simplesect><title>Multi-user support</title>
<para>Starting at version 0.11, Nix has multi-user support. This
means that non-privileged users can securely install software. Each
user can have a different <emphasis>profile</emphasis>, a set of
packages in the Nix store that appear in the users
<envar>PATH</envar>. If a user installs a package that another user
has already installed previously, the package wont be built or
downloaded a second time. At the same time, it is not possible for
one user to inject a Trojan horse into a package that might be used by
another user.</para>
<!--
<para>More details can be found in Section 3 of our <a
href="docs/papers.html#securesharing">ASE 2005 paper</a>.</para>
-->
</simplesect>
<simplesect><title>Atomic upgrades and rollbacks</title>
<para>Since package management operations never overwrite packages in
the Nix store but just add new versions in different paths, they are
<emphasis>atomic</emphasis>. So during a package upgrade, there is no
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 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>
<screen>
$ nix-env --upgrade <replaceable>some-packages</replaceable>
$ nix-env --rollback
</screen>
</simplesect>
<simplesect><title>Garbage collection</title>
<para>When you install a package like this…
<screen>
$ nix-env --uninstall firefox
</screen>
the package isnt deleted from the system right away (after all, you
might want to do a rollback, or it might be in the profiles of other
users). Instead, unused packages can be deleted safely by running the
<emphasis>garbage collector</emphasis>:
<screen>
$ nix-collect-garbage
</screen>
This deletes all packages that arent in use by any user profile or by
a currently running program.</para>
</simplesect>
<simplesect><title>Functional package language</title>
<para>Packages are built from <emphasis>Nix expressions</emphasis>,
which is a simple functional language. A Nix expression describes
everything that goes into a package build action (a “derivation”):
other packages, sources, the build script, environment variables for
the build script, etc. Nix tries very hard to ensure that Nix
expressions are <emphasis>deterministic</emphasis>: building a Nix
expression twice should yield the same result.</para>
<para>Because its a functional language, its easy to support
building variants of a package: turn the Nix expression into a
function and call it any number of times with the appropriate
arguments. Due to the hashing scheme, variants dont conflict with
each other in the Nix store.</para>
</simplesect>
<simplesect><title>Transparent source/binary deployment</title>
<para>Nix expressions generally describe how to build a package from
source, so an installation action like
<screen>
$ nix-env --install firefox
</screen>
<emphasis>could</emphasis> cause quite a bit of build activity, as not
only Firefox but also all its dependencies (all the way up to the C
library and the compiler) would have to built, at least if they are
not already in the Nix store. This is a <emphasis>source deployment
model</emphasis>. For most users, building from source is not very
pleasant as it takes far too long. However, Nix can automatically
skip building from source and download a pre-built binary instead if
it knows about it. <emphasis>Nix channels</emphasis> provide Nix
expressions along with pre-built binaries.</para>
<!--
<para>source deployment model (like <a
href="http://www.gentoo.org/">Gentoo</a>) and a binary model (like
RPM)</para>
-->
</simplesect>
<simplesect><title>Binary patching</title>
<para>In addition to downloading binaries automatically if theyre
available, Nix can download binary deltas that patch an existing
package in the Nix store into a new version. This speeds up
upgrades.</para>
</simplesect>
<simplesect><title>Nix Packages collection</title>
<para>We provide a large set of Nix expressions containing hundreds of
existing Unix packages, the <emphasis>Nix Packages
collection</emphasis> (Nixpkgs).</para>
</simplesect>
<simplesect><title>Service deployment</title>
<para>Nix can be used not only for rolling out packages, but also
complete <emphasis>configurations</emphasis> of services. This is
done by treating all the static bits of a service (such as software
packages, configuration files, control scripts, static web pages,
etc.) as “packages” that can be built by Nix expressions. As a
result, all the features above apply to services as well: for
instance, you can roll back a web server configuration if a
configuration change turns out to be undesirable, you can easily have
multiple instances of a service (e.g., a test and production server),
and because the whole service is built in a purely functional way from
a Nix expression, it is repeatable so you can easily reproduce the
service on another machine.</para>
<!--
<para>You can read more about this in our <a
href="docs/papers.html#servicecm">SCM-12 paper</a>.</para>
-->
</simplesect>
<simplesect><title>Portability</title>
<para>Nix should run on most Unix systems, including Linux, FreeBSD and
Mac OS X. It is also supported on Windows using Cygwin.</para>
</simplesect>
<simplesect><title>NixOS</title>
<para>NixOS is a Linux distribution based on Nix. It uses Nix not
just for package management but also to manage the system
configuration (e.g., to build configuration files in
<filename>/etc</filename>). This means, among other things, that its
possible to easily roll back the entire configuration of the system to
an earlier state. Also, users can install software without root
privileges. For more information and downloads, see the <link
xlink:href="http://nixos.org/">NixOS homepage</link>.</para>
</simplesect>
<!-- other features:
- build farms
- reproducibility (Nix expressions allows whole configuration to be rebuilt)
-->
</section>
<section><title>About us</title>
<para>Nix was originally developed at the <link
xlink:href="http://www.cs.uu.nl/">Department of Information and
Computing Sciences</link>, Utrecht University by the <link
xlink:href="http://www.cs.uu.nl/wiki/Trace/WebHome">TraCE
project</link> (2003-2008). The project was funded by the Software
Engineering Research Program <link
xlink:href="http://www.jacquard.nl/">Jacquard</link> to improve the
support for variability in software systems. Further funding is now
provided by the NIRICT LaQuSo Build Farm project.</para>
</section>
<section><title>About this manual</title>
<para>Nix is a system for the deployment of software. Software
deployment is concerned with the creation, distribution, and
management of software components (<quote>packages</quote>). Its main
features are:
<itemizedlist>
<listitem><para>It helps you make sure that dependency specifications
are complete. In general in a deployment system you have to specify
for each component what its dependencies are, but there are no
guarantees that this specification is complete. If you forget a
dependency, then the component will build and work correctly on
<emphasis>your</emphasis> machine if you have the dependency
installed, but not on the end user's machine if it's not
there.</para></listitem>
<listitem><para>It is possible to have <emphasis>multiple versions or
variants</emphasis> of a component installed at the same time. In
contrast, in systems such as RPM different versions of the same
package tend to install to the same location in the file system, so
installing one version will remove the other. This is especially
important if you want to use applications that have conflicting
requirements on different versions of a component (e.g., application A
requires version 1.0 of library X, while application B requires a
non-backwards compatible version 1.1).</para></listitem>
<listitem><para>Users can have different <quote>views</quote>
(<quote>profiles</quote> in Nix parlance) on the set of installed
applications in a system. For instance, one user can have version 1.0
of some package visible, while another is using version 1.1, and a
third doesn't use it at all.</para></listitem>
<listitem><para>It is possible to atomically
<emphasis>upgrade</emphasis> software. I.e., there is no time window
during an upgrade in which part of the old version and part of the new
version are simultaneously visible (which might well cause the
component to fail).</para></listitem>
<listitem><para>Likewise, it is possible to atomically roll back after
an install, upgrade, or uninstall action. That is, in a fast (O(1))
operation the previous configuration of the system can be restored.
This is because upgrade or uninstall actions don't actually remove
components from the system.</para></listitem>
<listitem><para>Unused components can be
<emphasis>garbage-collected</emphasis> automatically and safely: when
you remove an application from a profile, its dependencies will be
deleted by the garbage collector only if there are no other active
applications using them.</para></listitem>
<listitem><para>Nix supports both source-based deployment models
(where you distribute <emphasis>Nix expressions</emphasis> that tell
Nix how to build software from source) and binary-based deployment
models. The latter is more-or-less transparent: installation of
components is always based on Nix expressions, but if the expressions
have been built before and Nix knows that the resulting binaries are
available somewhere, it will use those instead.</para></listitem>
<listitem><para>Nix is flexible in the deployment policies that it
supports. There is a clear separation between the tools that
implement basic Nix <emphasis>mechanisms</emphasis> (e.g., building
Nix expressions), and the tools that implement various deployment
<emphasis>policies</emphasis>. For instance, there is a concept of
<quote>Nix channels</quote> that can be used to keep software
installations up-to-date automatically from a network source. This is
a policy that is implemented by a fairly short Perl script, which can
be adapted easily to achieve similar policies.</para></listitem>
<listitem><para>Nix component builds aim to be <quote>pure</quote>;
that is, unaffected by anything other than the declared dependencies.
This means that if a component was built successfully once, it can be
rebuilt again on another machine and the result will be the same. We
cannot <emphasis>guarantee</emphasis> this (e.g., if the build depends
on the time-of-day), but Nix (and the tools in the Nix Packages
collection) takes special care to help achieve this.</para></listitem>
<listitem><para>Nix expressions (the things that tell Nix how to build
components) are self-contained: they describe not just components but
complete compositions. In other words, Nix expressions also describe
how to build all the dependencies. This is in contrast to component
specification languages like RPM spec files, which might say that a
component X depends on some other component Y, but since it does not
describe <emphasis>exactly</emphasis> what Y is, the result of
building or running X might be different on different machines.
Combined with purity, self-containedness ensures that a component that
<quote>works</quote> on one machine also works on another, when
deployed using Nix.</para></listitem>
<listitem><para>The Nix expression language makes it easy to describe
variability in components (e.g., optional features or
dependencies).</para></listitem>
<listitem><para>Nix is ideal for building build farms that do
continuous builds of software from a version management system, since
it can take care of building all the dependencies as well. Also, Nix
only rebuilds components that have changed, so there are no
unnecessary builds. In addition, Nix can transparently distribute
build jobs over different machines, including different
platforms.</para></listitem>
<listitem><para>Nix can be used not only for software deployment, but
also for <emphasis>service deployment</emphasis>, such as the
deployment of a complete web server with all its configuration files,
static pages, software dependencies, and so on. Nix's advantages for
software deployment also apply here: for instance, the ability
trivially to have multiple configurations at the same time, or the
ability to do rollbacks.</para></listitem>
<listitem><para>Nix can efficiently upgrade between different versions
of a component through <emphasis>binary patching</emphasis>. If
patches are available on a server, and you try to install a new
version of some component, Nix will automatically apply a patch (or
sequence of patches), if available, to transform the installed
component into the new version.</para></listitem>
</itemizedlist>
</para>
<para>This manual tells you how to install and use Nix and how to
write Nix expressions for software not already in the Nix Packages
collection. It also discusses some advanced topics, such as setting
up a Nix-based build farm.</para>
up a Nix-based build farm, and doing service deployment using
Nix.</para>
</section>
<section><title>License</title>
<para>Nix is free software; you can redistribute it and/or modify it
under the terms of the <link
xlink:href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General
Public License</link> as published by the <link
xlink:href="http://www.fsf.org/">Free Software Foundation</link>;
either version 2.1 of the License, or (at your option) any later
version. Nix is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.</para>
</section>
<section><title>More information</title>
<para>Some background information on Nix can be found in a number of
papers. The ICSE 2004 paper <citetitle
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/immdsd-icse2004-final.pdf'>Imposing
<note><para>Some background information on Nix can be found in a
number of papers. The ICSE 2004 paper <citetitle
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
a Memory Management Discipline on Software Deployment</citetitle>
discusses the hashing mechanism used to ensure reliable dependency
identification and non-interference between different versions and
variants of packages. The LISA 2004 paper <citetitle
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/nspfssd-lisa2004-final.pdf'>Nix:
xlink:href='http://www.cs.uu.nl/~eelco/pubs/nspfssd-lisa2004-final.pdf'>Nix:
A Safe and Policy-Free System for Software Deployment</citetitle>
gives a more general discussion of Nix from a system-administration
perspective. The CBSE 2005 paper <citetitle
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
Upgrading in a Purely Functional Component Deployment Model
</citetitle> is about transparent patch deployment in Nix. The SCM-12
paper <citetitle
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/servicecm-scm12-final.pdf'>
</citetitle> is about transparent patch deployment in Nix. Finally,
the SCM-12 paper <citetitle
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
Service Configuration Management</citetitle> shows how services (e.g.,
web servers) can be deployed and managed through Nix. A short
overview of NixOS is given in the HotOS XI paper <citetitle
xlink:href="http://www.st.ewi.tudelft.nl/~dolstra/pubs/hotos-final.pdf">Purely
Functional System Configuration Management</citetitle>. The Nix
homepage has <link
xlink:href="http://nixos.org/docs/papers.html">an up-to-date list
of Nix-related papers</link>.</para>
<para>Nix is the subject of Eelco Dolstras PhD thesis <citetitle
xlink:href="http://igitur-archive.library.uu.nl/dissertations/2006-0118-200031/index.htm">The
Purely Functional Software Deployment Model</citetitle>, which
contains most of the papers listed above.</para>
<para>Nix has a homepage at <link
xlink:href="http://nixos.org/"/>.</para>
</section>
web servers) can be deployed and managed through Nix.</para></note>
</chapter>

View File

@@ -14,10 +14,9 @@
<surname>Dolstra</surname>
</personname>
<affiliation>
<orgname>Delft University of Technology</orgname>
<orgdiv>Department of Software Technology</orgdiv>
<orgname>Utrecht University</orgname>
<orgdiv>Faculty of Science, Department of Information and Computing Sciences</orgdiv>
</affiliation>
<contrib>Author</contrib>
</author>
<copyright>
@@ -25,12 +24,10 @@
<year>2005</year>
<year>2006</year>
<year>2007</year>
<year>2008</year>
<year>2009</year>
<holder>Eelco Dolstra</holder>
</copyright>
<date>September 2009</date>
<date>September 2007</date>
</info>
@@ -51,29 +48,72 @@
<section>
<title>Main commands</title>
<xi:include href="nix-env.xml" />
<xi:include href="nix-instantiate.xml" />
<xi:include href="nix-store.xml" />
<section xml:id="sec-nix-env">
<title>nix-env</title>
<xi:include href="nix-env.xml" />
</section>
<section xml:id="sec-nix-instantiate">
<title>nix-instantiate</title>
<xi:include href="nix-instantiate.xml" />
</section>
<section xml:id="sec-nix-store">
<title>nix-store</title>
<xi:include href="nix-store.xml" />
</section>
</section>
<section>
<title>Utilities</title>
<xi:include href="nix-build.xml" />
<xi:include href="nix-channel.xml" />
<xi:include href="nix-collect-garbage.xml" />
<xi:include href="nix-copy-closure.xml" />
<xi:include href="nix-hash.xml" />
<xi:include href="nix-install-package.xml" />
<xi:include href="nix-prefetch-url.xml" />
<xi:include href="nix-pull.xml" />
<xi:include href="nix-push.xml" />
<xi:include href="nix-worker.xml" />
<section xml:id="sec-nix-build">
<title>nix-build</title>
<xi:include href="nix-build.xml" />
</section>
<section xml:id="sec-nix-channel">
<title>nix-channel</title>
<xi:include href="nix-channel.xml" />
</section>
<section xml:id="sec-nix-collect-garbage">
<title>nix-collect-garbage</title>
<xi:include href="nix-collect-garbage.xml" />
</section>
<section xml:id="sec-nix-copy-closure">
<title>nix-copy-closure</title>
<xi:include href="nix-copy-closure.xml" />
</section>
<section xml:id="sec-nix-hash">
<title>nix-hash</title>
<xi:include href="nix-hash.xml" />
</section>
<section xml:id="sec-nix-install-package">
<title>nix-install-package</title>
<xi:include href="nix-install-package.xml" />
</section>
<section xml:id="sec-nix-pack-closure">
<title>nix-pack-closure</title>
<xi:include href="nix-pack-closure.xml" />
</section>
<section xml:id="sec-nix-prefetch-url">
<title>nix-prefetch-url</title>
<xi:include href="nix-prefetch-url.xml" />
</section>
<section xml:id="sec-nix-pull">
<title>nix-pull</title>
<xi:include href="nix-pull.xml" />
</section>
<section xml:id="sec-nix-push">
<title>nix-push</title>
<xi:include href="nix-push.xml" />
</section>
<section xml:id="sec-nix-unpack-closure">
<title>nix-unpack-closure</title>
<xi:include href="nix-unpack-closure.xml" />
</section>
</section>
</appendix>
<xi:include href="troubleshooting.xml" />
<!-- <xi:include href="bugs.xml" /> -->
<xi:include href="bugs.xml" />
<xi:include href="glossary.xml" />
<appendix>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-build">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-build</refentrytitle>
@@ -20,7 +19,6 @@
<command>nix-build</command>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-channel">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-channel</refentrytitle>
@@ -63,11 +62,11 @@ also <xref linkend="sec-channels" />.</para>
<varlistentry><term><option>--update</option></term>
<listitem><para>Downloads the Nix expressions of all subscribed
channels, makes them the default for <command>nix-env</command>
operations (by symlinking them in the directory
<filename>~/.nix-defexpr</filename>), and performs a
<command>nix-pull</command> on the manifests of all channels to
make pre-built binaries available.</para></listitem>
channels, makes the conjunction of these the default for
<command>nix-env</command> operations (by calling <command>nix-env
-I</command>), and performs a <command>nix-pull</command> on the
manifests of all channels to make pre-built binaries
available.</para></listitem>
</varlistentry>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-collect-garbage">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-collect-garbage</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-copy-closure">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-copy-closure</refentrytitle>
@@ -129,7 +128,7 @@ those paths. If this bothers you, use
<para>Copy Firefox with all its dependencies to a remote machine:
<screen>
$ nix-copy-closure --to alice@itchy.labs $(type -tP firefox)</screen>
$ nix-copy-closure alice@itchy.labs $(type -tP firefox)</screen>
</para>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-env">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-env</refentrytitle>
@@ -20,7 +19,13 @@
<command>nix-env</command>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
<replaceable>attrPath</replaceable>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--file</option></arg>
@@ -40,6 +45,9 @@
<replaceable>system</replaceable>
</arg>
<arg><option>--dry-run</option></arg>
<arg><option>--from-expression</option></arg>
<arg><option>-E</option></arg>
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
<arg choice='plain'><replaceable>operation</replaceable></arg>
<arg rep='repeat'><replaceable>options</replaceable></arg>
<arg rep='repeat'><replaceable>arguments</replaceable></arg>
@@ -50,7 +58,7 @@
<refsection><title>Description</title>
<para>The command <command>nix-env</command> is used to manipulate Nix
user environments. User environments are sets of software packages
user environments. User environments are sets of software components
available to a user at some point in time. In other words, they are a
synthesised view of the programs available in the Nix store. There
may be many user environments: different users can have different
@@ -143,33 +151,13 @@ linkend="sec-common-options" />.</para>
<varlistentry><term><filename>~/.nix-defexpr</filename></term>
<listitem><para>A directory that contains the default Nix
expressions used by the <option>--install</option>,
<option>--upgrade</option>, and <option>--query
--available</option> operations to obtain derivations. The
<option>--file</option> option may be used to override this
default.</para>
<para>The Nix expressions in this directory are combined into a
single attribute set, with each file as an attribute that has the
name of the file. Thus, if <filename>~/.nix-defexpr</filename>
contains two files, <filename>foo</filename> and
<filename>bar</filename>, then the default Nix expression will
essentially be
<programlisting>
{
foo = import ~/.nix-defexpr/foo;
bar = import ~/.nix-defexpr/bar;
}</programlisting>
</para>
<para>The command <command>nix-channel</command> places symlinks
to the downloaded Nix expressions from each subscribed channel in
this directory.</para>
</listitem>
<!-- !!! .nix-defexpr can be a directory now -->
<listitem><para>The default Nix expression used by the
<option>--install</option>, <option>--upgrade</option>, and
<option>--query --available</option> operations to obtain
derivations. The <option>--file</option> option may be used to
override this default.</para></listitem>
</varlistentry>
@@ -202,7 +190,6 @@ linkend="sec-common-options" />.</para>
<arg choice='plain'><option>--install</option></arg>
<arg choice='plain'><option>-i</option></arg>
</group>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<group choice='opt'>
<arg choice='plain'><option>--preserve-installed</option></arg>
<arg choice='plain'><option>-P</option></arg>
@@ -234,21 +221,11 @@ number of possible ways:
<para>If there are multiple derivations matching a name in
<replaceable>args</replaceable> that have the same name (e.g.,
<literal>gcc-3.3.6</literal> and <literal>gcc-4.1.1</literal>), then
the derivation with the highest <emphasis>priority</emphasis> is
used. A derivation can define a priority by declaring the
<varname>meta.priority</varname> attribute. This attribute should
be a number, with a higher value denoting a lower priority. The
default priority is <literal>0</literal>.</para>
<para>If there are multiple matching derivations with the same
priority, then the derivation with the highest version will be
installed.</para>
<para>You can force the installation of multiple derivations with
the same name by being specific about the versions. For instance,
<literal>nix-env -i gcc-3.3.6 gcc-4.1.1</literal> will install both
version of GCC (and will probably cause a user environment
conflict!).</para></listitem>
only the highest version will be installed. You can force the
installation of multiple derivations with the same name by being
specific about the versions. For instance, <literal>nix-env -i
gcc-3.3.6 gcc-4.1.1</literal> will install both version of GCC (and
will probably cause a user environment conflict!).</para></listitem>
<listitem><para>If <link
linkend='opt-attr'><option>--attr</option></link>
@@ -295,15 +272,6 @@ number of possible ways:
<variablelist>
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
<listitem><para>Use only derivations for which a substitute is
registered, i.e., there is a pre-built binary available that can
be downloaded in lieu of building the derivation. Thus, no
packages will be built from source.</para></listitem>
</varlistentry>
<varlistentry><term><option>--preserve-installed</option></term>
<term><option>-P</option></term>
@@ -419,7 +387,7 @@ the following paths will be substituted:
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-upgrade"><title>Operation <option>--upgrade</option></title>
<refsection><title>Operation <option>--upgrade</option></title>
<refsection><title>Synopsis</title>
@@ -429,7 +397,6 @@ the following paths will be substituted:
<arg choice='plain'><option>--upgrade</option></arg>
<arg choice='plain'><option>-u</option></arg>
</group>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<group choice='opt'>
<arg choice='plain'><option>--lt</option></arg>
<arg choice='plain'><option>--leq</option></arg>
@@ -504,9 +471,6 @@ installed.</para>
</variablelist>
<para>For the other flags, see <option
linkend="rsec-nix-env-install">--install</option>.</para>
</refsection>
<refsection><title>Examples</title>
@@ -527,7 +491,7 @@ upgrading `mozilla-1.2' to `mozilla-1.4'</screen>
</refsection>
<refsection xml:id="ssec-version-comparisons"><title>Versions</title>
<refsection><title>Versions</title>
<para>The upgrade operation determines whether a derivation
<varname>y</varname> is an upgrade of a derivation
@@ -616,111 +580,6 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
<refsection><title>Synopsis</title>
<cmdsynopsis>
<command>nix-env</command>
<arg choice='plain'><option>--set-flag</option></arg>
<arg choice='plain'><replaceable>name</replaceable></arg>
<arg choice='plain'><replaceable>value</replaceable></arg>
<arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The <option>--set-flag</option> operation allows meta attributes
of installed packages to be modified. There are several attributes
that can be usefully modified, because they affect the behaviour of
<command>nix-env</command> or the user environment build
script:
<itemizedlist>
<listitem><para><varname>priority</varname> can be changed to
resolve filename clashes. The user environment build script uses
the <varname>meta.priority</varname> attribute of derivations to
resolve filename collisions between packages. Lower priority values
denote a higher priority. For instance, the GCC wrapper package and
the Binutils package in Nixpkgs both have a file
<filename>bin/ld</filename>, so previously if you tried to install
both you would get a collision. Now, on the other hand, the GCC
wrapper declares a higher priority than Binutils, so the formers
<filename>bin/ld</filename> is symlinked in the user
environment.</para></listitem>
<listitem><para><varname>keep</varname> can be set to
<literal>true</literal> to prevent the package from being upgraded
or replaced. This is useful if you want to hang on to an older
version of a package.</para></listitem>
<listitem><para><varname>active</varname> can be set to
<literal>false</literal> to “disable” the package. That is, no
symlinks will be generated to the files of the package, but it
remains part of the profile (so it wont be garbage-collected). It
can be set back to <literal>true</literal> to re-enable the
package.</para></listitem>
</itemizedlist>
</para>
</refsection>
<refsection><title>Examples</title>
<para>To prevent the currently installed Firefox from being upgraded:
<screen>
$ nix-env --set-flag keep true firefox</screen>
After this, <command>nix-env -u</command> will ignore Firefox.</para>
<para>To disable the currently installed Firefox, then install a new
Firefox while the old remains part of the profile:
<screen>
$ nix-env -q \*
firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
$ nix-env --preserve-installed -i firefox-2.0.0.11
installing `firefox-2.0.0.11'
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
<lineannotation>(i.e., cant have two active at the same time)</lineannotation>
$ nix-env --set-flag active false firefox
setting flag on `firefox-2.0.0.9'
$ nix-env --preserve-installed -i firefox-2.0.0.11
installing `firefox-2.0.0.11'
$ nix-env -q \*
firefox-2.0.0.11 <lineannotation>(the enabled one)</lineannotation>
firefox-2.0.0.9 <lineannotation>(the disabled one)</lineannotation></screen>
</para>
<para>To make files from <literal>binutils</literal> take precedence
over files from <literal>gcc</literal>:
<screen>
$ nix-env --set-flag priority 5 binutils
$ nix-env --set-flag priority 10 gcc</screen>
</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--query</option></title>
@@ -733,14 +592,13 @@ $ nix-env --set-flag priority 10 gcc</screen>
<arg choice='plain'><option>--query</option></arg>
<arg choice='plain'><option>-q</option></arg>
</group>
<arg><option>--xml</option></arg>
<group choice='opt'>
<arg choice='plain'><option>--installed</option></arg>
<arg choice='plain'><option>--available</option></arg>
<arg choice='plain'><option>-a</option></arg>
</group>
<sbr />
<arg>
<group choice='req'>
<arg choice='plain'><option>--status</option></arg>
@@ -749,8 +607,8 @@ $ nix-env --set-flag priority 10 gcc</screen>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr-path</option></arg>
<arg choice='plain'><option>-P</option></arg>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
</arg>
<arg><option>--no-name</option></arg>
@@ -764,28 +622,6 @@ $ nix-env --set-flag priority 10 gcc</screen>
<arg><option>--drv-path</option></arg>
<arg><option>--out-path</option></arg>
<arg><option>--description</option></arg>
<arg><option>--meta</option></arg>
<sbr />
<arg><option>--xml</option></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--prebuilt-only</option></arg>
<arg choice='plain'><option>-b</option></arg>
</group>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
<replaceable>attribute-path</replaceable>
</arg>
<sbr />
<arg choice='plain' rep='repeat'><replaceable>names</replaceable></arg>
</cmdsynopsis>
@@ -862,16 +698,6 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
<listitem><para>Show only derivations for which a substitute is
registered, i.e., there is a pre-built binary available that can
be downloaded in lieu of building the derivation. Thus, this
shows all packages that probably can be installed
quickly.</para></listitem>
</varlistentry>
<varlistentry><term><option>--status</option></term>
<term><option>-s</option></term>
@@ -891,8 +717,8 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--attr-path</option></term>
<term><option>-P</option></term>
<varlistentry><term><option>--attr</option></term>
<term><option>-a</option></term>
<listitem><para>Print the <emphasis>attribute path</emphasis> of
the derivation, which can be used to unambiguously select it using
@@ -915,35 +741,35 @@ user environment elements, etc. -->
<listitem><para>Compare installed versions to available versions,
or vice versa (if <option>--available</option> is given). This is
useful for quickly seeing whether upgrades for installed
packages are available in a Nix expression. A column is added
components are available in a Nix expression. A column is added
with the following meaning:
<variablelist>
<varlistentry><term><literal>&lt;</literal> <replaceable>version</replaceable></term>
<listitem><para>A newer version of the package is available
<listitem><para>A newer version of the component is available
or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>=</literal> <replaceable>version</replaceable></term>
<listitem><para>At most the same version of the package is
<listitem><para>At most the same version of the component is
available or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>></literal> <replaceable>version</replaceable></term>
<listitem><para>Only older versions of the package are
<listitem><para>Only older versions of the component are
available or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>- ?</literal></term>
<listitem><para>No version of the package is available or
<listitem><para>No version of the component is available or
installed.</para></listitem>
</varlistentry>
@@ -984,14 +810,6 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--meta</option></term>
<listitem><para>Print all of the meta-attributes of the
derivation. This option is only available with
<option>--xml</option>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-hash">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-hash</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-install-package">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-install-package</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-instantiate">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-instantiate</refentrytitle>

View File

@@ -178,5 +178,100 @@
</productionset>
</sect1>
<sect1>
<title>Semantics</title>
<sect2>
<title>Built-in functions</title>
<para>
The Nix language provides the following built-in function
(<quote>primops</quote>):
</para>
<variablelist>
<varlistentry>
<term><function>import</function>
<replaceable>e</replaceable></term>
<listitem>
<para>
Evaluates the expression <replaceable>e</replaceable>,
which must yield a path value. The Nix expression
stored at this path in the file system is then read,
parsed, and evaluated. Returns the result of the
evaluation of the Nix expression just read.
</para>
<para>
Example: <literal>import ./foo.nix</literal> evaluates
the expression stored in <filename>foo.nix</filename>
(in the directory containing the expression in which the
<function>import</function> occurs).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>derivation</function>
<replaceable>e</replaceable></term>
<listitem>
<para>
Evaluates the expression <replaceable>e</replaceable>,
which must yield an attribute set. [...]
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>baseNameOf</function>
<replaceable>e</replaceable></term>
<listitem>
<para>
Evaluates the expression <replaceable>e</replaceable>,
which must yield a string value, and returns a string
representing its <emphasis>base name</emphasis>. This
is the substring following the last path separator
(<literal>/</literal>).
</para>
<para>
Example: <literal>baseNameOf "/foo/bar"</literal>
returns <literal>"bar"</literal>, and
<literal>baseNameOf "/foo/bar/"</literal> returns
<literal>""</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>toString</function>
<replaceable>e</replaceable></term>
<listitem>
<para>
Evaluates the expression <replaceable>e</replaceable>
and coerces it into a string, if possible. Only
strings, paths, and URIs can be so coerced.
</para>
<para>
Example: <literal>toString
http://www.cs.uu.nl/</literal> returns
<literal>"http://www.cs.uu.nl/"</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
</sect1>
</appendix>

View File

@@ -0,0 +1,82 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-pack-closure</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="source">Nix</refmiscinfo>
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
</refmeta>
<refnamediv>
<refname>nix-pack-closure</refname>
<refpurpose>pack the closure of a store path into a single file that
can be unpacked with
<command>nix-unpack-closure</command></refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-pack-closure</command>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>The command <command>nix-pack-closure</command> packs the
contents of the store paths <replaceable>paths</replaceable> and
<emphasis>all their dependencies</emphasis> into a single file, which
is written to standard output. (That is, it
<emphasis>serialises</emphasis> <replaceable>paths</replaceable>.)
The output can then be unpacked into the Nix store of another machine
using <command>nix-unpack-closure</command>.</para>
<para>Together, <command>nix-pack-closure</command> and
<command>nix-unpack-closure</command> provide a quick and easy way to
deploy a package to a different machine. However, as the output of
<command>nix-pack-closure</command> tends to be rather large (since it
contains all dependencies), its not very efficient.
<command>nix-push</command> and <command>nix-pull</command> are more
efficient, but are also a bit more cumbersome to use.</para>
</refsection>
<refsection><title>Examples</title>
<para>To copy some instance of Subversion with all its dependencies to
another machine:
<screen>
$ nix-pack-closure /nix/store/hj232g1r...-subversion-1.3.0 > svn.closure
<lineannotation>Copy <!-- !!! <filename> -->svn.closure to the remote machine, then on the remote machine do:</lineannotation>
$ nix-unpack-closure &lt; svn.closure</screen>
</para>
<para>Copy the program <command>azureus</command> with all its
dependencies to the machine <literal>scratchy</literal>:
<screen>
$ nix-pack-closure $(which azureus) | ssh scratchy nix-unpack-closure</screen>
</para>
<para>As a variation on the previous example, copy
<command>azureus</command>, and also install it in the users profile
on the target machine:
<screen>
$ nix-pack-closure $(which azureus) | ssh scratchy 'nix-env -i $(nix-unpack-closure)'</screen>
</para>
</refsection>
</refentry>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-prefetch-url">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-prefetch-url</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-pull">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-pull</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-push">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-push</refentrytitle>

View File

@@ -1,7 +1,6 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-store">
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-store</refentrytitle>
@@ -129,7 +128,6 @@ lrwxrwxrwx 1 ... 2005-03-13 21:10 /home/eelco/bla/result -> /nix/store/1r1134
<arg choice='plain'><option>-r</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
<arg><option>--dry-run</option></arg>
</cmdsynopsis>
</refsection>
@@ -168,11 +166,6 @@ the specified store paths. Realisation is a somewhat overloaded term:
output. (For non-derivations argument, the argument itself is
printed.)</para>
<para>If the <option>--dry-run</option> option is used, then
<command>nix-store</command> will print on standard error a
description of what packages would be built or downloaded, and then
quit.</para>
</refsection>
@@ -211,8 +204,6 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
<arg choice='plain'><option>--print-dead</option></arg>
<arg choice='plain'><option>--delete</option></arg>
</group>
<arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
<arg><option>--max-links</option> <replaceable>nrlinks</replaceable></arg>
</cmdsynopsis>
</refsection>
@@ -267,34 +258,7 @@ the Nix store not reachable via file system references from a set of
</variablelist>
<para>By default, all unreachable paths are deleted. The following
options control what gets deleted and in what order:
<variablelist>
<varlistentry><term><option>--max-freed</option> <replaceable>bytes</replaceable></term>
<listitem><para>Keep deleting paths until at least
<replaceable>bytes</replaceable> bytes have been
deleted, then stop.</para></listitem>
</varlistentry>
<varlistentry><term><option>--max-links</option> <replaceable>nrlinks</replaceable></term>
<listitem><para>Keep deleting paths until the hard link count on
<filename>/nix/store</filename> is less than
<replaceable>nrlinks</replaceable>, then stop. This is useful for
very large Nix stores on filesystems with a 32000 subdirectories
limit (like <literal>ext3</literal>).</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>The behaviour of the collector is also influenced by the <link
<para>The behaviour of the collector is influenced by the <link
linkend="conf-gc-keep-outputs"><literal>gc-keep-outputs</literal></link>
and <link
linkend="conf-gc-keep-derivations"><literal>gc-keep-derivations</literal></link>
@@ -320,13 +284,6 @@ deleting `/nix/store/kq82idx6g0nyzsp2s14gfsc38npai7lf-cairo-1.0.4.tar.gz.drv'
</para>
<para>To delete at least 100 MiBs of unreachable paths:
<screen>
$ nix-store --gc --max-freed $((100 * 1024 * 1024))</screen>
</para>
</refsection>
@@ -342,7 +299,7 @@ $ nix-store --gc --max-freed $((100 * 1024 * 1024))</screen>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--delete</option></arg>
<arg choice='plain'><option>--gc</option></arg>
<arg><option>--ignore-liveness</option></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
@@ -404,7 +361,6 @@ error: cannot delete path `/nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4'
<arg choice='plain'><option>--tree</option></arg>
<arg choice='plain'><option>--binding</option> <replaceable>name</replaceable></arg>
<arg choice='plain'><option>--hash</option></arg>
<arg choice='plain'><option>--roots</option></arg>
</group>
<arg><option>--use-output</option></arg>
<arg><option>-u</option></arg>
@@ -587,20 +543,12 @@ query is applied to the target of the symlink.</para>
<varlistentry><term><option>--hash</option></term>
<listitem><para>Prints the SHA-256 hash of the contents of the
store paths <replaceable>paths</replaceable>. Since the hash is
store path <replaceable>paths</replaceable>. Since the hash is
stored in the Nix database, this is a fast
operation.</para></listitem>
</varlistentry>
<varlistentry><term><option>--roots</option></term>
<listitem><para>Prints the garbage collector roots that point,
directly or indirectly, at the store paths
<replaceable>paths</replaceable>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
@@ -678,18 +626,6 @@ $ gv graph.ps</screen>
</para>
<para>Show every garbage collector root that points to a store path
that depends on <command>svn</command>:
<screen>
$ nix-store -q --roots $(which svn)
/nix/var/nix/profiles/default-81-link
/nix/var/nix/profiles/default-82-link
/nix/var/nix/profiles/per-user/eelco/profile-97-link
</screen>
</para>
</refsection>
@@ -843,222 +779,4 @@ archive is read from standard input.</para>
</refsection>
<!--######################################################################-->
<refsection xml:id='refsec-nix-store-export'><title>Operation <option>--export</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--export</option></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--export</option> writes a serialisation
of the specified store paths to standard output in a format that can
be imported into another Nix store with <command
linkend="refsec-nix-store-import">nix-store --import</command>. This
is like <command linkend="refsec-nix-store-dump">nix-store
--dump</command>, except that the NAR archive produced by that command
doesnt contain the necessary meta-information to allow it to be
imported into another Nix store (namely, the set of references of the
path).</para>
<para>This command does not produce a <emphasis>closure</emphasis> of
the specified paths, so if a store path references other store paths
that are missing in the target Nix store, the import will fail. To
copy a whole closure, do something like
<screen>
$ nix-store --export $(nix-store -qR <replaceable>paths</replaceable>) > out</screen>
</para>
<para>For an example of how <option>--export</option> and
<option>--import</option> can be used, see the source of the <command
linkend="sec-nix-copy-closure">nix-copy-closure</command>
command.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection xml:id='refsec-nix-store-import'><title>Operation <option>--import</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--import</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--export</option> reads a serialisation of
a set of store paths produced by <command
linkend="refsec-nix-store-export">nix-store --import</command> from
standard input and adds those store paths to the Nix store. Paths
that already exist in the Nix store are ignored. If a path refers to
another path that doesnt exist in the Nix store, the import
fails.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--optimise</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--optimise</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--optimise</option> reduces Nix store disk
space usage by finding identical files in the store and hard-linking
them to each other. It typically reduces the size of the store by
something like 25-35%. Only regular files and symlinks are
hard-linked in this manner. Files are considered identical when they
have the same NAR archive serialisation: that is, regular files must
have the same contents and permission (executable or non-executable),
and symlinks must have the same contents.</para>
<para>After completion, or when the command is interrupted, a report
on the achieved savings is printed on standard error.</para>
<para>Use <option>-vv</option> or <option>-vvv</option> to get some
progress indication.</para>
</refsection>
<refsection><title>Example</title>
<screen>
$ nix-store --optimise
hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1'
<replaceable>...</replaceable>
541838819 bytes (516.74 MiB) freed by hard-linking 54143 files;
there are 114486 files with equal contents out of 215894 files in total
</screen>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--read-log</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<group choice='req'>
<arg choice='plain'><option>--read-log</option></arg>
<arg choice='plain'><option>-l</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--read-log</option> prints the build log
of the specified store paths on standard output. The build log is
whatever the builder of a derivation wrote to standard output and
standard error. If a store path is not a derivation, the deriver of
the store path is used.</para>
<para>Build logs are kept in
<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>
</refsection>
<refsection><title>Example</title>
<screen>
$ nix-store -l $(which ktorrent)
building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
unpacking sources
unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz
ktorrent-2.2.1/
ktorrent-2.2.1/NEWS
<replaceable>...</replaceable>
</screen>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--dump-db</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--dump-db</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--dump-db</option> writes a dump of the
Nix database to standard output. It can be loaded into an empty Nix
store using <option>--load-db</option>. This is useful for making
backups and when migrating to different database schemas.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--dump-db</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--load-db</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--load-db</option> reads a dump of the Nix
database created by <option>--dump-db</option> from standard input and
loads it into the Nix database.</para>
</refsection>
</refsection>
</refentry>

View File

@@ -0,0 +1,42 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle>nix-unpack-closure</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="source">Nix</refmiscinfo>
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
</refmeta>
<refnamediv>
<refname>nix-unpack-closure</refname>
<refpurpose>unpack the closure of a store path created by <command>nix-pack-closure</command> into the Nix store</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-unpack-closure</command>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>The command <command>nix-unpack-closure</command> unpacks the
closure of a set of store paths created by
<command>nix-pack-closure</command> into the local Nix store. The
closure is a single file read from standard input. See the
description of <command>nix-pack-closure</command> for details and
examples.</para>
<para>The top-level paths in the closure (i.e., the paths passed to
the original <command>nix-pack-closure</command> call that created the
closure) are printed on standard output. These paths can be passed,
for instance, to <literal>nix-env -i</literal> to install them into a
user environment on the target machine.</para>
</refsection>
</refentry>

View File

@@ -1,35 +0,0 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-worker">
<refmeta>
<refentrytitle>nix-worker</refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">Nix</refmiscinfo>
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
</refmeta>
<refnamediv>
<refname>nix-worker</refname>
<refpurpose>Nix multi-user support daemon</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-worker</command>
<arg choice="plain"><option>--daemon</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>The Nix daemon is necessary in multi-user Nix installations. It
performs build actions and other operations on the Nix store on behalf
of unprivileged users.</para>
</refsection>
</refentry>

View File

@@ -13,10 +13,6 @@
</group>
<replaceable>number</replaceable>
</arg>
<arg>
<arg><option>--max-silent-time</option></arg>
<replaceable>number</replaceable>
</arg>
<arg><option>--keep-going</option></arg>
<arg><option>-k</option></arg>
<arg><option>--keep-failed</option></arg>
@@ -24,7 +20,6 @@
<arg><option>--fallback</option></arg>
<arg><option>--readonly-mode</option></arg>
<arg><option>--log-type</option> <replaceable>type</replaceable></arg>
<arg><option>--show-trace</option></arg>
<sbr />
</nop>

View File

@@ -103,17 +103,6 @@
</varlistentry>
<varlistentry xml:id="opt-max-silent-time"><term><option>--max-silent-time</option></term>
<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-build-max-silent-time'><literal>build-max-silent-time</literal></link>
configuration setting. <literal>0</literal> means no
time-out.</para></listitem>
</varlistentry>
<varlistentry><term><option>--keep-going</option></term>
<term><option>-k</option></term>
@@ -251,14 +240,14 @@
<programlisting>
{ # The system (e.g., `i686-linux') for which to build the packages.
system ? builtins.currentSystem
system ? __currentSystem
<replaceable>...</replaceable>
}: <replaceable>...</replaceable></programlisting>
So if you call this Nix expression (e.g., when you do
<literal>nix-env -i <replaceable>pkgname</replaceable></literal>),
the function will be called automatically using the value <link
linkend='builtin-currentSystem'><literal>builtins.currentSystem</literal></link>
linkend='builtin-currentSystem'><literal>__currentSystem</literal></link>
for the <literal>system</literal> argument. You can override this
using <option>--arg</option>, e.g., <literal>nix-env -i
<replaceable>pkgname</replaceable> --arg system
@@ -268,17 +257,6 @@
</varlistentry>
<varlistentry><term><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
<listitem><para>This option is like <option>--arg</option>, only the
value is not a Nix expression but a string. So instead of
<literal>--arg system \"i686-linux\"</literal> (the outer quotes are
to keep the shell happy) you can say <literal>--argstr system
i686-linux</literal>.</para></listitem>
</varlistentry>
<varlistentry xml:id="opt-attr"><term><option>--attr</option> / <option>-A</option>
<replaceable>attrPath</replaceable></term>
@@ -305,14 +283,6 @@
</varlistentry>
<varlistentry><term><option>--show-trace</option></term>
<listitem><para>Causes Nix to print out a stack trace in case of Nix
expression evaluation errors.</para></listitem>
</varlistentry>
</variablelist>

View File

@@ -1,22 +0,0 @@
<nop xmlns="http://docbook.org/ns/docbook">
<arg>
<group choice='req'>
<arg choice='plain'><option>--prebuilt-only</option></arg>
<arg choice='plain'><option>-b</option></arg>
</group>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
</arg>
<arg><option>--from-expression</option></arg>
<arg><option>-E</option></arg>
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
</nop>

View File

@@ -6,9 +6,9 @@
<para>This chapter discusses how to do package management with Nix,
i.e., how to obtain, install, upgrade, and erase packages. This is
i.e., how to obtain, install, upgrade, and erase components. This is
the “users” perspective of the Nix system — people
who want to <emphasis>create</emphasis> packages should consult
who want to <emphasis>create</emphasis> components should consult
<xref linkend='chap-writing-nix-expressions' />.</para>
@@ -16,8 +16,8 @@ who want to <emphasis>create</emphasis> packages should consult
<para>The main command for package management is <link
linkend="sec-nix-env"><command>nix-env</command></link>. You can use
it to install, upgrade, and erase packages, and to query what
packages are installed or are available for installation.</para>
it to install, upgrade, and erase components, and to query what
components are installed or are available for installation.</para>
<para>In Nix, different users can have different “views”
on the set of installed applications. That is, there might be lots of
@@ -30,18 +30,18 @@ environment</emphasis>, which is just a directory tree consisting of
symlinks to the files of the active applications. </para>
<para>Components are installed from a set of <emphasis>Nix
expressions</emphasis> that tell Nix how to build those packages,
expressions</emphasis> that tell Nix how to build those components,
including, if necessary, their dependencies. There is a collection of
Nix expressions called the Nix Package collection that contains
packages ranging from basic development stuff such as GCC and Glibc,
components ranging from basic development stuff such as GCC and Glibc,
to end-user applications like Mozilla Firefox. (Nix is however not
tied to the Nix Package collection; you could write your own Nix
expressions based on it, or completely new ones.) You can download
the latest version from <link
xlink:href='http://nixos.org/nixpkgs/download.html' />.</para>
xlink:href='http://nix.cs.uu.nl/dist/nix' />.</para>
<para>Assuming that you have downloaded and unpacked a release of Nix
Packages, you can view the set of available packages in the release:
Packages, you can view the set of available components in the release:
<screen>
$ nix-env -qaf nixpkgs-<replaceable>version</replaceable> '*'
@@ -74,7 +74,7 @@ gcc-4.1.1</screen>
</para>
<para>It is also possible to see the <emphasis>status</emphasis> of
available packages, i.e., whether they are installed into the user
available components, i.e., whether they are installed into the user
environment and/or present in the system:
<screen>
@@ -86,24 +86,24 @@ IPS bison-1.875d
...</screen>
The first character (<literal>I</literal>) indicates whether the
package is installed in your current user environment. The second
component is installed in your current user environment. The second
(<literal>P</literal>) indicates whether it is present on your system
(in which case installing it into your user environment would be a
very quick operation). The last one (<literal>S</literal>) indicates
whether there is a so-called <emphasis>substitute</emphasis> for the
package, which is Nixs mechanism for doing binary deployment. It
just means that Nix knows that it can fetch a pre-built package from
component, which is Nixs mechanism for doing binary deployment. It
just means that Nix knows that it can fetch a pre-built component from
somewhere (typically a network server) instead of building it
locally.</para>
<para>So now that we have a set of Nix expressions we can build the
packages contained in them. This is done using <literal>nix-env
components contained in them. This is done using <literal>nix-env
-i</literal>. For instance,
<screen>
$ nix-env -f nixpkgs-<replaceable>version</replaceable> -i subversion</screen>
will install the package called <literal>subversion</literal> (which
will install the component called <literal>subversion</literal> (which
is, of course, the <link
xlink:href='http://subversion.tigris.org/'>Subversion version
management system</link>).</para>
@@ -112,21 +112,21 @@ management system</link>).</para>
Subversion and all its dependencies. This will take quite a while —
typically an hour or two on modern machines. Fortunately, there is a
faster way (so do a Ctrl-C on that install operation!): you just need
to tell Nix that pre-built binaries of all those packages are
to tell Nix that pre-built binaries of all those components are
available somewhere. This is done using the
<command>nix-pull</command> command, which must be supplied with a URL
containing a <emphasis>manifest</emphasis> describing what binaries
are available. This URL should correspond to the Nix Packages release
that youre using. For instance, if you obtained a release from <link
xlink:href='http://nixos.org/releases/nixpkgs/nixpkgs-0.12pre11712-4lrp7j8x'
/>, then you should do:
xlink:href='http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/' />, then
you should do:
<screen>
$ nix-pull http://nixos.org/releases/nixpkgs/nixpkgs-0.12pre11712-4lrp7j8x/MANIFEST</screen>
$ nix-pull http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/MANIFEST</screen>
If you then issue the installation command, it should start
downloading binaries from <systemitem
class='fqdomainname'>nixos.org</systemitem>, instead of building
class='fqdomainname'>nix.cs.uu.nl</systemitem>, instead of building
them from source. This might still take a while since all
dependencies must be downloaded, but on a reasonably fast connection
such as an DSL line its on the order of a few minutes.</para>
@@ -153,7 +153,7 @@ expressions, use <parameter>-i</parameter> instead of
<parameter>-u</parameter>; <parameter>-i</parameter> will remove
whatever version is already installed.</para>
<para>You can also upgrade all packages for which there are newer
<para>You can also upgrade all components for which there are newer
versions:
<screen>
@@ -199,19 +199,19 @@ set.</para></footnote></para>
implementing the ability to allow different users to have different
configurations, and to do atomic upgrades and rollbacks. To
understand how they work, its useful to know a bit about how Nix
works. In Nix, packages are stored in unique locations in the
works. In Nix, components are stored in unique locations in the
<emphasis>Nix store</emphasis> (typically,
<filename>/nix/store</filename>). For instance, a particular version
of the Subversion package might be stored in a directory
of the Subversion component might be stored in a directory
<filename>/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/</filename>,
while another version might be stored in
<filename>/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2</filename>.
The long strings prefixed to the directory names are cryptographic
hashes<footnote><para>160-bit truncations of SHA-256 hashes encoded in
a base-32 notation, to be precise.</para></footnote> of
<emphasis>all</emphasis> inputs involved in building the package
<emphasis>all</emphasis> inputs involved in building the component
sources, dependencies, compiler flags, and so on. So if two
packages differ in any way, they end up in different locations in
components differ in any way, they end up in different locations in
the file system, so they dont interfere with each other. <xref
linkend='fig-user-environments' /> shows a part of a typical Nix
store.</para>
@@ -231,12 +231,12 @@ $ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn</screen>
every time you want to run Subversion. Of course we could set up the
<envar>PATH</envar> environment variable to include the
<filename>bin</filename> directory of every package we want to use,
<filename>bin</filename> directory of every component we want to use,
but this is not very convenient since changing <envar>PATH</envar>
doesnt take effect for already existing processes. The solution Nix
uses is to create directory trees of symlinks to
<emphasis>activated</emphasis> packages. These are called
<emphasis>user environments</emphasis> and they are packages
<emphasis>activated</emphasis> components. These are called
<emphasis>user environments</emphasis> and they are components
themselves (though automatically generated by
<command>nix-env</command>), so they too reside in the Nix store. For
instance, in <xref linkend='fig-user-environments' /> the user
@@ -285,8 +285,8 @@ operation, a new user environment and generation link are created
based on the current one, and finally the <filename>default</filename>
symlink is made to point at the new generation. This last step is
atomic on Unix, which explains how we can do atomic upgrades. (Note
that the building/installing of new packages doesnt interfere in
any way with old packages, since they are stored in different
that the building/installing of new components doesnt interfere in
any way with old components, since they are stored in different
locations in the Nix store.)</para>
<para>If you find that you want to undo a <command>nix-env</command>
@@ -331,7 +331,7 @@ default profile, respectively. If the profile doesnt exist, it will
be created automatically. You should be careful about storing a
profile in another location than the <filename>profiles</filename>
directory, since otherwise it might not be used as a root of the
garbage collector (see <xref linkend='sec-garbage-collection'
garbage collector (see section <xref linkend='sec-garbage-collection'
/>).</para>
<para>All <command>nix-env</command> operations work on the profile
@@ -352,18 +352,18 @@ This will <emphasis>not</emphasis> change the
<para><command>nix-env</command> operations such as upgrades
(<option>-u</option>) and uninstall (<option>-e</option>) never
actually delete packages from the system. All they do (as shown
actually delete components from the system. All they do (as shown
above) is to create a new user environment that no longer contains
symlinks to the “deleted” packages.</para>
symlinks to the “deleted” components.</para>
<para>Of course, since disk space is not infinite, unused packages
<para>Of course, since disk space is not infinite, unused components
should be removed at some point. You can do this by running the Nix
garbage collector. It will remove from the Nix store any package
garbage collector. It will remove from the Nix store any component
not used (directly or indirectly) by any generation of any
profile.</para>
<para>Note however that as long as old generations reference a
package, it will not be deleted. After all, we wouldnt be able to
component, it will not be deleted. After all, we wouldnt be able to
do a rollback otherwise. So in order for garbage collection to be
effective, you should also delete (some) old generations. Of course,
this should only be done if you are certain that you will not need to
@@ -458,7 +458,7 @@ URL.</para>
<command>nix-channel --add</command>, e.g.,
<screen>
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
$ nix-channel --add http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable</screen>
subscribes you to a channel that always contains that latest version
of the Nix Packages collection. (Instead of
@@ -486,7 +486,7 @@ makes the union of each channels Nix expressions the default for
<screen>
$ nix-env -u '*'</screen>
to upgrade all packages in your profile to the latest versions
to upgrade all components in your profile to the latest versions
available in the subscribed channels.</para>
</section>
@@ -496,27 +496,31 @@ available in the subscribed channels.</para>
<para>Often, when you want to install a specific package (e.g., from
the <link
xlink:href="http://nixos.org/nixpkgs/">Nix
Packages collection</link>), subscribing to a channel is a bit
cumbersome. And channels dont help you at all if you want to install
an older version of a package than the one provided by the current
contents of the channel, or a package that has been removed from the
channel. Thats when <emphasis>one-click installs</emphasis> come in
handy: you can just go to the web page that contains the package,
click on it, and it will be installed with all the necessary
dependencies.</para>
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/">Nix
Packages collection</link> or from our <link
xlink:href='http://nix.cs.uu.nl/dist/'>release server</link>),
subscribing to a channel is a bit cumbersome. And channels dont help
you at all if you want to install an older version of a package than
the one provided by the current contents of the channel, or a package
that has been removed from the channel. Thats when
<emphasis>one-click installs</emphasis> come in handy: you can just go
to the web page that contains the package, click on it, and it will be
installed with all the necessary dependencies.</para>
<para>For instance, you can go to <link
xlink:href="http://hydra.nixos.org/jobset/nixpkgs/trunk/channel/latest"
/> and click on any link for the individual packages for your
platform. The first time you do this, your browser will ask what to
do with <literal>application/nix-package</literal> files. You should
open them with <filename>/nix/bin/nix-install-package</filename>.
This will open a window that asks you to confirm that you want to
install the package. When you answer <literal>Y</literal>, the
package and all its dependencies will be installed. This is a binary
deployment mechanism — you get packages pre-compiled for the selected
platform type.</para>
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/" />
— or to any older release of Nix Packages — and click on any link for
the individual packages for your platform (say, <link
xlink:href='http://nix.cs.uu.nl/dist/nix/nixpkgs-0.10pre6622/pkgs/subversion-1.4.0-i686-linux.nixpkg'><literal>subversion-1.4.0</literal>
for <literal>i686-linux</literal></link>). The first time you do
this, your browser will ask what to do with
<literal>application/nix-package</literal> files. You should open
them with <filename>/nix/bin/nix-install-package</filename>. This
will open a window that asks you to confirm that you want to install
the package. When you answer <literal>Y</literal>, the package and
all its dependencies will be installed. This is a binary deployment
mechanism — you get packages pre-compiled for the selected platform
type.</para>
<para>You can also install <literal>application/nix-package</literal>
files from the command line directly. See <xref
@@ -525,82 +529,4 @@ linkend='sec-nix-install-package' /> for details.</para>
</section>
<section xml:id="sec-sharing-packages"><title>Sharing packages between machines</title>
<para>Sometimes you want to copy a package from one machine to
another. Or, you want to install some packages and you know that
another machine already has some or all of those packages or their
dependencies. In that case there are mechanisms to quickly copy
packages between machines.</para>
<para>The command <command
linkend="sec-nix-copy-closure">nix-copy-closure</command> copies a Nix
store path along with all its dependencies to or from another machine
via the SSH protocol. It doesnt copy store paths that are already
present on the target machine. For example, the following command
copies Firefox with all its dependencies:
<screen>
$ nix-copy-closure --to alice@itchy.example.org $(type -p firefox)</screen>
See <xref linkend='sec-nix-copy-closure' /> for details.</para>
<para>With <command linkend='refsec-nix-store-export'>nix-store
--export</command> and <command
linkend='refsec-nix-store-import'>nix-store --import</command> you can
write the closure of a store path (that is, the path and all its
dependencies) to a file, and then unpack that file into another Nix
store. For example,
<screen>
$ nix-store --export $(type -p firefox) > firefox.closure</screen>
writes the closure of Firefox to a file. You can then copy this file
to another machine and install the closure:
<screen>
$ nix-store --import &lt; firefox.closure</screen>
Any store paths in the closure that are already present in the target
store are ignored. It is also possible to pipe the export into
another command, e.g. to copy and install a closure directly to/on
another machine:
<screen>
$ nix-store --export $(type -p firefox) | bzip2 | \
ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
But note that <command>nix-copy-closure</command> is generally more
efficient in this example because it only copies paths that are not
already present in the target Nix store.</para>
<para>Finally, if you can mount the Nix store of a remote machine in
your local filesystem, Nix can copy paths from the remote Nix store to
the local Nix store <emphasis>on demand</emphasis>. For instance,
suppose that you mount a remote machine containing a Nix store via
<command
xlink:href="http://fuse.sourceforge.net/sshfs.html">sshfs</command>:
<screen>
$ sshfs alice@itchy.example.org:/ /mnt</screen>
You should then set the <envar>NIX_OTHER_STORES</envar> environment
variable to tell Nix about this remote Nix store:
<screen>
$ export NIX_OTHER_STORES=/mnt/nix</screen>
Then if you do any Nix operation, e.g.
<screen>
$ nix-env -i firefox</screen>
and Nix has to build a path that it sees is already present in
<filename>/mnt/nix</filename>, then it will just copy from there
instead of building it from source.</para>
</section>
</chapter>

View File

@@ -1,6 +1,5 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="chap-quick-start">
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Quick Start</title>
@@ -11,9 +10,9 @@ to the following chapters.</para>
<orderedlist>
<listitem><para>Download a source tarball, RPM or Deb from <link
xlink:href='http://nixos.org/'/>. Build source distributions using
the regular sequence:
<listitem><para>Download a source tarball or RPM from <link
xlink:href='http://www.cs.uu.nl/groups/ST/Trace/Nix'/>. Build source
distributions using the regular sequence:
<screen>
$ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2
@@ -21,28 +20,19 @@ $ ./configure
$ make
$ make install <lineannotation>(as root)</lineannotation></screen>
This will install the Nix binaries in <filename>/usr/local</filename>
and keep the Nix store and other state in <filename>/nix</filename>.
You can change the former by specifying
<option>--prefix=<replaceable>path</replaceable></option>. The
location of the store can be changed using
<option>--with-store-dir=<replaceable>path</replaceable></option>.
However, you shouldn't change the store location, if at all possible,
since that will make it impossible to use pre-built binaries from the
Nixpkgs channel and other channels. The location of the state can be
changed using
<option>--localstatedir=<replaceable>path</replaceable>.</option></para></listitem>
<listitem><para>You should add
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
to your <filename>~/.bashrc</filename> (or some other login
This will install Nix in <filename>/nix</filename>. You shouldn't
change the prefix if at all possible since that will make it
impossible to use our pre-built components. Alternatively, you could
grab an RPM if you're on an RPM-based system. You should also add
<filename>/nix/etc/profile.d/nix.sh</filename> to your
<filename>~/.bashrc</filename> (or some other login
file).</para></listitem>
<listitem><para>Subscribe to the Nix Packages channel.
<screen>
$ nix-channel --add \
http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable</screen>
</para></listitem>
@@ -50,14 +40,14 @@ $ nix-channel --add \
<screen>
$ nix-channel --update</screen>
Note that this in itself doesn't download any packages, it just
Note that this in itself doesn't download any components, it just
downloads the Nix expressions that build them and stores them
somewhere (under <filename>~/.nix-defexpr</filename>, in case you're
curious). Also, it registers the fact that pre-built binaries are
available remotely.</para></listitem>
<listitem><para>See what installable packages are currently available
in the channel:
<listitem><para>See what installable components are currently
available in the channel:
<screen>
$ nix-env -qa * <lineannotation>(mind the quotes!)</lineannotation>
@@ -69,13 +59,13 @@ libxslt-1.1.0
</para></listitem>
<listitem><para>Install some packages from the channel:
<listitem><para>Install some components from the channel:
<screen>
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
This should download pre-built packages; it should not build them
locally (if it does, something went wrong).</para></listitem>
This should download the pre-built components; it should not build
them locally (if it does, something went wrong).</para></listitem>
<listitem><para>Test that they work:
@@ -102,13 +92,13 @@ $ nix-env -e hello</screen>
$ nix-channel --update
$ nix-env -u '*'</screen>
The latter command will upgrade each installed package for which there
is a “newer” version (as determined by comparing the version
The latter command will upgrade each installed component for which
there is a “newer” version (as determined by comparing the version
numbers).</para></listitem>
<listitem><para>You can also install specific packages directly from
your web browser. For instance, you can go to <link
xlink:href="http://hydra.nixos.org/jobset/nixpkgs/trunk/channel/latest" />
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/" />
and click on any link for the individual packages for your platform.
Associate <literal>application/nix-package</literal> with the program
<filename>/nix/bin/nix-install-package</filename>. A window should
@@ -117,7 +107,7 @@ appear asking you whether its okay to install the package. Say
installed.</para></listitem>
<listitem><para>If you're unhappy with the result of a
<command>nix-env</command> action (e.g., an upgraded package turned
<command>nix-env</command> action (e.g., an upgraded component turned
out not to work properly), you can go back:
<screen>
@@ -134,7 +124,7 @@ $ nix-collect-garbage -d</screen>
<!--
The first command deletes old “generations” of your profile (making
rollbacks impossible, but also making the packages in those old
rollbacks impossible, but also making the components in those old
generations available for garbage collection), while the second
command actually deletes them.-->

View File

@@ -8,593 +8,115 @@
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.15"><title>Release 0.15 (March 17, 2010)</title>
<para>This is a bug-fix release. Among other things, it fixes
building on Mac OS X (Snow Leopard), and improves the contents of
<filename>/etc/passwd</filename> and <filename>/etc/group</filename>
in <literal>chroot</literal> builds.</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title>
<para>This release has the following improvements:</para>
<itemizedlist>
<listitem>
<para>The garbage collector now starts deleting garbage much
faster than before. It no longer determines liveness of all paths
in the store, but does so on demand.</para>
</listitem>
<listitem>
<para>Added a new operation, <command>nix-store --query
--roots</command>, that shows the garbage collector roots that
directly or indirectly point to the given store paths.</para>
</listitem>
<listitem>
<para>Removed support for converting Berkeley DB-based Nix
databases to the new schema.</para>
</listitem>
<listitem>
<para>Removed the <option>--use-atime</option> and
<option>--max-atime</option> garbage collector options. They were
not very useful in practice.</para>
</listitem>
<listitem>
<para>On Windows, Nix now requires Cygwin 1.7.x.</para>
</listitem>
<listitem>
<para>A few bug fixes.</para>
</listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.13"><title>Release 0.13 (November 5,
2009)</title>
<para>This is primarily a bug fix release. It has some new
features:</para>
<itemizedlist>
<listitem>
<para>Syntactic sugar for writing nested attribute sets. Instead of
<programlisting>
{
foo = {
bar = 123;
xyzzy = true;
};
a = { b = { c = "d"; }; };
}
</programlisting>
you can write
<programlisting>
{
foo.bar = 123;
foo.xyzzy = true;
a.b.c = "d";
}
</programlisting>
This is useful, for instance, in NixOS configuration files.</para>
</listitem>
<listitem>
<para>Support for Nix channels generated by Hydra, the Nix-based
continuous build system. (Hydra generates NAR archives on the
fly, so the size and hash of these archives isnt known in
advance.)</para>
</listitem>
<listitem>
<para>Support <literal>i686-linux</literal> builds directly on
<literal>x86_64-linux</literal> Nix installations. This is
implemented using the <function>personality()</function> syscall,
which causes <command>uname</command> to return
<literal>i686</literal> in child processes.</para>
</listitem>
<listitem>
<para>Various improvements to the <literal>chroot</literal>
support. Building in a <literal>chroot</literal> works quite well
now.</para>
</listitem>
<listitem>
<para>Nix no longer blocks if it tries to build a path and another
process is already building the same path. Instead it tries to
build another buildable path first. This improves
parallelism.</para>
</listitem>
<listitem>
<para>Support for large (> 4 GiB) files in NAR archives.</para>
</listitem>
<listitem>
<para>Various (performance) improvements to the remote build
mechanism.</para>
</listitem>
<listitem>
<para>New primops: <varname>builtins.addErrorContext</varname> (to
add a string to stack traces — useful for debugging),
<varname>builtins.isBool</varname>,
<varname>builtins.isString</varname>,
<varname>builtins.isInt</varname>,
<varname>builtins.intersectAttrs</varname>.</para>
</listitem>
<listitem>
<para>OpenSolaris support (Sander van der Burg).</para>
</listitem>
<listitem>
<para>Stack traces are no longer displayed unless the
<option>--show-trace</option> option is used.</para>
</listitem>
<listitem>
<para>The scoping rules for <literal>inherit
(<replaceable>e</replaceable>) ...</literal> in recursive
attribute sets have changed. The expression
<replaceable>e</replaceable> can now refer to the attributes
defined in the containing set.</para>
</listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (November 20,
2008)</title>
<itemizedlist>
<listitem>
<para>Nix no longer uses Berkeley DB to store Nix store metadata.
The principal advantages of the new storage scheme are: it works
properly over decent implementations of NFS (allowing Nix stores
to be shared between multiple machines); no recovery is needed
when a Nix process crashes; no write access is needed for
read-only operations; no more running out of Berkeley DB locks on
certain operations.</para>
<para>You still need to compile Nix with Berkeley DB support if
you want Nix to automatically convert your old Nix store to the
new schema. If you dont need this, you can build Nix with the
<filename>configure</filename> option
<option>--disable-old-db-compat</option>.</para>
<para>After the automatic conversion to the new schema, you can
delete the old Berkeley DB files:
<screen>
$ cd /nix/var/nix/db
$ rm __db* log.* derivers references referrers reserved validpaths DB_CONFIG</screen>
The new metadata is stored in the directories
<filename>/nix/var/nix/db/info</filename> and
<filename>/nix/var/nix/db/referrer</filename>. Though the
metadata is stored in human-readable plain-text files, they are
not intended to be human-editable, as Nix is rather strict about
the format.</para>
<para>The new storage schema may or may not require less disk
space than the Berkeley DB environment, mostly depending on the
cluster size of your file system. With 1 KiB clusters (which
seems to be the <literal>ext3</literal> default nowadays) it
usually takes up much less space.</para>
</listitem>
<listitem><para>There is a new substituter that copies paths
directly from other (remote) Nix stores mounted somewhere in the
filesystem. For instance, you can speed up an installation by
mounting some remote Nix store that already has the packages in
question via NFS or <literal>sshfs</literal>. The environment
variable <envar>NIX_OTHER_STORES</envar> specifies the locations of
the remote Nix directories,
e.g. <literal>/mnt/remote-fs/nix</literal>.</para></listitem>
<listitem><para>New <command>nix-store</command> operations
<option>--dump-db</option> and <option>--load-db</option> to dump
and reload the Nix database.</para></listitem>
<listitem><para>The garbage collector has a number of new options to
allow only some of the garbage to be deleted. The option
<option>--max-freed <replaceable>N</replaceable></option> tells the
collector to stop after at least <replaceable>N</replaceable> bytes
have been deleted. The option <option>--max-links
<replaceable>N</replaceable></option> tells it to stop after the
link count on <filename>/nix/store</filename> has dropped below
<replaceable>N</replaceable>. This is useful for very large Nix
stores on filesystems with a 32000 subdirectories limit (like
<literal>ext3</literal>). The option <option>--use-atime</option>
causes store paths to be deleted in order of ascending last access
time. This allows non-recently used stuff to be deleted. The
option <option>--max-atime <replaceable>time</replaceable></option>
specifies an upper limit to the last accessed time of paths that may
be deleted. For instance,
<screen>
$ nix-store --gc -v --max-atime $(date +%s -d "2 months ago")</screen>
deletes everything that hasnt been accessed in two months.</para></listitem>
<listitem><para><command>nix-env</command> now uses optimistic
profile locking when performing an operation like installing or
upgrading, instead of setting an exclusive lock on the profile.
This allows multiple <command>nix-env -i / -u / -e</command>
operations on the same profile in parallel. If a
<command>nix-env</command> operation sees at the end that the profile
was changed in the meantime by another process, it will just
restart. This is generally cheap because the build results are
still in the Nix store.</para></listitem>
<listitem><para>The option <option>--dry-run</option> is now
supported by <command>nix-store -r</command> and
<command>nix-build</command>.</para></listitem>
<listitem><para>The information previously shown by
<option>--dry-run</option> (i.e., which derivations will be built
and which paths will be substituted) is now always shown by
<command>nix-env</command>, <command>nix-store -r</command> and
<command>nix-build</command>. The total download size of
substitutable paths is now also shown. For instance, a build will
show something like
<screen>
the following derivations will be built:
/nix/store/129sbxnk5n466zg6r1qmq1xjv9zymyy7-activate-configuration.sh.drv
/nix/store/7mzy971rdm8l566ch8hgxaf89x7lr7ik-upstart-jobs.drv
...
the following paths will be downloaded/copied (30.02 MiB):
/nix/store/4m8pvgy2dcjgppf5b4cj5l6wyshjhalj-samba-3.2.4
/nix/store/7h1kwcj29ip8vk26rhmx6bfjraxp0g4l-libunwind-0.98.6
...</screen>
</para></listitem>
<listitem><para>Language features:
<itemizedlist>
<listitem><para>@-patterns as in Haskell. For instance, in a
function definition
<programlisting>f = args @ {x, y, z}: <replaceable>...</replaceable>;</programlisting>
<varname>args</varname> refers to the argument as a whole, which
is further pattern-matched against the attribute set pattern
<literal>{x, y, z}</literal>.</para></listitem>
<listitem><para><literal>...</literal>” (ellipsis) patterns.
An attribute set pattern can now say <literal>...</literal> at
the end of the attribute name list to specify that the function
takes <emphasis>at least</emphasis> the listed attributes, while
ignoring additional attributes. For instance,
<programlisting>{stdenv, fetchurl, fuse, ...}: <replaceable>...</replaceable></programlisting>
defines a function that accepts any attribute set that includes
at least the three listed attributes.</para></listitem>
<listitem><para>New primops:
<varname>builtins.parseDrvName</varname> (split a package name
string like <literal>"nix-0.12pre12876"</literal> into its name
and version components, e.g. <literal>"nix"</literal> and
<literal>"0.12pre12876"</literal>),
<varname>builtins.compareVersions</varname> (compare two version
strings using the same algorithm that <command>nix-env</command>
uses), <varname>builtins.length</varname> (efficiently compute
the length of a list), <varname>builtins.mul</varname> (integer
multiplication), <varname>builtins.div</varname> (integer
division).
<!-- <varname>builtins.genericClosure</varname> -->
</para></listitem>
</itemizedlist>
</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now supports
<literal>mirror://</literal> URLs, provided that the environment
variable <envar>NIXPKGS_ALL</envar> points at a Nixpkgs
tree.</para></listitem>
<listitem><para>Removed the commands
<command>nix-pack-closure</command> and
<command>nix-unpack-closure</command>. You can do almost the same
thing but much more efficiently by doing <literal>nix-store --export
$(nix-store -qR <replaceable>paths</replaceable>) > closure</literal> and
<literal>nix-store --import &lt;
closure</literal>.</para></listitem>
<listitem><para>Lots of bug fixes, including a big performance bug in
the handling of <literal>with</literal>-expressions.</para></listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (December 31,
2007)</title>
<para>Nix 0.11 has many improvements over the previous stable release.
The most important improvement is secure multi-user support. It also
features many usability enhancements and language extensions, many of
them prompted by NixOS, the purely functional Linux distribution based
on Nix. Here is an (incomplete) list:</para>
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (TBA)</title>
<itemizedlist>
<listitem><para>Secure multi-user support. A single Nix store can
now be shared between multiple (possible untrusted) users. This is
an important feature for NixOS, where it allows non-root users to
install software. The old setuid method for sharing a store between
multiple users has been removed. Details for setting up a
multi-user store can be found in the manual.</para></listitem>
<listitem><para>TODO: multi-user support. The old setuid method for
sharing a store between multiple users has been
removed.</para></listitem>
<listitem><para>The new command <command>nix-copy-closure</command>
gives you an easy and efficient way to exchange software between
machines. It copies the missing parts of the closure of a set of
store path to or from a remote machine via
<command>ssh</command>.</para></listitem>
store path to or from a remote machine.</para></listitem>
<listitem><para>A new kind of string literal: strings between double
single-quotes (<literal>''</literal>) have indentation
“intelligently” removed. This allows large strings (such as shell
scripts or configuration file fragments in NixOS) to cleanly follow
the indentation of the surrounding expression. It also requires
much less escaping, since <literal>''</literal> is less common in
most languages than <literal>"</literal>.</para></listitem>
<listitem><para><command>nix-env</command> <option>--set</option>
modifies the current generation of a profile so that it contains
exactly the specified derivation, and nothing else. For example,
<literal>nix-env -p /nix/var/nix/profiles/browser --set
firefox</literal> lets the profile named
<filename>browser</filename> contain just Firefox.</para></listitem>
<listitem><para><command>nix-env</command> now maintains
meta-information about installed packages in profiles. The
meta-information is the contents of the <varname>meta</varname>
attribute of derivations, such as <varname>description</varname> or
<varname>homepage</varname>. The command <literal>nix-env -q --xml
--meta</literal> shows all meta-information.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now by default
computes the SHA-256 hash of the file instead of the MD5 hash. In
calls to <function>fetchurl</function> you should pass an
<literal>sha256</literal> attribute instead of
<literal>md5</literal>. You can pass either a hexadecimal or a
base-32 encoding of the hash.</para></listitem>
<listitem><para><command>nix-env</command> now uses the
<varname>meta.priority</varname> attribute of derivations to resolve
filename collisions between packages. Lower priority values denote
a higher priority. For instance, the GCC wrapper package and the
Binutils package in Nixpkgs both have a file
<filename>bin/ld</filename>, so previously if you tried to install
both you would get a collision. Now, on the other hand, the GCC
wrapper declares a higher priority than Binutils, so the formers
<filename>bin/ld</filename> is symlinked in the user
environment.</para></listitem>
<listitem><para><command>nix-env -i / -u</command>: instead of
breaking package ties by version, break them by priority and version
number. That is, if there are multiple packages with the same name,
then pick the package with the highest priority, and only use the
version if there are multiple packages with the same
priority.</para>
<para>This makes it possible to mark specific versions/variant in
Nixpkgs more or less desirable than others. A typical example would
be a beta version of some package (e.g.,
<literal>gcc-4.2.0rc1</literal>) which should not be installed even
though it is the highest version, except when it is explicitly
selected (e.g., <literal>nix-env -i
gcc-4.2.0rc1</literal>).</para></listitem>
<listitem><para><command>nix-env --set-flag</command> allows meta
attributes of installed packages to be modified. There are several
attributes that can be usefully modified, because they affect the
behaviour of <command>nix-env</command> or the user environment
build script:
<itemizedlist>
<listitem><para><varname>meta.priority</varname> can be changed
to resolve filename clashes (see above).</para></listitem>
<listitem><para><varname>meta.keep</varname> can be set to
<literal>true</literal> to prevent the package from being
upgraded or replaced. Useful if you want to hang on to an older
version of a package.</para></listitem>
<listitem><para><varname>meta.active</varname> can be set to
<literal>false</literal> to “disable” the package. That is, no
symlinks will be generated to the files of the package, but it
remains part of the profile (so it wont be garbage-collected).
Set it back to <literal>true</literal> to re-enable the
package.</para></listitem>
</itemizedlist>
</para></listitem>
<listitem><para><command>nix-env -q</command> now has a flag
<option>--prebuilt-only</option> (<option>-b</option>) that causes
<command>nix-env</command> to show only those derivations whose
output is already in the Nix store or that can be substituted (i.e.,
downloaded from somewhere). In other words, it shows the packages
that can be installed “quickly”, i.e., dont need to be built from
source. The <option>-b</option> flag is also available in
<command>nix-env -i</command> and <command>nix-env -u</command> to
filter out derivations for which no pre-built binary is
available.</para></listitem>
<listitem><para>The new option <option>--argstr</option> (in
<command>nix-env</command>, <command>nix-instantiate</command> and
<command>nix-build</command>) is like <option>--arg</option>, except
that the value is a string. For example, <literal>--argstr system
i686-linux</literal> is equivalent to <literal>--arg system
\"i686-linux\"</literal> (note that <option>--argstr</option>
prevents annoying quoting around shell arguments).</para></listitem>
<listitem><para><command>nix-store</command> has a new operation
<option>--read-log</option> (<option>-l</option>)
<parameter>paths</parameter> that shows the build log of the given
paths.</para></listitem>
<!--
<listitem><para>TODO: <varname>allowedReferences</varname> for
checking the set of references in the output of a
derivation.</para></listitem>
<listitem><para>TODO: semantic cleanups of string concatenation
etc. (mostly in r6740).</para></listitem>
-->
<listitem><para>Nix now uses Berkeley DB 4.5. The database is
upgraded automatically, but you should be careful not to use old
versions of Nix that still use Berkeley DB 4.4.</para></listitem>
<listitem><para>TODO: now using Berkeley DB 4.5.</para></listitem>
<!-- foo
<listitem><para>TODO: option <option>- -reregister</option> in
<command>nix-store - -register-validity</command>.</para></listitem>
-->
<listitem><para>TODO: option <option>--reregister</option> in
<command>nix-store --register-validity</command>.</para></listitem>
<listitem><para>The option <option>--max-silent-time</option>
(corresponding to the configuration setting
<literal>build-max-silent-time</literal>) allows you to set a
timeout on builds — if a build produces no output on
<literal>stdout</literal> or <literal>stderr</literal> for the given
number of seconds, it is terminated. This is useful for recovering
automatically from builds that are stuck in an infinite
loop.</para></listitem>
<listitem><para>TODO: magic <varname>exportReferencesGraph</varname>
attribute.</para></listitem>
<listitem><para><command>nix-channel</command>: each subscribed
channel is its own attribute in the top-level expression generated
for the channel. This allows disambiguation (e.g. <literal>nix-env
-i -A nixpkgs_unstable.firefox</literal>).</para></listitem>
<listitem><para>TODO: option <option>--max-silent-time</option>,
configuration setting
<literal>build-max-silent-time</literal>.</para></listitem>
<listitem><para>The substitutes table has been removed from the
database. This makes operations such as <command>nix-pull</command>
and <command>nix-channel --update</command> much, much
faster.</para></listitem>
<listitem><para>TODO: <command>nix-env</command>
<option>--set</option>.</para></listitem>
<listitem><para>TODO: <option>--argstr</option>.</para></listitem>
<listitem><para><command>nix-pull</command> now supports
bzip2-compressed manifests. This speeds up
channels.</para></listitem>
<listitem><para>TODO: <command>nix-env</command> now maintains meta
info about installed packages in user environments. <option>-q
--xml --meta</option> to show all meta info.</para></listitem>
<listitem><para>TODO: <command>nix-env</command>
<option>--set-flag</option>. Specific flags:
<literal>active</literal>, <literal>priority</literal>,
<literal>keep</literal>.</para></listitem>
<listitem><para>TODO: <command>nix-env</command> <option>-i</option>
/ <option>-u</option> take package priorities into
account.</para></listitem>
<listitem><para><command>nix-env -q</command> now has a flag
<option>--prebuilt-only</option> (<option>-b</option>) that causes
<command>nix-env</command> to show only those derivations whose
output is already in the Nix store or that can be substituted (i.e.,
downloaded from somewhere). In other words, it shows the packages
that can be installed “quickly”, i.e., dont need to be built from
source.</para></listitem>
<listitem><para>TODO: new built-ins
<function>builtins.attrNames</function>,
<function>builtins.filterSource</function>,
<function>builtins.sub</function>,
<function>builtins.stringLength</function>,
<function>builtins.substring</function>.</para></listitem>
<listitem><para>TODO: each subscribed channel is its own attribute
in the top-level expression generated for the channel, this allows
disambiguation (<command>nix-env -qaA</command>).</para></listitem>
<listitem><para>TODO: substitutes table is gone, registering
substitutes is now much faster.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now has a
limited form of caching. This is used by
<command>nix-channel</command> to prevent unnecessary downloads when
the channel hasnt changed.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now by default
computes the SHA-256 hash of the file instead of the MD5 hash. In
calls to <function>fetchurl</function> you should pass the
<literal>sha256</literal> attribute instead of
<literal>md5</literal>. You can pass either a hexadecimal or a
base-32 encoding of the hash.</para></listitem>
<listitem><para>Nix can now perform builds in an automatically
generated “chroot”. This prevents a builder from accessing stuff
outside of the Nix store, and thus helps ensure purity. This is an
experimental feature.</para></listitem>
<listitem><para>The new command <command>nix-store
--optimise</command> reduces Nix store disk space usage by finding
identical files in the store and hard-linking them to each other.
It typically reduces the size of the store by something like
25-35%.</para></listitem>
<listitem><para><filename>~/.nix-defexpr</filename> can now be a
directory, in which case the Nix expressions in that directory are
combined into an attribute set, with the file names used as the
names of the attributes. The command <command>nix-env
--import</command> (which set the
<filename>~/.nix-defexpr</filename> symlink) is
removed.</para></listitem>
<listitem><para>Derivations can specify the new special attribute
<varname>allowedReferences</varname> to enforce that the references
in the output of a derivation are a subset of a declared set of
paths. For example, if <varname>allowedReferences</varname> is an
empty list, then the output must not have any references. This is
used in NixOS to check that generated files such as initial ramdisks
for booting Linux dont have any dependencies.</para></listitem>
<listitem><para>The new attribute
<varname>exportReferencesGraph</varname> allows builders access to
the references graph of their inputs. This is used in NixOS for
tasks such as generating ISO-9660 images that contain a Nix store
populated with the closure of certain paths.</para></listitem>
<listitem><para>Fixed-output derivations (like
<function>fetchurl</function>) can define the attribute
<varname>impureEnvVars</varname> to allow external environment
variables to be passed to builders. This is used in Nixpkgs to
support proxy configuration, among other things.</para></listitem>
<listitem><para>Several new built-in functions:
<function>builtins.attrNames</function>,
<function>builtins.filterSource</function>,
<function>builtins.isAttrs</function>,
<function>builtins.isFunction</function>,
<function>builtins.listToAttrs</function>,
<function>builtins.stringLength</function>,
<function>builtins.sub</function>,
<function>builtins.substring</function>,
<function>throw</function>,
<function>builtins.trace</function>,
<function>builtins.readFile</function>.</para></listitem>
</itemizedlist>

View File

@@ -46,11 +46,6 @@ h3 /* subsections */
font-size: 125%;
}
div.simplesect h2
{
font-size: 110%;
}
div.appendix h3
{
font-size: 150%;

View File

@@ -4,9 +4,78 @@
<title>Troubleshooting</title>
<para>This section provides solutions for some common problems. See
the <link xlink:href="http://bugs.strategoxt.org/browse/NIX">Nix
bug tracker</link> for a list of currently known issues.</para>
<para>This section provides solutions for some common problems.</para>
<section><title>Berkeley DB: <quote>Cannot allocate memory</quote></title>
<para>Symptom: Nix operations (in particular the
<command>nix-store</command> operations <option>--gc</option>,
<option>--verify</option>, and <option>--clear-substitutes</option>
the latter being called by <command>nix-channel --update</command>)
failing:
<screen>
$ nix-store --verify
error: Db::del: Cannot allocate memory</screen>
</para>
<para>Possible solution: make sure that no Nix processes are running,
then do:
<screen>
$ cd /nix/var/nix/db
$ rm __db.00*</screen>
</para>
</section>
<section><title>Berkeley DB gives weird error messages</title>
<para>Symptom: you get error messages such as
<screen>
Berkeley DB message: Finding last valid log LSN: file: 1 offset 28
Berkeley DB error: file validpaths (meta pgno = 0) has LSN [483][34721].
Berkeley DB error: end of log is [1][28]
Berkeley DB error: /nix/var/nix/db/validpaths: unexpected file type or format</screen>
or other weird Berkeley DB errors, and they dont go away (i.e.,
automatic recovery doesnt work). This may be the case after a system
crash.</para>
<para>Solution: first try to run <command>db_recover</command> and
then <link linkend='refsec-nix-store-verify'><command>nix-store
--verify</command></link>:
<screen>
$ db_recover -h /nix/var/nix/db
$ nix-store --verify</screen>
(Make sure that you have the right version of
<command>db_recover</command>, namely, Berkeley DB 4.4 for Nix 0.10,
and 4.5 for Nix 0.11.)</para>
<para>If that doesnt work, its time to bring out the big guns:
<screen>
$ cd /nix/var/nix
$ cp -pr db db-backup <lineannotation>(making a backup just in case)</lineannotation>
$ cd db
$ rm __db.* log* <lineannotation>(removing the Berkeley DB environment)</lineannotation>
$ mkdir tmp
$ for i in *; do db_dump $i | (cd tmp &amp;&amp; db_load $i); done
<lineannotation>(ignore error messages about non-database files like “reserved”)</lineannotation>
$ mv tmp/* .
$ nix-store --verify</screen>
</para>
</section>
<section><title>Collisions in <command>nix-env</command></title>
@@ -76,8 +145,7 @@ Furthermore, the <literal>st_nlink</literal> field of the
<para>This only happens on very large Nix installations (such as build
machines).</para>
<para>Quick solution: run the garbage collector. You may want to use
the <option>--max-links</option> option.</para>
<para>Quick solution: run the garbage collector.</para>
<para>Real solution: put the Nix store on a file system that supports
more than 32,000 subdirectories per directory, such as ReiserFS.

File diff suppressed because it is too large Load Diff

82
externals/Makefile.am vendored
View File

@@ -1,11 +1,78 @@
# Berkeley DB
DB = db-4.5.20
$(DB).tar.gz:
@echo "Nix requires Berkeley DB to build."
@echo "Please download version 4.5.20 from"
@echo " http://download-east.oracle.com/berkeley-db/db-4.5.20.tar.gz"
@echo "and place it in the externals/ directory."
false
$(DB): $(DB).tar.gz
gunzip < $(srcdir)/$(DB).tar.gz | tar xvf -
(cd $(DB) && $(patch) -p1) < $(srcdir)/bdb-cygwin.patch
have-db:
$(MAKE) $(DB)
touch have-db
if HAVE_BDB
build-db:
else
build-db: have-db
(pfx=`pwd` && \
cd $(DB)/build_unix && \
CC="$(CC)" CXX="$(CXX)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" \
../dist/configure --prefix=$$pfx/inst-bdb \
--enable-cxx --disable-shared --disable-cryptography \
--disable-replication --disable-verify && \
$(MAKE) && \
$(MAKE) install_include install_lib)
touch build-db
endif
# CWI ATerm
ATERM = aterm-2.4.2-fixes-r2
$(ATERM).tar.bz2:
@echo "Nix requires the CWI ATerm library to build."
@echo "Please download version 2.4.2-fixes-r2 from"
@echo " http://losser.st-lab.cs.uu.nl/~eelco/dist/aterm-2.4.2-fixes-r2.tar.bz2"
@echo "and place it in the externals/ directory."
false
$(ATERM): $(ATERM).tar.bz2
bunzip2 < $(srcdir)/$(ATERM).tar.bz2 | tar xvf -
have-aterm:
$(MAKE) $(ATERM)
touch have-aterm
if HAVE_ATERM
build-aterm:
else
build-aterm: have-aterm
(pfx=`pwd` && \
cd $(ATERM) && \
CC="$(CC)" ./configure --prefix=$$pfx/inst-aterm \
--disable-shared --enable-static && \
$(MAKE) && \
$(MAKE) install)
touch build-aterm
endif
# bzip2
BZIP2 = bzip2-1.0.5
BZIP2 = bzip2-1.0.4
$(BZIP2).tar.gz:
@echo "Nix requires bzip2 to build."
@echo "Please download version 1.0.5 from"
@echo " http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz"
@echo "Please download version 1.0.4 from"
@echo " http://www.bzip.org/1.0.4/bzip2-1.0.4.tar.gz"
@echo "and place it in the externals/ directory."
false
@@ -32,10 +99,11 @@ install:
endif
all: build-bzip2
all: build-db build-aterm build-bzip2
EXTRA_DIST = $(BZIP2).tar.gz
EXTRA_DIST = $(DB).tar.gz $(ATERM).tar.bz2 $(BZIP2).tar.gz \
bdb-cygwin.patch
ext-clean:
$(RM) -f have-bzip2 build-bzip2
$(RM) -rf $(BZIP2)
$(RM) -f have-db build-db have-aterm build-aterm
$(RM) -rf $(DB) $(ATERM) $(BZIP2)

22
externals/bdb-cygwin.patch vendored Normal file
View File

@@ -0,0 +1,22 @@
diff -rc db-4.5.20-orig/os/os_flock.c db-4.5.20/os/os_flock.c
*** db-4.5.20-orig/os/os_flock.c 2006-10-13 12:36:12.000000000 +0200
--- db-4.5.20/os/os_flock.c 2006-10-13 12:40:11.000000000 +0200
***************
*** 30,35 ****
--- 30,44 ----
DB_ASSERT(dbenv, F_ISSET(fhp, DB_FH_OPENED) && fhp->fd != -1);
+ #ifdef __CYGWIN__
+ /*
+ * Windows file locking interferes with read/write operations, so we
+ * map the ranges to an area past the end of the file.
+ */
+ DB_ASSERT(dbenv, offset < (off_t) 1 << 62);
+ offset += (off_t) 1 << 62;
+ #endif
+
fl.l_start = offset;
fl.l_len = 1;
fl.l_type = acquire ? F_WRLCK : F_UNLCK;
Only in db-4.5.20/os: os_flock.c~

View File

@@ -76,10 +76,10 @@ The hook `nix-mode-hook' is run when Nix mode is started.
("\\<baseNameOf\\>" . font-lock-builtin-face)
("\\<toString\\>" . font-lock-builtin-face)
("\\<isNull\\>" . font-lock-builtin-face)
("\\<\\([a-zA-Z_][a-zA-Z0-9_']*\\)[ \t]*="
(1 font-lock-variable-name-face nil nil))
("[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)
))

View File

@@ -1,37 +0,0 @@
" Vim syntax file
" Language: nix
" Maintainer: Marc Weber <marco-oweber@gmx.de>
" Modify and commit if you feel that way
" Last Change: 2007 Dec
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
finish
endif
syn keyword nixKeyword let throw inherit import true false null with
syn keyword nixConditional if else then
syn keyword nixBrace ( ) { } =
syn keyword nixBuiltin __currentSystem __currentTime __isFunction __getEnv __trace __toPath __pathExists
\ __readFile __toXML __toFile __filterSource __attrNames __getAttr __hasAttr __isAttrs __listToAttrs __isList
\ __head __tail __add __sub __lessThan __substring __stringLength
syn match nixAttr "\w\+\ze\s*="
syn match nixFuncArg "\zs\w\+\ze\s*:"
syn region nixStringParam start=+\${+ end=+}+
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
syn match nixEndOfLineComment "#.*$"
syn region nixStringIndented start=+''+ skip=+'''\|''${\|"+ end=+''+ contains=nixStringParam
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
hi def link nixKeyword Keyword
hi def link nixConditional Conditional
hi def link nixBrace Special
hi def link nixString String
hi def link nixStringIndented String
hi def link nixBuiltin Special
hi def link nixStringParam Macro
hi def link nixMultiLineComment Comment
hi def link nixEndOfLineComment Comment
hi def link nixAttr Identifier
hi def link nixFuncArg Identifier

View File

@@ -29,6 +29,25 @@
#gc-keep-derivations = true
### Option `gc-reserved-space'
#
# This option specifies how much space should be reserved in normal
# use so that the garbage collector can run succesfully. Since the
# garbage collector must perform Berkeley DB transactions, it needs
# some disk space for itself. However, when the disk is full, this
# space is not available, so the collector would not be able to run
# precisely when it is most needed.
#
# For this reason, when Nix is run, it allocates a file
# /nix/var/nix/db/reserved of the size specified by this option. When
# the garbage collector is run, this file is deleted before the
# Berkeley DB environment is opened. This should give it enough room
# to proceed.
#
# The default is "1048576" (1 MiB).
#gc-reserved-space = 1048576
### Option `env-keep-derivations'
#
# If `false' (default), derivations are not stored in Nix user
@@ -61,7 +80,7 @@
### Option `build-max-silent-time'
#
# This option defines the maximum number of seconds that a builder can
# This option defines the maximum number of seconds that builder can
# go without producing any data on standard output or standard error.
# This is useful (for instance in a automated build system) to catch
# builds that are stuck in an infinite loop, or to catch remote builds
@@ -116,55 +135,21 @@
#build-users-group =
### Option `build-use-chroot'
### Option `system'
#
# If set to `true', builds will be performed in a chroot environment,
# i.e., the build will be isolated from the normal file system
# hierarchy and will only see the Nix store, the temporary build
# directory, and the directories configured with the
# `build-chroot-dirs' option (such as /proc and /dev). This is useful
# to prevent undeclared dependencies on files in directories such as
# /usr/bin.
# This option specifies the canonical Nix system name of the current
# installation, such as `i686-linux' or `powerpc-darwin'. Nix can
# only build derivations whose `system' attribute equals the value
# specified here. In general, it never makes sense to modify this
# value from its default, since you can use it to `lie' about the
# platform you are building on (e.g., perform a Mac OS build on a
# Linux machine; the result would obviously be wrong). It only makes
# sense if the Nix binaries can run on multiple platforms, e.g.,
# `universal binaries' that run on `powerpc-darwin' and `i686-darwin'.
#
# The use of a chroot requires that Nix is run as root (but you can
# still use the "build users" feature to perform builds under
# different users than root). Currently, chroot builds only work on
# Linux because Nix uses "bind mounts" to make the Nix store and other
# directories available inside the chroot.
#
# The default is `false'.
# It defaults to the canonical Nix system name detected by `configure'
# at build time.
#
# Example:
# build-use-chroot = true
#build-use-chroot = false
### Option `build-chroot-dirs'
#
# When builds are performed in a chroot environment, Nix will mount
# (using `mount --bind' on Linux) some directories from the normal
# file system hierarchy inside the chroot. These are the Nix store,
# the temporary build directory (usually /tmp/nix-<pid>-<number>) and
# the directories listed here. The default is "/dev /dev/pts /proc".
# Files in /dev (such as /dev/null) are needed by many builds, and
# some files in /proc may also be needed occasionally.
#
# Example:
# build-use-chroot = /dev /proc /bin
#build-chroot-dirs = /dev /dev/pts /proc
### Option `build-cache-failure'
#
# If this option is enabled, Nix will do negative caching; that is, it
# will remember failed builds, and won't attempt to try to build them
# again if you ask for it. Negative caching is disabled by default
# because Nix cannot distinguish between permanent build errors (e.g.,
# a syntax error in a source file) and transient build errors (e.g., a
# full disk), as they both cause the builder to return a non-zero exit
# code. You can clear the cache by doing `rm -f
# /nix/var/nix/db/failed/*'.
#
# Example:
# build-cache-failure = true
#build-cache-failure = false
# system = i686-darwin
#system =

View File

@@ -13,10 +13,11 @@ Version: @version@
Release: 1
License: GPL
Group: Software Deployment
URL: http://nixos.org/
URL: http://nix.cs.uu.nl/
Source0: %{name}-@version@.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
Prefix: /usr
%define _prefix /nix
Prefix: %{_prefix}
Requires: /usr/bin/perl
Requires: curl
@@ -46,14 +47,14 @@ if test -n "%{enable_setuid}"; then
extraFlags="$extraFlags --with-nix-group=%{nix_group}"
fi
fi
./configure --prefix=%{_prefix} --sysconfdir=/etc $extraFlags
./configure --prefix=%{_prefix} $extraFlags
make
make check
%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install
rm $RPM_BUILD_ROOT/etc/nix/nix.conf
rm $RPM_BUILD_ROOT/%{_prefix}/etc/nix/nix.conf
strip $RPM_BUILD_ROOT/%{_prefix}/bin/* || true
%clean
@@ -75,9 +76,10 @@ fi
%{_prefix}/lib
%{_prefix}/libexec
%{_prefix}/include
%{_prefix}/var
%{_prefix}/share
/etc/profile.d/nix.sh
/nix/var
/nix/store
%{_prefix}/store
%config
/etc/nix
%{_prefix}/etc
#%doc
#%{_prefix}/share/nix/manual

View File

@@ -1,206 +0,0 @@
{ nixpkgs ? ../nixpkgs }:
let
jobs = rec {
tarball =
{ nix ? {outPath = ./.; rev = 1234;}
, officialRelease ? false
}:
with import nixpkgs {};
releaseTools.sourceTarball {
name = "nix-tarball";
version = builtins.readFile ./version;
src = nix;
inherit officialRelease;
buildInputs =
[ curl bison flex2533 perl libxml2 libxslt w3m bzip2
tetex dblatex nukeReferences
];
configureFlags = ''
--with-docbook-rng=${docbook5}/xml/rng/docbook
--with-docbook-xsl=${docbook5_xsl}/xml/xsl/docbook
--with-xml-flags=--nonet
'';
# Include the Bzip2 tarball in the distribution.
preConfigure = ''
stripHash ${bzip2.src}
cp -pv ${bzip2.src} externals/$strippedName
# TeX needs a writable font cache.
export VARTEXFONTS=$TMPDIR/texfonts
'';
preDist = ''
make -C doc/manual install prefix=$out
make -C doc/manual manual.pdf prefix=$out
cp doc/manual/manual.pdf $out/manual.pdf
# The PDF containes filenames of included graphics (see
# http://www.tug.org/pipermail/pdftex/2007-August/007290.html).
# This causes a retained dependency on dblatex, which Hydra
# doesn't like (the output of the tarball job is distributed
# to Windows and Macs, so there should be no Linux binaries
# in the closure).
nuke-refs $out/manual.pdf
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
'';
};
build =
{ tarball ? jobs.tarball {}
, system ? "i686-linux"
}:
with import nixpkgs {inherit system;};
releaseTools.nixBuild {
name = "nix";
src = tarball;
buildInputs = [curl perl bzip2 openssl];
configureFlags = ''
--disable-init-state
--with-bzip2=${bzip2}
'';
};
/*
static =
{ tarball ? jobs.tarball {}
, system ? "i686-linux"
}:
with import nixpkgs {inherit system;};
releaseTools.binaryTarball {
name = "nix-static-tarball";
src = tarball;
buildInputs = [curl perl bzip2];
configureFlags = ''
--disable-init-state
--with-bzip2=${bzip2}
--enable-static-nix
'';
};
*/
coverage =
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {};
releaseTools.coverageAnalysis {
name = "nix-build";
src = tarball;
buildInputs = [
curl perl bzip2 openssl
# These are for "make check" only:
graphviz libxml2 libxslt
];
configureFlags = ''
--disable-init-state --disable-shared
--with-bzip2=${bzip2}
'';
lcovFilter = ["*/boost/*" "*-tab.*"];
# We call `dot', and even though we just use it to
# syntax-check generated dot files, it still requires some
# fonts. So provide those.
FONTCONFIG_FILE = texFunctions.fontsConf;
};
rpm_fedora5i386 = makeRPM_i686 (diskImages: diskImages.fedora5i386) 10;
rpm_fedora9i386 = makeRPM_i686 (diskImages: diskImages.fedora9i386) 20;
rpm_fedora9x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora9x86_64) 20;
rpm_fedora10i386 = makeRPM_i686 (diskImages: diskImages.fedora10i386) 30;
rpm_fedora10x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora10x86_64) 30;
rpm_fedora11i386 = makeRPM_i686 (diskImages: diskImages.fedora11i386) 40;
rpm_fedora11x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora11x86_64) 40;
rpm_fedora12i386 = makeRPM_i686 (diskImages: diskImages.fedora12i386) 50;
rpm_fedora12x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora12x86_64) 50;
rpm_opensuse103i386 = makeRPM_i686 (diskImages: diskImages.opensuse103i386) 40;
rpm_opensuse110i386 = makeRPM_i686 (diskImages: diskImages.opensuse110i386) 50;
rpm_opensuse110x86_64 = makeRPM_x86_64 (diskImages: diskImages.opensuse110x86_64) 50;
deb_debian40i386 = makeDeb_i686 (diskImages: diskImages.debian40i386) 40;
deb_debian40x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian40x86_64) 40;
deb_debian50i386 = makeDeb_i686 (diskImages: diskImages.debian50i386) 50;
deb_debian50x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian50x86_64) 50;
deb_ubuntu804i386 = makeDeb_i686 (diskImages: diskImages.ubuntu804i386) 20;
deb_ubuntu804x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu804x86_64) 20;
deb_ubuntu810i386 = makeDeb_i686 (diskImages: diskImages.ubuntu810i386) 30;
deb_ubuntu810x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu810x86_64) 30;
deb_ubuntu904i386 = makeDeb_i686 (diskImages: diskImages.ubuntu904i386) 40;
deb_ubuntu904x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu904x86_64) 40;
deb_ubuntu910i386 = makeDeb_i686 (diskImages: diskImages.ubuntu910i386) 50;
deb_ubuntu910x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu910x86_64) 50;
};
makeRPM_i686 = makeRPM "i686-linux";
makeRPM_x86_64 = makeRPM "x86_64-linux";
makeRPM =
system: diskImageFun: prio:
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {inherit system;};
releaseTools.rpmBuild rec {
name = "nix-rpm-${diskImage.name}";
src = tarball;
diskImage = diskImageFun vmTools.diskImages;
memSize = 1024;
meta = { schedulingPriority = toString prio; };
};
makeDeb_i686 = makeDeb "i686-linux";
makeDeb_x86_64 = makeDeb "x86_64-linux";
makeDeb =
system: diskImageFun: prio:
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {inherit system;};
releaseTools.debBuild {
name = "nix-deb";
src = tarball;
diskImage = diskImageFun vmTools.diskImages;
memSize = 1024;
meta = { schedulingPriority = toString prio; };
configureFlags = "--sysconfdir=/etc";
debRequires = ["curl"];
};
in jobs

View File

@@ -1,27 +1,22 @@
bin_SCRIPTS = nix-collect-garbage \
nix-pull nix-push nix-prefetch-url \
nix-install-package nix-channel nix-build \
nix-copy-closure
nix-pack-closure nix-unpack-closure \
nix-copy-closure
noinst_SCRIPTS = nix-profile.sh generate-patches.pl \
find-runtime-roots.pl build-remote.pl nix-reduce-build \
copy-from-other-stores.pl nix-http-export.cgi
noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl
nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl
install-exec-local: readmanifest.pm download-using-manifests.pl copy-from-other-stores.pl find-runtime-roots.pl
install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roots.pl
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
$(INSTALL_PROGRAM) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
$(INSTALL_DATA) readmanifest.pm $(DESTDIR)$(libexecdir)/nix
$(INSTALL_DATA) readconfig.pm $(DESTDIR)$(libexecdir)/nix
$(INSTALL_DATA) ssh.pm $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) generate-patches.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) copy-from-other-stores.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
include ../substitute.mk
@@ -32,13 +27,9 @@ EXTRA_DIST = nix-collect-garbage.in \
nix-channel.in \
readmanifest.pm.in \
readconfig.pm.in \
ssh.pm \
nix-build.in \
download-using-manifests.pl.in \
copy-from-other-stores.pl.in \
generate-patches.pl.in \
nix-pack-closure.in nix-unpack-closure.in \
nix-copy-closure.in \
find-runtime-roots.pl.in \
build-remote.pl.in \
nix-reduce-build.in \
nix-http-export.cgi.in
find-runtime-roots.pl.in

View File

@@ -1,240 +0,0 @@
#! @perl@ -w -I@libexecdir@/nix
use Fcntl ':flock';
use English '-no_match_vars';
use IO::Handle;
use ssh qw/sshOpts openSSHConnection/;
# 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"} = "";
my $loadIncreased = 0;
my ($amWilling, $localSystem, $neededSystem, $drvPath, $maxSilentTime) = @ARGV;
$maxSilentTime = 0 unless defined $maxSilentTime;
sub sendReply {
my $reply = shift;
print STDERR "# $reply\n";
}
sub decline {
sendReply "decline";
exit 0;
}
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
decline unless defined $currentLoad;
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
decline if !defined $conf || ! -e $conf;
my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem);
# Read the list of machines.
my @machines;
open CONF, "< $conf" or die;
while (<CONF>) {
chomp;
s/\#.*$//g;
next if /^\s*$/;
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)(\s+([0-9\.]+))?\s*$/ or die;
push @machines,
{ hostName => $1
, systemTypes => [split(/,/, $2)]
, sshKeys => $3
, maxJobs => $4
, speedFactor => 1.0 * ($6 || 1)
, enabled => 1
};
}
close CONF;
# Acquire the exclusive lock on $currentLoad/main-lock.
my $mainLock = "$currentLoad/main-lock";
open MAINLOCK, ">>$mainLock" or die;
flock(MAINLOCK, LOCK_EX) or die;
sub openSlotLock {
my ($machine, $slot) = @_;
my $slotLockFn = "$currentLoad/" . (join '+', @{$machine->{systemTypes}}) . "-" . $machine->{hostName} . "-$slot";
my $slotLock = new IO::Handle;
open $slotLock, ">>$slotLockFn" or die;
return $slotLock;
}
my $hostName;
my $slotLock;
while (1) {
# Find all machine that can execute this build, i.e., that support
# builds for the given platform 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}}) {
$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 (defined $ENV{NIX_DEBUG_HOOK}) {
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";
exit 0;
} else {
decline;
}
}
# 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.
@sshOpts = ("-i", $machine->{sshKeys}, "-x");
$hostName = $machine->{hostName};
last if openSSHConnection $hostName;
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
$machine->{enabled} = 0;
}
# Tell Nix we've accepted the build.
sendReply "accept";
my $x = <STDIN>;
chomp $x;
if ($x ne "okay") {
exit 0;
}
# Do the actual build.
print STDERR "building `$drvPath' on `$hostName'\n";
my $inputs = `cat inputs`; die if ($? != 0);
$inputs =~ s/\n/ /g;
my $outputs = `cat outputs`; die if ($? != 0);
$outputs =~ s/\n/ /g;
print "copying inputs...\n";
my $maybeSign = "";
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
system("NIX_SSHOPTS=\"@sshOpts\" @bindir@/nix-copy-closure $hostName $maybeSign $drvPath $inputs") == 0
or die "cannot copy inputs to $hostName: $?";
print "building...\n";
my $buildFlags = "--max-silent-time $maxSilentTime";
# `-tt' forces allocation of a pseudo-terminal. This is required to
# make the remote nix-store process receive a signal when the
# connection dies. Without it, the remote process might continue to
# run indefinitely (that is, until it next tries to write to
# stdout/stderr).
if (system("ssh $hostName @sshOpts -tt 'nix-store --realise $buildFlags $drvPath > /dev/null'") != 0) {
# If we couldn't run ssh or there was an ssh problem (indicated by
# exit code 255), then we return exit code 1; otherwise we assume
# that the builder failed, which we indicate to Nix using exit
# code 100. It's important to distinguish between the two because
# the first is a transient failure and the latter is permanent.
my $res = $? == -1 || ($? >> 8) == 255 ? 1 : 100;
print STDERR "build of `$drvPath' on `$hostName' failed with exit code $?\n";
exit $res;
}
print "build of `$drvPath' on `$hostName' succeeded\n";
foreach my $output (split '\n', $outputs) {
my $maybeSignRemote = "";
$maybeSignRemote = "--sign" if $UID != 0;
system("ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output' | @bindir@/nix-store --import > /dev/null") == 0
or die "cannot copy $output from $hostName: $?";
}

View File

@@ -1,98 +0,0 @@
#! @perl@ -w
use strict;
use File::Basename;
use IO::Handle;
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
STDOUT->autoflush(1);
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
my @remoteStores;
foreach my $dir (@remoteStoresAll) {
push @remoteStores, glob($dir);
}
sub findStorePath {
my $storePath = shift;
my $storePathName = basename $storePath;
foreach my $store (@remoteStores) {
# Determine whether $storePath exists by looking for the
# existence of the info file, and if so, get store path info
# from that file. This rather breaks abstraction: we should
# be using `nix-store' for that. But right now there is no
# good way to tell nix-store to access a store mounted under a
# different location (there's $NIX_STORE, but that only works
# if the remote store is mounted under its "real" location).
my $infoFile = "$store/var/nix/db/info/$storePathName";
my $storePath2 = "$store/store/$storePathName";
if (-f $infoFile && -e $storePath2) {
return ($infoFile, $storePath2);
}
}
}
if ($ARGV[0] eq "--query") {
while (<STDIN>) {
my $cmd = $_; chomp $cmd;
if ($cmd eq "have") {
my $storePath = <STDIN>; chomp $storePath;
(my $infoFile) = findStorePath $storePath;
print STDOUT ($infoFile ? "1\n" : "0\n");
}
elsif ($cmd eq "info") {
my $storePath = <STDIN>; chomp $storePath;
(my $infoFile) = findStorePath $storePath;
if (!$infoFile) {
print "0\n";
next; # not an error
}
print "1\n";
my $deriver = "";
my @references = ();
open INFO, "<$infoFile" or die "cannot read info file $infoFile\n";
while (<INFO>) {
chomp;
/^([\w-]+): (.*)$/ or die "bad info file";
my $key = $1;
my $value = $2;
if ($key eq "Deriver") { $deriver = $value; }
elsif ($key eq "References") { @references = split ' ', $value; }
}
close INFO;
print "$deriver\n";
print scalar @references, "\n";
print "$_\n" foreach @references;
print "0\n"; # !!! showing size not supported (yet)
}
else { die "unknown command `$cmd'"; }
}
}
elsif ($ARGV[0] eq "--substitute") {
die unless scalar @ARGV == 2;
my $storePath = $ARGV[1];
(my $infoFile, my $sourcePath) = findStorePath $storePath;
die unless $infoFile;
print "\n*** Copying `$storePath' from `$sourcePath'\n\n";
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $storePath") == 0
or die "cannot copy `$sourcePath' to `$storePath'";
}
else { die; }

View File

@@ -5,13 +5,22 @@ use readmanifest;
use POSIX qw(strftime);
use File::Temp qw(tempdir);
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
STDOUT->autoflush(1);
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "@localstatedir@/nix/manifests");
my $manifestDir = "@localstatedir@/nix/manifests";
my $logFile = "@localstatedir@/log/nix/downloads";
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
# Create a temporary directory.
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
chdir $tmpDir or die "cannot change to `$tmpDir': $!";
my $tmpNar = "$tmpDir/nar";
my $tmpNar2 = "$tmpDir/nar2";
END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; }
# Load all manifests.
my %narFiles;
@@ -19,61 +28,49 @@ my %localPaths;
my %patches;
for my $manifest (glob "$manifestDir/*.nixmanifest") {
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
if ($version < 3) {
# print STDERR "reading $manifest\n";
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
exit 1;
}
if ($version >= 10) {
print STDERR "manifest `$manifest' is too new; please delete it or upgrade Nix\n";
exit 1;
}
}
# Parse the arguments.
if ($ARGV[0] eq "--query") {
if ($ARGV[0] eq "--query-paths") {
foreach my $storePath (keys %narFiles) { print "$storePath\n"; }
foreach my $storePath (keys %localPaths) { print "$storePath\n"; }
exit 0;
}
while (<STDIN>) {
my $cmd = $_; chomp $cmd;
if ($cmd eq "have") {
my $storePath = <STDIN>; chomp $storePath;
print STDOUT ((defined $narFiles{$storePath} or defined $localPaths{$storePath})
? "1\n" : "0\n");
elsif ($ARGV[0] eq "--query-info") {
shift @ARGV;
foreach my $storePath (@ARGV) {
my $info;
if (defined $narFiles{$storePath}) {
$info = @{$narFiles{$storePath}}[0];
}
elsif ($cmd eq "info") {
my $storePath = <STDIN>; chomp $storePath;
my $info;
if (defined $narFiles{$storePath}) {
$info = @{$narFiles{$storePath}}[0];
}
elsif (defined $localPaths{$storePath}) {
$info = @{$localPaths{$storePath}}[0];
}
else {
print "0\n";
next; # not an error
}
print "1\n";
print "$info->{deriver}\n";
my @references = split " ", $info->{references};
print scalar @references, "\n";
print "$_\n" foreach @references;
my $size = $info->{size} || 0;
print "$size\n";
elsif (defined $localPaths{$storePath}) {
$info = @{$localPaths{$storePath}}[0];
}
else {
next; # not an error
}
print "$storePath\n";
print "$info->{deriver}\n";
my @references = split " ", $info->{references};
my $count = scalar @references;
print "$count\n";
foreach my $reference (@references) {
print "$reference\n";
}
else { die "unknown command `$cmd'"; }
}
exit 0;
}
elsif ($ARGV[0] ne "--substitute") {
die;
die "syntax: $0 [--query-paths | --query-info PATHS... | --substitute PATH]\n";
}
@@ -81,16 +78,6 @@ die unless scalar @ARGV == 2;
my $targetPath = $ARGV[1];
# Create a temporary directory.
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
my $tmpNar = "$tmpDir/nar";
my $tmpNar2 = "$tmpDir/nar2";
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
my $date = strftime ("%F %H:%M:%S UTC", gmtime (time));
print LOGFILE "$$ get $targetPath $date\n";
@@ -103,7 +90,7 @@ foreach my $localPath (@{$localPathList}) {
my $sourcePath = $localPath->{copyFrom};
if (-e $sourcePath) {
print "\n*** Step 1/1: copying from $sourcePath\n";
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $targetPath") == 0
system("@bindir@/nix-store --dump $sourcePath | @bindir@/nix-store --restore $targetPath") == 0
or die "cannot copy `$sourcePath' to `$targetPath'";
exit 0;
}
@@ -152,7 +139,7 @@ addToQueue $targetPath;
sub isValidPath {
my $p = shift;
return system("$binDir/nix-store --check-validity '$p' 2> /dev/null") == 0;
return system("@bindir@/nix-store --check-validity '$p' 2> /dev/null") == 0;
}
sub parseHash {
@@ -186,13 +173,15 @@ while ($queueFront < scalar @queue) {
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
my $format = "--base32";
$format = "" if $baseHashAlgo eq "md5";
my $hash = `$binDir/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
my $hash = `@bindir@/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
chomp $hash;
# print " MY HASH is $hash\n";
if ($hash ne $baseHash) {
print LOGFILE "$$ rejecting $patch->{basePath}\n";
next;
}
}
# print " PATCH from $patch->{basePath}\n";
addToQueue $patch->{basePath};
addEdge $patch->{basePath}, $u, $patch->{size}, "patch", $patch;
}
@@ -200,12 +189,10 @@ while ($queueFront < scalar @queue) {
# Add NAR file edges to the start node.
my $narFileList = $narFiles{$u};
foreach my $narFile (@{$narFileList}) {
# !!! how to handle files whose size is not known in advance?
# For now, assume some arbitrary size (1 MB).
addEdge "start", $u, ($narFile->{size} || 1000000), "narfile", $narFile;
# print " NAR from $narFile->{url}\n";
addEdge "start", $u, $narFile->{size}, "narfile", $narFile;
if ($u eq $targetPath) {
my $size = $narFile->{size} || -1;
print LOGFILE "$$ full-download-would-be $size\n";
print LOGFILE "$$ full-download-would-be $narFile->{size}\n";
}
}
@@ -231,6 +218,8 @@ while (scalar @todo > 0) {
my $u_ = $graph{$u};
# print "IN $u $u_->{d}\n";
foreach my $edge (@{$u_->{edges}}) {
my $v_ = $graph{$edge->{end}};
if ($v_->{d} > $u_->{d} + $edge->{weight}) {
@@ -238,6 +227,7 @@ while (scalar @todo > 0) {
# Store the edge; to edge->start is actually the
# predecessor.
$v_->{pred} = $edge;
# print " RELAX $edge->{end} $v_->{d}\n";
}
}
}
@@ -259,18 +249,20 @@ while ($cur ne "start") {
my $curStep = 1;
my $maxStep = scalar @path;
sub downloadFile {
my $url = shift;
sub downloadFile {
my $url = shift;
my ($hashAlgo, $hash) = parseHash(shift);
$ENV{"PRINT_PATH"} = 1;
$ENV{"QUIET"} = 1;
my ($hash, $path) = `$binDir/nix-prefetch-url '$url'`;
$ENV{"NIX_HASH_ALGO"} = $hashAlgo;
my ($hash2, $path) = `@bindir@/nix-prefetch-url '$url' '$hash'`;
die "download of `$url' failed" unless $? == 0;
chomp $hash2;
chomp $path;
die "hash mismatch, expected $hash, got $hash2" if $hash ne $hash2;
return $path;
}
my $finalNarHash;
while (scalar @path > 0) {
my $edge = pop @path;
my $u = $edge->{start};
@@ -287,7 +279,7 @@ while (scalar @path > 0) {
# as a base to one or more patches. So turn the base path
# into a NAR archive, to which we can apply the patch.
print " packing base path...\n";
system("$binDir/nix-store --dump $v > $tmpNar") == 0
system("@bindir@/nix-store --dump $v > $tmpNar") == 0
or die "cannot dump `$v'";
}
}
@@ -300,7 +292,7 @@ while (scalar @path > 0) {
# Download the patch.
print " downloading patch...\n";
my $patchPath = downloadFile "$patch->{url}";
my $patchPath = downloadFile "$patch->{url}", "$patch->{hash}";
# Apply the patch to the NAR archive produced in step 1 (for
# the already present path) or a later step (for patch sequences).
@@ -315,23 +307,20 @@ while (scalar @path > 0) {
# This was the last patch. Unpack the final NAR archive
# into the target path.
print " unpacking patched archive...\n";
system("$binDir/nix-store --restore $v < $tmpNar2") == 0
system("@bindir@/nix-store --restore $v < $tmpNar2") == 0
or die "cannot unpack $tmpNar2 into `$v'";
}
$finalNarHash = $patch->{narHash};
}
elsif ($edge->{type} eq "narfile") {
my $narFile = $edge->{info};
print "downloading `$narFile->{url}' into `$v'\n";
my $size = $narFile->{size} || -1;
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
print LOGFILE "$$ narfile $narFile->{url} $narFile->{size} $v\n";
# Download the archive.
print " downloading archive...\n";
my $narFilePath = downloadFile "$narFile->{url}";
my $narFilePath = downloadFile "$narFile->{url}", "$narFile->{hash}";
if ($curStep < $maxStep) {
# The archive will be used a base to a patch.
@@ -340,39 +329,14 @@ while (scalar @path > 0) {
} else {
# Unpack the archive into the target path.
print " unpacking archive...\n";
system("@bunzip2@ < '$narFilePath' | $binDir/nix-store --restore '$v'") == 0
system("@bunzip2@ < '$narFilePath' | @bindir@/nix-store --restore '$v'") == 0
or die "cannot unpack `$narFilePath' into `$v'";
}
$finalNarHash = $narFile->{narHash};
}
$curStep++;
}
# Make sure that the hash declared in the manifest matches what we
# downloaded and unpacked.
if (defined $finalNarHash) {
my ($hashAlgo, $hash) = parseHash $finalNarHash;
# The hash in the manifest can be either in base-16 or base-32.
# Handle both.
my $extraFlag =
($hashAlgo eq "sha256" && length($hash) != 64)
? "--base32" : "";
my $hash2 = `@bindir@/nix-hash --type $hashAlgo $extraFlag $targetPath`
or die "cannot compute hash of path `$targetPath'";
chomp $hash2;
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2"
if $hash ne $hash2;
} else {
die "cannot check integrity of the downloaded path since its hash is not known";
}
print LOGFILE "$$ success\n";
close LOGFILE;

View File

@@ -2,8 +2,6 @@
use strict;
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
my $addDrvLink = 0;
my $addOutLink = 1;
@@ -11,9 +9,6 @@ my $addOutLink = 1;
my $outLink;
my $drvLink;
my $dryRun = 0;
my $verbose = 0;
my @instArgs = ();
my @buildArgs = ();
my @exprs = ();
@@ -78,51 +73,22 @@ EOF
elsif ($arg eq "--attr" or $arg eq "-A") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
die "$0: `--attr' requires an argument\n" unless $n < scalar @ARGV;
push @instArgs, ("--attr", $ARGV[$n]);
}
elsif ($arg eq "--arg" || $arg eq "--argstr") {
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
elsif ($arg eq "--arg") {
die "$0: `--arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ("--arg", $ARGV[$n + 1], $ARGV[$n + 2]);
$n += 2;
}
elsif ($arg eq "--log-type") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n]);
push @buildArgs, ($arg, $ARGV[$n]);
}
elsif ($arg eq "--option") {
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
push @buildArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
$n += 2;
}
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type") {
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @buildArgs, ($arg, $ARGV[$n]);
}
elsif ($arg eq "--dry-run") {
push @buildArgs, "--dry-run";
$dryRun = 1;
}
elsif ($arg eq "--show-trace") {
push @instArgs, $arg;
}
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
push @buildArgs, $arg;
push @instArgs, $arg;
$verbose = 1;
}
elsif (substr($arg, 0, 1) eq "-") {
push @buildArgs, $arg;
}
@@ -151,29 +117,21 @@ foreach my $expr (@exprs) {
# Instantiate.
my @drvPaths;
# !!! would prefer the perl 5.8.0 pipe open feature here.
my $pid = open(DRVPATHS, "-|") || exec "$binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
my $pid = open(DRVPATHS, "-|") || exec "@bindir@/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
if (!close DRVPATHS) {
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
exit 1;
}
close DRVPATHS or exit 1;
foreach my $drvPath (@drvPaths) {
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
print STDERR "derivation is $target\n" if $verbose;
print STDERR "store derivation is $target\n";
}
# Build.
my @outPaths;
$pid = open(OUTPATHS, "-|") || exec "$binDir/nix-store", "--add-root", $outLink, "--indirect", "-rv",
$pid = open(OUTPATHS, "-|") || exec "@bindir@/nix-store", "--add-root", $outLink, "--indirect", "-rv",
@buildArgs, @drvPaths;
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
if (!close OUTPATHS) {
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
exit 1;
}
next if $dryRun;
close OUTPATHS or exit 1;
foreach my $outPath (@outPaths) {
my $target = readlink $outPath or die "cannot read symlink `$outPath'";

View File

@@ -78,9 +78,6 @@ sub removeChannel {
sub update {
readChannels;
# Create the manifests directory if it doesn't exist.
mkdir "$stateDir/manifests", 0755 unless -e "$stateDir/manifests";
# Do we have write permission to the manifests directory? If not,
# then just skip pulling the manifest and just download the Nix
# expressions. If the user is a non-privileged user in a
@@ -88,6 +85,11 @@ sub update {
# source.
if (-W "$stateDir/manifests") {
# Remove all the old manifests.
for my $manifest (glob "$stateDir/manifests/*.nixmanifest") {
unlink $manifest or die "cannot remove `$manifest': $!";
}
# Pull cache manifests.
foreach my $url (@channels) {
#print "pulling cache manifest from `$url'\n";
@@ -123,13 +125,15 @@ sub update {
my $rootFile = "$rootsDir/per-user/$userName/channels";
# Build the Nix expression.
# Instantiate the Nix expression.
print "unpacking channel Nix expressions...\n";
my $outPath = `\\
@bindir@/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\
@datadir@/nix/corepkgs/channels/unpack.nix \\
--argstr system @system@ --arg inputs '$inputs'`
or die "cannot unpack the channels";
my $storeExpr = `@bindir@/nix-instantiate --add-root '$rootFile'.tmp @datadir@/nix/corepkgs/channels/unpack.nix --argstr system @system@ --arg inputs '$inputs'`
or die "cannot instantiate Nix expression";
chomp $storeExpr;
# Build the resulting derivation.
my $outPath = `@bindir@/nix-store --add-root '$rootFile' -r '$storeExpr'`
or die "cannot realise store expression";
chomp $outPath;
unlink "$rootFile.tmp";

View File

@@ -4,8 +4,6 @@ use strict;
my $profilesDir = "@localstatedir@/nix/profiles";
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
# Process the command line arguments.
my @args = ();
@@ -36,7 +34,7 @@ sub removeOldGenerations {
$name = $dir . "/" . $name;
if (-l $name && (readlink($name) =~ /link/)) {
print STDERR "removing old generations of profile $name\n";
system("$binDir/nix-env", "-p", $name, "--delete-generations", "old");
system("@bindir@/nix-env", "-p", $name, "--delete-generations", "old");
}
elsif (! -l $name && -d $name) {
removeOldGenerations $name;
@@ -50,4 +48,4 @@ removeOldGenerations $profilesDir if $removeOld;
# Run the actual garbage collector.
exec "$binDir/nix-store", "--gc", @args;
exec "@bindir@/nix-store", "--gc", @args;

View File

@@ -1,13 +1,12 @@
#! @perl@ -w -I@libexecdir@/nix
#! @perl@ -w
use ssh;
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;
if (scalar @ARGV < 1) {
print STDERR <<EOF
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] [--gzip] PATHS...
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] PATHS...
EOF
;
exit 1;
@@ -16,11 +15,12 @@ EOF
# Get the target host.
my $sshHost;
my @sshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
my $sign = 0;
my $compressor = "";
my $decompressor = "";
my $compressor = "cat";
my $decompressor = "cat";
my $toMode = 1;
@@ -35,8 +35,8 @@ while (@ARGV) {
$sign = 1;
}
elsif ($arg eq "--gzip") {
$compressor = "| gzip";
$decompressor = "gunzip |";
$compressor = "gzip";
$decompressor = "gunzip";
}
elsif ($arg eq "--from") {
$toMode = 0;
@@ -53,30 +53,40 @@ while (@ARGV) {
}
openSSHConnection $sshHost or die "$0: unable to start SSH\n";
if ($toMode) { # Copy TO the remote machine.
my @allStorePaths;
my %storePathsSeen;
# Get the closure of this path.
my $pid = open(READ, "$binDir/nix-store --query --requisites @storePaths|") or die;
foreach my $storePath (@storePaths) {
# $arg might be a symlink to the store, so resolve it.
my $storePath2 = (`$binDir/nix-store --query --resolve '$storePath'`
or die "cannot resolve `$storePath'");
chomp $storePath2;
# Get the closure of this path.
my $pid = open(READ,
"$binDir/nix-store --query --requisites '$storePath2'|") or die;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
push @allStorePaths, $_;
}
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
if (!defined $storePathsSeen{$_}) {
push @allStorePaths, $_;
$storePathsSeen{$_} = 1;
}
}
close READ or die "nix-store failed: $?";
close READ or die "nix-store failed: $?";
}
# Ask the remote host which paths are invalid.
open(READ, "ssh $sshHost @sshOpts nix-store --check-validity --print-invalid @allStorePaths|");
open(READ, "ssh @sshOpts $sshHost nix-store --check-validity --print-invalid @allStorePaths|");
my @missing = ();
while (<READ>) {
chomp;
print STDERR "target machine needs $_\n";
push @missing, $_;
}
close READ or die;
@@ -84,11 +94,9 @@ if ($toMode) { # Copy TO the remote machine.
# Export the store paths and import them on the remote machine.
if (scalar @missing > 0) {
print STDERR "copying these missing paths:\n";
print STDERR " $_\n" foreach @missing;
my $extraOpts = "";
$extraOpts .= "--sign" if $sign == 1;
system("nix-store --export $extraOpts @missing $compressor | ssh $sshHost @sshOpts '$decompressor nix-store --import'") == 0
system("nix-store --export $extraOpts @missing | $compressor | ssh @sshOpts $sshHost '$decompressor | nix-store --import'") == 0
or die "copying store paths to remote machine `$sshHost' failed: $?";
}
@@ -104,11 +112,15 @@ else { # Copy FROM the remote machine.
"ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
my @allStorePaths;
my %storePathsSeen;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
push @allStorePaths, $_;
if (!defined $storePathsSeen{$_}) {
push @allStorePaths, $_;
$storePathsSeen{$_} = 1;
}
}
close READ or die "nix-store on remote machine `$sshHost' failed: $?";
@@ -119,6 +131,7 @@ else { # Copy FROM the remote machine.
my @missing = ();
while (<READ>) {
chomp;
print STDERR "local machine needs $_\n";
push @missing, $_;
}
close READ or die;
@@ -126,12 +139,10 @@ else { # Copy FROM the remote machine.
# Export the store paths on the remote machine and import them on locally.
if (scalar @missing > 0) {
print STDERR "copying these missing paths:\n";
print STDERR " $_\n" foreach @missing;
my $extraOpts = "";
$extraOpts .= "--sign" if $sign == 1;
system("ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor @bindir@/nix-store --import") == 0
or die "copying store paths from remote machine `$sshHost' failed: $?";
system("ssh @sshOpts $sshHost 'nix-store --export $extraOpts @missing | $compressor' | $decompressor | @bindir@/nix-store --import") == 0
or die "copying store paths to remote machine `$sshHost' failed: $?";
}
}

View File

@@ -1,51 +0,0 @@
#! /bin/sh
export HOME=/tmp
export NIX_REMOTE=daemon
TMP_DIR="${TMP_DIR:-/tmp/nix-export}"
@coreutils@/mkdir -p "$TMP_DIR" || true
@coreutils@/chmod a+r "$TMP_DIR"
needed_path="?$QUERY_STRING"
needed_path="${needed_path#*[?&]needed_path=}"
needed_path="${needed_path%%&*}"
#needed_path="$(echo $needed_path | ./unhttp)"
needed_path="${needed_path//%2B/+}"
needed_path="${needed_path//%3D/=}"
echo needed_path: "$needed_path" >&2
NIX_STORE="${NIX_STORE_DIR:-/nix/store}"
echo NIX_STORE: "${NIX_STORE}" >&2
full_path="${NIX_STORE}"/"$needed_path"
if [ "$needed_path" != "${needed_path%.drv}" ]; then
echo "Status: 403 You should create the derivation file yourself"
echo "Content-Type: text/plain"
echo
echo "Refusing to disclose derivation contents"
exit
fi
if @bindir@/nix-store --check-validity "$full_path"; then
if ! [ -e nix-export/"$needed_path".nar.gz ]; then
@bindir@/nix-store --export "$full_path" | @gzip@ > "$TMP_DIR"/"$needed_path".nar.gz
@coreutils@/ln -fs "$TMP_DIR"/"$needed_path".nar.gz nix-export/"$needed_path".nar.gz
fi;
echo "Status: 301 Moved"
echo "Location: nix-export/"$needed_path".nar.gz"
echo
else
echo "Status: 404 No such path found"
echo "Content-Type: text/plain"
echo
echo "Path not found:"
echo "$needed_path"
echo "checked:"
echo "$full_path"
fi

View File

@@ -3,8 +3,6 @@
use strict;
use File::Temp qw(tempdir);
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
sub usageError {
print STDERR <<EOF;
@@ -61,7 +59,7 @@ if ($interactive && !defined $ENV{"NIX_HAVE_TERMINAL"}) {
$ENV{"NIX_HAVE_TERMINAL"} = "1";
$ENV{"LD_LIBRARY_PATH"} = "";
foreach my $term ("xterm", "konsole", "gnome-terminal", "xterm") {
exec($term, "-e", "$binDir/nix-install-package", @ARGV);
exec($term, "-e", "@bindir@/nix-install-package", @ARGV);
}
die "cannot execute `xterm'";
}
@@ -123,18 +121,13 @@ if ($interactive) {
}
# Store the manifest in the temporary directory so that we don't
# pollute /nix/var/nix/manifests.
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
print "\nPulling manifests...\n";
system("$binDir/nix-pull", $manifestURL) == 0
system("@bindir@/nix-pull", $manifestURL) == 0
or barf "nix-pull failed: $?";
print "\nInstalling package...\n";
system("$binDir/nix-env", "--install", $outPath, "--force-name", $drvName, @extraNixEnvArgs) == 0
system("@bindir@/nix-env", "--install", $outPath, "--force-name", $drvName, @extraNixEnvArgs) == 0
or barf "nix-env failed: $?";

View File

@@ -0,0 +1,75 @@
#! @perl@ -w
# This tool computes the closure of a path (using "nix-store --query
# --requisites") and puts the contents of each path in the closure in
# a big NAR archive that can be installed on another Nix installation
# using "nix-unpack-closure".
# TODO: make this program "streamy", i.e., don't use a temporary
# directory.
use strict;
use File::Temp qw(tempdir);
my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;
my $tmpDir = tempdir("nix-pack-closure.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
mkdir "$tmpDir/contents", 0755 or die;
mkdir "$tmpDir/references", 0755 or die;
mkdir "$tmpDir/derivers", 0755 or die;
open TOPLEVEL, ">$tmpDir/top-level" or die;
my %storePaths;
while (@ARGV) {
my $storePath = shift @ARGV;
# $storePath might be a symlink to the store, so resolve it.
$storePath = (`$binDir/nix-store --query --resolve '$storePath'`
or die "cannot resolve `$storePath'");
chomp $storePath;
print TOPLEVEL $storePath, "\n";
# Get the closure of this path.
my $pid = open(READ,
"$binDir/nix-store --query --requisites '$storePath'|") or die;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
$storePaths{$_} = "";
}
close READ or die "nix-store failed: $?";
}
close TOPLEVEL or die;
foreach my $storePath (sort(keys %storePaths)) {
print STDERR "packing `$storePath'...\n";
$storePath =~ /\/([^\/]+)$/;
my $name = $1;
system("$binDir/nix-store --dump '$storePath' > $tmpDir/contents/$name") == 0
or die "nix-store --dump failed on `$storePath': $?";
system("$binDir/nix-store --query --references '$storePath' > $tmpDir/references/$name") == 0
or die "nix-store --query --references failed on `$storePath': $?";
system("$binDir/nix-store --query --deriver '$storePath' > $tmpDir/derivers/$name") == 0
or die "nix-store --query --deriver failed on `$storePath': $?";
}
# Write a NAR archive of everything to standard output.
system("nix-store --dump '$tmpDir'") == 0
or die "nix-store --dump failed";

View File

@@ -21,10 +21,7 @@ if test -z "$url"; then
exit 1
fi
# Handle escaped characters in the URI. `+', `=' and `?' are the only
# characters that are valid in Nix store path names but have a special
# meaning in URIs.
name=$(basename "$url" | @sed@ -e 's/%2b/+/g' -e 's/%3d/=/g' -e 's/%3f/\?/g')
name=$(basename "$url")
if test -z "$name"; then echo "invalid url"; exit 1; fi
@@ -39,62 +36,19 @@ if test -n "$expHash"; then
fi
mkTempDir() {
if test -n "$tmpPath"; then return; fi
local i=0
while true; do
if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
tmpPath=$TMPDIR/nix-prefetch-url-$$-$i
if mkdir "$tmpPath"; then break; fi
# !!! to bad we can't check for ENOENT in mkdir, so this check
# is slightly racy (it bombs out if somebody just removed
# $tmpPath...).
if ! test -e "$tmpPath"; then exit 1; fi
i=$((i + 1))
done
trap removeTempDir EXIT SIGINT SIGQUIT
}
removeTempDir() {
if test -n "$tmpPath"; then
rm -rf "$tmpPath" || true
fi
}
doDownload() {
@curl@ $cacheFlags --fail --location --max-redirs 20 --disable-epsv \
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
}
# Hack to support the mirror:// scheme from Nixpkgs.
if test "${url:0:9}" = "mirror://"; then
if test -z "$NIXPKGS_ALL"; then
echo "Resolving mirror:// URLs requires Nixpkgs. Please point \$NIXPKGS_ALL at a Nixpkgs tree." >&2
exit 1
fi
mkTempDir
nix-build "$NIXPKGS_ALL" -A resolveMirrorURLs --argstr url "$url" -o $tmpPath/urls > /dev/null
expanded=($(cat $tmpPath/urls))
if test "${#expanded[*]}" = 0; then
echo "$0: cannot resolve $url." >&2
exit 1
fi
echo "$url expands to ${expanded[*]} (using ${expanded[0]})" >&2
url="${expanded[0]}"
fi
# If we don't know the hash or a file with that hash doesn't exist,
# download the file and add it to the store.
if test -z "$finalPath"; then
mkTempDir
tmpPath=/tmp/nix-prefetch-url-$$ # !!! security?
tmpFile=$tmpPath/$name
mkdir $tmpPath # !!! retry if tmpPath already exists
# Optionally do timestamp-based caching of the download.
# Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
@@ -103,7 +57,7 @@ if test -z "$finalPath"; then
# garbage-collected independently.
if test -n "$NIX_DOWNLOAD_CACHE"; then
echo -n "$url" > $tmpPath/url
urlHash=$(@bindir@/nix-hash --type sha256 --base32 --flat $tmpPath/url)
urlHash=$(nix-hash --type sha256 --base32 --flat $tmpPath/url)
echo "$url" > "$NIX_DOWNLOAD_CACHE/$urlHash.url"
cachedHashFN="$NIX_DOWNLOAD_CACHE/$urlHash.$hashType"
cachedTimestampFN="$NIX_DOWNLOAD_CACHE/$urlHash.stamp"
@@ -144,6 +98,8 @@ if test -z "$finalPath"; then
# Add the downloaded file to the Nix store.
finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)
if test -n "$tmpPath"; then rm -rf $tmpPath || true; fi
if test -n "$expHash" -a "$expHash" != "$hash"; then
echo "hash mismatch for URL \`$url'" >&2
exit 1

View File

@@ -7,12 +7,5 @@ if test -n "$HOME"; then
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
fi
export PATH=$NIX_LINK/bin:$PATH
fi
# This is a quick hack to make fontconfig-based packages in Nixpkgs
# work out of the box on non-NixOS systems. Of course, we should
# really fix fontconfig...
if test -z "$FONTCONFIG_FILE" -a -e /etc/fonts/fonts.conf; then
export FONTCONFIG_FILE=/etc/fonts/fonts.conf
export PATH=$NIX_LINK/bin:@prefix@/bin:$PATH
fi

View File

@@ -7,23 +7,23 @@ use readmanifest;
my $tmpDir = tempdir("nix-pull.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
my $libexecDir = ($ENV{"NIX_LIBEXEC_DIR"} or "@libexecdir@");
my $storeDir = ($ENV{"NIX_STORE_DIR"} or "@storedir@");
my $stateDir = ($ENV{"NIX_STATE_DIR"} or "@localstatedir@/nix");
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "$stateDir/manifests");
my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;
my $libexecDir = $ENV{"NIX_LIBEXEC_DIR"};
$libexecDir = "@libexecdir@" unless defined $libexecDir;
my $stateDir = $ENV{"NIX_STATE_DIR"};
$stateDir = "@localstatedir@/nix" unless defined $stateDir;
my $storeDir = $ENV{"NIX_STORE_DIR"};
$storeDir = "@storedir@" unless defined $storeDir;
# Prevent access problems in shared-stored installations.
umask 0022;
# Create the manifests directory if it doesn't exist.
if (! -e $manifestDir) {
mkdir $manifestDir, 0755 or die "cannot create directory `$manifestDir'";
}
# Process the URLs specified on the command line.
my %narFiles;
my %localPaths;
@@ -51,7 +51,7 @@ sub processURL {
# First see if a bzipped manifest is available.
if (system("@curl@ --fail --silent --head '$url'.bz2 > /dev/null") == 0) {
print "fetching list of Nix archives at `$url.bz2'...\n";
print "obtaining list of Nix archives at `$url.bz2'...\n";
my $bzipped = downloadFile "$url.bz2";
$manifest = "$tmpDir/MANIFEST";
@@ -69,11 +69,10 @@ sub processURL {
print "obtaining list of Nix archives at `$url'...\n";
$manifest = downloadFile $url;
}
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
die "`$url' is not a manifest or it is too old (i.e., for Nix <= 0.7)\n" if $version < 3;
die "manifest `$url' is too new\n" if $version >= 5;
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
die "`$url' is not manifest or it is too old (i.e., for Nix <= 0.7)\n";
}
if ($skipWrongStore) {
foreach my $path (keys %narFiles) {
@@ -92,31 +91,11 @@ sub processURL {
my $hash = `$binDir/nix-hash --flat '$manifest'`
or die "cannot hash `$manifest'";
chomp $hash;
my $urlFile = "$manifestDir/$baseName-$hash.url";
open URL, ">$urlFile" or die "cannot create `$urlFile'";
print URL "$url";
close URL;
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
unlink $finalPath if -e $finalPath;
symlink("$manifest", "$finalPath")
my $finalPath = "$stateDir/manifests/$baseName-$hash.nixmanifest";
system("@coreutils@/ln", "-sfn", "$manifest", "$finalPath") == 0
or die "cannot link `$finalPath to `$manifest'";
# Delete all old manifests downloaded from this URL.
for my $urlFile2 (glob "$manifestDir/*.url") {
next if $urlFile eq $urlFile2;
open URL, "<$urlFile2" or die;
my $url2 = <URL>;
chomp $url2;
close URL;
next unless $url eq $url2;
my $base = $urlFile2; $base =~ s/.url$//;
unlink "${base}.url";
unlink "${base}.nixmanifest";
}
}
while (@ARGV) {

View File

@@ -16,7 +16,8 @@ my $curl = "@curl@ --fail --silent";
my $extraCurlFlags = ${ENV{'CURL_FLAGS'}};
$curl = "$curl $extraCurlFlags" if defined $extraCurlFlags;
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;
my $dataDir = $ENV{"NIX_DATA_DIR"};
$dataDir = "@datadir@" unless defined $dataDir;
@@ -106,7 +107,7 @@ foreach my $storePath (@storePaths) {
# Construct a Nix expression that creates a Nix archive.
my $nixexpr =
"((import $dataDir/nix/corepkgs/nar/nar.nix) " .
"{storePath = builtins.storePath \"$storePath\"; system = \"@system@\"; hashAlgo = \"$hashAlgo\";}) ";
"{storePath = builtins.toPath \"$storePath\"; system = \"@system@\"; hashAlgo = \"$hashAlgo\";}) ";
print NIX $nixexpr;
}
@@ -115,9 +116,9 @@ print NIX "]";
close NIX;
# Instantiate store derivations from the Nix expression.
# Instantiate store expressions from the Nix expression.
my @storeExprs;
print STDERR "instantiating store derivations...\n";
print STDERR "instantiating store expressions...\n";
my $pid = open(READ, "$binDir/nix-instantiate $nixExpr|")
or die "cannot run nix-instantiate";
while (<READ>) {
@@ -128,7 +129,7 @@ while (<READ>) {
close READ or die "nix-instantiate failed: $?";
# Build the derivations.
# Realise the store expressions.
print STDERR "creating archives...\n";
my @narPaths;
@@ -140,6 +141,11 @@ while (scalar @tmp > 0) {
my @tmp2 = @tmp[0..$n - 1];
@tmp = @tmp[$n..scalar @tmp - 1];
# Note: we disable build hooks because of the impure path
# reference (see above). Even if that is fixed, using a hook
# probably wouldn't make that much sense; pumping lots of data
# around just to compress them won't gain that much.
$ENV{"NIX_BUILD_HOOK"} = "";
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
or die "cannot run nix-store";
while (<READ>) {
@@ -218,14 +224,13 @@ writeManifest $manifest, \%narFiles, \%patches;
sub copyFile {
my $src = shift;
my $dst = shift;
my $tmp = "$dst.tmp.$$";
system("@coreutils@/cp", $src, $tmp) == 0 or die "cannot copy file";
rename($tmp, $dst) or die "cannot rename file: $!";
system("@coreutils@/cp", $src, "$dst.tmp") == 0 or die "cannot copy file";
rename("$dst.tmp", "$dst") or die "cannot rename file";
}
# Upload/copy the archives.
print STDERR "uploading/copying archives...\n";
# Upload the archives.
print STDERR "uploading archives...\n";
sub archiveExists {
my $name = shift;
@@ -239,12 +244,9 @@ foreach my $narArchive (@narArchives) {
my $basename = $1;
if ($localCopy) {
# Since nix-push creates $dst atomically, if it exists we
# don't have to copy again.
my $dst = "$localArchivesDir/$basename";
if (! -f "$localArchivesDir/$basename") {
print STDERR " $narArchive\n";
copyFile $narArchive, $dst;
copyFile $narArchive, "$localArchivesDir/$basename";
}
}
else {

View File

@@ -1,171 +0,0 @@
#! @shell@
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
cd "$WORKING_DIRECTORY";
if test -z "$1" || test "a--help" = "a$1" ; then
echo 'nix-reduce-build (paths or Nix expressions) -- (package sources)' >&2
echo As in: >&2
echo nix-reduce-build /etc/nixos/nixos -- ssh://user@somewhere.nowhere.example.org >&2
echo nix-reduce-build /etc/nixos/nixos -- \\
echo " " \''http://somewhere.nowhere.example.org/nix/nix-http-export.cgi?needed_path='\' >&2
echo " store path name will be added into the end of the URL" >&2
echo nix-reduce-build /etc/nixos/nixos -- file://home/user/nar/ >&2
echo " that should be a directory where gzipped 'nix-store --export' ">&2
echo " files are located (they should have .nar.gz extension)" >&2
echo " Or all together: " >&2
echo -e nix-reduce-build /expr.nix /e2.nix -- \\\\\\\n\
" ssh://a@b.example.com http://n.example.com/get-nar?q= file://nar/" >&2
echo " Also supports best-effort local builds of failing expression set:" >&2
echo "nix-reduce-build /e.nix -- nix-daemon:// nix-self://" >&2
echo " nix-daemon:// builds using daemon"
echo " nix-self:// builds directly using nix-store from current installation" >&2
echo " nix-daemon-fixed:// and nix-self-fixed:// do the same, but only for" >&2;
echo "derivations with specified output hash (sha256, sha1 or md5)." >&2
echo " nix-daemon-substitute:// and nix-self-substitute:// try to substitute" >&2;
echo "maximum amount of paths" >&2;
echo " nix-daemon-build:// and nix-self-build:// try to build (not substitute)" >&2;
echo "maximum amount of paths" >&2;
echo " If no package sources are specified, required paths are listed." >&2;
exit;
fi;
while ! test "$1" = "--" || test "$1" = "" ; do
echo "$1" >> initial; >&2
shift;
done
shift;
echo Will work on $(cat initial | wc -l) targets. >&2
while read ; do
case "$REPLY" in
${NIX_STORE_DIR:-/nix/store}/*)
echo "$REPLY" >> paths; >&2
;;
*)
(
IFS=: ;
nix-instantiate $REPLY >> paths;
);
;;
esac;
done < initial;
echo Proceeding $(cat paths | wc -l) paths. >&2
while read; do
case "$REPLY" in
*.drv)
echo "$REPLY" >> derivers; >&2
;;
*)
nix-store --query --deriver "$REPLY" >>derivers;
;;
esac;
done < paths;
echo Found $(cat derivers | wc -l) derivers. >&2
cat derivers | xargs nix-store --query -R > derivers-closure;
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We need $(cat needed-paths | wc -l) paths. >&2
egrep '[.]drv$' derivers-closure > critical-derivers;
if test -z "$1" ; then
cat needed-paths;
fi;
refresh_critical_derivers() {
echo "Finding needed derivers..." >&2;
cat critical-derivers | while read; do
if ! (nix-store --query --outputs "$REPLY" | xargs nix-store --check-validity &> /dev/null;); then
echo "$REPLY";
fi;
done > new-critical-derivers;
mv new-critical-derivers critical-derivers;
echo The needed paths are realized by $(cat critical-derivers | wc -l) derivers. >&2
}
build_here() {
cat critical-derivers | while read; do
echo "Realising $REPLY using nix-daemon" >&2
@bindir@/nix-store -r "${REPLY}"
done;
}
try_to_substitute(){
cat needed-paths | while read ; do
echo "Building $REPLY using nix-daemon" >&2
@bindir@/nix-store -r "${NIX_STORE_DIR:-/nix/store}/${REPLY##*/}"
done;
}
for i in "$@"; do
sshHost="${i#ssh://}";
httpHost="${i#http://}";
httpsHost="${i#https://}";
filePath="${i#file:/}";
if [ "$i" != "$sshHost" ]; then
cat needed-paths | while read; do
echo "Getting $REPLY and its closure over ssh" >&2
nix-copy-closure --from "$sshHost" --gzip "$REPLY" </dev/null || true;
done;
elif [ "$i" != "$httpHost" ] || [ "$i" != "$httpsHost" ]; then
cat needed-paths | while read; do
echo "Getting $REPLY over http/https" >&2
curl ${BAD_CERTIFICATE:+-k} -L "$i${REPLY##*/}" | gunzip | nix-store --import;
done;
elif [ "$i" != "$filePath" ] ; then
cat needed-paths | while read; do
echo "Installing $REPLY from file" >&2
gunzip < "$filePath/${REPLY##*/}".nar.gz | nix-store --import;
done;
elif [ "$i" = "nix-daemon://" ] ; then
NIX_REMOTE=daemon try_to_substitute;
refresh_critical_derivers;
NIX_REMOTE=daemon build_here;
elif [ "$i" = "nix-self://" ] ; then
NIX_REMOTE= try_to_substitute;
refresh_critical_derivers;
NIX_REMOTE= build_here;
elif [ "$i" = "nix-daemon-fixed://" ] ; then
refresh_critical_derivers;
cat critical-derivers | while read; do
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
echo "Realising $REPLY using nix-daemon" >&2
NIX_REMOTE=daemon @bindir@/nix-store -r "${REPLY}"
fi;
done;
elif [ "$i" = "nix-self-fixed://" ] ; then
refresh_critical_derivers;
cat critical-derivers | while read; do
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
echo "Realising $REPLY using direct Nix build" >&2
NIX_REMOTE= @bindir@/nix-store -r "${REPLY}"
fi;
done;
elif [ "$i" = "nix-daemon-substitute://" ] ; then
NIX_REMOTE=daemon try_to_substitute;
elif [ "$i" = "nix-self-substitute://" ] ; then
NIX_REMOTE= try_to_substitute;
elif [ "$i" = "nix-daemon-build://" ] ; then
refresh_critical_derivers;
NIX_REMOTE=daemon build_here;
elif [ "$i" = "nix-self-build://" ] ; then
refresh_critical_derivers;
NIX_REMOTE= build_here;
fi;
mv needed-paths wanted-paths;
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We still need $(cat needed-paths | wc -l) paths. >&2
done;
cd /
rm -r "$WORKING_DIRECTORY"

View File

@@ -0,0 +1,88 @@
#! @perl@ -w
# This tool unpacks the closures created by "nix-pack-closure" and
# adds them to the Nix store.
# TODO: make this program "streamy", i.e., don't use a temporary
# directory.
use strict;
use File::Temp qw(tempdir);
my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;
my $tmpDir = tempdir("nix-unpack-closure.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
# Unpack the NAR archive on standard input.
system("nix-store --restore '$tmpDir/unpacked'") == 0
or die "nix-store --restore failed";
open VALID, ">$tmpDir/validity" or die;
# For each path in the closure that is not yet valid, add it to the
# store. TODO: use proper locking. Or even better, let nix-store do
# this.
opendir(DIR, "$tmpDir/unpacked/contents") or die "cannot open directory: $!";
foreach my $name (sort(readdir DIR)) {
next if $name eq "." or $name eq "..";
my $storePath = "@storedir@/$name"; # !!!
# !!! this really isn't a good validity check!
system "$binDir/nix-store --check-validity '$storePath' 2> /dev/null";
if ($? != 0) {
print STDERR "unpacking `$storePath'...\n";
# !!! race
system("@coreutils@/rm -rf '$storePath'") == 0
or die "cannot remove `$storePath': $?";
system("$binDir/nix-store --restore '$storePath' < '$tmpDir/unpacked/contents/$name'") == 0
or die "nix-store --dump failed on `$storePath': $?";
print VALID "$storePath\n";
open DRV, "<$tmpDir/unpacked/derivers/$name" or die;
my $deriver = <DRV>;
chomp $deriver;
$deriver = "" if $deriver eq "unknown-deriver";
close DRV;
my @refs;
open REFS, "<$tmpDir/unpacked/references/$name" or die;
while (<REFS>) {
chomp;
push @refs, $_;
}
close REFS;
print VALID "$deriver\n";
print VALID (scalar @refs), "\n";
foreach my $ref (@refs) {
print VALID "$ref\n";
}
}
}
closedir(DIR) or die;
# Register the invalid paths as valid.
system("nix-store --register-validity <'$tmpDir/validity'") == 0
or die "nix-store --register-validity failed";
# Show the top-level paths so that something useful can be done with
# them, e.g., passing them to `nix-env -i'.
if (-e "$tmpDir/unpacked/top-level") {
open TOPLEVEL, "<$tmpDir/unpacked/top-level" or die;
while (<TOPLEVEL>) { print "$_"; }
close TOPLEVEL;
}

View File

@@ -2,7 +2,10 @@ use strict;
sub addPatch {
my ($patches, $storePath, $patch) = @_;
my $patches = shift;
my $storePath = shift;
my $patch = shift;
my $allowConflicts = shift;
$$patches{$storePath} = []
unless defined $$patches{$storePath};
@@ -11,9 +14,15 @@ sub addPatch {
my $found = 0;
foreach my $patch2 (@{$patchList}) {
$found = 1 if
$patch2->{url} eq $patch->{url} &&
$patch2->{basePath} eq $patch->{basePath};
if ($patch2->{url} eq $patch->{url}) {
if ($patch2->{hash} eq $patch->{hash}) {
$found = 1 if ($patch2->{basePath} eq $patch->{basePath});
} else {
die "conflicting hashes for URL $patch->{url}, " .
"namely $patch2->{hash} and $patch->{hash}"
unless $allowConflicts;
}
}
}
push @{$patchList}, $patch if !$found;
@@ -23,7 +32,12 @@ sub addPatch {
sub readManifest {
my ($manifest, $narFiles, $localPaths, $patches) = @_;
my $manifest = shift;
my $narFiles = shift;
my $localPaths = shift;
my $patches = shift;
my $allowConflicts = shift;
$allowConflicts = 0 unless defined $allowConflicts;
open MANIFEST, "<$manifest"
or die "cannot open `$manifest': $!";
@@ -84,7 +98,15 @@ sub readManifest {
my $found = 0;
foreach my $narFile (@{$narFileList}) {
$found = 1 if $narFile->{url} eq $url;
if ($narFile->{url} eq $url) {
if ($narFile->{hash} eq $hash) {
$found = 1;
} else {
die "conflicting hashes for URL $url, " .
"namely $narFile->{hash} and $hash"
unless $allowConflicts;
}
}
}
if (!$found) {
push @{$narFileList},
@@ -102,7 +124,7 @@ sub readManifest {
, basePath => $basePath, baseHash => $baseHash
, narHash => $narHash, patchType => $patchType
, hashAlgo => $hashAlgo
};
}, $allowConflicts;
}
elsif ($type eq "localPath") {
@@ -149,8 +171,12 @@ sub readManifest {
}
sub writeManifest {
my ($manifest, $narFiles, $patches) = @_;
sub writeManifest
{
my $manifest = shift;
my $narFiles = shift;
my $patches = shift;
my $copySources = shift;
open MANIFEST, ">$manifest.tmp"; # !!! check exclusive
@@ -164,9 +190,9 @@ sub writeManifest {
print MANIFEST "{\n";
print MANIFEST " StorePath: $storePath\n";
print MANIFEST " NarURL: $narFile->{url}\n";
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
print MANIFEST " Hash: $narFile->{hash}\n";
print MANIFEST " NarHash: $narFile->{narHash}\n";
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
print MANIFEST " Size: $narFile->{size}\n";
print MANIFEST " References: $narFile->{references}\n"
if defined $narFile->{references} && $narFile->{references} ne "";
print MANIFEST " Deriver: $narFile->{deriver}\n"

View File

@@ -1,47 +0,0 @@
use strict;
use File::Temp qw(tempdir);
our @sshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
my $sshStarted = 0;
my $sshHost;
# Open a master SSH connection to `host', unless there already is a
# running master connection (as determined by `-O check').
sub openSSHConnection {
my ($host) = @_;
die if $sshStarted;
$sshHost = $host;
return 1 if system("ssh $sshHost @sshOpts -O check 2> /dev/null") == 0;
my $tmpDir = tempdir("nix-ssh.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
push @sshOpts, "-S", "$tmpDir/control";
# Start the master. We can't use the `-f' flag (fork into
# background after establishing the connection) because then the
# child continues to run if we are killed. So instead make SSH
# print "started" when it has established the connection, and wait
# until we see that.
open SSH, "ssh $sshHost @sshOpts -M -N -o LocalCommand='echo started' -o PermitLocalCommand=yes |" or die;
while (<SSH>) {
chomp;
last if /started/;
}
$sshStarted = 1;
return 1;
}
# Tell the master SSH client to exit.
sub closeSSHConnection {
if ($sshStarted) {
system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0
or warn "unable to stop SSH master: $?";
}
}
END { my $saved = $?; closeSSHConnection; $? = $saved; }
return 1;

View File

@@ -1,3 +1,5 @@
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
nix-log2xml bsdiff-4.3
EXTRA_DIST = aterm-helper.pl

178
src/aterm-helper.pl Executable file
View File

@@ -0,0 +1,178 @@
#! /usr/bin/perl -w
# This program generates C/C++ code for efficiently manipulating
# ATerms. It generates functions to build and match ATerms according
# to a set of constructor definitions defined in a file read from
# standard input. A constructor is defined by a line with the
# following format:
#
# SYM | ARGS | TYPE | FUN?
#
# where SYM is the name of the constructor, ARGS is a
# whitespace-separated list of argument types, TYPE is the type of the
# resulting ATerm (which should be `ATerm' or a type synonym for
# `ATerm'), and the optional FUN is used to construct the names of the
# build and match functions (it defaults to SYM; overriding it is
# useful if there are overloaded constructors, e.g., with different
# arities). Note that SYM may be empty.
#
# A line of the form
#
# VAR = EXPR
#
# causes a ATerm variable to be generated that is initialised to the
# value EXPR.
#
# Finally, a line of the form
#
# init NAME
#
# causes the initialisation function to be called `NAME'. This
# function must be called before any of the build/match functions or
# the generated variables are used.
die if scalar @ARGV != 2;
my $syms = "";
my $init = "";
my $initFun = "init";
open HEADER, ">$ARGV[0]";
open IMPL, ">$ARGV[1]";
print HEADER "#include <aterm2.h>\n";
print HEADER "#ifdef __cplusplus\n";
print HEADER "namespace nix {\n";
print HEADER "#endif\n\n\n";
print IMPL "namespace nix {\n";
while (<STDIN>) {
s/\#.*//;
next if (/^\s*$/);
if (/^\s*(\w*)\s*\|([^\|]*)\|\s*(\w+)\s*\|\s*(\w+)?/) {
my $const = $1;
my @types = split ' ', $2;
my $result = $3;
my $funname = $4;
$funname = $const unless defined $funname;
my $formals = "";
my $formals2 = "";
my $args = "";
my $unpack = "";
my $n = 1;
foreach my $type (@types) {
my $realType = $type;
$args .= ", ";
if ($type eq "string") {
# $args .= "(ATerm) ATmakeAppl0(ATmakeAFun((char *) e$n, 0, ATtrue))";
# $type = "const char *";
$type = "ATerm";
$args .= "e$n";
# !!! in the matcher, we should check that the
# argument is a string (i.e., a nullary application).
} elsif ($type eq "int") {
$args .= "(ATerm) ATmakeInt(e$n)";
} elsif ($type eq "ATermList" || $type eq "ATermBlob") {
$args .= "(ATerm) e$n";
} else {
$args .= "e$n";
}
$formals .= ", " if $formals ne "";
$formals .= "$type e$n";
$formals2 .= ", ";
$formals2 .= "$type & e$n";
my $m = $n - 1;
# !!! more checks here
if ($type eq "int") {
$unpack .= " e$n = ATgetInt((ATermInt) ATgetArgument(e, $m));\n";
} elsif ($type eq "ATermList") {
$unpack .= " e$n = (ATermList) ATgetArgument(e, $m);\n";
} elsif ($type eq "ATermBlob") {
$unpack .= " e$n = (ATermBlob) ATgetArgument(e, $m);\n";
} elsif ($realType eq "string") {
$unpack .= " e$n = ATgetArgument(e, $m);\n";
$unpack .= " if (ATgetType(e$n) != AT_APPL) return false;\n";
} else {
$unpack .= " e$n = ATgetArgument(e, $m);\n";
}
$n++;
}
my $arity = scalar @types;
print HEADER "extern AFun sym$funname;\n\n";
print IMPL "AFun sym$funname = 0;\n";
if ($arity == 0) {
print HEADER "extern ATerm const$funname;\n\n";
print IMPL "ATerm const$funname = 0;\n";
}
print HEADER "static inline $result make$funname($formals) __attribute__ ((pure, nothrow));\n";
print HEADER "static inline $result make$funname($formals) {\n";
if ($arity == 0) {
print HEADER " return const$funname;\n";
}
elsif ($arity <= 6) {
print HEADER " return (ATerm) ATmakeAppl$arity(sym$funname$args);\n";
} else {
$args =~ s/^,//;
print HEADER " ATerm array[$arity] = {$args};\n";
print HEADER " return (ATerm) ATmakeApplArray(sym$funname, array);\n";
}
print HEADER "}\n\n";
print HEADER "#ifdef __cplusplus\n";
print HEADER "static inline bool match$funname(ATerm e$formals2) {\n";
print HEADER " if (ATgetType(e) != AT_APPL || (AFun) ATgetAFun(e) != sym$funname) return false;\n";
print HEADER "$unpack";
print HEADER " return true;\n";
print HEADER "}\n";
print HEADER "#endif\n\n\n";
$init .= " sym$funname = ATmakeAFun(\"$const\", $arity, ATfalse);\n";
$init .= " ATprotectAFun(sym$funname);\n";
if ($arity == 0) {
$init .= " const$funname = (ATerm) ATmakeAppl0(sym$funname);\n";
$init .= " ATprotect(&const$funname);\n";
}
}
elsif (/^\s*(\w+)\s*=\s*(.*)$/) {
my $name = $1;
my $value = $2;
print HEADER "extern ATerm $name;\n";
print IMPL "ATerm $name = 0;\n";
$init .= " $name = $value;\n";
}
elsif (/^\s*init\s+(\w+)\s*$/) {
$initFun = $1;
}
else {
die "bad line: `$_'";
}
}
print HEADER "void $initFun();\n\n";
print HEADER "static inline const char * aterm2String(ATerm t) {\n";
print HEADER " return (const char *) ATgetName(ATgetAFun(t));\n";
print HEADER "}\n\n";
print IMPL "\n";
print IMPL "void $initFun() {\n";
print IMPL "$init";
print IMPL "}\n";
print HEADER "#ifdef __cplusplus\n";
print HEADER "}\n";
print HEADER "#endif\n\n\n";
print IMPL "}\n";
close HEADER;
close IMPL;

View File

@@ -1,6 +1,3 @@
noinst_PROGRAMS = bin2c
bin2c_SOURCES = bin2c.c
bin2c$(EXEEXT): bin2c.c
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -o bin2c bin2c.c

View File

@@ -1,6 +1,6 @@
SUBDIRS = format
nobase_pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
shared_ptr.hpp weak_ptr.hpp throw_exception.hpp \
enable_shared_from_this.hpp \
detail/shared_count.hpp detail/workaround.hpp

View File

@@ -36,7 +36,6 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
static off_t offtin(u_char *buf)
{

View File

@@ -2,25 +2,25 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
names.cc
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc
pkginclude_HEADERS = \
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
names.hh symbol-table.hh
get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la
BUILT_SOURCES = \
BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \
parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc
EXTRA_DIST = lexer.l parser.y
EXTRA_DIST = lexer.l parser.y nixexpr-ast.def nixexpr-ast.cc
AM_CXXFLAGS = \
-I$(srcdir)/.. \
-I$(srcdir)/.. ${bdb_include} ${aterm_include} \
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
AM_CFLAGS = \
${aterm_include}
# Parser generation.
@@ -32,6 +32,15 @@ lexer-tab.cc lexer-tab.hh: lexer.l
$(flex) --outfile lexer-tab.cc --header-file=lexer-tab.hh $(srcdir)/lexer.l
# ATerm helper function generation.
nixexpr-ast.cc nixexpr-ast.hh: ../aterm-helper.pl nixexpr-ast.def
$(perl) $(srcdir)/../aterm-helper.pl nixexpr-ast.hh nixexpr-ast.cc < $(srcdir)/nixexpr-ast.def
CLEANFILES =
# SDF stuff (not built by default).
nix.tbl: nix.sdf
sdf2table -m Nix -s -i nix.sdf -o nix.tbl

View File

@@ -1,12 +1,23 @@
#include "attr-path.hh"
#include "nixexpr-ast.hh"
#include "util.hh"
namespace nix {
void findAlongAttrPath(EvalState & state, const string & attrPath,
const Bindings & autoArgs, Expr * e, Value & v)
bool isAttrs(EvalState & state, Expr e, ATermMap & attrs)
{
e = evalExpr(state, e);
ATermList dummy;
if (!matchAttrs(e, dummy)) return false;
queryAllAttrs(e, attrs, false);
return true;
}
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
const ATermMap & autoArgs, Expr e)
{
Strings tokens = tokenizeString(attrPath, ".");
@@ -14,10 +25,8 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
string curPath;
state.mkThunk_(v, e);
foreach (Strings::iterator, i, tokens) {
for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) {
if (!curPath.empty()) curPath += ".";
curPath += *i;
@@ -29,10 +38,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */
Value vTmp;
state.autoCallFunction(autoArgs, v, vTmp);
v = vTmp;
state.forceValue(v);
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
/* It should evaluate to either an attribute set or an
expression, according to what is specified in the
@@ -40,31 +46,36 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
if (apType == apAttr) {
if (v.type != tAttrs)
ATermMap attrs;
if (!isAttrs(state, e, attrs))
throw TypeError(
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
% curPath % showType(v));
Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
if (a == v.attrs->end())
% curPath % showType(e));
e = attrs.get(toATerm(attr));
if (!e)
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
v = a->second.value;
}
else if (apType == apIndex) {
if (v.type != tList)
ATermList es;
if (!matchList(e, es))
throw TypeError(
format("the expression selected by the selection path `%1%' should be a list but is %2%")
% curPath % showType(v));
% curPath % showType(e));
if (attrIndex >= v.list.length)
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
v = *v.list.elems[attrIndex];
e = ATelementAt(es, attrIndex);
if (!e)
throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath);
}
}
return e;
}

View File

@@ -10,8 +10,8 @@
namespace nix {
void findAlongAttrPath(EvalState & state, const string & attrPath,
const Bindings & autoArgs, Expr * e, Value & v);
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
const ATermMap & autoArgs, Expr e);
}

View File

@@ -9,7 +9,7 @@ namespace nix {
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
Bindings & autoArgs)
ATermMap & autoArgs)
{
if (arg != "--arg" && arg != "--argstr") return false;
@@ -19,13 +19,11 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
string name = *i++;
if (i == argsEnd) throw error;
string value = *i++;
Value & v(autoArgs[state.symbols.create(name)].value);
if (arg == "--arg")
state.mkThunk_( v, parseExprFromString(state, value, absPath(".")));
else
mkString(v, value);
Expr e = arg == "--arg"
? parseExprFromString(state, value, absPath("."))
: makeStr(value);
autoArgs.set(toATerm(name), e);
return true;
}

View File

@@ -9,7 +9,7 @@ namespace nix {
/* Some common option parsing between nix-env and nix-instantiate. */
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
Bindings & autoArgs);
ATermMap & autoArgs);
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,165 +3,17 @@
#include <map>
#include "aterm.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
namespace nix {
class Hash;
class EvalState;
struct Env;
struct Value;
struct Attr;
typedef std::map<Symbol, Attr> Bindings;
typedef enum {
tInt = 1,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList,
tThunk,
tApp,
tLambda,
tCopy,
tBlackhole,
tPrimOp,
tPrimOpApp,
} ValueType;
typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v);
struct Value
{
ValueType type;
union
{
int integer;
bool boolean;
/* Strings in the evaluator carry a so-called `context' (the
ATermList) which is a list of strings representing store
paths. This is to allow users to write things like
"--with-freetype2-library=" + freetype + "/lib"
where `freetype' is a derivation (or a source to be copied
to the store). If we just concatenated the strings without
keeping track of the referenced store paths, then if the
string is used as a derivation attribute, the derivation
will not have the correct dependencies in its inputDrvs and
inputSrcs.
The semantics of the context is as follows: when a string
with context C is used as a derivation attribute, then the
derivations in C will be added to the inputDrvs of the
derivation, and the other store paths in C will be added to
the inputSrcs of the derivations.
For canonicity, the store paths should be in sorted order. */
struct {
const char * s;
const char * * context; // must be in sorted order
} string;
const char * path;
Bindings * attrs;
struct {
unsigned int length;
Value * * elems;
} list;
struct {
Env * env;
Expr * expr;
} thunk;
struct {
Value * left, * right;
} app;
struct {
Env * env;
ExprLambda * fun;
} lambda;
Value * val;
struct {
PrimOp fun;
char * name;
unsigned int arity;
} primOp;
struct {
Value * left, * right;
unsigned int argsLeft;
} primOpApp;
};
};
struct Env
{
Env * up;
unsigned int prevWith; // nr of levels up to next `with' environment
Value values[0];
};
struct Attr
{
Value value;
Pos * pos;
Attr() : pos(&noPos) { };
};
static inline void mkInt(Value & v, int n)
{
v.type = tInt;
v.integer = n;
}
static inline void mkBool(Value & v, bool b)
{
v.type = tBool;
v.boolean = b;
}
static inline void mkThunk(Value & v, Env & env, Expr * expr)
{
v.type = tThunk;
v.thunk.env = &env;
v.thunk.expr = expr;
}
static inline void mkCopy(Value & v, Value & src)
{
v.type = tCopy;
v.val = &src;
}
static inline void mkApp(Value & v, Value & left, Value & right)
{
v.type = tApp;
v.app.left = &left;
v.app.right = &right;
}
void mkString(Value & v, const char * s);
void mkString(Value & v, const string & s, const PathSet & context = PathSet());
void mkPath(Value & v, const char * s);
typedef std::map<Path, PathSet> DrvRoots;
typedef std::map<Path, Hash> DrvHashes;
/* Cache for calls to addToStore(); maps source paths to the store
@@ -170,153 +22,80 @@ typedef std::map<Path, Path> SrcToStore;
struct EvalState;
std::ostream & operator << (std::ostream & str, Value & v);
/* Note: using a ATermVector is safe here, since when we call a primop
we also have an ATermList on the stack. */
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
class EvalState
extern int cacheTerms; // 0 = don't, 1 = do, 2 = "cell" terms only
struct EvalState
{
public:
ATermMap normalForms;
ATermMap primOps;
DrvRoots drvRoots;
DrvHashes drvHashes; /* normalised derivation hashes */
SymbolTable symbols;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem;
private:
SrcToStore srcToStore;
bool allowUnsafeEquality;
unsigned int nrEvaluated;
unsigned int nrCached;
std::map<Path, Expr *> parseTrees;
bool strictMode;
ATermMap parsings; /* path -> expr mapping */
public:
EvalState();
~EvalState();
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);
/* Evaluate an expression to normal form, storing the result in
value `v'. */
void eval(Expr * e, Value & v);
void eval(Env & env, Expr * e, Value & v);
/* Evaluation the expression, then verify that it has the expected
type. */
bool evalBool(Env & env, Expr * e);
void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
void forceValue(Value & v);
/* Force a value, then recursively force list elements and
attributes. */
void strictForceValue(Value & v);
/* Force `v', and then verify that it has the expected type. */
int forceInt(Value & v);
bool forceBool(Value & v);
void forceAttrs(Value & v);
void forceList(Value & v);
void forceFunction(Value & v); // either lambda or primop
string forceString(Value & v);
string forceString(Value & v, PathSet & context);
string forceStringNoCtx(Value & v);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect.q */
string coerceToString(Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(Value & v, PathSet & context);
private:
/* The base environment, containing the builtin functions and
values. */
Env & baseEnv;
unsigned int baseEnvDispl;
public:
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
private:
void createBaseEnv();
void addConstant(const string & name, Value & v);
void addPrimOps();
void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp);
Value * lookupVar(Env * env, const VarRef & var);
friend class ExprVar;
friend class ExprAttrs;
friend class ExprLet;
public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2);
void callFunction(Value & fun, Value & arg, Value & v);
/* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */
void autoCallFunction(const Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
Value * allocValues(unsigned int count);
Env & allocEnv(unsigned int size);
void mkList(Value & v, unsigned int length);
void mkAttrs(Value & v);
void mkThunk_(Value & v, Expr * expr);
void cloneAttrs(Value & src, Value & dst);
/* Print statistics. */
void printStats();
private:
unsigned long nrEnvs;
unsigned long nrValuesInEnvs;
unsigned long nrValues;
unsigned long nrListElems;
unsigned long nrEvaluated;
unsigned int recursionDepth;
unsigned int maxRecursionDepth;
char * deepestStack; /* for measuring stack usage */
friend class RecursionCounter;
};
/* Return a string representing the type of the value `v'. */
string showType(const Value & v);
/* Evaluate an expression to normal form. */
Expr evalExpr(EvalState & state, Expr e);
/* Evaluate an expression read from the given file to normal form. */
Expr evalFile(EvalState & state, const Path & path);
/* Evaluate an expression, and recursively evaluate list elements and
attributes. If `canonicalise' is true, we remove things like
position information and make sure that attribute sets are in
sorded order. */
Expr strictEvalExpr(EvalState & state, Expr e);
/* Specific results. */
string evalString(EvalState & state, Expr e, PathSet & context);
string evalStringNoCtx(EvalState & state, Expr e);
int evalInt(EvalState & state, Expr e);
bool evalBool(EvalState & state, Expr e);
ATermList evalList(EvalState & state, Expr e);
/* Flatten nested lists into a single list (or expand a singleton into
a list). */
ATermList flattenList(EvalState & state, Expr e);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. */
string coerceToString(EvalState & state, Expr e, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
/* Path coercion. Converts strings, paths and derivations to a path.
The result is guaranteed to be an canonicalised, absolute path.
Nothing is copied to the store. */
Path coerceToPath(EvalState & state, Expr e, PathSet & context);
/* Automatically call a function for which each argument has a default
value or has a binding in the `args' map. Note: result is a call,
not a normal form; it should be evaluated by calling evalExpr(). */
Expr autoCallFunction(Expr e, const ATermMap & args);
/* Print statistics. */
void printEvalStats(EvalState & state);
}

142
src/libexpr/expr-to-xml.cc Normal file
View File

@@ -0,0 +1,142 @@
#include "expr-to-xml.hh"
#include "xml-writer.hh"
#include "nixexpr-ast.hh"
#include "aterm.hh"
#include "util.hh"
namespace nix {
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
attrs[name] = value;
return attrs;
}
/* set<Expr> is safe because all the expressions are also reachable
from the stack, therefore can't be garbage-collected. */
typedef set<Expr> ExprSet;
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
ExprSet & drvsSeen);
static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
PathSet & context, ExprSet & drvsSeen)
{
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
for (StringSet::iterator i = names.begin(); i != names.end(); ++i) {
XMLOpenElement _(doc, "attr", singletonAttrs("name", *i));
printTermAsXML(attrs.get(toATerm(*i)), doc, context, drvsSeen);
}
}
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
ExprSet & drvsSeen)
{
XMLAttrs attrs;
string s;
ATerm s2;
int i;
ATermList as, es, formals;
ATerm body, pos;
checkInterrupt();
if (matchStr(e, s, context)) /* !!! show the context? */
doc.writeEmptyElement("string", singletonAttrs("value", s));
else if (matchPath(e, s2))
doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
else if (matchNull(e))
doc.writeEmptyElement("null");
else if (matchInt(e, i))
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
else if (e == eTrue)
doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
else if (e == eFalse)
doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
else if (matchAttrs(e, as)) {
ATermMap attrs;
queryAllAttrs(e, attrs);
Expr a = attrs.get(toATerm("type"));
if (a && matchStr(a, s, context) && s == "derivation") {
XMLAttrs xmlAttrs;
Path outPath, drvPath;
a = attrs.get(toATerm("drvPath"));
if (matchStr(a, drvPath, context))
xmlAttrs["drvPath"] = drvPath;
a = attrs.get(toATerm("outPath"));
if (matchStr(a, outPath, context))
xmlAttrs["outPath"] = outPath;
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvsSeen.find(e) == drvsSeen.end()) {
drvsSeen.insert(e);
showAttrs(attrs, doc, context, drvsSeen);
} else
doc.writeEmptyElement("repeated");
}
else {
XMLOpenElement _(doc, "attrs");
showAttrs(attrs, doc, context, drvsSeen);
}
}
else if (matchList(e, es)) {
XMLOpenElement _(doc, "list");
for (ATermIterator i(es); i; ++i)
printTermAsXML(*i, doc, context, drvsSeen);
}
else if (matchFunction(e, formals, body, pos)) {
XMLOpenElement _(doc, "function");
for (ATermIterator i(formals); i; ++i) {
Expr name; ValidValues valids; ATerm dummy;
if (!matchFormal(*i, name, valids, dummy)) abort();
XMLOpenElement _(doc, "arg", singletonAttrs("name", aterm2String(name)));
ATermList valids2;
if (matchValidValues(valids, valids2)) {
for (ATermIterator j(valids2); j; ++j) {
XMLOpenElement _(doc, "value");
printTermAsXML(*j, doc, context, drvsSeen);
}
}
}
}
else
doc.writeEmptyElement("unevaluated");
}
void printTermAsXML(Expr e, std::ostream & out, PathSet & context)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
ExprSet drvsSeen;
printTermAsXML(e, doc, context, drvsSeen);
}
}

View File

@@ -0,0 +1,16 @@
#ifndef __EXPR_TO_XML_H
#define __EXPR_TO_XML_H
#include <string>
#include <map>
#include "nixexpr.hh"
#include "aterm.hh"
namespace nix {
void printTermAsXML(Expr e, std::ostream & out, PathSet & context);
}
#endif /* !__EXPR_TO_XML_H */

View File

@@ -1,4 +1,5 @@
#include "get-drvs.hh"
#include "nixexpr-ast.hh"
#include "util.hh"
@@ -7,10 +8,17 @@ namespace nix {
string DrvInfo::queryDrvPath(EvalState & state) const
{
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sDrvPath);
if (drvPath == "") {
Expr a = attrs->get(toATerm("drvPath"));
/* Backwards compatibility hack with user environments made by
Nix <= 0.10: these contain illegal Path("") expressions. */
ATerm t;
if (a && matchPath(evalExpr(state, a), t))
return aterm2String(t);
PathSet context;
(string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
(string &) drvPath = a ? coerceToPath(state, a, context) : "";
}
return drvPath;
}
@@ -18,10 +26,11 @@ string DrvInfo::queryDrvPath(EvalState & state) const
string DrvInfo::queryOutPath(EvalState & state) const
{
if (outPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sOutPath);
if (outPath == "") {
Expr a = attrs->get(toATerm("outPath"));
if (!a) throw TypeError("output path missing");
PathSet context;
(string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
(string &) outPath = coerceToPath(state, a, context);
}
return outPath;
}
@@ -29,85 +38,93 @@ string DrvInfo::queryOutPath(EvalState & state) const
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
{
if (metaInfoRead) return meta;
MetaInfo meta;
(bool &) metaInfoRead = true;
Bindings::iterator a = attrs->find(state.sMeta);
if (a == attrs->end()) return meta; /* fine, empty meta information */
Expr a = attrs->get(toATerm("meta"));
if (!a) return meta; /* fine, empty meta information */
state.forceAttrs(a->second.value);
ATermMap attrs2;
queryAllAttrs(evalExpr(state, a), attrs2);
foreach (Bindings::iterator, i, *a->second.value.attrs) {
MetaValue value;
state.forceValue(i->second.value);
if (i->second.value.type == tString) {
value.type = MetaValue::tpString;
value.stringValue = i->second.value.string.s;
} else if (i->second.value.type == tInt) {
value.type = MetaValue::tpInt;
value.intValue = i->second.value.integer;
} else if (i->second.value.type == tList) {
value.type = MetaValue::tpStrings;
for (unsigned int j = 0; j < i->second.value.list.length; ++j)
value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j]));
} else continue;
((MetaInfo &) meta)[i->first] = value;
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
Expr e = evalExpr(state, i->value);
string s;
PathSet context;
if (matchStr(e, s, context))
meta[aterm2String(i->key)] = s;
/* For future compatibility, ignore attribute values that are
not strings. */
}
return meta;
}
MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
string DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
{
/* !!! evaluates all meta attributes => inefficient */
return queryMetaInfo(state)[name];
MetaInfo meta = queryMetaInfo(state);
MetaInfo::iterator i = meta.find(name);
return i == meta.end() ? "" : i->second;
}
void DrvInfo::setMetaInfo(const MetaInfo & meta)
{
metaInfoRead = true;
this->meta = meta;
ATermMap metaAttrs;
for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i)
metaAttrs.set(toATerm(i->first),
makeAttrRHS(makeStr(i->second), makeNoPos()));
attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
}
/* Cache for already considered attrsets. */
typedef set<Bindings *> Done;
/* Cache for already evaluated derivations. Usually putting ATerms in
a STL container is unsafe (they're not scanning for GC roots), but
here it doesn't matter; everything in this set is reachable from
the stack as well. */
typedef set<Expr> Exprs;
/* Evaluate value `v'. If it evaluates to an attribute set of type
`derivation', then put information about it in `drvs' (unless it's
already in `doneExprs'). The result boolean indicates whether it
makes sense for the caller to recursively search for derivations in
`v'. */
static bool getDerivation(EvalState & state, Value & v,
const string & attrPath, DrvInfos & drvs, Done & done)
/* Evaluate expression `e'. If it evaluates to an attribute set of
type `derivation', then put information about it in `drvs' (unless
it's already in `doneExprs'). The result boolean indicates whether
it makes sense for the caller to recursively search for derivations
in `e'. */
static bool getDerivation(EvalState & state, Expr e,
const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
{
try {
state.forceValue(v);
if (!state.isDerivation(v)) return true;
ATermList es;
e = evalExpr(state, e);
if (!matchAttrs(e, es)) return true;
boost::shared_ptr<ATermMap> attrs(new ATermMap());
queryAllAttrs(e, *attrs, false);
Expr a = attrs->get(toATerm("type"));
if (!a || evalStringNoCtx(state, a) != "derivation") return true;
/* Remove spurious duplicates (e.g., an attribute set like
`rec { x = derivation {...}; y = x;}'. */
if (done.find(v.attrs) != done.end()) return false;
done.insert(v.attrs);
if (doneExprs.find(e) != doneExprs.end()) return false;
doneExprs.insert(e);
DrvInfo drv;
Bindings::iterator i = v.attrs->find(state.sName);
a = attrs->get(toATerm("name"));
/* !!! We really would like to have a decent back trace here. */
if (i == v.attrs->end()) throw TypeError("derivation name missing");
drv.name = state.forceStringNoCtx(i->second.value);
if (!a) throw TypeError("derivation name missing");
drv.name = evalStringNoCtx(state, a);
i = v.attrs->find(state.sSystem);
if (i == v.attrs->end())
a = attrs->get(toATerm("system"));
if (!a)
drv.system = "unknown";
else
drv.system = state.forceStringNoCtx(i->second.value);
drv.system = evalStringNoCtx(state, a);
drv.attrs = v.attrs;
drv.attrs = attrs;
drv.attrPath = attrPath;
@@ -120,11 +137,11 @@ static bool getDerivation(EvalState & state, Value & v,
}
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
{
Done done;
Exprs doneExprs;
DrvInfos drvs;
getDerivation(state, v, "", drvs, done);
getDerivation(state, e, "", drvs, doneExprs);
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
@@ -137,73 +154,77 @@ static string addToPath(const string & s1, const string & s2)
}
static void getDerivations(EvalState & state, Value & vIn,
const string & pathPrefix, const Bindings & autoArgs,
DrvInfos & drvs, Done & done)
static void getDerivations(EvalState & state, Expr e,
const string & pathPrefix, const ATermMap & autoArgs,
DrvInfos & drvs, Exprs & doneExprs)
{
Value v;
state.autoCallFunction(autoArgs, vIn, v);
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
/* Process the expression. */
ATermList es;
DrvInfo drv;
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
return;
else if (v.type == tAttrs) {
if (matchAttrs(e, es)) {
ATermMap drvMap(ATgetLength(es));
queryAllAttrs(e, drvMap);
/* !!! undocumented hackery to support combining channels in
nix-env.cc. */
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
/* Consider the attributes in sorted order to get more
deterministic behaviour in nix-env operations (e.g. when
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
typedef std::map<string, Symbol> SortedSymbols;
SortedSymbols attrs;
foreach (Bindings::iterator, i, *v.attrs)
attrs.insert(std::pair<string, Symbol>(i->first, i->first));
foreach (SortedSymbols::iterator, i, attrs) {
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
string pathPrefix2 = addToPath(pathPrefix, i->first);
Value & v2((*v.attrs)[i->second].value);
if (combineChannels)
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
Expr e2 = drvMap.get(toATerm("_combineChannels"));
bool combineChannels = e2 && evalBool(state, e2);
for (ATermMap::const_iterator i = drvMap.begin(); i != drvMap.end(); ++i) {
startNest(nest, lvlDebug,
format("evaluating attribute `%1%'") % aterm2String(i->key));
string pathPrefix2 = addToPath(pathPrefix, aterm2String(i->key));
if (combineChannels) {
if (((string) aterm2String(i->key)) != "_combineChannels")
getDerivations(state, i->value, pathPrefix2, autoArgs, drvs, doneExprs);
}
else if (getDerivation(state, i->value, pathPrefix2, drvs, doneExprs)) {
/* If the value of this attribute is itself an
attribute set, should we recurse into it? => Only
if it has a `recurseForDerivations = true'
attribute. */
if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(j->second.value))
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
ATermList es;
Expr e = evalExpr(state, i->value);
if (matchAttrs(e, es)) {
ATermMap attrs(ATgetLength(es));
queryAllAttrs(e, attrs, false);
if (((e2 = attrs.get(toATerm("recurseForDerivations")))
&& evalBool(state, e2)))
getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
}
}
}
return;
}
else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n) {
if (matchList(e, es)) {
int n = 0;
for (ATermIterator i(es); i; ++i, ++n) {
startNest(nest, lvlDebug,
format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
}
return;
}
else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
}
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs)
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
const ATermMap & autoArgs, DrvInfos & drvs)
{
Done done;
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
Exprs doneExprs;
getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
}

View File

@@ -12,16 +12,7 @@
namespace nix {
struct MetaValue
{
enum { tpNone, tpString, tpStrings, tpInt } type;
string stringValue;
Strings stringValues;
int intValue;
};
typedef std::map<string, MetaValue> MetaInfo;
typedef std::map<string, string> MetaInfo;
struct DrvInfo
@@ -29,24 +20,21 @@ struct DrvInfo
private:
string drvPath;
string outPath;
bool metaInfoRead;
MetaInfo meta;
public:
string name;
string attrPath; /* path towards the derivation */
string system;
/* !!! make this private */
Bindings * attrs;
DrvInfo() : metaInfoRead(false), attrs(0) { };
/* !!! these should really be hidden, and setMetaInfo() should
make a copy since the ATermMap can be shared between multiple
DrvInfos. */
boost::shared_ptr<ATermMap> attrs;
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
MetaInfo queryMetaInfo(EvalState & state) const;
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
string queryMetaInfo(EvalState & state, const string & name) const;
void setDrvPath(const string & s)
{
@@ -65,12 +53,13 @@ public:
typedef list<DrvInfo> DrvInfos;
/* 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);
/* Evaluate expression `e'. If it evaluates to a derivation, store
information about the derivation in `drv' and return true.
Otherwise, return false. */
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs);
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
const ATermMap & autoArgs, DrvInfos & drvs);
}

View File

@@ -4,11 +4,12 @@
%x STRING
%x IND_STRING
%{
#include "aterm.hh"
#include "nixexpr.hh"
#include "nixexpr-ast.hh"
#define BISON_HEADER_HACK
#include "parser-tab.hh"
@@ -19,16 +20,13 @@ namespace nix {
static void initLoc(YYLTYPE * loc)
{
loc->first_line = loc->last_line = 1;
loc->first_column = loc->last_column = 1;
loc->first_line = 1;
loc->first_column = 1;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
loc->first_line = loc->last_line;
loc->first_column = loc->last_column;
while (len--) {
switch (*s++) {
case '\r':
@@ -36,17 +34,17 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
s++;
/* fall through */
case '\n':
++loc->last_line;
loc->last_column = 1;
++loc->first_line;
loc->first_column = 1;
break;
default:
++loc->last_column;
++loc->first_column;
}
}
}
static Expr * unescapeStr(const char * s)
static Expr unescapeStr(const char * s)
{
string t;
char c;
@@ -66,7 +64,7 @@ static Expr * unescapeStr(const char * s)
}
else t += c;
}
return new ExprString(t);
return makeStr(toATerm(t), ATempty);
}
@@ -96,7 +94,6 @@ let { return LET; }
in { return IN; }
rec { return REC; }
inherit { return INHERIT; }
\.\.\. { return ELLIPSIS; }
\=\= { return EQ; }
\!\= { return NEQ; }
@@ -106,53 +103,28 @@ inherit { return INHERIT; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
{ID} { yylval->id = strdup(yytext); return ID; }
{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ }
{INT} { int n = atoi(yytext); /* !!! overflow */
yylval->n = n;
yylval->t = ATmake("<int>", n);
return INT;
}
\" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
/* !!! Not quite right: we want a follow restriction on
"$", it shouldn't be followed by a "{". Right now
"$\"" will be consumed as part of a string, rather
than a "$" followed by the string terminator.
Disallow "$\"" for now. */
yylval->e = unescapeStr(yytext);
/* !!! Not quite right: we want a follow restriction on "$", it
shouldn't be followed by a "{". Right now "$\"" will be consumed
as part of a string, rather than a "$" followed by the string
terminator. Disallow "$\"" for now. */
yylval->t = unescapeStr(yytext); /* !!! alloc */
return STR;
}
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<STRING>\" { BEGIN(INITIAL); return '"'; }
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->e = new ExprIndStr(yytext);
return IND_STR;
}
<IND_STRING>\'\'\$ {
yylval->e = new ExprIndStr("$");
return IND_STR;
}
<IND_STRING>\'\'\' {
yylval->e = new ExprIndStr("''");
return IND_STR;
}
<IND_STRING>\'\'\\. {
yylval->e = unescapeStr(yytext + 2);
return IND_STR;
}
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->e = new ExprIndStr("'");
return IND_STR;
}
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
{PATH} { yylval->path = strdup(yytext); return PATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */
@@ -176,10 +148,4 @@ void backToString(yyscan_t scanner)
BEGIN(STRING);
}
void backToIndString(yyscan_t scanner)
{
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
BEGIN(IND_STRING);
}
}

View File

@@ -1,13 +1,10 @@
%% Note: this SDF grammar is no longer used in the Nix expression
%% parser and may not be up to date.
definition
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top level syntax.
module Main
module Nix
imports Nix-Exprs Nix-Layout
@@ -41,7 +38,6 @@ exports
"with" Expr ";" Expr -> Expr {cons("With")}
"rec" "{" Bind* "}" -> Expr {cons("Rec")}
"let" Bind* "in" Expr -> Expr {cons("Let")}
"let" "{" Bind* "}" -> Expr {cons("LetRec")}
"{" Bind* "}" -> Expr {cons("Attrs")}
@@ -106,8 +102,7 @@ exports
[0-9]+ -> Int
"\"" (~[\"\\] | ("\\" ~[]) )* "\"" -> Str
"''" (~[\"\\] | ("\\" ~[]) )* "''" -> Str
"\"" ~[\n\"]* "\"" -> Str
[a-zA-Z0-9\.\_\-\+]* ("/"[a-zA-Z0-9\.\_\-\+]+)+ -> Path

View File

@@ -0,0 +1,94 @@
init initNixExprHelpers
Pos | string int int | Pos |
NoPos | | Pos |
Function | ATermList Expr Pos | Expr |
Function1 | string Expr Pos | Expr |
Assert | Expr Expr Pos | Expr |
With | Expr Expr Pos | Expr |
If | Expr Expr Expr | Expr |
OpNot | Expr | Expr |
OpEq | Expr Expr | Expr |
OpNEq | Expr Expr | Expr |
OpAnd | Expr Expr | Expr |
OpOr | Expr Expr | Expr |
OpImpl | Expr Expr | Expr |
OpUpdate | Expr Expr | Expr |
SubPath | Expr Expr | Expr |
OpHasAttr | Expr string | Expr |
OpPlus | Expr Expr | Expr |
OpConcat | Expr Expr | Expr |
ConcatStrings | ATermList | Expr |
Call | Expr Expr | Expr |
Select | Expr string | Expr |
Var | string | Expr |
Int | int | Expr |
# Strings in the evaluator carry a so-called `context' (the ATermList)
# which is a list of strings representing store paths. This is to
# allow users to write things like
#
# "--with-freetype2-library=" + freetype + "/lib"
#
# where `freetype' is a derivation (or a source to be copied to the
# store). If we just concatenated the strings without keeping track
# of the referenced store paths, then if the string is used as a
# derivation attribute, the derivation will not have the correct
# dependencies in its inputDrvs and inputSrcs.
#
# The semantics of the context is as follows: when a string with
# context C is used as a derivation attribute, then the derivations in
# C will be added to the inputDrvs of the derivation, and the other
# store paths in C will be added to the inputSrcs of the derivations.
#
# For canonicity, the store paths should be in sorted order.
Str | string ATermList | Expr |
Str | string | Expr | ObsoleteStr
# A path is a reference to a file system object that is to be copied
# to the Nix store when used as a derivation attribute. When it is
# concatenated to a string (i.e., `str + path'), it is also copied and
# the resulting store path is concatenated to the string (with the
# store path in the context). If a string or path is concatenated to
# a path (i.e., `path + str' or `path + path'), the result is a new
# path (if the right-hand side is a string, the context must be
# empty).
Path | string | Expr |
List | ATermList | Expr |
BlackHole | | Expr |
Undefined | | Expr |
Removed | | Expr |
PrimOp | int ATermBlob ATermList | Expr |
Attrs | ATermList | Expr |
Closed | Expr | Expr |
Rec | ATermList ATermList | Expr |
Bool | ATerm | Expr |
Null | | Expr |
Bind | string Expr Pos | ATerm |
Bind | string Expr | ATerm | ObsoleteBind
Inherit | Expr ATermList Pos | ATerm |
Scope | | Expr |
Cell | int Expr | Expr |
Formal | string ValidValues DefaultValue | ATerm |
ValidValues | ATermList | ValidValues |
UnrestrictedValues | | ValidValues |
DefaultValue | Expr | DefaultValue |
NoDefaultValue | | DefaultValue |
True | | ATerm |
False | | ATerm |
PrimOpDef | int ATermBlob | ATerm |
AttrRHS | Expr Pos | ATerm |
eTrue = makeBool(makeTrue())
eFalse = makeBool(makeFalse())

View File

@@ -1,325 +1,512 @@
#include "nixexpr.hh"
#include "derivations.hh"
#include "util.hh"
#include "aterm.hh"
#include "eval.hh" // !!! urgh
#include <cstdlib>
#include "nixexpr-ast.hh"
#include "nixexpr-ast.cc"
namespace nix {
/* Displaying abstract syntax trees. */
std::ostream & operator << (std::ostream & str, Expr & e)
string showPos(ATerm pos)
{
e.show(str);
return str;
ATerm path;
int line, column;
if (matchNoPos(pos)) return "undefined position";
if (!matchPos(pos, path, line, column))
throw badTerm("position expected", pos);
return (format("`%1%', line %2%") % aterm2String(path) % line).str();
}
void Expr::show(std::ostream & str)
ATerm bottomupRewrite(TermFun & f, ATerm e)
{
abort();
}
checkInterrupt();
void ExprInt::show(std::ostream & str)
{
str << n;
}
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATerm args[arity];
void ExprString::show(std::ostream & str)
{
str << "\"" << s << "\""; // !!! escaping
}
void ExprPath::show(std::ostream & str)
{
str << s;
}
void ExprVar::show(std::ostream & str)
{
str << info.name;
}
void ExprSelect::show(std::ostream & str)
{
str << "(" << *e << ")." << name;
}
void ExprOpHasAttr::show(std::ostream & str)
{
str << "(" << *e << ") ? " << name;
}
void ExprAttrs::show(std::ostream & str)
{
if (recursive) str << "rec ";
str << "{ ";
foreach (list<Inherited>::iterator, i, inherited)
str << "inherit " << i->first.name << "; ";
foreach (Attrs::iterator, i, attrs)
str << i->first << " = " << *i->second.first << "; ";
str << "}";
}
void ExprList::show(std::ostream & str)
{
str << "[ ";
foreach (vector<Expr *>::iterator, i, elems)
str << "(" << **i << ") ";
str << "]";
}
void ExprLambda::show(std::ostream & str)
{
str << "(";
if (matchAttrs) {
str << "{ ";
bool first = true;
foreach (Formals::Formals_::iterator, i, formals->formals) {
if (first) first = false; else str << ", ";
str << i->name;
if (i->def) str << " ? " << *i->def;
}
str << " }";
if (!arg.empty()) str << " @ ";
for (int i = 0; i < arity; ++i)
args[i] = bottomupRewrite(f, ATgetArgument(e, i));
e = (ATerm) ATmakeApplArray(fun, args);
}
if (!arg.empty()) str << arg;
str << ": " << *body << ")";
else if (ATgetType(e) == AT_LIST) {
ATermList in = (ATermList) e;
ATermList out = ATempty;
for (ATermIterator i(in); i; ++i)
out = ATinsert(out, bottomupRewrite(f, *i));
e = (ATerm) ATreverse(out);
}
return f(e);
}
void ExprLet::show(std::ostream & str)
{
str << "let ";
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited)
str << "inherit " << i->first.name << "; ";
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
str << i->first << " = " << *i->second.first << "; ";
str << "in " << *body;
}
void ExprWith::show(std::ostream & str)
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
{
str << "with " << *attrs << "; " << *body;
}
ATermList bnds;
if (!matchAttrs(e, bnds))
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
void ExprIf::show(std::ostream & str)
{
str << "if " << *cond << " then " << *then << " else " << *else_;
}
void ExprAssert::show(std::ostream & str)
{
str << "assert " << *cond << "; " << *body;
}
void ExprOpNot::show(std::ostream & str)
{
str << "! " << *e;
}
void ExprConcatStrings::show(std::ostream & str)
{
bool first = true;
foreach (vector<Expr *>::iterator, i, *es) {
if (first) first = false; else str << " + ";
str << **i;
for (ATermIterator i(bnds); i; ++i) {
ATerm name;
Expr e;
ATerm pos;
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
attrs.set(name, withPos ? makeAttrRHS(e, pos) : e);
}
}
std::ostream & operator << (std::ostream & str, const Pos & pos)
Expr queryAttr(Expr e, const string & name)
{
if (!pos.line)
str << "undefined position";
else
str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str();
return str;
ATerm dummy;
return queryAttr(e, name, dummy);
}
Pos noPos;
/* Computing levels/displacements for variables. */
void Expr::bindVars(const StaticEnv & env)
Expr queryAttr(Expr e, const string & name, ATerm & pos)
{
abort();
}
ATermList bnds;
if (!matchAttrs(e, bnds))
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
void ExprInt::bindVars(const StaticEnv & env)
{
}
void ExprString::bindVars(const StaticEnv & env)
{
}
void ExprPath::bindVars(const StaticEnv & env)
{
}
void VarRef::bind(const StaticEnv & env)
{
/* Check whether the variable appears in the environment. If so,
set its level and displacement. */
const StaticEnv * curEnv;
unsigned int level;
int withLevel = -1;
for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
if (curEnv->isWith) {
if (withLevel == -1) withLevel = level;
} else {
StaticEnv::Vars::const_iterator i = curEnv->vars.find(name);
if (i != curEnv->vars.end()) {
fromWith = false;
this->level = level;
displ = i->second;
return;
}
for (ATermIterator i(bnds); i; ++i) {
ATerm name2, pos2;
Expr e;
if (!matchBind(*i, name2, e, pos2))
abort(); /* can't happen */
if (aterm2String(name2) == name) {
pos = pos2;
return e;
}
}
/* 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 EvalError(format("undefined variable `%1%'") % name);
fromWith = true;
this->level = withLevel;
return 0;
}
void ExprVar::bindVars(const StaticEnv & env)
Expr makeAttrs(const ATermMap & attrs)
{
info.bind(env);
ATermList bnds = ATempty;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
Expr e;
ATerm pos;
if (!matchAttrRHS(i->value, e, pos))
abort(); /* can't happen */
bnds = ATinsert(bnds, makeBind(i->key, e, pos));
}
return makeAttrs(bnds);
}
void ExprSelect::bindVars(const StaticEnv & env)
extern unsigned int substs;
extern unsigned int substsCached;
extern bool closedTerms;
extern bool substCache;
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e);
static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
{
e->bindVars(env);
}
checkInterrupt();
void ExprOpHasAttr::bindVars(const StaticEnv & env)
{
e->bindVars(env);
}
//if (subs.size() == 0) return e;
void ExprAttrs::bindVars(const StaticEnv & env)
{
if (recursive) {
StaticEnv newEnv(false, &env);
unsigned int displ = 0;
ATerm name, pos, e2;
foreach (ExprAttrs::Attrs::iterator, i, attrs)
newEnv.vars[i->first] = displ++;
substs++;
foreach (list<Inherited>::iterator, i, inherited) {
newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
/* As an optimisation, don't substitute in subterms known to be
closed. */
if (closedTerms && matchClosed(e, e2)) return e;
if (matchVar(e, name)) {
Expr sub = subs.lookup(name);
Expr wrapped;
/* Add a "closed" wrapper around terms that aren't already
closed. The check is necessary to prevent repeated
wrapping, e.g., closed(closed(closed(...))), which kills
caching. */
return sub ? ((!closedTerms || matchClosed(sub, wrapped)) ? sub : makeClosed(sub)) : e;
}
/* In case of a function, filter out all variables bound by this
function. */
ATermList formals;
ATerm body;
if (matchFunction(e, formals, body, pos)) {
ATermMap map(ATgetLength(formals));
for (ATermIterator i(formals); i; ++i) {
ATerm d1, d2;
if (!matchFormal(*i, name, d1, d2)) abort();
if (subs.lookup(name))
map.set(name, constRemoved);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(newEnv);
}
else {
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(env);
foreach (list<Inherited>::iterator, i, inherited)
i->first.bind(env);
}
}
void ExprList::bindVars(const StaticEnv & env)
{
foreach (vector<Expr *>::iterator, i, elems)
(*i)->bindVars(env);
}
void ExprLambda::bindVars(const StaticEnv & env)
{
StaticEnv newEnv(false, &env);
unsigned int displ = 0;
if (!arg.empty()) newEnv.vars[arg] = displ++;
if (matchAttrs) {
foreach (Formals::Formals_::iterator, i, formals->formals)
newEnv.vars[i->name] = displ++;
foreach (Formals::Formals_::iterator, i, formals->formals)
if (i->def) i->def->bindVars(newEnv);
}
body->bindVars(newEnv);
}
void ExprLet::bindVars(const StaticEnv & env)
{
StaticEnv newEnv(false, &env);
unsigned int displ = 0;
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = displ++;
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) {
newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
i->second.first->bindVars(newEnv);
body->bindVars(newEnv);
}
void ExprWith::bindVars(const StaticEnv & env)
{
/* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous
`with' if this one doesn't contain the desired attribute. */
const StaticEnv * curEnv;
unsigned int level;
prevWith = 0;
for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
if (curEnv->isWith) {
prevWith = level;
break;
if (map.size() == 0)
return makeFunction(
(ATermList) substitute(done, subs, (ATerm) formals),
substitute(done, subs, body), pos);
else {
Substitution subs2(&subs, &map);
ATermMap done2(128);
return makeFunction(
(ATermList) substitute(done2, subs2, (ATerm) formals),
substitute(done2, subs2, body), pos);
}
}
if (matchFunction1(e, name, body, pos)) {
if (subs.lookup(name)) {
ATermMap map(1);
map.set(name, constRemoved);
ATermMap done2(128);
return makeFunction1(name, substitute(done2, Substitution(&subs, &map), body), pos);
} else
return makeFunction1(name, substitute(done, subs, body), pos);
}
/* Idem for a mutually recursive attribute set. */
ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds)) {
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i)
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
map.set(name, constRemoved);
for (ATermIterator i(nrbnds); i; ++i)
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
map.set(name, constRemoved);
if (map.size() == 0)
return makeRec(
(ATermList) substitute(done, subs, (ATerm) rbnds),
(ATermList) substitute(done, subs, (ATerm) nrbnds));
else {
ATermMap done2(128);
return makeRec(
(ATermList) substitute(done2, Substitution(&subs, &map), (ATerm) rbnds),
(ATermList) substitute(done, subs, (ATerm) nrbnds));
}
}
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATerm args[arity];
bool changed = false;
for (int i = 0; i < arity; ++i) {
ATerm arg = ATgetArgument(e, i);
args[i] = substitute(done, subs, arg);
if (args[i] != arg) changed = true;
}
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
}
if (ATgetType(e) == AT_LIST) {
unsigned int len = ATgetLength((ATermList) e);
ATerm es[len];
ATermIterator i((ATermList) e);
bool changed = false;
for (unsigned int j = 0; i; ++i, ++j) {
es[j] = substitute(done, subs, *i);
if (es[j] != *i) changed = true;
}
if (!changed) return e;
ATermList out = ATempty;
for (unsigned int j = len; j; --j)
out = ATinsert(out, es[j - 1]);
return (ATerm) out;
}
return e;
}
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e)
{
Expr res = done[e];
if (substCache && res) {
substsCached++;
return res;
}
res = substitute2(done, subs, e);
done.set(e, res);
return res;
}
Expr substitute(const Substitution & subs, Expr e)
{
ATermMap done(256);
return substitute(done, subs, e);
}
Expr allocCells(Expr e)
{
checkInterrupt();
ATerm e2;
if (matchClosed(e, e2)) return e;
int i;
if (matchCell(e, i, e2))
return allocCell(allocCells(e2));
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATerm args[arity];
bool changed = false;
for (int i = 0; i < arity; ++i) {
ATerm arg = ATgetArgument(e, i);
args[i] = allocCells(arg);
if (args[i] != arg) changed = true;
}
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
}
if (ATgetType(e) == AT_LIST) {
unsigned int len = ATgetLength((ATermList) e);
ATerm es[len];
ATermIterator i((ATermList) e);
for (unsigned int j = 0; i; ++i, ++j)
es[j] = allocCells(*i);
ATermList out = ATempty;
for (unsigned int j = len; j; --j)
out = ATinsert(out, es[j - 1]);
return (ATerm) out;
}
return e;
}
/* We use memoisation to prevent exponential complexity on heavily
shared ATerms (remember, an ATerm is a graph, not a tree!). Note
that using an STL set is fine here wrt to ATerm garbage collection
since all the ATerms in the set are already reachable from
somewhere else. */
static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
{
if (done.find(e) != done.end()) return;
done.insert(e);
attrs->bindVars(env);
StaticEnv newEnv(true, &env);
body->bindVars(newEnv);
ATerm name, pos, value;
ATermList formals;
ATerm with, body;
ATermList rbnds, nrbnds;
if (matchVar(e, name)) {
if (!defs.get(name))
throw EvalError(format("undefined variable `%1%'")
% aterm2String(name));
}
else if (matchFunction(e, formals, body, pos)) {
ATermMap defs2(defs);
for (ATermIterator i(formals); i; ++i) {
ATerm d1, d2;
if (!matchFormal(*i, name, d1, d2)) abort();
defs2.set(name, (ATerm) ATempty);
}
for (ATermIterator i(formals); i; ++i) {
ATerm valids, deflt;
set<Expr> done2;
if (!matchFormal(*i, name, valids, deflt)) abort();
checkVarDefs2(done, defs, valids);
checkVarDefs2(done2, defs2, deflt);
}
set<Expr> done2;
checkVarDefs2(done2, defs2, body);
}
else if (matchFunction1(e, name, body, pos)) {
ATermMap defs2(defs);
defs2.set(name, (ATerm) ATempty);
set<Expr> done2;
checkVarDefs2(done2, defs2, body);
}
else if (matchRec(e, rbnds, nrbnds)) {
checkVarDefs2(done, defs, (ATerm) nrbnds);
ATermMap defs2(defs);
for (ATermIterator i(rbnds); i; ++i) {
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
defs2.set(name, (ATerm) ATempty);
}
for (ATermIterator i(nrbnds); i; ++i) {
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
defs2.set(name, (ATerm) ATempty);
}
set<Expr> done2;
checkVarDefs2(done2, defs2, (ATerm) rbnds);
}
else if (matchWith(e, with, body, pos)) {
/* We can't check the body without evaluating the definitions
(which is an arbitrary expression), so we don't do that
here but only when actually evaluating the `with'. */
checkVarDefs2(done, defs, with);
}
else if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)
checkVarDefs2(done, defs, ATgetArgument(e, i));
}
else if (ATgetType(e) == AT_LIST)
for (ATermIterator i((ATermList) e); i; ++i)
checkVarDefs2(done, defs, *i);
}
void ExprIf::bindVars(const StaticEnv & env)
void checkVarDefs(const ATermMap & defs, Expr e)
{
cond->bindVars(env);
then->bindVars(env);
else_->bindVars(env);
set<Expr> done;
checkVarDefs2(done, defs, e);
}
void ExprAssert::bindVars(const StaticEnv & env)
struct Canonicalise : TermFun
{
cond->bindVars(env);
body->bindVars(env);
}
ATerm operator () (ATerm e)
{
/* Remove position info. */
ATerm path;
int line, column;
if (matchPos(e, path, line, column))
return makeNoPos();
void ExprOpNot::bindVars(const StaticEnv & env)
/* Sort attribute sets. */
ATermList _;
if (matchAttrs(e, _)) {
ATermMap attrs;
queryAllAttrs(e, attrs);
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
ATermList attrs2 = ATempty;
for (StringSet::reverse_iterator i = names.rbegin(); i != names.rend(); ++i)
attrs2 = ATinsert(attrs2,
makeBind(toATerm(*i), attrs.get(toATerm(*i)), makeNoPos()));
return makeAttrs(attrs2);
}
return e;
}
};
Expr canonicaliseExpr(Expr e)
{
e->bindVars(env);
Canonicalise canonicalise;
return bottomupRewrite(canonicalise, e);
}
void ExprConcatStrings::bindVars(const StaticEnv & env)
Expr makeBool(bool b)
{
foreach (vector<Expr *>::iterator, i, *es)
(*i)->bindVars(env);
return b ? eTrue : eFalse;
}
bool matchStr(Expr e, string & s, PathSet & context)
{
ATermList l;
ATerm s_;
if (!matchStr(e, s_, l)) return false;
s = aterm2String(s_);
for (ATermIterator i(l); i; ++i)
context.insert(aterm2String(*i));
return true;
}
Expr makeStr(const string & s, const PathSet & context)
{
return makeStr(toATerm(s), toATermList(context));
}
string showType(Expr e)
{
ATerm t1, t2, t3;
ATermList l1;
ATermBlob b1;
int i1;
if (matchStr(e, t1, l1)) return "a string";
if (matchPath(e, t1)) return "a path";
if (matchNull(e)) return "null";
if (matchInt(e, i1)) return "an integer";
if (matchBool(e, t1)) return "a boolean";
if (matchFunction(e, l1, t1, t2)) return "a function";
if (matchFunction1(e, t1, t2, t3)) return "a function";
if (matchAttrs(e, l1)) return "an attribute set";
if (matchList(e, l1)) return "a list";
if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function";
return "an unknown type";
}
string showValue(Expr e)
{
PathSet context;
string s;
ATerm s2;
int i;
if (matchStr(e, s, context)) {
string u;
for (string::iterator i = s.begin(); i != s.end(); ++i)
if (*i == '\"' || *i == '\\') u += "\\" + *i;
else if (*i == '\n') u += "\\n";
else if (*i == '\r') u += "\\r";
else if (*i == '\t') u += "\\t";
else u += *i;
return "\"" + u + "\"";
}
if (matchPath(e, s2)) return aterm2String(s2);
if (matchNull(e)) return "null";
if (matchInt(e, i)) return (format("%1%") % i).str();
if (e == eTrue) return "true";
if (e == eFalse) return "false";
/* !!! incomplete */
return "<unknown>";
}
static unsigned int cellCount = 0;
Expr allocCell(Expr e)
{
if (cacheTerms != 2) return e;
int i;
Expr e2;
if (matchCell(e, i, e2)) return e;
return makeCell(cellCount++, e);
}
}

View File

@@ -3,268 +3,129 @@
#include <map>
#include "symbol-table.hh"
#include "aterm-map.hh"
#include "types.hh"
namespace nix {
MakeError(EvalError, Error)
MakeError(ParseError, Error)
MakeError(AssertionError, EvalError)
MakeError(ThrownError, AssertionError)
MakeError(Abort, EvalError)
MakeError(TypeError, EvalError)
/* Position objects. */
/* Nix expressions are represented as ATerms. The maximal sharing
property of the ATerm library allows us to implement caching of
normals forms efficiently. */
typedef ATerm Expr;
struct Pos
typedef ATerm DefaultValue;
typedef ATerm ValidValues;
typedef ATerm Pos;
/* A STL vector of ATerms. Should be used with great care since it's
stored on the heap, and the elements are therefore not roots to the
ATerm garbage collector. */
typedef vector<ATerm> ATermVector;
extern Expr constRemoved;
/* A substitution is a linked list of ATermMaps that map names to
identifiers. We use a list of ATermMaps rather than a single to
make it easy to grow or shrink a substitution when entering a
scope. */
struct Substitution
{
string file;
unsigned int line, column;
Pos() : line(0), column(0) { };
Pos(const string & file, unsigned int line, unsigned int column)
: file(file), line(line), column(column) { };
};
ATermMap * map;
const Substitution * prev;
extern Pos noPos;
std::ostream & operator << (std::ostream & str, const Pos & pos);
struct Env;
struct Value;
struct EvalState;
struct StaticEnv;
/* Abstract syntax of Nix expressions. */
struct Expr
{
virtual void show(std::ostream & str);
virtual void bindVars(const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v);
};
std::ostream & operator << (std::ostream & str, Expr & e);
#define COMMON_METHODS \
void show(std::ostream & str); \
void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const StaticEnv & env);
struct ExprInt : Expr
{
int n;
ExprInt(int n) : n(n) { };
COMMON_METHODS
};
struct ExprString : Expr
{
string s;
ExprString(const string & s) : s(s) { };
COMMON_METHODS
};
/* Temporary class used during parsing of indented strings. */
struct ExprIndStr : Expr
{
string s;
ExprIndStr(const string & s) : s(s) { };
};
struct ExprPath : Expr
{
string s;
ExprPath(const string & s) : s(s) { };
COMMON_METHODS
};
struct VarRef
{
Symbol name;
/* Whether the variable comes from an environment (e.g. a rec, let
or function argument) or from a "with". */
bool fromWith;
/* In the former case, the value is obtained by going `level'
levels up from the current environment and getting the
`displ'th value in that environment. In the latter case, the
value is obtained by getting the attribute named `name' from
the attribute set stored in the environment that is `level'
levels up from the current one.*/
unsigned int level;
unsigned int displ;
VarRef(const Symbol & name) : name(name) { };
void bind(const StaticEnv & env);
};
struct ExprVar : Expr
{
VarRef info;
ExprVar(const Symbol & name) : info(name) { };
COMMON_METHODS
};
struct ExprSelect : Expr
{
Expr * e;
Symbol name;
ExprSelect(Expr * e, const Symbol & name) : e(e), name(name) { };
COMMON_METHODS
};
struct ExprOpHasAttr : Expr
{
Expr * e;
Symbol name;
ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
COMMON_METHODS
};
struct ExprAttrs : Expr
{
bool recursive;
typedef std::pair<Expr *, Pos> Attr;
typedef std::pair<VarRef, Pos> Inherited;
typedef std::map<Symbol, Attr> Attrs;
Attrs attrs;
list<Inherited> inherited;
std::map<Symbol, Pos> attrNames; // used during parsing
ExprAttrs() : recursive(false) { };
COMMON_METHODS
};
struct ExprList : Expr
{
std::vector<Expr *> elems;
ExprList() { };
COMMON_METHODS
};
struct Formal
{
Symbol name;
Expr * def;
Formal(const Symbol & name, Expr * def) : name(name), def(def) { };
};
struct Formals
{
typedef std::list<Formal> Formals_;
Formals_ formals;
std::set<Symbol> argNames; // used during parsing
bool ellipsis;
};
struct ExprLambda : Expr
{
Pos pos;
Symbol arg;
bool matchAttrs;
Formals * formals;
Expr * body;
ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body)
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
Substitution(const Substitution * prev, ATermMap * map)
{
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% arg % pos);
};
COMMON_METHODS
this->prev = prev;
this->map = map;
}
Expr lookup(Expr name) const
{
Expr x;
for (const Substitution * s(this); s; s = s->prev)
if ((x = s->map->get(name)))
return x == constRemoved ? 0 : x;
return 0;
}
};
struct ExprLet : Expr
/* Show a position. */
string showPos(ATerm pos);
/* Generic bottomup traversal over ATerms. The traversal first
recursively descends into subterms, and then applies the given term
function to the resulting term. */
struct TermFun
{
ExprAttrs * attrs;
Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
COMMON_METHODS
};
struct ExprWith : Expr
{
Pos pos;
Expr * attrs, * body;
unsigned int prevWith;
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
COMMON_METHODS
};
struct ExprIf : Expr
{
Expr * cond, * then, * else_;
ExprIf(Expr * cond, Expr * then, Expr * else_) : cond(cond), then(then), else_(else_) { };
COMMON_METHODS
};
struct ExprAssert : Expr
{
Pos pos;
Expr * cond, * body;
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
COMMON_METHODS
};
struct ExprOpNot : Expr
{
Expr * e;
ExprOpNot(Expr * e) : e(e) { };
COMMON_METHODS
};
#define MakeBinOp(name, s) \
struct Expr##name : Expr \
{ \
Expr * e1, * e2; \
Expr##name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
void show(std::ostream & str) \
{ \
str << *e1 << " " s " " << *e2; \
} \
void bindVars(const StaticEnv & env) \
{ \
e1->bindVars(env); e2->bindVars(env); \
} \
void eval(EvalState & state, Env & env, Value & v); \
};
MakeBinOp(App, "")
MakeBinOp(OpEq, "==")
MakeBinOp(OpNEq, "!=")
MakeBinOp(OpAnd, "&&")
MakeBinOp(OpOr, "||")
MakeBinOp(OpImpl, "->")
MakeBinOp(OpUpdate, "//")
MakeBinOp(OpConcatLists, "++")
struct ExprConcatStrings : Expr
{
vector<Expr *> * es;
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
COMMON_METHODS
virtual ~TermFun() { }
virtual ATerm operator () (ATerm e) = 0;
};
ATerm bottomupRewrite(TermFun & f, ATerm e);
/* Static environments are used to map variable names onto (level,
displacement) pairs used to obtain the value of the variable at
runtime. */
struct StaticEnv
{
bool isWith;
const StaticEnv * up;
typedef std::map<Symbol, unsigned int> Vars;
Vars vars;
StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
};
/* Query all attributes in an attribute set expression. The
expression must be in normal form. */
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false);
/* Query a specific attribute from an attribute set expression. The
expression must be in normal form. */
Expr queryAttr(Expr e, const string & name);
Expr queryAttr(Expr e, const string & name, ATerm & pos);
/* Create an attribute set expression from an Attrs value. */
Expr makeAttrs(const ATermMap & attrs);
/* Perform a set of substitutions on an expression. */
Expr substitute(const Substitution & subs, Expr e);
/* Check whether all variables are defined in the given expression.
Throw an exception if this isn't the case. */
void checkVarDefs(const ATermMap & def, Expr e);
/* Canonicalise a Nix expression by sorting attributes and removing
location information. */
Expr canonicaliseExpr(Expr e);
/* Create an expression representing a boolean. */
Expr makeBool(bool b);
/* Manipulation of Str() nodes. Note: matchStr() does not clear
context! */
bool matchStr(Expr e, string & s, PathSet & context);
Expr makeStr(const string & s, const PathSet & context = PathSet());
/* Showing types, values. */
string showType(Expr e);
string showValue(Expr e);
Expr allocCell(Expr e); // make an updateable cell (for simulating conventional laziness)
Expr allocCells(Expr e); // re-allocate all cells in e
}

View File

@@ -8,11 +8,12 @@ namespace nix {
/* Parse a Nix expression from the specified file. If `path' refers
to a directory, then "/default.nix" is appended. */
Expr * parseExprFromFile(EvalState & state, Path path);
to a directory, the "/default.nix" is appended. */
Expr parseExprFromFile(EvalState & state, Path path);
/* Parse a Nix expression from the specified string. */
Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath);
Expr parseExprFromString(EvalState & state, const string & s,
const Path & basePath);
}

View File

@@ -20,13 +20,14 @@
#include <stdlib.h>
#include <string.h>
#include "aterm.hh"
#include "util.hh"
#include "nixexpr.hh"
#include "parser-tab.hh"
#include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
#include "nixexpr.hh"
#include "nixexpr-ast.hh"
using namespace nix;
@@ -37,181 +38,48 @@ namespace nix {
struct ParseData
{
SymbolTable & symbols;
Expr * result;
Expr result;
Path basePath;
Path path;
string error;
Symbol sLetBody;
ParseData(SymbolTable & symbols)
: symbols(symbols)
, sLetBody(symbols.create("<let-body>"))
{ };
};
static string showAttrPath(const vector<Symbol> & attrPath)
static Expr fixAttrs(int recursive, ATermList as)
{
string s;
foreach (vector<Symbol>::const_iterator, i, attrPath) {
if (!s.empty()) s += '.';
s += *i;
}
return s;
}
static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
{
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)
{
vector<Symbol> attrPath; attrPath.push_back(attr);
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
}
static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
Expr * e, const Pos & pos)
{
unsigned int n = 0;
foreach (vector<Symbol>::const_iterator, i, attrPath) {
n++;
ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i);
if (j != attrs->attrs.end()) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.first);
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second);
attrs = attrs2;
} else {
if (attrs->attrNames.find(*i) != attrs->attrNames.end())
dupAttr(attrPath, pos, attrs->attrNames[*i]);
attrs->attrNames[*i] = pos;
if (n == attrPath.size())
attrs->attrs[*i] = ExprAttrs::Attr(e, pos);
else {
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[*i] = ExprAttrs::Attr(nested, pos);
attrs = nested;
ATermList bs = ATempty, cs = ATempty;
ATermList * is = recursive ? &cs : &bs;
for (ATermIterator i(as); i; ++i) {
ATermList names;
Expr src;
ATerm pos;
if (matchInherit(*i, src, names, pos)) {
bool fromScope = matchScope(src);
for (ATermIterator j(names); j; ++j) {
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
*is = ATinsert(*is, makeBind(*j, allocCell(rhs), pos));
}
}
} else bs = ATinsert(bs, *i);
}
}
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%")
% formal.name % pos);
formals->formals.push_front(formal);
formals->argNames.insert(formal.name);
}
static Expr * stripIndentation(vector<Expr *> & es)
{
if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
bool atStartOfLine = true; /* = seen only whitespace in the current line */
unsigned int minIndent = 1000000;
unsigned int curIndent = 0;
foreach (vector<Expr *>::iterator, i, es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (e->s[j] == ' ')
curIndent++;
else if (e->s[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
} else {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
} else if (e->s[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
}
}
/* Strip spaces from each line. */
vector<Expr *> * es2 = new vector<Expr *>;
atStartOfLine = true;
unsigned int curDropped = 0;
unsigned int n = es.size();
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
if (!e) {
atStartOfLine = false;
curDropped = 0;
es2->push_back(*i);
continue;
}
string s2;
for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (e->s[j] == ' ') {
if (curDropped++ >= minIndent)
s2 += e->s[j];
}
else if (e->s[j] == '\n') {
curDropped = 0;
s2 += e->s[j];
} else {
atStartOfLine = false;
curDropped = 0;
s2 += e->s[j];
}
} else {
s2 += e->s[j];
if (e->s[j] == '\n') atStartOfLine = true;
}
}
/* Remove the last line if it is empty and consists only of
spaces. */
if (n == 1) {
string::size_type p = s2.find_last_of('\n');
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}
es2->push_back(new ExprString(s2));
}
return new ExprConcatStrings(es2);
if (recursive)
return makeRec(bs, cs);
else
return makeAttrs(bs);
}
void backToString(yyscan_t scanner);
void backToIndString(yyscan_t scanner);
static Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
extern bool posInfo;
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
{
return Pos(data->path, loc.first_line, loc.first_column);
return posInfo ? makePos(toATerm(data->path),
loc->first_line, loc->first_column) : makeNoPos();
}
#define CUR_POS makeCurPos(*yylocp, data)
#define CUR_POS makeCurPos(yylocp, data)
}
@@ -219,47 +87,46 @@ static Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{
data->error = (format("%1%, at %2%")
% error % makeCurPos(*loc, data)).str();
data->error = (format("%1%, at `%2%':%3%:%4%")
% error % data->path % loc->first_line % loc->first_column).str();
}
/* Make sure that the parse stack is scanned by the ATerm garbage
collector. */
static void * mallocAndProtect(size_t size)
{
void * p = malloc(size);
if (p) ATprotectMemory(p, size);
return p;
}
static void freeAndUnprotect(void * p)
{
ATunprotectMemory(p);
free(p);
}
#define YYMALLOC mallocAndProtect
#define YYFREE freeAndUnprotect
#endif
%}
%union {
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
nix::Formals * formals;
nix::Formal * formal;
int n;
char * id; // !!! -> Symbol
char * path;
char * uri;
std::vector<nix::Symbol> * ids;
std::vector<nix::Expr *> * string_parts;
ATerm t;
ATermList ts;
}
%type <e> start expr expr_function expr_if expr_op
%type <e> expr_app expr_select expr_simple
%type <list> expr_list
%type <attrs> binds
%type <formals> formals
%type <formal> formal
%type <ids> ids attrpath
%type <string_parts> string_parts ind_string_parts
%token <id> ID ATTRPATH
%token <e> STR IND_STR
%token <n> INT
%token <path> PATH
%token <uri> URI
%type <t> start expr expr_function expr_if expr_op
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
%type <ts> binds ids expr_list formals string_parts
%token <t> ID INT STR PATH URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
%token DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE
%token ELLIPSIS
%nonassoc IMPL
%left OR
@@ -279,172 +146,197 @@ start: expr { data->result = $1; };
expr: expr_function;
expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
| '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
| '{' formals '}' '@' ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
| ID '@' '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
: '{' formals '}' ':' expr_function
{ $$ = makeFunction($2, $5, CUR_POS); }
| ID ':' expr_function
{ $$ = makeFunction1($1, $3, CUR_POS); }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
{ $$ = makeAssert($2, $4, CUR_POS); }
| WITH expr ';' expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); }
{ $$ = makeWith($2, $4, CUR_POS); }
| LET binds IN expr_function
{ $$ = new ExprLet($2, $4); }
{ $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); }
| expr_if
;
expr_if
: IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); }
: IF expr THEN expr ELSE expr
{ $$ = makeIf($2, $4, $6); }
| expr_op
;
expr_op
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
| expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); }
| expr_op '+' expr_op
{ vector<Expr *> * l = new vector<Expr *>;
l->push_back($1);
l->push_back($3);
$$ = new ExprConcatStrings(l);
}
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
: '!' expr_op %prec NEG { $$ = makeOpNot($2); }
| expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
| expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
| expr_op OR expr_op { $$ = makeOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
| expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
| expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
| expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
| expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
| expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
| expr_app
;
expr_app
: expr_app expr_select
{ $$ = new ExprApp($1, $2); }
{ $$ = makeCall($1, $2); }
| expr_select { $$ = $1; }
;
expr_select
: expr_select '.' ID
{ $$ = new ExprSelect($1, data->symbols.create($3)); }
{ $$ = makeSelect($1, $3); }
| expr_simple { $$ = $1; }
;
expr_simple
: ID { $$ = new ExprVar(data->symbols.create($1)); }
| INT { $$ = new ExprInt($1); }
: ID { $$ = makeVar($1); }
| INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
| '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. */
if ($2->empty()) $$ = new ExprString("");
else if ($2->size() == 1) $$ = $2->front();
else $$ = new ExprConcatStrings($2);
if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
else $$ = makeConcatStrings(ATreverse($2));
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(*$2);
}
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| URI { $$ = new ExprString($1); }
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
| URI { $$ = makeStr($1, ATempty); }
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
| LET '{' binds '}'
{ $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); }
{ $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); }
| REC '{' binds '}'
{ $3->recursive = true; $$ = $3; }
{ $$ = fixAttrs(1, $3); }
| '{' binds '}'
{ $$ = $2; }
| '[' expr_list ']' { $$ = $2; }
{ $$ = fixAttrs(0, $2); }
| '[' expr_list ']' { $$ = makeList($2); }
;
string_parts
: string_parts STR { $$ = $1; $1->push_back($2); }
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
;
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
: string_parts STR { $$ = ATinsert($1, $2); }
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
| { $$ = ATempty; }
;
binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
| binds INHERIT ids ';'
{ $$ = $1;
foreach (vector<Symbol>::iterator, i, *$3) {
if ($$->attrNames.find(*i) != $$->attrNames.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]);
Pos pos = makeCurPos(@3, data);
$$->inherited.push_back(ExprAttrs::Inherited(*i, pos));
$$->attrNames[*i] = pos;
}
}
| binds INHERIT '(' expr ')' ids ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
foreach (vector<Symbol>::iterator, i, *$6) {
if ($$->attrNames.find(*i) != $$->attrNames.end())
dupAttr(*i, makeCurPos(@6, data), $$->attrNames[*i]);
$$->attrs[*i] = ExprAttrs::Attr(new ExprSelect($4, *i), makeCurPos(@6, data));
$$->attrNames[*i] = makeCurPos(@6, data);
}}
| { $$ = new ExprAttrs; }
: binds bind { $$ = ATinsert($1, $2); }
| { $$ = ATempty; }
;
ids
: ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
| { $$ = new vector<Symbol>; }
bind
: ID '=' expr ';'
{ $$ = makeBind($1, allocCell($3), CUR_POS); }
| INHERIT inheritsrc ids ';'
{ $$ = makeInherit($2, $3, CUR_POS); }
;
attrpath
: attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
| ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
inheritsrc
: '(' expr ')' { $$ = $2; }
| { $$ = makeScope(); }
;
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
expr_list
: expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
| { $$ = new ExprList; }
: expr_select expr_list { $$ = ATinsert($2, $1); }
/* yes, this is right-recursive, but it doesn't matter since
otherwise we would need ATreverse which requires unbounded
stack space */
| { $$ = ATempty; }
;
formals
: formal ',' formals
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
| formal
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
{ $$ = new Formals; $$->ellipsis = false; }
| ELLIPSIS
{ $$ = new Formals; $$->ellipsis = true; }
: formal ',' formals { $$ = ATinsert($3, $1); } /* idem - right recursive */
| formal { $$ = ATinsert(ATempty, $1); }
| { $$ = ATempty; }
;
formal
: ID { $$ = new Formal(data->symbols.create($1), 0); }
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
: ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); }
| ID ':' '[' expr_list ']' { $$ = makeFormal($1, makeValidValues($4), makeNoDefaultValue()); }
| ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); }
;
%%
#include "eval.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <eval.hh>
namespace nix {
static Expr * parse(EvalState & state, const char * text,
const Path & path, const Path & basePath)
static void checkAttrs(ATermMap & names, ATermList bnds)
{
for (ATermIterator i(bnds); i; ++i) {
ATerm name;
Expr e;
ATerm pos;
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
if (names.get(name))
throw EvalError(format("duplicate attribute `%1%' at %2%")
% aterm2String(name) % showPos(pos));
names.set(name, name);
}
}
static void checkAttrSets(ATerm e)
{
ATermList formals;
ATerm body, pos;
if (matchFunction(e, formals, body, pos)) {
ATermMap names(ATgetLength(formals));
for (ATermIterator i(formals); i; ++i) {
ATerm name;
ATerm d1, d2;
if (!matchFormal(*i, name, d1, d2)) abort();
if (names.get(name))
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
names.set(name, name);
}
}
ATermList bnds;
if (matchAttrs(e, bnds)) {
ATermMap names(ATgetLength(bnds));
checkAttrs(names, bnds);
}
ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds)) {
ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds));
checkAttrs(names, rbnds);
checkAttrs(names, nrbnds);
}
if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)
checkAttrSets(ATgetArgument(e, i));
}
else if (ATgetType(e) == AT_LIST)
for (ATermIterator i((ATermList) e); i; ++i)
checkAttrSets(*i);
}
static Expr parse(EvalState & state,
const char * text, const Path & path,
const Path & basePath)
{
yyscan_t scanner;
ParseData data(state.symbols);
ParseData data;
data.basePath = basePath;
data.path = path;
@@ -453,22 +345,30 @@ static Expr * parse(EvalState & state, const char * text,
int res = yyparse(scanner, &data);
yylex_destroy(scanner);
if (res) throw ParseError(data.error);
if (res) throw EvalError(data.error);
try {
data.result->bindVars(state.staticBaseEnv);
checkVarDefs(state.primOps, data.result);
} catch (Error & e) {
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
throw EvalError(format("%1%, in `%2%'") % e.msg() % path);
}
checkAttrSets(data.result);
return data.result;
}
Expr * parseExprFromFile(EvalState & state, Path path)
Expr parseExprFromFile(EvalState & state, Path path)
{
assert(path[0] == '/');
#if 0
/* Perhaps this is already an imploded parse tree? */
Expr e = ATreadFromNamedFile(path.c_str());
if (e) return e;
#endif
/* If `path' is a symlink, follow it. This is so that relative
path references work. */
struct stat st;
@@ -485,12 +385,17 @@ Expr * parseExprFromFile(EvalState & state, Path path)
if (S_ISDIR(st.st_mode))
path = canonPath(path + "/default.nix");
Expr cached = state.parsings.get(toATerm(path));
if (cached) return cached;
/* Read and parse the input file. */
return parse(state, readFile(path).c_str(), path, dirOf(path));
cached = parse(state, readFile(path).c_str(), path, dirOf(path));
state.parsings.set(toATerm(path), cached);
return cached;
}
Expr * parseExprFromString(EvalState & state,
Expr parseExprFromString(EvalState & state,
const string & s, const Path & basePath)
{
return parse(state, s.c_str(), "(string)", basePath);

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +0,0 @@
#ifndef __SYMBOL_TABLE_H
#define __SYMBOL_TABLE_H
#include <map>
#include <tr1/unordered_set>
#include "types.hh"
namespace nix {
/* Symbol table used by the parser and evaluator to represent and look
up identifiers and attribute sets efficiently.
SymbolTable::create() converts a string into a symbol. Symbols
have the property that they can be compared efficiently (using a
pointer equality test), because the symbol table stores only one
copy of each string. */
class Symbol
{
private:
const string * s; // pointer into SymbolTable
Symbol(const string * s) : s(s) { };
friend class SymbolTable;
public:
bool operator == (const Symbol & s2) const
{
return s == s2.s;
}
bool operator != (const Symbol & s2) const
{
return s != s2.s;
}
bool operator < (const Symbol & s2) const
{
return s < s2.s;
}
operator const string & () const
{
return *s;
}
bool empty() const
{
return s->empty();
}
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
};
inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
{
str << *sym.s;
return str;
}
class SymbolTable
{
private:
typedef std::tr1::unordered_set<string> Symbols;
Symbols symbols;
public:
Symbol create(const string & s)
{
std::pair<Symbols::iterator, bool> res = symbols.insert(s);
return Symbol(&*res.first);
}
unsigned int size() const
{
return symbols.size();
}
};
}
#endif /* !__SYMBOL_TABLE_H */

View File

@@ -1,161 +0,0 @@
#include "value-to-xml.hh"
#include "xml-writer.hh"
#include "util.hh"
#include <cstdlib>
namespace nix {
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
attrs[name] = value;
return attrs;
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
{
xmlAttrs["path"] = pos.file;
xmlAttrs["line"] = (format("%1%") % pos.line).str();
xmlAttrs["column"] = (format("%1%") % pos.column).str();
}
static void showAttrs(EvalState & state, bool strict, bool location,
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
StringSet names;
foreach (Bindings::iterator, i, attrs)
names.insert(i->first);
foreach (StringSet::iterator, i, names) {
Attr & a(attrs[state.symbols.create(*i)]);
XMLAttrs xmlAttrs;
xmlAttrs["name"] = *i;
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location,
a.value, doc, context, drvsSeen);
}
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
checkInterrupt();
if (strict) state.forceValue(v);
switch (v.type) {
case tInt:
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
break;
case tBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
break;
case tString:
/* !!! show the context? */
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
break;
case tPath:
doc.writeEmptyElement("path", singletonAttrs("value", v.path));
break;
case tNull:
doc.writeEmptyElement("null");
break;
case tAttrs:
if (state.isDerivation(v)) {
XMLAttrs xmlAttrs;
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
Path drvPath;
a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value);
if (a->second.value.type == tString)
xmlAttrs["drvPath"] = drvPath = a->second.value.string.s;
}
a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value);
if (a->second.value.type == tString)
xmlAttrs["outPath"] = a->second.value.string.s;
}
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) {
drvsSeen.insert(drvPath);
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
} else
doc.writeEmptyElement("repeated");
}
else {
XMLOpenElement _(doc, "attrs");
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
}
break;
case tList: {
XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.list.length; ++n)
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
break;
}
case tLambda: {
XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->matchAttrs) {
XMLAttrs attrs;
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
break;
}
default:
doc.writeEmptyElement("unevaluated");
}
}
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
PathSet drvsSeen;
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
}
}

View File

@@ -1,17 +0,0 @@
#ifndef __VALUE_TO_XML_H
#define __VALUE_TO_XML_H
#include <string>
#include <map>
#include "nixexpr.hh"
#include "eval.hh"
namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context);
}
#endif /* !__VALUE_TO_XML_H */

View File

@@ -1,10 +1,6 @@
pkglib_LTLIBRARIES = libmain.la
libmain_la_SOURCES = shared.cc
libmain_la_LIBADD = ../libstore/libstore.la
pkginclude_HEADERS = shared.hh
libmain_la_SOURCES = shared.cc shared.hh
AM_CXXFLAGS = \
-DNIX_STORE_DIR=\"$(storedir)\" \
@@ -15,5 +11,5 @@ AM_CXXFLAGS = \
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DNIX_VERSION=\"$(VERSION)\" \
-I$(srcdir)/.. -I$(srcdir)/../libutil \
-I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil \
-I$(srcdir)/../libstore

View File

@@ -4,7 +4,6 @@
#include "globals.hh"
#include "store-api.hh"
#include "util.hh"
#include "misc.hh"
#include <iostream>
#include <cctype>
@@ -13,6 +12,8 @@
#include <sys/stat.h>
#include <unistd.h>
#include <aterm2.h>
namespace nix {
@@ -48,34 +49,6 @@ void printGCWarning()
}
void printMissing(const PathSet & paths)
{
unsigned long long downloadSize;
PathSet willBuild, willSubstitute, unknown;
queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize);
if (!willBuild.empty()) {
printMsg(lvlInfo, format("the following derivations will be built:"));
foreach (PathSet::iterator, i, willBuild)
printMsg(lvlInfo, format(" %1%") % *i);
}
if (!willSubstitute.empty()) {
printMsg(lvlInfo, format("the following paths will be downloaded/copied (%.2f MiB):") %
(downloadSize / (1024.0 * 1024.0)));
foreach (PathSet::iterator, i, willSubstitute)
printMsg(lvlInfo, format(" %1%") % *i);
}
if (!unknown.empty()) {
printMsg(lvlInfo, format("don't know how to build the following paths%1%:")
% (readOnlyMode ? " (may be caused by read-only store access)" : ""));
foreach (PathSet::iterator, i, unknown)
printMsg(lvlInfo, format(" %1%") % *i);
}
}
static void setLogType(string lt)
{
if (lt == "pretty") logType = ltPretty;
@@ -85,6 +58,30 @@ static void setLogType(string lt)
}
static unsigned int getIntArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
++i;
if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
int n;
if (!string2Int(*i, n) || n < 0)
throw UsageError(format("`%1%' requires a non-negative integer") % opt);
return n;
}
struct RemoveTempRoots
{
~RemoveTempRoots()
{
removeTempRoots();
}
};
void initDerivationsHelpers();
static void closeStore()
{
try {
@@ -102,15 +99,6 @@ static void closeStore()
}
RemoveTempRoots::~RemoveTempRoots()
{
removeTempRoots();
}
static bool showTrace = false;
/* Initialize and reorder arguments, then call the actual argument
processor. */
static void initAndRun(int argc, char * * argv)
@@ -126,10 +114,9 @@ static void initAndRun(int argc, char * * argv)
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
} else
if (subs == "default")
substituters.push_back(nixLibexecDir + "/nix/download-using-manifests.pl");
else
substituters = tokenizeString(subs, ":");
/* Get some settings from the configuration file. */
@@ -138,29 +125,23 @@ static void initAndRun(int argc, char * * argv)
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
/* Catch SIGINT. */
struct sigaction act;
struct sigaction act, oact;
act.sa_handler = sigintHandler;
sigfillset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGINT, &act, 0))
if (sigaction(SIGINT, &act, &oact))
throw SysError("installing handler for SIGINT");
if (sigaction(SIGTERM, &act, 0))
if (sigaction(SIGTERM, &act, &oact))
throw SysError("installing handler for SIGTERM");
if (sigaction(SIGHUP, &act, 0))
if (sigaction(SIGHUP, &act, &oact))
throw SysError("installing handler for SIGHUP");
/* Ignore SIGPIPE. */
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
if (sigaction(SIGPIPE, &act, 0))
if (sigaction(SIGPIPE, &act, &oact))
throw SysError("ignoring SIGPIPE");
/* Reset SIGCHLD to its default. */
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(SIGCHLD, &act, 0))
throw SysError("resetting SIGCHLD");
/* There is no privacy in the Nix system ;-) At least not for
now. In particular, store objects should be readable by
everybody. This prevents nasty surprises when using a shared
@@ -171,6 +152,9 @@ static void initAndRun(int argc, char * * argv)
string lt = getEnv("NIX_LOG_TYPE");
if (lt != "") setLogType(lt);
/* ATerm stuff. !!! find a better place to put this */
initDerivationsHelpers();
/* Put the arguments in a vector. */
Strings args, remaining;
while (argc--) args.push_back(*argv++);
@@ -181,7 +165,7 @@ static void initAndRun(int argc, char * * argv)
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
string arg = *i;
if (string(arg, 0, 4) == "-at-") ;
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && !isdigit(arg[1])) {
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
for (unsigned int j = 1; j < arg.length(); j++)
if (isalpha(arg[j]))
remaining.push_back((string) "-" + arg[j]);
@@ -208,8 +192,6 @@ static void initAndRun(int argc, char * * argv)
; /* !!! obsolete - remove eventually */
else if (arg == "--no-build-output" || arg == "-Q")
buildVerbosity = lvlVomit;
else if (arg == "--print-build-trace")
printBuildTrace = true;
else if (arg == "--help") {
printHelp();
return;
@@ -225,28 +207,17 @@ static void initAndRun(int argc, char * * argv)
else if (arg == "--fallback")
tryFallback = true;
else if (arg == "--max-jobs" || arg == "-j")
maxBuildJobs = getIntArg<unsigned int>(arg, i, args.end());
maxBuildJobs = getIntArg(arg, i, args.end());
else if (arg == "--readonly-mode")
readOnlyMode = true;
else if (arg == "--max-silent-time")
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
else if (arg == "--no-build-hook")
useBuildHook = false;
else if (arg == "--show-trace")
showTrace = true;
else if (arg == "--option") {
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string name = *i;
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string value = *i;
overrideSetting(name, tokenizeString(value));
}
maxSilentTime = getIntArg(arg, i, args.end());
else remaining.push_back(arg);
}
/* Automatically clean up the temporary roots file when we
exit. */
RemoveTempRoots removeTempRoots __attribute__((unused));
RemoveTempRoots removeTempRoots; /* unused variable - don't remove */
/* Make sure that the database gets closed properly, even if
terminate() is called (which happens sometimes due to bugs in
@@ -325,6 +296,10 @@ int main(int argc, char * * argv)
if (argc == 0) abort();
setuidInit();
/* ATerm setup. */
ATerm bottomOfStack;
ATinit(argc, argv, &bottomOfStack);
/* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
@@ -352,9 +327,7 @@ int main(int argc, char * * argv)
% e.what() % programId);
return 1;
} catch (BaseError & e) {
printMsg(lvlError, format("error: %1%%2%") % (showTrace ? e.prefix() : "") % e.msg());
if (e.prefix() != "" && !showTrace)
printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
printMsg(lvlError, format("error: %1%") % e.msg());
return 1;
} catch (std::exception & e) {
printMsg(lvlError, format("error: %1%") % e.what());

View File

@@ -22,34 +22,16 @@ extern std::string programId;
namespace nix {
MakeError(UsageError, nix::Error);
/* Ugh. No better place to put this. */
Path makeRootName(const Path & gcRoot, int & counter);
void printGCWarning();
void printMissing(const PathSet & paths);
template<class N> N getIntArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
++i;
if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
N n;
if (!string2Int(*i, n))
throw UsageError(format("`%1%' requires an integer argument") % opt);
return n;
}
/* Whether we're running setuid. */
extern bool setuidMode;
extern volatile ::sig_atomic_t blockInt;
struct RemoveTempRoots
{
~RemoveTempRoots();
};
MakeError(UsageError, nix::Error);
}

View File

@@ -1,16 +1,22 @@
pkglib_LTLIBRARIES = libstore.la
libstore_la_SOURCES = \
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
globals.cc references.cc pathlocks.cc gc.cc \
optimise-store.cc
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
globals.cc db.cc references.cc pathlocks.cc gc.cc
pkginclude_HEADERS = \
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
globals.hh references.hh pathlocks.hh \
worker-protocol.hh
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
globals.hh db.hh references.hh pathlocks.hh \
worker-protocol.hh
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la
BUILT_SOURCES = derivations-ast.cc derivations-ast.hh
EXTRA_DIST = derivations-ast.def derivations-ast.cc
AM_CXXFLAGS = -Wall \
-I$(srcdir)/.. -I$(srcdir)/../libutil
-I$(srcdir)/.. ${bdb_include} ${aterm_include} -I$(srcdir)/../libutil
derivations-ast.cc derivations-ast.hh: ../aterm-helper.pl derivations-ast.def
$(perl) $(srcdir)/../aterm-helper.pl derivations-ast.hh derivations-ast.cc < $(srcdir)/derivations-ast.def

File diff suppressed because it is too large Load Diff

468
src/libstore/db.cc Normal file
View File

@@ -0,0 +1,468 @@
#include "db.hh"
#include "util.hh"
#include "pathlocks.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <memory>
#include <db_cxx.h>
namespace nix {
/* Wrapper class to ensure proper destruction. */
class DestroyDbc
{
Dbc * dbc;
public:
DestroyDbc(Dbc * _dbc) : dbc(_dbc) { }
~DestroyDbc() { dbc->close(); /* close() frees dbc */ }
};
class DestroyDbEnv
{
DbEnv * dbenv;
public:
DestroyDbEnv(DbEnv * _dbenv) : dbenv(_dbenv) { }
~DestroyDbEnv() {
if (dbenv) {
if (dbenv->get_DB_ENV()) dbenv->close(0);
delete dbenv;
}
}
void release() { dbenv = 0; };
};
static void rethrow(DbException & e)
{
throw Error(e.what());
}
Transaction::Transaction()
: txn(0)
{
}
Transaction::Transaction(Database & db)
: txn(0)
{
begin(db);
}
Transaction::~Transaction()
{
if (txn) abort();
}
void Transaction::begin(Database & db)
{
assert(txn == 0);
db.requireEnv();
try {
db.env->txn_begin(0, &txn, 0);
} catch (DbException e) { rethrow(e); }
}
void Transaction::commit()
{
if (!txn) throw Error("commit called on null transaction");
debug(format("committing transaction %1%") % (void *) txn);
DbTxn * txn2 = txn;
txn = 0;
try {
txn2->commit(0);
} catch (DbException e) { rethrow(e); }
}
void Transaction::abort()
{
if (!txn) throw Error("abort called on null transaction");
debug(format("aborting transaction %1%") % (void *) txn);
DbTxn * txn2 = txn;
txn = 0;
try {
txn2->abort();
} catch (DbException e) { rethrow(e); }
}
void Transaction::moveTo(Transaction & t)
{
if (t.txn) throw Error("target txn already exists");
t.txn = txn;
txn = 0;
}
void Database::requireEnv()
{
checkInterrupt();
if (!env) throw Error("database environment is not open "
"(maybe you don't have sufficient permission?)");
}
Db * Database::getDb(TableId table)
{
if (table == 0)
throw Error("database table is not open "
"(maybe you don't have sufficient permission?)");
std::map<TableId, Db *>::iterator i = tables.find(table);
if (i == tables.end())
throw Error("unknown table id");
return i->second;
}
Database::Database()
: env(0)
, nextId(1)
{
}
Database::~Database()
{
close();
}
void openEnv(DbEnv * & env, const string & path, u_int32_t flags)
{
try {
createDirs(path);
} catch (SysError & e) {
if (e.errNo == EPERM || e.errNo == EACCES)
throw DbNoPermission(format("cannot create the Nix database in `%1%'") % path);
else
throw;
}
try {
env->open(path.c_str(),
DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
DB_CREATE | flags,
0666);
} catch (DbException & e) {
printMsg(lvlError, format("environment open failed: %1%") % e.what());
throw;
}
}
static int my_fsync(int fd)
{
return 0;
}
static void errorPrinter(const DbEnv * env, const char * errpfx, const char * msg)
{
printMsg(lvlError, format("Berkeley DB error: %1%") % msg);
}
static void messagePrinter(const DbEnv * env, const char * msg)
{
printMsg(lvlError, format("Berkeley DB message: %1%") % msg);
}
void Database::open2(const string & path, bool removeOldEnv)
{
if (env) throw Error(format("environment already open"));
debug(format("opening database environment"));
/* Create the database environment object. */
DbEnv * env = new DbEnv(0);
DestroyDbEnv deleteEnv(env);
env->set_errcall(errorPrinter);
env->set_msgcall(messagePrinter);
if (getEnv("NIX_DEBUG_DB_REGISTER") == "1")
env->set_verbose(DB_VERB_REGISTER, 1);
env->set_verbose(DB_VERB_RECOVERY, 1);
/* Smaller log files. */
env->set_lg_bsize(32 * 1024); /* default */
env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
/* Write the log, but don't sync. This protects transactions
against application crashes, but if the system crashes, some
transactions may be undone. An acceptable risk, I think. */
env->set_flags(DB_TXN_WRITE_NOSYNC | DB_LOG_AUTOREMOVE, 1);
/* Increase the locking limits. If you ever get `Dbc::get: Cannot
allocate memory' or similar, especially while running
`nix-store --verify', just increase the following number, then
run db_recover on the database to remove the existing DB
environment (since changes only take effect on new
environments). */
env->set_lk_max_locks(10000);
env->set_lk_max_lockers(10000);
env->set_lk_max_objects(10000);
env->set_lk_detect(DB_LOCK_DEFAULT);
/* Dangerous, probably, but from the docs it *seems* that BDB
shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it still
fsync()s sometimes. */
db_env_set_func_fsync(my_fsync);
if (removeOldEnv) {
printMsg(lvlError, "removing old Berkeley DB database environment...");
env->remove(path.c_str(), DB_FORCE);
return;
}
openEnv(env, path, DB_REGISTER | DB_RECOVER);
deleteEnv.release();
this->env = env;
}
void Database::open(const string & path)
{
try {
open2(path, false);
} catch (DbException e) {
if (e.get_errno() == DB_VERSION_MISMATCH) {
/* Remove the environment while we are holding the global
lock. If things go wrong there, we bail out.
!!! argh, we abolished the global lock :-( */
open2(path, true);
/* Try again. */
open2(path, false);
/* Force a checkpoint, as per the BDB docs. */
env->txn_checkpoint(DB_FORCE, 0, 0);
printMsg(lvlError, "database succesfully upgraded to new version");
}
#if 0
else if (e.get_errno() == DB_RUNRECOVERY) {
/* If recovery is needed, do it. */
printMsg(lvlError, "running recovery...");
open2(path, false, true);
}
#endif
else
rethrow(e);
}
}
void Database::close()
{
if (!env) return;
/* Close the database environment. */
debug(format("closing database environment"));
try {
for (std::map<TableId, Db *>::iterator i = tables.begin();
i != tables.end(); )
{
std::map<TableId, Db *>::iterator j = i;
++j;
closeTable(i->first);
i = j;
}
/* Do a checkpoint every 128 kilobytes, or every 5 minutes. */
env->txn_checkpoint(128, 5, 0);
env->close(0);
} catch (DbException e) { rethrow(e); }
delete env;
env = 0;
}
TableId Database::openTable(const string & tableName, bool sorted)
{
requireEnv();
TableId table = nextId++;
try {
Db * db = new Db(env, 0);
try {
db->open(0, tableName.c_str(), 0,
sorted ? DB_BTREE : DB_HASH,
DB_CREATE | DB_AUTO_COMMIT, 0666);
} catch (...) {
delete db;
throw;
}
tables[table] = db;
} catch (DbException e) { rethrow(e); }
return table;
}
void Database::closeTable(TableId table)
{
try {
Db * db = getDb(table);
db->close(DB_NOSYNC);
delete db;
tables.erase(table);
} catch (DbException e) { rethrow(e); }
}
void Database::deleteTable(const string & table)
{
try {
env->dbremove(0, table.c_str(), 0, DB_AUTO_COMMIT);
} catch (DbException e) { rethrow(e); }
}
bool Database::queryString(const Transaction & txn, TableId table,
const string & key, string & data)
{
checkInterrupt();
try {
Db * db = getDb(table);
Dbt kt((void *) key.c_str(), key.length());
Dbt dt;
int err = db->get(txn.txn, &kt, &dt, 0);
if (err) return false;
if (!dt.get_data())
data = "";
else
data = string((char *) dt.get_data(), dt.get_size());
} catch (DbException e) { rethrow(e); }
return true;
}
bool Database::queryStrings(const Transaction & txn, TableId table,
const string & key, Strings & data)
{
string d;
if (!queryString(txn, table, key, d))
return false;
data = unpackStrings(d);
return true;
}
void Database::setString(const Transaction & txn, TableId table,
const string & key, const string & data)
{
checkInterrupt();
try {
Db * db = getDb(table);
Dbt kt((void *) key.c_str(), key.length());
Dbt dt((void *) data.c_str(), data.length());
db->put(txn.txn, &kt, &dt, 0);
} catch (DbException e) { rethrow(e); }
}
void Database::setStrings(const Transaction & txn, TableId table,
const string & key, const Strings & data, bool deleteEmpty)
{
if (deleteEmpty && data.size() == 0)
delPair(txn, table, key);
else
setString(txn, table, key, packStrings(data));
}
void Database::delPair(const Transaction & txn, TableId table,
const string & key)
{
checkInterrupt();
try {
Db * db = getDb(table);
Dbt kt((void *) key.c_str(), key.length());
db->del(txn.txn, &kt, 0);
/* Non-existence of a pair with the given key is not an
error. */
} catch (DbException e) { rethrow(e); }
}
void Database::enumTable(const Transaction & txn, TableId table,
Strings & keys, const string & keyPrefix)
{
try {
Db * db = getDb(table);
Dbc * dbc;
db->cursor(txn.txn, &dbc, 0);
DestroyDbc destroyDbc(dbc);
Dbt kt, dt;
u_int32_t flags = DB_NEXT;
if (!keyPrefix.empty()) {
flags = DB_SET_RANGE;
kt = Dbt((void *) keyPrefix.c_str(), keyPrefix.size());
}
while (dbc->get(&kt, &dt, flags) != DB_NOTFOUND) {
checkInterrupt();
string data((char *) kt.get_data(), kt.get_size());
if (!keyPrefix.empty() &&
string(data, 0, keyPrefix.size()) != keyPrefix)
break;
keys.push_back(data);
flags = DB_NEXT;
}
} catch (DbException e) { rethrow(e); }
}
void Database::clearTable(const Transaction & txn, TableId table)
{
try {
Db * db = getDb(table);
u_int32_t count;
db->truncate(txn.txn, &count, 0);
} catch (DbException e) { rethrow(e); }
}
}

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