@@ -1,63 +1,180 @@
11#!/usr/bin/env sh
22
3−if [ "$#" -lt 2 ]; then
4− cat <<EOF
5−
6−Usage: $0 TARGET EXECUTABLE
7−
8−Create a single-file bundle from the nixpkgs attribute "TARGET".
9−EXECUTABLE should be relative to the TARGET's output path.
10−
11−The TARGET is either an attribute in nixpkgs, or an absolute path to the
12−store.
13−
14−For example:
15−
16−$ $0 hello /bin/hello
17−$ ./hello
18−Hello, world!
19−
3+fail() {
4+ echo "Try '$0 --help'." >&2
5+ exit 2
6+}
7+
8+build() {
9+ linkdir=$(mktemp -d)
10+ nix build "$@" --out-link "$linkdir/result"
11+ readlink "$linkdir/result"
12+}
13+
14+check_arg() {
15+ if [ $# -le 1 ]
16+ then
17+ echo "error: flag '$1' requires an argument" >&2
18+ fail
19+ fi
20+}
21+
22+nix_file=`dirname $0`/default.nix
23+
24+# Read arguments
25+
26+build_flags=()
27+unset bin
28+targets=()
29+
30+while [ $# -ne 0 ]
31+do
32+ case "$1" in
33+ -f|--file|-I|--include)
34+ check_arg "$@"
35+ build_flags+=("$1")
36+ shift
37+ build_flags+=("$1")
38+ shift
39+ ;;
40+ --arg|--argstr)
41+ if [ $# -le 2 ]
42+ then
43+ echo "error: flag '$1' requires two arguments" >&2
44+ fail
45+ fi
46+ build_flags+=("$1")
47+ shift
48+ build_flags+=("$1")
49+ shift
50+ build_flags+=("$1")
51+ shift
52+ ;;
53+ --bin)
54+ check_arg "$@"
55+ shift
56+ bin="$1"
57+ shift
58+ ;;
59+ -h|--help)
60+ cat <<EOF >&2
61+Usage: $0 <FLAGS>... [<TARGET>] <targets>...
62+
63+Summary: Similar to nix build, but creates a single-file bundle.
64+
65+Flags:
66+ --arg <NAME> <EXPR> argument to be passed to Nix functions
67+ --argstr <NAME> <STRING> string-valued argument to be passed to Nix functions
68+ -f, --file <FILE> evaluate FILE rather than the default
69+ -I, --include <PATH> add a path to the list of locations used to look up <...> file names
70+ --bin <BINARY> the binary to bundle, relative to TARGET's output path; defaults to
71+ the lexicographically first file inside /bin
72+
73+Examples:
74+
75+ To bundle GNU Hello from NixOS 19.03:
76+ $ $0 -f channel:nixos-19.03 hello --bin /bin/hello
77+
78+ To bundle the executable from within a project that has a default.nix for build:
79+ $ $0 --bin /bin/name-of-the-executable
2080EOF
21−
22− exit 1
81+ exit 0
82+ ;;
83+ --)
84+ shift
85+ break
86+ ;;
87+ -*)
88+ echo "error: unrecognized flag '$1'" >&2
89+ fail
90+ ;;
91+ *)
92+ targets+=("$1")
93+ shift
94+ ;;
95+ esac
96+done
97+
98+while [ $# -ne 0 ]
99+do
100+ targets+=("$1")
101+ shift
102+done
103+
104+# Add application-specific extra targets
105+
106+if [ "${targets[0]}" = "nix-bundle" ] || [ "${targets[0]}" = "nixStable" ] || [ "${targets[0]}" = "nixUnstable" ] || [ "${targets[0]}" = "nix" ]
107+then
108+ targets+=("bash")
109+ targets+=("bzip2")
110+ targets+=("coreutils")
111+ targets+=("gnutar")
112+ targets+=("gzip")
113+ targets+=("xz")
23114fi
24115
25−nix_file=$(dirname "$0")/default.nix
116+# Create tmp directory for the links produced by `nix build`
117+
118+linkdir=$(mktemp -d)
119+build_flags+=("--out-link")
120+build_flags+=("$linkdir/result")
121+
122+# Build targets
123+
124+target_path=
125+extra_target_paths=()
126+
127+for target in "${targets[@]}"
128+do
129+ nix build "${build_flags[@]}" "$target" || exit $?
130+ out_link="$linkdir/$(ls "$linkdir")"
131+ if [ -z "$target_path" ]
132+ then
133+ target_path="$(readlink "$out_link")"
134+ else
135+ extra_target_paths+=("\"$(readlink "$out_link")\"")
136+ fi
137+ rm "$out_link"
138+done
139+
140+if [ -z "$target_path" ]
141+then
142+ nix build "${build_flags[@]}" || exit $?
143+ out_link="$(ls "$linkdir")"
144+ target_path="$(readlink "$out_link")"
145+fi
26146
27−target="$1"
28−shift
147+# Apply default for --bin
29148
30−extraTargets=
31−if [ "$#" -gt 1 ]; then
32− while [ "$#" -gt 1 ]; do
33− extraTargets="$extraTargets $1"
34− shift
35− done
149+if [ -z ${bin+x} ]
150+then
151+ bin=$(ls "$target_path/bin" | head -1)
36152fi
37153
38−exec="$1"
39−shift
154+# Determine bootstrap function -- This seems like a total hack!
40155
41156bootstrap=nix-bootstrap
42−if [ "$target" = "nix-bundle" ] || [ "$target" = "nixStable" ] || [ "$target" = "nixUnstable" ] || [ "$target" = "nix" ]; then
43− bootstrap=nix-bootstrap-nix
44−elif [ -n "$extraTargets" ]; then
157+if ! [ -z "$extra_target_paths" ]; then
45158 bootstrap=nix-bootstrap-path
46159fi
47160
48−expr="with import <nixpkgs> {}; with import $nix_file {}; $bootstrap { target = $target; extraTargets = [ $extraTargets ]; run = \"$exec\"; }"
161+# Run bootstrap function
162+
163+expr="(with import $nix_file {}; $bootstrap { target = \"$target_path\"; extraTargets = [ ${extra_target_paths[@]} ]; run = \"$bin\"; })"
164+
165+echo $expr
49166
50−drv=$(nix-instantiate --no-gc-warning -E "$expr")
167+out=$(nix-store --no-gc-warning -r $(nix-instantiate --no-gc-warning -E "$expr"))
51168
52−out=$(nix-store --no-gc-warning --realize "$drv")
169+# Copy the created bundle to the working directory
53170
54171if [ -z "$out" ]; then
55172 >&2 echo "$0 failed. Exiting."
56173 exit 1
57174elif [ -t 1 ]; then
58− filename=$(basename "$exec")
175+ filename=$(basename $bin)
59176 echo "Nix bundle created at $filename."
60− cp -f "$out" "$filename"
177+ cp -f $out $filename
61178else
62− echo "$out"
179+ cat $out
63180fi