@@ -1,63 +1,170 @@
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+# Create tmp directory for the links produced by `nix build`
105+
106+linkdir=$(mktemp -d)
107+build_flags+=("--out-link")
108+build_flags+=("$linkdir/result")
109+
110+# Build targets
111+
112+target_path=
113+extra_target_paths=()
114+
115+for target in "${targets[@]}"
116+do
117+ nix build "${build_flags[@]}" "$target" || exit $?
118+ out_link="$linkdir/$(ls "$linkdir")"
119+ if [ -z "$target_path" ]
120+ then
121+ target_path="$(readlink "$out_link")"
122+ else
123+ extra_target_paths+=("\"$(readlink "$out_link")\"")
124+ fi
125+ rm "$out_link"
126+done
127+
128+if [ -z "$target_path" ]
129+then
130+ nix build "${build_flags[@]}" || exit $?
131+ out_link="$(ls "$linkdir")"
132+ target_path="$(readlink "$out_link")"
23133fi
24134
25−nix_file=$(dirname "$0")/default.nix
26−
27−target="$1"
28−shift
135+# Apply default for --bin
29136
30−extraTargets=
31−if [ "$#" -gt 1 ]; then
32− while [ "$#" -gt 1 ]; do
33− extraTargets="$extraTargets $1"
34− shift
35− done
137+if [ -z ${bin+x} ]
138+then
139+ bin=$(ls "$target_path/bin" | head -1)
36140fi
37141
38−exec="$1"
39−shift
142+# Determine bootstrap function -- This seems like a total hack!
40143
41144bootstrap=nix-bootstrap
42−if [ "$target" = "nix-bundle" ] || [ "$target" = "nixStable" ] || [ "$target" = "nixUnstable" ] || [ "$target" = "nix" ]; then
145+if [ "${targets[0]}" = "nix-bundle" ] || [ "${targets[0]}" = "nixStable" ] || [ "${targets[0]}" = "nixUnstable" ] || [ "${targets[0]}" = "nix" ]; then
43146 bootstrap=nix-bootstrap-nix
44−elif [ -n "$extraTargets" ]; then
147+elif ! [ -z "$extra_target_paths" ]; then
45148 bootstrap=nix-bootstrap-path
46149fi
47150
48−expr="with import <nixpkgs> {}; with import $nix_file {}; $bootstrap { target = $target; extraTargets = [ $extraTargets ]; run = \"$exec\"; }"
151+# Run bootstrap function
152+
153+expr="(with import $nix_file {}; $bootstrap { target = \"$target_path\"; extraTargets = [ ${extra_target_paths[@]} ]; run = \"$bin\"; })"
154+
155+echo $expr
49156
50−drv=$(nix-instantiate --no-gc-warning -E "$expr")
157+out=$(nix-store --no-gc-warning -r $(nix-instantiate --no-gc-warning -E "$expr"))
51158
52−out=$(nix-store --no-gc-warning --realize "$drv")
159+# Copy the created bundle to the working directory
53160
54161if [ -z "$out" ]; then
55162 >&2 echo "$0 failed. Exiting."
56163 exit 1
57164elif [ -t 1 ]; then
58− filename=$(basename "$exec")
165+ filename=$(basename $bin)
59166 echo "Nix bundle created at $filename."
60− cp -f "$out" "$filename"
167+ cp -f $out $filename
61168else
62− echo "$out"
169+ cat $out
63170fi