diff --git a/configure.ac b/configure.ac index 1842b1e15..7decbc7d9 100644 --- a/configure.ac +++ b/configure.ac @@ -99,9 +99,19 @@ static char buf[1024];]], AC_LANG_POP(C++) +# Check for chroot support (requires chroot() and bind mounts). +AC_CHECK_FUNCS([chroot]) +AC_CHECK_HEADERS([sys/param.h], [], [], []) +AC_CHECK_HEADERS([sys/mount.h], [], [], +[#ifdef HAVE_SYS_PARAM_H +# include +# endif +]) + + # Check for AC_LANG_PUSH(C++) -AC_CHECK_HEADERS([locale]) +AC_CHECK_HEADERS([locale], [], [], []) AC_LANG_POP(C++) @@ -114,7 +124,7 @@ fi ]) NEED_PROG(curl, curl) -NEED_PROG(shell, sh) +NEED_PROG(shell, bash) NEED_PROG(patch, patch) AC_PATH_PROG(xmllint, xmllint, false) AC_PATH_PROG(xsltproc, xsltproc, false) diff --git a/doc/manual/Makefile.am b/doc/manual/Makefile.am index b9a6b6184..ac030ee56 100644 --- a/doc/manual/Makefile.am +++ b/doc/manual/Makefile.am @@ -20,7 +20,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \ FIGURES = figures/user-environments.png MANUAL_SRCS = manual.xml introduction.xml installation.xml \ - package-management.xml writing-nix-expressions.xml \ + package-management.xml writing-nix-expressions.xml builtins.xml \ build-farm.xml \ $(man1_MANS:.1=.xml) \ troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \ diff --git a/doc/manual/builtins.xml b/doc/manual/builtins.xml new file mode 100644 index 000000000..1ce40a607 --- /dev/null +++ b/doc/manual/builtins.xml @@ -0,0 +1,751 @@ +
+ +Built-in functions + + +This section lists the functions and constants built into the +Nix expression evaluator. (The built-in function +derivation is discussed above.) Some built-ins, +such as derivation, 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 builtins +built-in value, which is an attribute set that contains all built-in +functions and values. For instance, derivation +is also available as builtins.derivation. + + + + + + abort s + + Abort Nix expression evaluation, print error + message s. + + + + + builtins.add + e1 e2 + + Return the sum of the integers + e1 and + e2. + + + + + builtins.attrNames + attrs + + Return the names of the attributes in the + attribute set attrs in a sorted list. + For instance, builtins.attrNames {y = 1; x = + "foo";} evaluates to ["x" "y"]. + There is no built-in function attrValues, but + you can easily define it yourself: + + +attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); + + + + + + + baseNameOf s + + Return the base name of the + string s, that is, everything following + the final slash in the string. This is similar to the GNU + basename command. + + + + + builtins + + The attribute set builtins + contains all the built-in functions and values. You can use + builtins to test for the availability of + features in the Nix installation, e.g., + + +if builtins ? getEnv then builtins.getEnv "PATH" else "" + + This allows a Nix expression to fall back gracefully on older Nix + installations that don’t have the desired built-in function. + However, in that case you should not write + + +if builtins ? getEnv then __getEnv "PATH" else "" + + This Nix expression will trigger an “undefined variable” error on + older Nix versions since __getEnv doesn’t + exist. builtins.getEnv, on the other hand, is + safe since builtins always exists and attribute + selection is lazy, so it’s only performed if the test + succeeds. + + + + + builtins.currentSystem + + The built-in value currentSystem + evaluates to the Nix platform identifier for the Nix installation + on which the expression is being evaluated, such as + "i686-linux" or + "powerpc-darwin". + + + + + + + + + + + derivation + attrs + + derivation is described in + . + + + + + dirOf s + + Return the directory part of the string + s, that is, everything before the final + slash in the string. This is similar to the GNU + dirname command. + + + + + builtins.filterSource + e1 e2 + + + + 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 source-dir as + an input to a Nix expression, e.g. + + +stdenv.mkDerivation { + ... + src = ./source-dir; +} + + + However, if source-dir is a Subversion + working copy, then all those annoying .svn + subdirectories will also be copied to the store. Worse, the + contents of those directories may change a lot, causing lots of + spurious rebuilds. With filterSource you + can filter out the .svn directories: + + + src = builtins.filterSource + (path: type: type != "directory" || baseNameOf path != ".svn") + ./source-dir; + + + + + Thus, the first argument e1 + must be a predicate function that is called for each regular + file, directory or symlink in the source tree + e2. If the function returns + true, 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 "regular", + "directory", "symlink" or + "unknown" (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 + true for them, the copy will fail). + + + + + + + builtins.getAttr + s attrs + + getAttr returns the attribute + named s from the attribute set + attrs. Evaluation aborts if the + attribute doesn’t exist. This is a dynamic version of the + . operator, since s + is an expression rather than an identifier. + + + + + builtins.getEnv + s + + getEnv returns the value of + the environment variable s, or an empty + string if the variable doesn’t exist. This function should be + used with care, as it can introduce all sorts of nasty environment + dependencies in your Nix expression. + + getEnv is used in Nix Packages to + locate the file ~/.nixpkgs/config.nix, which + contains user-local settings for Nix Packages. (That is, it does + a getEnv "HOME" to locate the user’s home + directory.) + + + + + builtins.hasAttr + s attrs + + hasAttr returns + true if the attribute set + attrs has an attribute named + s, and false + otherwise. This is a dynamic version of the ? + operator, since s is an expression + rather than an identifier. + + + + + builtins.head + list + + Return the first element of a list; abort + evaluation if the argument isn’t a list or is an empty list. You + can test whether a list is empty by comparing it with + []. + + + + + import + path + + Load, parse and return the Nix expression in the + file path. Evaluation aborts if the + file doesn’t exist or contains an incorrect Nix + expression. import implements Nix’s 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. + + A Nix expression loaded by import must + not contain any free variables (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 + + +rec { + x = 123; + y = import ./foo.nix; +} + + then the following foo.nix will give an + error: + + +x + 456 + + since x is not in scope in + foo.nix. If you want x + to be available in foo.nix, you should pass + it as a function argument: + + +rec { + x = 123; + y = import ./foo.nix x; +} + + and + + +x: x + 456 + + (The function argument doesn’t have to be called + x in foo.nix; any name + would work.) + + + + + builtins.isAttrs + e + + Return true if + e evaluates to an attribute set, and + false otherwise. + + + + + builtins.isList + e + + Return true if + e evaluates to a list, and + false otherwise. + + + + + builtins.isFunction + e + + Return true if + e evaluates to a function, and + false otherwise. + + + + + isNull + e + + Return true if + e evaluates to null, + and false otherwise. + + This function is deprecated; + just write e == null instead. + + + + + + + builtins.lessThan + e1 e2 + + Return true if the integer + e1 is less than the integer + e2, and false + otherwise. Evaluation aborts if either + e1 or e2 + does not evaluate to an integer. + + + + + builtins.listToAttrs + e + + 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 + name specifying the name of the attribute, and + an attribute value specifying its value. + Example: + + +builtins.listToAttrs [ + {name = "foo"; value = 123;} + {name = "bar"; value = 456;} +] + + + evaluates to + + +{ foo = 123; bar = 456; } + + + + + + + map + f list + + Apply the function f to + each element in the list list. For + example, + + +map (x: "foo" + x) ["bar" "bla" "abc"] + + evaluates to ["foobar" "foobla" + "fooabc"]. + + + + + builtins.pathExists + path + + Return true if the path + path exists, and + false otherwise. One application of this + function is to conditionally include a Nix expression containing + user configuration: + + +let + fileName = builtins.getEnv "CONFIG_FILE"; + config = + if fileName != "" && builtins.pathExists (builtins.toPath fileName) + then import (builtins.toPath fileName) + else { someSetting = false; }; # default configuration +in config.someSetting + + (Note that CONFIG_FILE must be an absolute path for + this to work.) + + + + + + + + removeAttrs + attrs list + + Remove the attributes listed in + list from the attribute set + attrs. The attributes don’t have to + exist in attrs. For instance, + + +removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"] + + evaluates to {y = 2;}. + + + + + builtins.stringLength + e + + Return the length of the string + e. If e is + not a string, evaluation is aborted. + + + + + builtins.sub + e1 e2 + + Return the difference between the integers + e1 and + e2. + + + + + builtins.substr + start len + s + + Return the substring of + s from character position + start (zero-based) up to but not + including start + len. If + start is greater than the length of the + string, an empty string is returned, and if start + + len lies beyond the end of the string, only the + substring up to the end of the string is returned. + start must be + non-negative. + + + + + builtins.tail + list + + Return the second to last elements of a list; + abort evaluation if the argument isn’t a list or is an empty + list. + + + + + throw + s + + Throw an error message + s. This usually aborts Nix expression + evaluation, but in nix-env -qa 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 + abort). + + + + + builtins.toFile + name s + + Store the string s in a + file in the Nix store and return its path. The file has suffix + name. This file can be used as an + input to derivations. One application is to write builders + “inline”. For instance, the following Nix expression combines + and into one file: + + +{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; +} + + + + It is even possible for one file to refer to another, e.g., + + + builder = let + configFile = builtins.toFile "foo.conf" " + # This is some dummy configuration file. + ... + "; + in builtins.toFile "builder.sh" " + source $stdenv/setup + ... + cp ${configFile} $out/etc/foo.conf + "; + + Note that ${configFile} is an antiquotation + (see ), so the result of the + expression configFile (i.e., a path like + /nix/store/m7p7jfny445k...-foo.conf) will be + spliced into the resulting string. + + It is however not allowed to have files + mutually referring to each other, like so: + + +let + foo = builtins.toFile "foo" "...${bar}..."; + bar = builtins.toFile "bar" "...${foo}..."; +in foo + + This is not allowed because it would cause a cyclic dependency in + the computation of the cryptographic hashes for + foo and bar. + + + + + builtins.toPath s + + Convert the string value + s into a path value. The string + s must represent an absolute path + (i.e., must start with /). The path need not + exist. The resulting path is canonicalised, e.g., + builtins.toPath "//foo/xyzzy/../bar/" returns + /foo/bar. + + + + + toString e + + Convert the expression + e to a string. + e can be a string (in which case + toString is a no-op) or a path (e.g., + toString /foo/bar yields + "/foo/bar". + + + + + builtins.toXML e + + Return a string containing an XML representation + of e. The main application for + toXML is to communicate information with the + builder in a more structured format than plain environment + variables. + + + + shows an example where this is + the case. The builder is supposed to generate the configuration + file for a Jetty + servlet container. A servlet container contains a number + of servlets (*.war files) each exported under + a specific URI prefix. So the servlet configuration is a list of + attribute sets containing the path and + war of the servlet (). 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 wouldn’t work if fields are optional or + contain lists themselves). Instead the Nix expression is + converted to an XML representation with + toXML, which is unambiguous and can easily be + processed with the appropriate tools. For instance, in the + example an XSLT stylesheet () is applied to it () to + generate the XML configuration file for the Jetty server. The XML + representation produced from by toXML is shown in . + + Note that uses the toFile 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 + xsltproc ${stylesheet} + .... + + Passing information to a builder + using <function>toXML</function> + + $out/server-conf.xml]]> + + + + + + + + + + + + + "; + + servlets = builtins.toXML []]> + + + + XML representation produced by + <function>toXML</function> + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + builtins.trace + e1 e2 + + Evaluate e1 and print its + abstract syntax representation on standard error. Then return + e2. This function is useful for + debugging. + + + + + + + +
diff --git a/doc/manual/conf-file.xml b/doc/manual/conf-file.xml index 79faa05fd..acf0eb4f5 100644 --- a/doc/manual/conf-file.xml +++ b/doc/manual/conf-file.xml @@ -118,6 +118,123 @@ env-keep-derivations = false + build-max-silent-time + + + + 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 command + line switch. + + The value 0 means that there is no + timeout. This is also the default. + + + + + + + build-users-group + + 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. + + 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 + /etc/group). Those user accounts should not + be used for any other purpose! + + 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.) + + The build users should have permission to create files in + the Nix store, but not delete them. Therefore, + /nix/store should be owned by the Nix + account, its group should be the group specified here, and its + mode should be 1775. + + 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 NIX_REMOTE is empty, the uid under which the Nix + daemon runs if NIX_REMOTE is + daemon, or the uid that owns the setuid + nix-worker program if NIX_REMOTE + is slave). Obviously, this should not be used + in multi-user settings with untrusted users. + + + + + + + build-use-chroot + + 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. + + 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. + + + + + + + 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 + /proc. Files in /dev (such as + /dev/null) are needed by many builds, and + some files in /proc may also be needed + occasionally. + + The value used on NixOS is + + +build-use-chroot = /dev /proc /bin + + to make the /bin/sh symlink available (which + is still needed by many builders). + + + + + + system This option specifies the canonical Nix system diff --git a/doc/manual/introduction.xml b/doc/manual/introduction.xml index 8080c80d7..7f7cd11d5 100644 --- a/doc/manual/introduction.xml +++ b/doc/manual/introduction.xml @@ -1,135 +1,304 @@ + xmlns:xlink="http://www.w3.org/1999/xlink" + xml:id="chap-introduction"> Introduction -Nix is a system for the deployment of software. Software -deployment is concerned with the creation, distribution, and -management of software components (packages). Its main -features are: - +
About Nix -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 -your machine if you have the dependency -installed, but not on the end user's machine if it's not -there. +Nix is a purely functional package manager. +This means that it treats packages like values in purely functional +programming languages such as Haskell — they are built by functions +that don’t have side-effects, and they never change after they have +been built. Nix stores packages in the Nix +store, usually the directory +/nix/store, where each package has its own unique +subdirectory such as -It is possible to have multiple versions or -variants 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). + +/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/ + -Users can have different views -(profiles 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. +where r8vvq9kq… is a unique identifier for the +package that captures all its dependencies (it’s a cryptographic hash +of the package’s build dependency graph). This enables many powerful +features. -It is possible to atomically -upgrade 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). -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. +Multiple versions -Unused components can be -garbage-collected 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. +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 don’t interfere with each other. -Nix supports both source-based deployment models -(where you distribute Nix expressions 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. +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. -Nix is flexible in the deployment policies that it -supports. There is a clear separation between the tools that -implement basic Nix mechanisms (e.g., building -Nix expressions), and the tools that implement various deployment -policies. For instance, there is a concept of -Nix channels 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. + -Nix component builds aim to be pure; -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 guarantee 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. -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 exactly 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 -works on one machine also works on another, when -deployed using Nix. +Complete dependencies -The Nix expression language makes it easy to describe -variability in components (e.g., optional features or -dependencies). +Nix helps you make sure that package dependency specifications +are complete. In general, when you’re 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 +component will build and work correctly on your +machine if you have the dependency installed, but not on the end +user's machine if it's not there. -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. +Since Nix on the other hand doesn’t install packages in “global” +locations like /usr/bin but in package-specific +directories, the risk of incomplete dependencies is greatly reduced. +This is because tools such as compilers don’t search in per-packages +directories such as +/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include, +so if a package builds correctly on your system, this is because you +specified the dependency explicitly. -Nix can be used not only for software deployment, but -also for service deployment, 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. +Runtime dependencies are found by scanning binaries for the hash +parts of Nix store paths (such as r8vvq9kq…). This +sounds risky, but it works extremely well. -Nix can efficiently upgrade between different versions -of a component through binary patching. 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. + - - +Multi-user support + +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 profile, a set of +packages in the Nix store that appear in the user’s +PATH. If a user installs a package that another user +has already installed previously, the package won’t 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. + + + + + + +Atomic upgrades and rollbacks + +Since package management operations never overwrite packages in +the Nix store but just add new versions in different paths, they are +atomic. 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 it’s started during that period. + +And since package aren’t overwritten, the old versions are still +there after an upgrade. This means that you can roll +back to the old version: + + +$ nix-env --upgrade some-packages +$ nix-env --rollback + + + + + +Garbage collection + +When you install a package like this… + + +$ nix-env --uninstall firefox + + +the package isn’t 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 +garbage collector: + + +$ nix-collect-garbage + + +This deletes all packages that aren’t in use by any user profile or by +a currently running program. + + + + +Functional package language + +Packages are built from Nix expressions, +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 deterministic: building a Nix +expression twice should yield the same result. + +Because it’s a functional language, it’s 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 don’t conflict with +each other in the Nix store. + + + + +Transparent source/binary deployment + +Nix expressions generally describe how to build a package from +source, so an installation action like + + +$ nix-env --install firefox + + +could 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 source deployment +model. 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. Nix channels provide Nix +expressions along with pre-built binaries. + + + + + + +Binary patching + +In addition to downloading binaries automatically if they’re +available, Nix can download binary deltas that patch an existing +package in the Nix store into a new version. This speeds up +upgrades. + + + + +Nix Packages collection + +We provide a large set of Nix expressions containing hundreds of +existing Unix packages, the Nix Packages +collection (Nixpkgs). + + + + +Service deployment + +Nix can be used not only for rolling out packages, but also +complete configurations 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. + + + + + + +Portability + +Nix should run on most Unix systems, including Linux, FreeBSD and +Mac OS X. It is also supported on Windows using Cygwin. + + + + +NixOS + +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 +/etc). This means, among other things, that it’s +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 NixOS homepage. + + + + + + +
+ + +
About us + +Nix was developed at the Department of Information and +Computing Sciences, Utrecht University by the TraCE +project. The project is funded by the Software Engineering +Research Program Jacquard to improve the +support for variability in software systems. + +
+ + +
About this manual 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, and doing service deployment using -Nix. +up a Nix-based build farm. -Some background information on Nix can be found in a -number of papers. The ICSE 2004 paper + + +
License + +Nix is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General +Public License as published by the Free Software Foundation; +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. + +
+ + +
More information + +Some background information on Nix can be found in a number of +papers. The ICSE 2004 paper Imposing a Memory Management Discipline on Software Deployment discusses the hashing mechanism used to ensure reliable dependency @@ -141,10 +310,27 @@ gives a more general discussion of Nix from a system-administration perspective. The CBSE 2005 paper Efficient Upgrading in a Purely Functional Component Deployment Model - is about transparent patch deployment in Nix. Finally, -the SCM-12 paper is about transparent patch deployment in Nix. The SCM-12 +paper Service Configuration Management shows how services (e.g., -web servers) can be deployed and managed through Nix. +web servers) can be deployed and managed through Nix. A short +overview of NixOS is given in the HotOS XI paper Purely +Functional System Configuration Management. The Nix +homepage has an up-to-date list +of Nix-related papers. + +Nix is the subject of Eelco Dolstra’s PhD thesis The +Purely Functional Software Deployment Model, which +contains most of the papers listed above. + +Nix has a homepage at . + +
+ diff --git a/doc/manual/opt-common-syn.xml b/doc/manual/opt-common-syn.xml index 9aaabb8e9..eadc45e3a 100644 --- a/doc/manual/opt-common-syn.xml +++ b/doc/manual/opt-common-syn.xml @@ -13,6 +13,10 @@ number + + + number + diff --git a/doc/manual/opt-common.xml b/doc/manual/opt-common.xml index 1d09fef95..7dfb7b8f8 100644 --- a/doc/manual/opt-common.xml +++ b/doc/manual/opt-common.xml @@ -103,6 +103,17 @@ + + + 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 build-max-silent-time + configuration setting. 0 means no + time-out. + + + diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index b19d9a382..46c540b87 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -92,7 +92,8 @@ 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., don’t need to be built from - source.
+ source. TODO: flag is also available in nix-env -i / + -u. TODO: new built-ins @@ -116,6 +117,9 @@ limited form of caching. This is used by nix-channel to prevent unnecessary downloads when the channel hasn’t changed. + + + TODO: chroot support. diff --git a/doc/manual/style.css b/doc/manual/style.css index e992a9a85..57d95c755 100644 --- a/doc/manual/style.css +++ b/doc/manual/style.css @@ -46,6 +46,11 @@ h3 /* subsections */ font-size: 125%; } +div.simplesect h2 +{ + font-size: 110%; +} + div.appendix h3 { font-size: 150%; diff --git a/doc/manual/writing-nix-expressions.xml b/doc/manual/writing-nix-expressions.xml index ce6c1f4ad..c4bc35cc7 100644 --- a/doc/manual/writing-nix-expressions.xml +++ b/doc/manual/writing-nix-expressions.xml @@ -1,6 +1,7 @@ + xml:id='chap-writing-nix-expressions' + xmlns:xi="http://www.w3.org/2001/XInclude"> Writing Nix Expressions @@ -1339,589 +1340,8 @@ command-line argument. See -
Built-in functions -This section lists the functions and constants built into the -Nix expression evaluator. (The built-in function -derivation is discussed above.) Some built-ins, -such as derivation, 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 builtins -built-in value, which is an attribute set that contains all built-in -functions and values. For instance, derivation -is also available as builtins.derivation. - - - - - - abort s - - Abort Nix expression evaluation, print error - message s. - - - - - builtins.add - e1 e2 - - Return the sum of the integers - e1 and - e2. - - - - - builtins.attrNames - attrs - - Return the names of the attributes in the - attribute set attrs in a sorted list. - For instance, builtins.attrNames {y = 1; x = - "foo";} evaluates to ["x" "y"]. - There is no built-in function attrValues, but - you can easily define it yourself: - - -attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); - - - - - - - baseNameOf s - - Return the base name of the - string s, that is, everything following - the final slash in the string. This is similar to the GNU - basename command. - - - - - builtins - - The attribute set builtins - contains all the built-in functions and values. You can use - builtins to test for the availability of - features in the Nix installation, e.g., - - -if builtins ? getEnv then builtins.getEnv "PATH" else "" - - This allows a Nix expression to fall back gracefully on older Nix - installations that don’t have the desired built-in function. - However, in that case you should not write - - -if builtins ? getEnv then __getEnv "PATH" else "" - - This Nix expression will trigger an “undefined variable” error on - older Nix versions since __getEnv doesn’t - exist. builtins.getEnv, on the other hand, is - safe since builtins always exists and attribute - selection is lazy, so it’s only performed if the test - succeeds. - - - - - builtins.currentSystem - - The built-in value currentSystem - evaluates to the Nix platform identifier for the Nix installation - on which the expression is being evaluated, such as - "i686-linux" or - "powerpc-darwin". - - - - - - - - - - - derivation - attrs - - derivation is described in - . - - - - - dirOf s - - Return the directory part of the string - s, that is, everything before the final - slash in the string. This is similar to the GNU - dirname command. - - - - - builtins.getAttr - s attrs - - getAttr returns the attribute - named s from the attribute set - attrs. Evaluation aborts if the - attribute doesn’t exist. This is a dynamic version of the - . operator, since s - is an expression rather than an identifier. - - - - - builtins.getEnv - s - - getEnv returns the value of - the environment variable s, or an empty - string if the variable doesn’t exist. This function should be - used with care, as it can introduce all sorts of nasty environment - dependencies in your Nix expression. - - getEnv is used in Nix Packages to - locate the file ~/.nixpkgs/config.nix, which - contains user-local settings for Nix Packages. (That is, it does - a getEnv "HOME" to locate the user’s home - directory.) - - - - - builtins.hasAttr - s attrs - - hasAttr returns - true if the attribute set - attrs has an attribute named - s, and false - otherwise. This is a dynamic version of the ? - operator, since s is an expression - rather than an identifier. - - - - - builtins.head - list - - Return the first element of a list; abort - evaluation if the argument isn’t a list or is an empty list. You - can test whether a list is empty by comparing it with - []. - - - - - import - path - - Load, parse and return the Nix expression in the - file path. Evaluation aborts if the - file doesn’t exist or contains an incorrect Nix - expression. import implements Nix’s 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. - - A Nix expression loaded by import must - not contain any free variables (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 - - -rec { - x = 123; - y = import ./foo.nix; -} - - then the following foo.nix will give an - error: - - -x + 456 - - since x is not in scope in - foo.nix. If you want x - to be available in foo.nix, you should pass - it as a function argument: - - -rec { - x = 123; - y = import ./foo.nix x; -} - - and - - -x: x + 456 - - (The function argument doesn’t have to be called - x in foo.nix; any name - would work.) - - - - - builtins.isList - e - - Return true if - e evaluates to a list, and - false otherwise. - - - - - isNull - e - - Return true if - e evaluates to null, - and false otherwise. - - This function is deprecated; - just write e == null instead. - - - - - - - builtins.lessThan - e1 e2 - - Return true if the integer - e1 is less than the integer - e2, and false - otherwise. Evaluation aborts if either - e1 or e2 - does not evaluate to an integer. - - - - - map - f list - - Apply the function f to - each element in the list list. For - example, - - -map (x: "foo" + x) ["bar" "bla" "abc"] - - evaluates to ["foobar" "foobla" - "fooabc"]. - - - - - builtins.pathExists - path - - Return true if the path - path exists, and - false otherwise. One application of this - function is to conditionally include a Nix expression containing - user configuration: - - -let - fileName = builtins.getEnv "CONFIG_FILE"; - config = - if fileName != "" && builtins.pathExists (builtins.toPath fileName) - then import (builtins.toPath fileName) - else { someSetting = false; }; # default configuration -in config.someSetting - - (Note that CONFIG_FILE must be an absolute path for - this to work.) - - - - - - - - removeAttrs - attrs list - - Remove the attributes listed in - list from the attribute set - attrs. The attributes don’t have to - exist in attrs. For instance, - - -removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"] - - evaluates to {y = 2;}. - - - - - builtins.tail - list - - Return the second to last elements of a list; - abort evaluation if the argument isn’t a list or is an empty - list. - - - - - builtins.toFile - name s - - Store the string s in a - file in the Nix store and return its path. The file has suffix - name. This file can be used as an - input to derivations. One application is to write builders - “inline”. For instance, the following Nix expression combines - and into one file: - - -{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; -} - - - - It is even possible for one file to refer to another, e.g., - - - builder = let - configFile = builtins.toFile "foo.conf" " - # This is some dummy configuration file. - ... - "; - in builtins.toFile "builder.sh" " - source $stdenv/setup - ... - cp ${configFile} $out/etc/foo.conf - "; - - Note that ${configFile} is an antiquotation - (see ), so the result of the - expression configFile (i.e., a path like - /nix/store/m7p7jfny445k...-foo.conf) will be - spliced into the resulting string. - - It is however not allowed to have files - mutually referring to each other, like so: - - -let - foo = builtins.toFile "foo" "...${bar}..."; - bar = builtins.toFile "bar" "...${foo}..."; -in foo - - This is not allowed because it would cause a cyclic dependency in - the computation of the cryptographic hashes for - foo and bar. - - - - - builtins.toPath s - - Convert the string value - s into a path value. The string - s must represent an absolute path - (i.e., must start with /). The path need not - exist. The resulting path is canonicalised, e.g., - builtins.toPath "//foo/xyzzy/../bar/" returns - /foo/bar. - - - - - toString e - - Convert the expression - e to a string. - e can be a string (in which case - toString is a no-op) or a path (e.g., - toString /foo/bar yields - "/foo/bar". - - - - - builtins.toXML e - - Return a string containing an XML representation - of e. The main application for - toXML is to communicate information with the - builder in a more structured format than plain environment - variables. - - - - shows an example where this is - the case. The builder is supposed to generate the configuration - file for a Jetty - servlet container. A servlet container contains a number - of servlets (*.war files) each exported under - a specific URI prefix. So the servlet configuration is a list of - attribute sets containing the path and - war of the servlet (). 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 wouldn’t work if fields are optional or - contain lists themselves). Instead the Nix expression is - converted to an XML representation with - toXML, which is unambiguous and can easily be - processed with the appropriate tools. For instance, in the - example an XSLT stylesheet () is applied to it () to - generate the XML configuration file for the Jetty server. The XML - representation produced from by toXML is shown in . - - Note that uses the toFile 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 - xsltproc ${stylesheet} - .... - - Passing information to a builder - using <function>toXML</function> - - $out/server-conf.xml]]> - - - - - - - - - - - - - "; - - servlets = builtins.toXML []]> - - - - XML representation produced by - <function>toXML</function> - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - -
+
diff --git a/mergeTrunkBackIn.sh b/mergeTrunkBackIn.sh index 45b57d888..388653b80 100755 --- a/mergeTrunkBackIn.sh +++ b/mergeTrunkBackIn.sh @@ -1,4 +1,4 @@ -svn merge -r 9476:9506 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk +svn merge -r 9549:9561 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk #already done: # 8628 @@ -23,3 +23,6 @@ svn merge -r 9476:9506 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk # 9445 # 9476 # 9506 +# 9536 +# 9549 +# 9561 diff --git a/nix.conf.example b/nix.conf.example index be6a955a8..a97554b18 100644 --- a/nix.conf.example +++ b/nix.conf.example @@ -80,7 +80,7 @@ ### Option `build-max-silent-time' # -# This option defines the maximum number of seconds that builder can +# 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 @@ -135,6 +135,44 @@ #build-users-group = +### Option `build-use-chroot' +# +# 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. +# +# 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'. +# +# 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--) and +# the directories listed here. The default is "/dev /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 /proc + + ### Option `system' # # This option specifies the canonical Nix system name of the current diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b8608bf79..91680abcf 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -298,18 +298,19 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args) return makeStr(getEnv(name)); } -/* for debugging purposes. print the first arg on stdout (perhaps stderr should be used?) - * and return the second + +/* Evaluate the first expression, and print its abstract syntax tree + on standard error. Then return the second expression. Useful for + debugging. */ static Expr prim_trace(EvalState & state, const ATermVector & args) { - //string str = evalStringNoCtx(state, args[0]); - - Expr a = evalExpr(state, args[0]); - printf("traced value: %s\n", atPrint(a).c_str()); + Expr e = evalExpr(state, args[0]); + printMsg(lvlError, format("trace: %1%") % e); return evalExpr(state, args[1]); } + static Expr prim_relativise(EvalState & state, const ATermVector & args) { PathSet context; /* !!! what to do? */ diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 7e142add3..2c89465b7 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -27,6 +27,19 @@ #include +/* Includes required for chroot support. */ +#include "config.h" + +#if HAVE_SYS_PARAM_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif + +#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) + + namespace nix { using std::map; @@ -585,6 +598,89 @@ void deletePathWrapped(const Path & path) ////////////////////////////////////////////////////////////////////// +/* Helper RAII class for automatically unmounting bind-mounts in + chroots. */ +struct BindMount +{ + Path source, target; + Paths created; + + BindMount() + { + } + + BindMount(const Path & source, const Path & target) + { + bind(source, target); + } + + ~BindMount() + { + try { + unbind(); + } catch (...) { + ignoreException(); + } + } + + void bind(const Path & source, const Path & target) + { +#if CHROOT_ENABLED + debug(format("bind mounting `%1%' to `%2%'") % source % target); + + this->source = source; + this->target = target; + + created = createDirs(target); + + if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) + throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target); +#endif + } + + void unbind() + { +#if CHROOT_ENABLED + if (source == "") return; + + debug(format("unmount bind-mount `%1%'") % target); + + /* Urgh. Unmount sometimes doesn't succeed right away because + the mount point is still busy. It shouldn't be, because + we've killed all the build processes by now (at least when + using a build user; see the check in killUser()). But + maybe this is because those processes are still zombies and + are keeping some kernel structures busy (open files, + current directories, etc.). So retry a few times + (actually, a 1 second sleep is almost certainly enough for + the zombies to be cleaned up). */ + unsigned int tries = 0; + while (umount(target.c_str()) == -1) { + if (errno == EBUSY && ++tries < 10) { + printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target); + sleep(1); + } + else + throw SysError(format("unmounting bind-mount `%1%' failed") % target); + } + + /* Get rid of the directories for the mount point created in + bind(). */ + for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) { + debug(format("deleting `%1%'") % *i); + if (remove(i->c_str()) == -1) + throw SysError(format("cannot unlink `%1%'") % *i); + } + + source = ""; +#endif + } +}; + + +////////////////////////////////////////////////////////////////////// + + class DerivationGoal : public Goal { private: @@ -635,6 +731,17 @@ private: Pipe toHook; Pipe fromHook; + /* Whether we're currently doing a chroot build. */ + bool useChroot; + + /* A RAII object to delete the chroot directory. */ + boost::shared_ptr autoDelChroot; + + /* In chroot builds, the list of bind mounts currently active. + The destructor of BindMount will cause the binds to be + unmounted. */ + list > bindMounts; + typedef void (DerivationGoal::*GoalState)(); GoalState state; @@ -690,7 +797,7 @@ private: void openLogFile(); /* Common initialisation to be performed in child processes (i.e., - both in builders and in build hooks. */ + both in builders and in build hooks). */ void initChild(); /* Delete the temporary directory, if we have one. */ @@ -1038,6 +1145,9 @@ void DerivationGoal::buildDone() deleteTmpDir(true); + /* In chroot builds, unmount the bind mounts ASAP. */ + bindMounts.clear(); /* the destructors will do the rest */ + /* Compute the FS closure of the outputs and register them as being valid. */ computeClosure(); @@ -1208,7 +1318,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook() throw SysError(format("executing `%1%'") % buildHook); } catch (std::exception & e) { - std::cerr << format("build hook error: %1%\n") % e.what(); + std::cerr << format("build hook error: %1%") % e.what() << std::endl; } quickExit(1); } @@ -1596,6 +1706,56 @@ void DerivationGoal::startBuilder() % buildUser.getGID() % nixStore); } + + /* Are we doing a chroot build? */ + useChroot = queryBoolSetting("build-use-chroot", false); + Path tmpRootDir; + + if (useChroot) { +#if CHROOT_ENABLED + /* Create a temporary directory in which we set up the chroot + environment using bind-mounts. + + !!! Big danger here: since we're doing this in /tmp, there + is a risk that the admin does something like "rm -rf + /tmp/chroot-nix-*" to clean up aborted builds, and if some + of the bind-mounts are still active, then "rm -rf" will + happily recurse into those mount points (thereby deleting, + say, /nix/store). Ideally, tmpRootDir should be created in + some special location (maybe in /nix/var/nix) where Nix + takes care of unmounting / deleting old chroots + automatically. */ + tmpRootDir = createTempDir("", "chroot-nix"); + + /* Clean up the chroot directory automatically, but don't + recurse; that would be very very bad if the unmount of a + bind-mount fails. Instead BindMount::unbind() unmounts and + deletes exactly those directories that it created to + produce the mount point, so that after all the BindMount + destructors have run, tmpRootDir should be empty. */ + autoDelChroot = boost::shared_ptr(new AutoDelete(tmpRootDir, false)); + + printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % tmpRootDir); + + Paths defaultDirs; + defaultDirs.push_back("/dev"); + defaultDirs.push_back("/proc"); + Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs); + + dirsInChroot.push_front(nixStore); + dirsInChroot.push_front(tmpDir); + + /* Push BindMounts at the front of the list so that they get + unmounted in LIFO order. (!!! Does the C++ standard + guarantee that list elements are destroyed in order?) */ + for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i) + bindMounts.push_front(boost::shared_ptr(new BindMount(*i, tmpRootDir + *i))); + +#else + throw Error("chroot builds are not supported on this platform"); +#endif + } + /* Run the builder. */ printMsg(lvlChatty, format("executing builder `%1%'") % @@ -1621,6 +1781,17 @@ void DerivationGoal::startBuilder() try { /* child */ +#if CHROOT_ENABLED + /* If building in a chroot, do the chroot right away. + initChild() will do a chdir() to the temporary build + directory to make sure the current directory is in the + chroot. (Actually the order doesn't matter, since due + to the bind mount tmpDir and tmpRootDit/tmpDir are the + same directories.) */ + if (useChroot && chroot(tmpRootDir.c_str()) == -1) + throw SysError(format("cannot change root directory to `%1%'") % tmpRootDir); +#endif + initChild(); /* Fill in the environment. */ @@ -1684,7 +1855,7 @@ void DerivationGoal::startBuilder() % drv.builder); } catch (std::exception & e) { - std::cerr << format("build error: %1%\n") % e.what(); + std::cerr << format("build error: %1%") % e.what() << std::endl; } quickExit(1); } @@ -1916,13 +2087,6 @@ void DerivationGoal::computeClosure() state_stateReferences, drvPath, 0); - //Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO) - RevisionClosure rivisionMapping; - rivisionMapping[statePath] = commitStatePathTxn(txn, statePath); - - //Save the new revision - setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision."); - //Convert stateInfo from drv to DB format //And set all interval-ed paths to zero to begin with DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs; @@ -1953,6 +2117,13 @@ void DerivationGoal::computeClosure() //register state options that may change DerivationStateOutput drvso = drv.stateOutputs["state"]; setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs); + + //Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO) + RevisionClosure rivisionMapping; + rivisionMapping[statePath] = commitStatePathTxn(txn, statePath); + + //Save the new revision + setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision."); //Shared state Path sharedState = drv.stateOutputs.find("state")->second.sharedState; @@ -2332,7 +2503,7 @@ void SubstitutionGoal::tryToRun() throw SysError(format("executing `%1%'") % sub); } catch (std::exception & e) { - std::cerr << format("substitute error: %1%\n") % e.what(); + std::cerr << format("substitute error: %1%") % e.what() << std::endl; } quickExit(1); } diff --git a/src/libstore/store-state.cc b/src/libstore/store-state.cc index e0f09fa53..8b9fc9d7a 100644 --- a/src/libstore/store-state.cc +++ b/src/libstore/store-state.cc @@ -185,13 +185,15 @@ Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath) CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath); Snapshots revisions_list; - + for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){ string thisdir = (*i).path; string type = (*i).type; unsigned int interval = (*i).interval; + //printMsg(lvlError, format("maybe ssing %1% %2%") % thisdir % type); + if(type == "none"){ continue; } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 7f4bc8726..c56e4229b 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -319,19 +319,19 @@ void makePathReadOnly(const Path & path) } -static Path tempName(const Path & tmpRoot) +static Path tempName(const Path & tmpRoot, const Path & prefix) { static int counter = 0; Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true); - return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str(); + return (format("%1%/%2%-%3%-%4%") % tmpRoot2 % prefix % getpid() % counter++).str(); } -Path createTempDir(const Path & tmpRoot) +Path createTempDir(const Path & tmpRoot, const Path & prefix) { while (1) { checkInterrupt(); - Path tmpDir = tempName(tmpRoot); + Path tmpDir = tempName(tmpRoot, prefix); if (mkdir(tmpDir.c_str(), 0777) == 0) { /* Explicitly set the group of the directory. This is to work around around problems caused by BSD's group @@ -350,13 +350,16 @@ Path createTempDir(const Path & tmpRoot) } } -void createDirs(const Path & path) +Paths createDirs(const Path & path) { - if (path == "/") return; - createDirs(dirOf(path)); - if (!pathExists(path)) + if (path == "/") return Paths(); + Paths created = createDirs(dirOf(path)); + if (!pathExists(path)) { if (mkdir(path.c_str(), 0777) == -1) throw SysError(format("creating directory `%1%'") % path); + created.push_back(path); + } + return created; } @@ -511,14 +514,25 @@ string drainFD(int fd) ////////////////////////////////////////////////////////////////////// -AutoDelete::AutoDelete(const string & p) : path(p) +AutoDelete::AutoDelete(const string & p, bool recursive) : path(p) { del = true; + this->recursive = recursive; } AutoDelete::~AutoDelete() { - if (del) deletePath(path); + try { + if (del) + if (recursive) + deletePath(path); + else { + if (remove(path.c_str()) == -1) + throw SysError(format("cannot unlink `%1%'") % path); + } + } catch (...) { + ignoreException(); + } } void AutoDelete::cancel() @@ -754,10 +768,10 @@ void killUser(uid_t uid) if (errno != EINTR) throw SysError(format("cannot kill processes for uid `%1%'") % uid); } - + } catch (std::exception & e) { - std::cerr << format("killing processes beloging to uid `%1%': %1%\n") - % uid % e.what(); + std::cerr << format("killing processes beloging to uid `%1%': %1%") + % uid % e.what() << std::endl; quickExit(1); } quickExit(0); @@ -812,7 +826,7 @@ string runProgram(Path program, bool searchPath, const Strings & args) throw SysError(format("executing `%1%'") % program); } catch (std::exception & e) { - std::cerr << "error: " << e.what() << std::endl; + std::cerr << "error: " << e.what() << std::endl; //TODO does not give the full error message } quickExit(1); } @@ -1201,21 +1215,6 @@ bool IsSymlink(const string FileName) return (S_ISLNK(my_stat.st_mode) != 0); } -/* -string getCallingUserName() -{ - //Linux - Strings empty; - string username = runProgram("whoami", true, empty); //the username of the user that is trying to build the component - //TODO Can be faked, so this is clearly unsafe ... :( - //Remove the trailing \n - int pos = username.find("\n",0); - username.erase(pos,1); - - return username; -} -*/ - /* adds the second PathSet after the first, but removing doubles from the second (union) * (We assume the first PathSet has no duplicates) * UNTESTED !!!!!!!!!!!!!! @@ -1321,8 +1320,15 @@ void symlinkPath(const Path & existingDir, const Path & newLinkName) //TODO bool * We do -snf for: * -s : symlinking * -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!) - * -n : Treat destination that is a symlink to a directory as if it were a normal file (This makes sure - * that newLinkName is really overwritten) + * -n : Treat destination that is a symlink to a directory as if it were a normal file: + * When the destination is an actual directory (not a symlink to one), there is no ambiguity. + * The link is created in that directory. But when the specified destination is a symlink to a directory, + * there are two ways to treat the user's request. ln can treat the destination just as it would a normal + * directory and create the link in it. On the other hand, the destination can be viewed as a non-directory + * - as the symlink itself. In that case, ln must delete or backup that symlink before creating the new link. + * The default is to treat a destination that is a symlink to a directory just like a directory. + * + * ((This makes sure that newLinkName is really overwritten) */ diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 6524a6bbf..f6d0ec7d9 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -79,10 +79,11 @@ void deletePath(const Path & path, unsigned long long & bytesFreed); void makePathReadOnly(const Path & path); /* Create a temporary directory. */ -Path createTempDir(const Path & tmpRoot = ""); +Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix"); -/* Create a directory and all its parents, if necessary. */ -void createDirs(const Path & path); +/* Create a directory and all its parents, if necessary. Returns the + list of created directories, in order of creation. */ +Paths createDirs(const Path & path); /* Create a file and write the given text to it. The file is written in binary mode (i.e., no end-of-line conversions). The path should @@ -175,8 +176,9 @@ class AutoDelete { Path path; bool del; + bool recursive; public: - AutoDelete(const Path & p); + AutoDelete(const Path & p, bool recursive = true); ~AutoDelete(); void cancel(); }; diff --git a/src/nix-env/help.txt b/src/nix-env/help.txt index f2201c575..e0697be88 100644 --- a/src/nix-env/help.txt +++ b/src/nix-env/help.txt @@ -63,8 +63,6 @@ Query flags: --out-path: print path of derivation output --description: print description --meta: print all meta attributes (only with --xml) - --prebuilt-only: only show derivations whose prebuilt binaries are - available on this machine or are downloadable Options: @@ -74,3 +72,5 @@ Options: --keep-failed / -K: keep temporary directories of failed builds --preserve-installed: do not replace currently installed versions in `-i' --system-filter SYSTEM: only use derivations for specified platform + --prebuilt-only / -b: only use derivations whose prebuilt binaries are + available on this machine or are downloadable diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index a80856fb1..7f2e606ab 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -49,8 +49,9 @@ struct InstallSourceInfo Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ Path profile; /* for srcProfile */ string systemFilter; /* for srcNixExprDrvs */ + bool prebuiltOnly; ATermMap autoArgs; - InstallSourceInfo() : autoArgs() { }; + InstallSourceInfo() : prebuiltOnly(false) { }; }; @@ -96,6 +97,8 @@ static bool parseInstallSourceOptions(Globals & globals, } else if (arg == "--attr" || arg == "-A") globals.instSource.type = srcAttrPath; + else if (arg == "--prebuilt-only" || arg == "-b") + globals.instSource.prebuiltOnly = true; else return false; return true; } @@ -339,9 +342,16 @@ static int comparePriorities(EvalState & state, } -static DrvInfos filterBySelector(EvalState & state, - const DrvInfos & allElems, - const Strings & args, bool newestOnly) +static bool isPrebuilt(EvalState & state, const DrvInfo & elem) +{ + return + store->isValidPath(elem.queryOutPath(state)) || + store->hasSubstitutes(elem.queryOutPath(state)); +} + + +static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, + const Strings & args, bool newestOnly, bool prebuiltOnly) { DrvNames selectors = drvNamesFromArgs(args); @@ -360,7 +370,8 @@ static DrvInfos filterBySelector(EvalState & state, DrvName drvName(j->name); if (i->matches(drvName)) { i->hits++; - matches.push_back(std::pair(*j, n)); + if (!prebuiltOnly || isPrebuilt(state, *j)) + matches.push_back(std::pair(*j, n)); } } @@ -450,7 +461,8 @@ static void queryInstSources(EvalState & state, loadDerivations(state, instSource.nixExprPath, instSource.systemFilter, instSource.autoArgs, "", allElems); - elems = filterBySelector(state, allElems, args, newestOnly); + elems = filterBySelector(state, allElems, args, + newestOnly, instSource.prebuiltOnly); break; } @@ -518,7 +530,7 @@ static void queryInstSources(EvalState & state, case srcProfile: { elems = filterBySelector(state, queryInstalled(state, instSource.profile), - args, newestOnly); + args, newestOnly, instSource.prebuiltOnly); break; } @@ -1152,7 +1164,7 @@ static void opQuery(Globals & globals, DrvInfos elems = filterBySelector(globals.state, source == sInstalled ? installedElems : availElems, - remaining, false); + remaining, false, prebuiltOnly); DrvInfos & otherElems(source == sInstalled ? availElems : installedElems); @@ -1193,12 +1205,6 @@ static void opQuery(Globals & globals, /* For XML output. */ XMLAttrs attrs; - if (prebuiltOnly) { - if (!store->isValidPath(i->queryOutPath(globals.state)) && - !store->hasSubstitutes(i->queryOutPath(globals.state))) - continue; - } - if (printStatus) { bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state)); bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end(); diff --git a/src/nix-state/nix-state.cc b/src/nix-state/nix-state.cc index 9bc623752..44ceb6b3b 100644 --- a/src/nix-state/nix-state.cc +++ b/src/nix-state/nix-state.cc @@ -197,7 +197,6 @@ static void revertToRevision(Strings opFlags, Strings opArgs) static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs) { Path statePath; - if(store->isValidStatePath(*(opArgs.begin()))) statePath = *(opArgs.begin()); else{ @@ -292,7 +291,18 @@ static void opShowSharedPaths(Strings opFlags, Strings opArgs) static void opUnshare(Strings opFlags, Strings opArgs) { - Path statePath = *(opArgs.begin()); + Path statePath; + if(store->isValidStatePath(*(opArgs.begin()))) + statePath = *(opArgs.begin()); + else{ + Path componentPath; + string binary; + string derivationPath; + bool isStateComponent; + Strings program_args; + getPathInfo_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args); + } + if(!store->isValidStatePath(statePath)) throw UsageError(format("Path '%1%' is not a valid state path.") % statePath); @@ -784,10 +794,10 @@ void run(Strings args) */ - //Manipulate options.... + //Manipulate options TODO only allow for root user ... else if (arg.substr(0,13) == "--identifier=") stateIdentifier = arg.substr(13,arg.length()); - else if (arg.substr(0,7) == "--user=") + else if (arg.substr(0,7) == "--nix-user=") username = arg.substr(7,arg.length()); diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index ace8d8a9b..b2962b5e7 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -240,8 +240,8 @@ static void performOp(Source & from, Sink & to, unsigned int op) } #endif - case wopIsValidPath: { - Path path = readStorePath(from); + case wopIsValidPath: { //we do a readString at isValidXXXX + Path path = readString(from); startWork(); bool result = store->isValidPath(path); stopWork(); @@ -250,7 +250,7 @@ static void performOp(Source & from, Sink & to, unsigned int op) } case wopIsValidStatePath: { - Path path = readStatePath(from); + Path path = readString(from); startWork(); bool result = store->isValidStatePath(path); stopWork(); @@ -259,7 +259,7 @@ static void performOp(Source & from, Sink & to, unsigned int op) } case wopIsValidComponentOrStatePath: { - Path path = readStoreOrStatePath(from); + Path path = readString(from); startWork(); bool result = store->isValidComponentOrStatePath(path); stopWork();