build: add --null
[aurutils.git] / lib / aur-build
blobdc9e3889c0b983466c305240fc89e0ed3aaefbea
1 #!/bin/bash
2 # aur-build - build packages to a local repository
3 [[ -v AUR_DEBUG ]] && set -o xtrace
4 set -o errexit
5 shopt -s extglob
6 argv0=build
7 machine=$(uname -m)
8 startdir=$PWD
9 PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
11 # Avoid CDPATH screwing with cd (#1047)
12 unset -v CDPATH
14 # default options
15 chroot=0 no_sync=0 overwrite=0 sign_pkg=0 run_pkgver=0 truncate=1
17 # default arguments (empty)
18 chroot_args=() pacconf_args=() repo_args=() repo_add_args=() pkglist_args=()
19 makepkg_args=() makechrootpkg_makepkg_args=() makepkg_common_args=() read_args=()
21 # default arguments
22 gpg_args=(--detach-sign --no-armor --batch)
23 makechrootpkg_args=(-cu) # -c to clean working copy, -u to sync local repository builds
25 args_csv() {
26 # shellcheck disable=SC2155
27 local str=$(printf '%s,' "$@")
28 printf '%s' "${str%,}"
31 diag_moved_packages() {
32 # Print diagnostic on non-moved packages (#794)
33 cat <<EOF >&2
34 Note:
35 aur-build encountered an error before moving packages to the local repository.
36 This may happen when signing built packages with gpg (aur build --sign),
37 or with certain makepkg errors.
39 The following files were preserved:
40 EOF
41 #shellcheck disable=SC2030
42 realpath -z -- "$@" | while read -rd ''; do
43 printf '%8s%s\n' ' ' "$REPLY"
44 done
47 diag_pacman_conf() {
48 cat <<EOF >&2
49 Error:
50 aur-build could not find a pacman.conf(5) file for container usage. Before
51 using --chroot, make sure this file is created and valid. See OPTIONS in
52 aur-build(1) for configuration details.
54 The following file paths were checked:
55 EOF
56 #shellcheck disable=SC2030
57 realpath -z -- "$@" | while read -rd ''; do
58 printf '%8s%s\n' ' ' "$REPLY"
59 done
62 trap_exit() {
63 if [[ ! -v AUR_DEBUG ]]; then
64 rm -rf -- "$tmp"
66 # Only remove package directory if all files were moved (#593)
67 if ! rm -df -- "$var_tmp"; then
68 diag_moved_packages "$var_tmp"/*
70 else
71 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
72 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$var_tmp"
76 usage() {
77 printf >&2 'usage: %s [-acfNS] [-d repo] [--root path] [--margs makepkg_arg...]\n' "$argv0"
78 exit 1
81 # mollyguard for makepkg
82 if (( UID == 0 )) && ! [[ -v AUR_ASROOT ]]; then
83 warning 'aur-%s is not meant to be run as root.' "$argv0"
84 warning 'To proceed anyway, set the %s variable.' 'AUR_ASROOT'
85 exit 1
88 ## option parsing
89 opt_short='a:d:D:U:AcCfnrsvzLNRST'
90 opt_long=('arg-file:' 'chroot' 'database:' 'force' 'root:' 'sign' 'gpg-sign'
91 'verify' 'directory:' 'no-sync' 'pacman-conf:' 'remove' 'pkgver'
92 'rmdeps' 'no-confirm' 'no-check' 'ignore-arch' 'log' 'new'
93 'makepkg-conf:' 'bind:' 'bind-rw:' 'prevent-downgrade' 'temp'
94 'syncdeps' 'clean' 'namcap' 'checkpkg' 'makepkg-args:' 'user:'
95 'margs:' 'buildscript:' 'null')
96 opt_hidden=('dump-options' 'ignorearch' 'noconfirm' 'nocheck' 'nosync' 'repo:'
97 'results:' 'results-append:')
99 if opts=$(getopt -o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
100 eval set -- "$opts"
101 else
102 usage
105 unset db_name db_path db_root makepkg_conf pacman_conf results_file pkgrel_incr queue
106 while true; do
107 case "$1" in
108 # build options
109 -a|--arg-file)
110 shift; queue=$1 ;;
111 -f|--force)
112 overwrite=1 ;;
113 -c|--chroot)
114 chroot=1 ;;
115 -d|--database|--repo)
116 shift; db_name=$1
117 repo_args+=(--repo "$1") ;;
118 --buildscript)
119 shift; makepkg_common_args+=(-p "$1")
120 pkglist_args+=(-p "$1") ;;
121 --nosync|--no-sync)
122 no_sync=1 ;;
123 --makepkg-conf)
124 shift; makepkg_conf=$1 ;;
125 --pacman-conf)
126 shift; pacman_conf=$1 ;;
127 --pkgver)
128 run_pkgver=1; makepkg_args+=(--noextract)
129 makechrootpkg_makepkg_args+=(--holdver) ;;
130 --root)
131 shift; db_root=$1
132 repo_args+=(--root "$1") ;;
133 -S|--sign|--gpg-sign)
134 sign_pkg=1; repo_add_args+=(-s) ;;
135 # chroot options
136 -D|--directory)
137 shift; chroot_args+=(-D "$1") ;;
138 --bind)
139 shift; makechrootpkg_args+=(-D "$1") ;;
140 --bind-rw)
141 shift; makechrootpkg_args+=(-d"$1") ;;
142 -N|--namcap)
143 makechrootpkg_args+=(-n) ;;
144 --checkpkg)
145 makechrootpkg_args+=(-C) ;;
146 -T|--temp)
147 makechrootpkg_args+=(-T) ;;
148 -U|--user)
149 shift; makechrootpkg_args+=(-U "$1")
150 pkglist_args+=(-U "$1") ;;
151 # makepkg options (common)
152 -A|--ignorearch|--ignore-arch)
153 makepkg_common_args+=(--ignorearch)
154 makechrootpkg_makepkg_args+=(--ignorearch) ;;
155 -n|--noconfirm|--no-confirm)
156 makepkg_common_args+=(--noconfirm) ;;
157 -r|--rmdeps)
158 makepkg_common_args+=(--rmdeps) ;;
159 -s|--syncdeps)
160 makepkg_common_args+=(--syncdeps) ;;
161 # makepkg options (build)
162 -C|--clean)
163 makepkg_args+=(--clean) ;;
164 -L|--log)
165 makepkg_args+=(--log) ;;
166 --nocheck|--no-check)
167 makepkg_args+=(--nocheck)
168 makechrootpkg_makepkg_args+=(--nocheck) ;;
169 --makepkg-args|--margs)
170 shift; IFS=, read -a arg -r <<< "$1"
171 makepkg_args+=("${arg[@]}")
172 makechrootpkg_makepkg_args+=("${arg[@]}") ;;
173 # repo-add options
174 -v|--verify)
175 repo_add_args+=(-v) ;;
176 -R|--remove)
177 repo_add_args+=(-R) ;;
178 --new)
179 repo_add_args+=(-n) ;;
180 --prevent-downgrade)
181 repo_add_args+=(-p) ;;
182 # other options
183 --results)
184 shift; results_file=$1 ;;
185 --results-append)
186 shift; results_file=$1; truncate=0 ;;
187 -z|--null)
188 read_args+=(-d $'\0') ;;
189 --dump-options)
190 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
191 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
192 exit ;;
193 --) shift; break ;;
194 esac
195 shift
196 done
198 # Assign environment variables
199 : "${db_ext=$AUR_DBEXT}" "${db_root=$AUR_DBROOT}" "${db_repo=$AUR_REPO}"
201 # shellcheck disable=SC2174
202 mkdir -pm 0700 -- "${TMPDIR:-/tmp}/aurutils-$UID"
203 tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXXX")
205 # shellcheck disable=SC2174
206 mkdir -pm 0700 -- "${TMPDIR:-/var/tmp}/aurutils-$UID"
207 var_tmp=$(mktemp -d --tmpdir="${TMPDIR:-/var/tmp/}" "aurutils-$UID/$argv0.XXXXXXXX")
209 trap 'trap_exit' EXIT
210 trap 'exit' INT
212 if (( chroot )); then
213 # XXX: default paths can be set through the Makefile
214 default_paths=("/etc/aurutils/pacman-$db_name.conf"
215 "/etc/aurutils/pacman-$machine.conf")
217 # Change the default /usr/share/devtools/pacman-extra.conf in aur-chroot to
218 # /etc/aurutils/pacman-<repo>.conf or /etc/aurutils/pacman-<uname>.conf in
219 # aur-build, and pass it on to aur-chroot (#824, #846)
220 if [[ ! -v pacman_conf ]] && [[ -f ${default_paths[0]} ]]; then
221 pacman_conf=${default_paths[0]}
222 elif [[ ! -v pacman_conf ]] && [[ -f ${default_paths[1]} ]]; then
223 pacman_conf=${default_paths[1]}
224 elif [[ ! -v pacman_conf ]]; then
225 diag_pacman_conf "${default_paths[@]}"
226 exit 2
227 elif [[ ! -f $pacman_conf ]]; then
228 diag_pacman_conf "$pacman_conf"
229 exit 2
231 chroot_args+=(--pacman-conf "$pacman_conf")
233 # The default path is /usr/share/devtools/makepkg-<uname.conf>, which is
234 # copied to <container path>/etc/makepkg.conf by arch-nspawn.
235 if [[ -v makepkg_conf ]]; then
236 chroot_args+=(--makepkg-conf "$makepkg_conf")
237 else
238 # When makechrootpkg calls makepkg inside the container, it uses the above
239 # makepkg.conf for most variables including PKGEXT. (makepkg --packagelist)
240 makepkg_conf=$(aur chroot --path "${chroot_args[@]}")/etc/makepkg.conf
241 unset PKGEXT
244 # Propagate makepkg/makechrootpkg arguments to aur-chroot
245 if (( ${#makechrootpkg_args[@]} )); then
246 chroot_args+=(--cargs "$(args_csv "${makechrootpkg_args[@]}")")
248 if (( ${#makechrootpkg_makepkg_args[@]} )); then
249 chroot_args+=(--margs "$(args_csv "${makechrootpkg_makepkg_args[@]}")")
253 # Propagate makepkg and pacman configuration to other tools. This needs to be
254 # done BEFORE retrieving the local repository name/root.
255 if [[ -v pacman_conf ]]; then
256 pacconf_args+=(--config "$pacman_conf")
259 if [[ -v makepkg_conf ]]; then
260 makepkg_common_args+=(--config "$makepkg_conf")
261 pkglist_args+=(--config "$makepkg_conf")
264 # Automatically choose the local repository based on the pacman configuration.
265 if [[ $db_name ]] && [[ $db_root ]]; then
266 db_path=$db_root/$db_name.${db_ext:-db}
267 db_path=$(realpath -- "$db_path")
268 else
269 { IFS=: read -r _ db_name
270 IFS=: read -r _ db_root
271 IFS=: read -r _ db_path # canonicalized
272 } < <(aur repo --status "${repo_args[@]}" "${pacconf_args[@]}")
273 wait "$!"
275 db_root=$(realpath -- "$db_root")
277 # Check that a valid database extension was retrieved (#700, #1038)
278 if [[ -z $AUR_DBEXT ]] && [[ $db_path =~ \.db$ ]]; then
279 printf >&2 '%s: %s does not have a valid database archive extension\n' "$argv0" "$db_path"
280 # TODO: this usually happens on file systems not supporting symbolic links
281 # (SMB/CIFS). Add a diagnostic to point towards AUR_DBEXT in this case
282 exit 2
285 # File permission checks
286 if [[ ! -f $db_path ]]; then
287 printf >&2 '%s: %s: not a regular file\n' "$argv0" "$db_path"
288 exit 2
289 elif [[ ! -w $db_path ]]; then
290 printf >&2 '%s: %s: permission denied\n' "$argv0" "$db_path"
291 exit 13
292 elif [[ -v pacman_conf ]] && [[ ! -r $pacman_conf ]]; then
293 printf >&2 '%s: %s: permission denied\n' "$argv0" "$pacman_conf"
294 exit 13
295 elif [[ -v makepkg_conf ]] && [[ ! -r $makepkg_conf ]]; then
296 printf >&2 '%s: %s: permission denied\n' "$argv0" "$makepkg_conf"
297 exit 13
300 # Write successfully built packages to file (#437, #980)
301 if [[ -v results_file ]]; then
302 results_file=$(realpath -- "$results_file")
303 (( truncate )) && true | tee "$results_file"
306 if (( chroot )); then
307 # Update pacman and makepkg configuration for the chroot build
308 # queue. A full system upgrade is run on the /root container to
309 # avoid lenghty upgrades for makechrootpkg -u.
310 aur chroot --create --update "${chroot_args[@]}"
313 if [[ -v queue ]]; then
314 exec {fd}< "$queue"
315 else
316 exec {fd}< <(echo "$startdir")
319 # Early consistency check for signed database
320 if (( ! sign_pkg )); then
321 db_sigs=("$db_root/$db_name".sig "$db_root/$db_name".files.sig)
323 if [[ -f ${db_sigs[0]} ]]; then
324 printf >&2 '%s: database signature found, but signing is disabled\n' "$argv0"
326 printf '%q\n' >&2 "${db_sigs[@]}"
327 exit 1
330 elif [[ -v GPGKEY ]]; then
331 #shellcheck disable=SC2086
332 ${AUR_GPG:-gpg} --list-keys "$GPGKEY"
333 gpg_args+=(-u "$GPGKEY")
336 while IFS= read "${read_args[@]}" -ru "$fd" path; do
337 cd -- "$startdir"
338 [[ $path ]] && cd -- "$path"
340 # Allow running repo-add(8) on existing packages (#839)
341 create_package=1
342 pkglist=()
344 # Run pkgver function before --packagelist (#500)
345 if (( run_pkgver )); then
346 #shellcheck disable=SC2086
347 ${AUR_MAKEPKG:-makepkg} -od "${makepkg_common_args[@]}" >&2
350 # Retrieve list of potential package paths. This is used to (optionally)
351 # check if package paths are already available in the local repository
352 # before builds. If so, the build is skipped and the path is passed to
353 # repo-add (create_package=0). If no paths are available, the package is
354 # assumed to not exist and the build proceeds as usual (create_package=1).
355 if (( ! overwrite )); then
356 exists=()
357 while read -r pkgpath; do
358 [[ -f $pkgpath ]] && exists+=("$pkgpath")
359 done < <(PKGDEST="$db_root" aur build--pkglist "${pkglist_args[@]}")
361 if ! wait "$!"; then
362 printf >&2 '%s: warning: failed to retrieve package list\n' "$argv0"
365 if (( ${#exists[@]} )); then
366 printf >&2 '%s: warning: skipping existing package (use -f to overwrite)\n' "$argv0"
367 create_package=0
369 printf '%q\n' >&2 "${exists[@]}"
370 pkglist=("${exists[@]}")
373 if (( ${#exists[@]} )) && [[ -v results_file ]]; then
374 printf "exist:file://%s\n" "${exists[@]}" | tee -a "$results_file" >/dev/null
378 if (( create_package )); then
379 if (( chroot )); then
380 PKGDEST="$var_tmp" aur chroot --build "${chroot_args[@]}"
381 else
382 #shellcheck disable=SC2086
383 PKGDEST="$var_tmp" LOGDEST="${LOGDEST:-$PWD}" \
384 ${AUR_MAKEPKG:-makepkg} "${makepkg_common_args[@]}" "${makepkg_args[@]}"
387 cd -- "$var_tmp"
388 pkglist=(!(*.sig)) # discard makepkg --sign from package list (#410)
389 else
390 cd -- "$var_tmp"
391 # pkglist has paths to $db_root/<pkg>
394 # Sign any packages without signatures, even if the packages are existing.
395 siglist=()
397 for p in "${pkglist[@]}"; do
398 # Package basename (equals $p if create_package=1)
399 p_base=${p##*/}
401 # Signature from makepkg --sign
402 if [[ -f $p_base.sig ]]; then
403 siglist+=("$p_base".sig)
405 # Skipped package build with signature
406 elif [[ -f $db_root/$p_base.sig ]] && [[ ! -f $p_base ]]; then
407 printf >&2 '%s: existing signature file %q\n' "$argv0" "$db_root/$p_base.sig"
409 # No candidate signature, generate one
410 elif (( sign_pkg )); then
411 #shellcheck disable=SC2086
412 ${AUR_GPG:-gpg} "${gpg_args[@]}" --output "$p_base".sig "$p"
414 printf >&2 '%s: created signature file %q\n' "$argv0" "$p_base".sig
415 siglist+=("$p_base".sig)
417 done
419 if (( create_package )); then
420 mv -f "${siglist[@]}" "${pkglist[@]}" "$db_root"
422 if [[ -v results_file ]]; then
423 printf "build:file://$db_root/%s\n" "${pkglist[@]}" | tee -a "$results_file" >/dev/null
426 elif (( ${#siglist[@]} )); then
427 mv -f "${siglist[@]}" "$db_root"
430 # Update database
431 #shellcheck disable=SC2086
432 env -C "$db_root" LANG=C ${AUR_REPO_ADD:-repo-add} "${repo_add_args[@]}" "$db_path" "${pkglist[@]}"
434 if (( chroot )) || (( no_sync )); then
435 continue
436 else
437 #shellcheck disable=SC2086
438 ${AUR_PACMAN_AUTH:-sudo} pacsync "$db_name"
439 #shellcheck disable=SC2086
440 ${AUR_PACMAN_AUTH:-sudo} pacsync "$db_name" --dbext=.files
442 # Retrieve upgrade targets
443 targets=()
444 while IFS='/' read -r repo name; do
445 [[ $repo == "$db_name" ]] && targets+=("$repo/$name")
446 done < <(pacman -Sup --print-format '%r/%n')
447 wait "$!"
449 if (( ${#targets[@]} )); then
450 printf >&2 "%s: upgrading packages in repository '%s'\n" "$argv0" "$db_name"
451 #shellcheck disable=SC2086
452 printf '%s\n' "${targets[@]}" | ${AUR_PACMAN_AUTH:-sudo} pacman -S --noconfirm -
455 done
457 exec {fd}<&-
459 # vim: set et sw=4 sts=4 ft=sh: