Eelco Dolstra, PhD Thesis, University of Utrecht
Philip Potter / @philandstuff
PhD viva: 18th January 2006
Tools like puppet, chef, rpm, dpkg written to scratch an itch
M. Burgess, Cfengine: a site configuration engine , USENIX Computing systems, Vol8, No. 3 1995
In fact, there's a whole Cfengine papers page
The nix package manager is an implementation of that theory
see also Naur's Programming as Theory Building for more exploration of this idea
rpm, dpkg, rubygems, bundler, zip, docker, rsync?
From the first sentence of Chapter 1
given identical inputs, the software should behave the same on an end-user machine as on the developer machine
-- section 1.1
/nix/store/n0a1y0yd54sh10p7rdi0alysscvac5x5-certificate-transparency-2016-01-14/bin/ct
Where other binaries might search for libjson in /usr/lib
or /usr/local/lib
, we search in
/nix/store/6hcccvdx29r4j8n0wxl85b6mlmq0gvs1-libjson-7.6.1
, and nowhere else
Only the exact same version is acceptable
$ nix-store --query --references /nix/store/n0a1y0yd54sh10p7rdi0alysscvac5x5-certificate-transparency-2016-01-14
$ nix-store --query --requisites /nix/store/n0a1y0yd54sh10p7rdi0alysscvac5x5-certificate-transparency-2016-01-14
This works for any target machine (with matching arch & kernel): Ubuntu, RHEL, CentOS...
Command: nix-copy-closure
An example of ./configure ; make ; make install
{ stdenv, fetchurl, autoreconfHook, libsass }:
stdenv.mkDerivation rec {
name = "sassc-${version}";
version = "3.3.2";
src = fetchurl {
url = "https://github.com/sass/sassc/archive/${version}.tar.gz";
sha256 = "15a2b2698639dfdc7bd6a5ba7a9ecdaf8ebb9f15503fb04dea1be3133308e41d";
};
patchPhase = ''
export SASSC_VERSION=${version}
'';
nativeBuildInputs = [ autoreconfHook ];
buildInputs = [ libsass ];
meta = with stdenv.lib; {
description = "A front-end for libsass";
homepage = https://github.com/sass/sassc/;
license = licenses.mit;
maintainers = with maintainers; [ codyopel pjones ];
platforms = platforms.unix;
};
}
{ stdenv, fetchurl, autoreconfHook, libsass }:
stdenv.mkDerivation rec {
# ...
}
{ stdenv, fetchurl, autoreconfHook, libsass }:
stdenv.mkDerivation rec {
name = "sassc-${version}";
version = "3.3.2";
src = fetchurl {
url = "https://github.com/sass/sassc/archive/${version}.tar.gz";
sha256 = "15a2b2698639dfdc7bd6a5ba7a9ecdaf8ebb9f15503fb04dea1be3133308e41d";
};
# ...
}
{ stdenv, fetchurl, autoreconfHook, libsass }:
stdenv.mkDerivation rec {
name = "sassc-${version}";
version = "3.3.2";
src = fetchurl # ...;
patchPhase = ''
export SASSC_VERSION=${version}
'';
nativeBuildInputs = [ autoreconfHook ];
buildInputs = [ libsass ];
# ...;
}
{ stdenv, fetchurl, autoreconfHook, libsass }:
stdenv.mkDerivation rec {
name = "sassc-${version}";
version = "3.3.2";
src = fetchurl # ...;
# ...;
meta = with stdenv.lib; {
description = "A front-end for libsass";
homepage = https://github.com/sass/sassc/;
license = licenses.mit;
maintainers = with maintainers; [ codyopel pjones ];
platforms = platforms.unix;
};
}
nix-shell --pure -A pkgs.sassc
env | grep libsass
{ stdenv, fetchurl, python3, texinfo, makeWrapper }:
stdenv.mkDerivation rec {
buildInputs = [ python3 texinfo makeWrapper ];
phases = "unpackPhase installPhase fixupPhase";
installPhase = ''
find -type f -name "*.py" | xargs sed -i "s@/usr/bin/env python3@$python3/bin/python3@g"
substituteInPlace setup.py --replace \
"fileout.write(('#!/usr/bin/env %s\n' % env).encode('utf-8'))" \
"fileout.write(('#!%s/bin/%s\n' % (os.environ['python3'], env)).encode('utf-8'))"
python3 setup.py --prefix=$out --freedom=partial install \
--with-shared-cache=$out/share/ponysay \
--with-bash
'';
}
{ stdenv, fetchgit, makeWrapper, ... configH}:
stdenv.mkDerivation rec {
# ...;
buildPhase = ''
cat >src/config.h <<EOF
${configH}
EOF
make
'';
}
class Foo {
Foo () {}
int run(Bar y) { return y.doIt(); }
}
export PYTHONPATH=/path/to/foo-module
python script-that-needs-foo-module.py
class Foo {
int x;
Foo (Bar y) { x = y.doIt(); }
int run() { return x; }
}
gcc -o foo foo.c -static -L/path/to/libjson -ljson
./foo # no runtime dependency on libjson
Note that y
and libjson
are not needed after construction or compilation, respectively
class Foo {
Bar x;
Foo (Bar y) { x = y; }
int run() { return x.doIt(); }
}
gcc -o foo foo.c -L/path/to/libjson -ljson # no `-static`
./foo # runtime dependency on libjson.so
Here, y
and libjson
need to be
retained after construction or compilation,
respectively, or the constructed artefact will fail
Has a built artefact retained a dependency, or is it now unneeded?
Use a conservative GC: scan for pointer references
GC roots are user profiles, system profiles, etc
Scanning is simply grep
for the package SHA256
It is possible to retain dependencies but "hide" them from the GC
This problem manifests (in theory) in regular GC and in nix's GC; and in practice is not a problem
A package description is a function that takes source code and returns compiled code
A binary substitution is a cached evaluation of the same function which can be used instead of rebuilding the package
(In this way we neatly have a single definition for source and binary packages: binary packages come "for free")
Two functions $f$ and $g$ are extensionally equal if, for all $x$, $f(x) = g(x)$.
Two software programs foo
and bar
are extensionally equal if,
for all input, foo
and bar
have the same behaviour.
Two functions $f$ and $g$ are intensionally equal if they have the same syntactic definition.
Two software programs foo
and bar
are intensionally equal if
they have the same representation on disk,
byte-for-byte.
Whether you accept substitution model depends on your definition of function purity.
A nix build may, on repeated evaluation, produce artefacts which are only extensionally equal, but not intensionally.
There is a growing community around reproducible builds (eg @ReproBuilds)
The value of reproducible builds is precisely that it makes builds respect intensional equality
Philip Potter / @philandstuff