Merge pull request #1033 from AladW/view-prefix
[aurutils.git] / lib / aur-build
blobdc4a43cefd680c4471eec66e588f9225c29f7050
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 # default options
12 chroot=0 no_sync=0 overwrite=0 sign_pkg=0 run_pkgver=0 truncate=1
14 # default arguments (empty)
15 chroot_args=() pacconf_args=() repo_args=() repo_add_args=() pkglist_args=()
16 makepkg_args=() makechrootpkg_makepkg_args=() makepkg_common_args=()
18 # default arguments
19 gpg_args=(--detach-sign --no-armor --batch)
20 makechrootpkg_args=(-cu) # -c to clean working copy, -u to sync local repository builds
22 args_csv() {
23 # shellcheck disable=SC2155
24 local str=$(printf '%s,' "$@")
25 printf '%s' "${str%,}"
28 diag_moved_packages() {
29 # Print diagnostic on non-moved packages (#794)
30 cat <<EOF >&2
31 Note:
32 aur-build encountered an error before moving packages to the local repository.
33 This may happen when signing built packages with gpg (aur build --sign),
34 or with certain makepkg errors.
36 The following files were preserved:
37 EOF
38 #shellcheck disable=SC2030
39 realpath -z -- "$@" | while read -rd ''; do
40 printf '%8s%s\n' ' ' "$REPLY"
41 done
44 diag_pacman_conf() {
45 cat <<EOF >&2
46 Error:
47 aur-build could not find a pacman.conf(5) file for container usage. Before
48 using --chroot, make sure this file is created and valid. See OPTIONS in
49 aur-build(1) for configuration details.
51 The following file paths were checked:
52 EOF
53 #shellcheck disable=SC2030
54 realpath -z -- "$@" | while read -rd ''; do
55 printf '%8s%s\n' ' ' "$REPLY"
56 done
59 run_msg() {
60 printf >&2 'Running %s\n' "${*:$1}"
61 "${@:2}"
64 trap_exit() {
65 if [[ ! -v AUR_DEBUG ]]; then
66 rm -rf -- "$tmp"
68 # Only remove package directory if all files were moved (#593)
69 if ! rm -df -- "$var_tmp"; then
70 diag_moved_packages "$var_tmp"/*
72 else
73 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
74 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$var_tmp"
78 usage() {
79 plain >&2 'usage: %s [-acfNS] [-d repo] [--root path] [--margs makepkg_arg...]' "$argv0"
80 exit 1
83 source /usr/share/makepkg/util/message.sh
84 source /usr/share/makepkg/util/parseopts.sh
86 if [[ ! -v NO_COLOR ]] && [[ ! -v AUR_DEBUG ]]; then
87 [[ -t 2 ]] && colorize
90 # mollyguard for makepkg
91 if (( UID == 0 )) && ! [[ -v AUR_ASROOT ]]; then
92 warning 'aur-%s is not meant to be run as root.' "$argv0"
93 warning 'To proceed anyway, set the %s variable.' 'AUR_ASROOT'
94 exit 1
97 ## option parsing
98 opt_short='a:d:D:U:AcCfnrsvLNRST'
99 opt_long=('arg-file:' 'chroot' 'database:' 'force' 'root:' 'sign' 'gpg-sign'
100 'verify' 'directory:' 'no-sync' 'pacman-conf:' 'remove' 'pkgver'
101 'rmdeps' 'no-confirm' 'no-check' 'ignore-arch' 'log' 'new'
102 'makepkg-conf:' 'bind:' 'bind-rw:' 'prevent-downgrade' 'temp'
103 'syncdeps' 'clean' 'namcap' 'checkpkg' 'makepkg-args:' 'user:'
104 'margs:' 'buildscript:')
105 opt_hidden=('dump-options' 'ignorearch' 'noconfirm' 'nocheck' 'nosync' 'repo:'
106 'results:' 'results-append:')
108 if ! parseopts "$opt_short" "${opt_long[@]}" "${opt_hidden[@]}" -- "$@"; then
109 usage
111 set -- "${OPTRET[@]}"
113 unset db_name db_path db_root makepkg_conf pacman_conf results_file queue
114 while true; do
115 case "$1" in
116 # build options
117 -a|--arg-file)
118 shift; queue=$1 ;;
119 -f|--force)
120 overwrite=1 ;;
121 -c|--chroot)
122 chroot=1 ;;
123 -d|--database|--repo)
124 shift; db_name=$1
125 repo_args+=(--repo "$1") ;;
126 --buildscript)
127 shift; makepkg_common_args+=(-p "$1")
128 pkglist_args+=(-p "$1") ;;
129 --nosync|--no-sync)
130 no_sync=1 ;;
131 --makepkg-conf)
132 shift; makepkg_conf=$1 ;;
133 --pacman-conf)
134 shift; pacman_conf=$1 ;;
135 --pkgver)
136 run_pkgver=1; makepkg_args+=(--noextract)
137 makechrootpkg_makepkg_args+=(--holdver) ;;
138 --root)
139 shift; db_root=$1
140 repo_args+=(--root "$1") ;;
141 -S|--sign|--gpg-sign)
142 sign_pkg=1; repo_add_args+=(-s) ;;
143 # chroot options
144 -D|--directory)
145 shift; chroot_args+=(-D "$1") ;;
146 --bind)
147 shift; makechrootpkg_args+=(-D "$1") ;;
148 --bind-rw)
149 shift; makechrootpkg_args+=(-d"$1") ;;
150 -N|--namcap)
151 makechrootpkg_args+=(-n) ;;
152 --checkpkg)
153 makechrootpkg_args+=(-C) ;;
154 -T|--temp)
155 makechrootpkg_args+=(-T) ;;
156 -U|--user)
157 shift; makechrootpkg_args+=(-U "$1")
158 pkglist_args+=(-U "$1") ;;
159 # makepkg options (common)
160 -A|--ignorearch|--ignore-arch)
161 makepkg_common_args+=(--ignorearch)
162 makechrootpkg_makepkg_args+=(--ignorearch) ;;
163 -n|--noconfirm|--no-confirm)
164 makepkg_common_args+=(--noconfirm) ;;
165 -r|--rmdeps)
166 makepkg_common_args+=(--rmdeps) ;;
167 -s|--syncdeps)
168 makepkg_common_args+=(--syncdeps) ;;
169 # makepkg options (build)
170 -C|--clean)
171 makepkg_args+=(--clean) ;;
172 -L|--log)
173 makepkg_args+=(--log) ;;
174 --nocheck|--no-check)
175 makepkg_args+=(--nocheck)
176 makechrootpkg_makepkg_args+=(--nocheck) ;;
177 --makepkg-args|--margs)
178 shift; IFS=, read -a arg -r <<< "$1"
179 makepkg_args+=("${arg[@]}")
180 makechrootpkg_makepkg_args+=("${arg[@]}") ;;
181 # repo-add options
182 -v|--verify)
183 repo_add_args+=(-v) ;;
184 -R|--remove)
185 repo_add_args+=(-R) ;;
186 --new)
187 repo_add_args+=(-n) ;;
188 --prevent-downgrade)
189 repo_add_args+=(-p) ;;
190 # other options
191 --results)
192 shift; results_file=$1 ;;
193 --results-append)
194 shift; results_file=$1; truncate=0 ;;
195 --dump-options)
196 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
197 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
198 exit ;;
199 --) shift; break ;;
200 esac
201 shift
202 done
204 # Assign environment variables
205 : "${db_ext=$AUR_DBEXT}" "${db_root=$AUR_DBROOT}" "${db_repo=$AUR_REPO}"
207 # shellcheck disable=SC2174
208 mkdir -pm 0700 "${TMPDIR:-/tmp}/aurutils-$UID"
209 tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXXX")
211 # shellcheck disable=SC2174
212 mkdir -pm 0700 "${TMPDIR:-/var/tmp}/aurutils-$UID"
213 var_tmp=$(mktemp -d --tmpdir="${TMPDIR:-/var/tmp/}" "aurutils-$UID/$argv0.XXXXXXXX")
215 trap 'trap_exit' EXIT
216 trap 'exit' INT
218 if (( chroot )); then
219 default_paths=("/etc/aurutils/pacman-$db_name.conf"
220 "/etc/aurutils/pacman-$machine.conf")
222 # Change the default /usr/share/devtools/pacman-extra.conf in aur-chroot to
223 # /etc/aurutils/pacman-<repo>.conf or /etc/aurutils/pacman-<uname>.conf in
224 # aur-build, and pass it on to aur-chroot (#824, #846)
225 if [[ ! -v pacman_conf ]] && [[ -f ${default_paths[0]} ]]; then
226 pacman_conf=${default_paths[0]}
227 elif [[ ! -v pacman_conf ]] && [[ -f ${default_paths[1]} ]]; then
228 pacman_conf=${default_paths[1]}
229 elif [[ ! -v pacman_conf ]]; then
230 diag_pacman_conf "${default_paths[@]}"
231 exit 2
232 elif [[ ! -f $pacman_conf ]]; then
233 diag_pacman_conf "$pacman_conf"
234 exit 2
236 chroot_args+=(--pacman-conf "$pacman_conf")
238 # The default path is /usr/share/devtools/makepkg-<uname.conf>, which is
239 # copied to <container path>/etc/makepkg.conf by arch-nspawn.
240 if [[ -v makepkg_conf ]]; then
241 chroot_args+=(--makepkg-conf "$makepkg_conf")
242 else
243 # When makechrootpkg calls makepkg inside the container, it uses the above
244 # makepkg.conf for most variables including PKGEXT. (makepkg --packagelist)
245 makepkg_conf=$(aur chroot --path "${chroot_args[@]}")/etc/makepkg.conf
246 unset PKGEXT
249 # Propagate makepkg/makechrootpkg arguments to aur-chroot
250 if (( ${#makechrootpkg_args[@]} )); then
251 chroot_args+=(--cargs "$(args_csv "${makechrootpkg_args[@]}")")
253 if (( ${#makechrootpkg_makepkg_args[@]} )); then
254 chroot_args+=(--margs "$(args_csv "${makechrootpkg_makepkg_args[@]}")")
258 # Propagate makepkg and pacman configuration to other tools. This needs to be
259 # done BEFORE retrieving the local repository name/root.
260 if [[ -v pacman_conf ]]; then
261 pacconf_args+=(--config "$pacman_conf")
264 if [[ -v makepkg_conf ]]; then
265 makepkg_common_args+=(--config "$makepkg_conf")
266 pkglist_args+=(--config "$makepkg_conf")
269 # Automatically choose the local repository based on the pacman configuration.
270 if [[ $db_name ]] && [[ $db_root ]]; then
271 db_path=$db_root/$db_name.${db_ext:-db}
272 db_path=$(realpath -- "$db_path")
273 else
274 { IFS=: read -r _ db_name
275 IFS=: read -r _ db_root
276 IFS=: read -r _ db_path # canonicalized
277 } < <(aur repo --status "${repo_args[@]}" "${pacconf_args[@]}")
278 wait "$!"
280 db_root=$(realpath -- "$db_root")
282 # File permission checks
283 if [[ ! -f $db_path ]]; then
284 error '%s: %s: not a regular file' "$argv0" "$db_path"
285 exit 2
286 elif [[ ! -w $db_path ]]; then
287 error '%s: %s: permission denied' "$argv0" "$db_path"
288 exit 13
289 elif [[ -v pacman_conf ]] && [[ ! -r $pacman_conf ]]; then
290 error '%s: %s: permission denied' "$argv0" "$pacman_conf"
291 exit 13
292 elif [[ -v makepkg_conf ]] && [[ ! -r $makepkg_conf ]]; then
293 error '%s: %s: permission denied' "$argv0" "$makepkg_conf"
294 exit 13
297 # Write successfully built packages to file (#437, #980)
298 if [[ -v results_file ]]; then
299 results_file=$(realpath -- "$results_file")
300 (( truncate )) && true | tee "$results_file"
303 if (( chroot )); then
304 # Update pacman and makepkg configuration for the chroot build
305 # queue. A full system upgrade is run on the /root container to
306 # avoid lenghty upgrades for makechrootpkg -u.
307 run_msg 2 aur chroot --create --update "${chroot_args[@]}"
310 if [[ -v queue ]]; then
311 exec {fd}< "$queue"
312 else
313 exec {fd}< <(printf '\n')
316 # Early consistency check for signed database
317 if (( ! sign_pkg )); then
318 db_sigs=("$db_root/$db_name".sig "$db_root/$db_name".files.sig)
320 if [[ -f ${db_sigs[0]} ]]; then
321 error '%s: database signature found, but signing is disabled' "$argv0"
323 printf '%q\n' >&2 "${db_sigs[@]}"
324 exit 1
327 elif [[ -v GPGKEY ]]; then
328 gpg --list-keys "$GPGKEY"
329 gpg_args+=(-u "$GPGKEY")
332 while IFS= read -ru "$fd" path; do
333 # Use two cd calls to handle absolute paths in --arg-file
334 cd "$startdir"
335 cd "$path"
337 # Allow running repo-add(8) on existing packages (#839)
338 create_package=1
339 pkglist=()
341 # Run pkgver function before --packagelist (#500)
342 if (( run_pkgver )); then
343 #shellcheck disable=SC2086
344 ${AUR_MAKEPKG:-makepkg} -od "${makepkg_common_args[@]}" >&2
347 # Check if the package is already built, but unlike makepkg, do not
348 # exit with an error when so. A warning avoids a queue of builds
349 # aborting because one member already exists.
350 if (( ! overwrite )); then
351 exists=()
352 while IFS=':' read -r pkgpath; do
353 [[ -f $pkgpath ]] && exists+=("$pkgpath")
354 done < <(
355 PKGDEST="$db_root" aur build--pkglist "${pkglist_args[@]}"
357 # Preserve the exit status from aur-build--pkglist (#671)
358 wait "$!"
360 if (( ${#exists[@]} )); then
361 warning '%s: skipping existing package (use -f to overwrite)' "$argv0"
362 create_package=0
364 printf '%q\n' >&2 "${exists[@]}"
365 pkglist=("${exists[@]}")
368 if (( ${#exists[@]} )) && [[ -v results_file ]]; then
369 printf "exist:file://%s\n" "${exists[@]}" | tee -a "$results_file" >/dev/null
373 if (( create_package )); then
374 if (( chroot )); then
375 PKGDEST="$var_tmp" run_msg 2 aur chroot --build "${chroot_args[@]}"
376 else
377 #shellcheck disable=SC2086
378 PKGDEST="$var_tmp" LOGDEST="${LOGDEST:-$PWD}" \
379 run_msg 2 ${AUR_MAKEPKG:-makepkg} "${makepkg_common_args[@]}" "${makepkg_args[@]}"
382 cd "$var_tmp"
383 pkglist=(!(*.sig)) # discard makepkg --sign from package list (#410)
384 else
385 cd "$var_tmp"
386 # pkglist has paths to $db_root/<pkg>
389 # Sign any packages without signatures, even if the packages are existing.
390 siglist=()
392 for p in "${pkglist[@]}"; do
393 # Package basename (equals $p if create_package=1)
394 p_base=${p##*/}
396 # Signature from makepkg --sign
397 if [[ -f $p_base.sig ]]; then
398 siglist+=("$p_base".sig)
400 # Skipped package build with signature
401 elif [[ -f $db_root/$p_base.sig ]] && [[ ! -f $p_base ]]; then
402 printf >&2 '%s: existing signature file %q\n' "$argv0" "$db_root/$p_base.sig"
404 # No candidate signature, generate one
405 elif (( sign_pkg )); then
406 #shellcheck disable=SC2086
407 ${AUR_GPG:-gpg} "${gpg_args[@]}" --output "$p_base".sig "$p"
409 printf >&2 '%s: created signature file %q\n' "$argv0" "$p_base".sig
410 siglist+=("$p_base".sig)
412 done
414 if (( create_package )); then
415 mv -f "${siglist[@]}" "${pkglist[@]}" "$db_root"
417 if [[ -v results_file ]]; then
418 printf "build:file://$db_root/%s\n" "${pkglist[@]}" | tee -a "$results_file" >/dev/null
421 elif (( ${#siglist[@]} )); then
422 mv -f "${siglist[@]}" "$db_root"
425 # Update database
426 cd "$db_root"
427 #shellcheck disable=SC2086
428 LANG=C ${AUR_REPO_ADD:-repo-add} "${repo_add_args[@]}" "$db_path" "${pkglist[@]}"
430 if (( chroot )) || (( no_sync )); then
431 continue
432 else
433 # Helper which can be used for e.g. sudoers rules (#1012)
434 # Like `makepkg --syncdeps`, this affects the host and so uses
435 # the host pacman configuration. --pacman-conf (which may also
436 # point to a world-writeable file) is not applied.
437 aur build--sync "$db_name"
439 done
441 exec {fd}<&-
443 # vim: set et sw=4 sts=4 ft=sh: