Merge pull request #1106 from AladW/depends-reqby-dict
[aurutils.git] / lib / aur-build
blob6dbd5e4743ef11c64fb3b3e82a768089cc41cd2f
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 # Print diagnostic on non-moved packages (#794)
32 diag_moved_packages() {
33 echo >&2 'Note:'
35 cat <<EOF | pr -to 4 >&2
36 aur-$argv0 encountered an error before moving packages to the local repository.
37 This may happen when signing built packages with gpg (aur $argv0 --sign),
38 or with certain makepkg errors.
40 The following files were preserved:
41 EOF
42 printf '%s\n' "${@@Q}" | pr -to 8 >&2
45 diag_pacman_conf() {
46 echo >&2 'Error:'
48 cat <<EOF | pr -to 4 >&2
49 aur-$argv0 could not find a pacman.conf(5) file for container usage. Before
50 using --chroot, make sure this file is created and valid. See OPTIONS in
51 aur-$argv0(1) for configuration details.
53 The following file paths were checked:
54 EOF
55 printf '%s\n' "${@@Q}" | pr -to 8 >&2
58 get_local_upgrades() {
59 local repo=$1
61 # pacman prints diagnostics (::) to standard output, but does not accept
62 # repositories starting in `:: `. Redirect output accordingly.
63 LANG=C pacman -Sup --print-format '%r/%n' | awk -F/ -v repo="$repo" '
64 $1 ~ repo {print $2}
65 $1 ~ /^:: / {print $0 >"/dev/stderr"}
67 return "${PIPESTATUS[0]}"
70 trap_exit() {
71 if [[ ! -v AUR_DEBUG ]]; then
72 rm -rf -- "$tmp"
74 # Only remove package directory if all files were moved (#593)
75 if ! rm -df -- "$var_tmp"; then
76 diag_moved_packages "$var_tmp"/*
78 else
79 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
80 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$var_tmp"
84 usage() {
85 printf >&2 'usage: %s [-acfNS] [-d repo] [--root path] [--margs makepkg_arg...]\n' "$argv0"
86 exit 1
89 # mollyguard for makepkg
90 if (( UID == 0 )) && [[ ! -v AUR_ASROOT ]]; then
91 printf >&2 'warning: aur-%s is not meant to be run as root.\n' "$argv0"
92 printf >&2 'warning: To proceed anyway, set the %s variable.\n' 'AUR_ASROOT'
93 exit 1
96 ## option parsing
97 opt_short='a:d:D:U:AcCfnrsvzLNRST'
98 opt_long=('arg-file:' 'chroot' 'database:' 'force' 'root:' 'sign' 'gpg-sign'
99 'verify' 'directory:' 'no-sync' 'pacman-conf:' 'remove' 'pkgver'
100 'rmdeps' 'no-confirm' 'no-check' 'ignore-arch' 'log' 'new'
101 'makepkg-conf:' 'bind:' 'bind-rw:' 'prevent-downgrade' 'temp'
102 'syncdeps' 'clean' 'namcap' 'checkpkg' 'makepkg-args:' 'user:'
103 'margs:' 'buildscript:' 'null')
104 opt_hidden=('dump-options' 'ignorearch' 'noconfirm' 'nocheck' 'nosync' 'repo:'
105 'results:' 'results-append:')
107 if opts=$(getopt -o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
108 eval set -- "$opts"
109 else
110 usage
113 unset db_name db_path db_root makepkg_conf pacman_conf results_file pkgrel_incr 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 # makepkg options (common)
159 -A|--ignorearch|--ignore-arch)
160 makepkg_common_args+=(--ignorearch)
161 makechrootpkg_makepkg_args+=(--ignorearch) ;;
162 -n|--noconfirm|--no-confirm)
163 makepkg_common_args+=(--noconfirm) ;;
164 -r|--rmdeps)
165 makepkg_common_args+=(--rmdeps) ;;
166 -s|--syncdeps)
167 makepkg_common_args+=(--syncdeps) ;;
168 # makepkg options (build)
169 -C|--clean)
170 makepkg_args+=(--clean) ;;
171 -L|--log)
172 makepkg_args+=(--log) ;;
173 --nocheck|--no-check)
174 makepkg_args+=(--nocheck)
175 makechrootpkg_makepkg_args+=(--nocheck) ;;
176 --makepkg-args|--margs)
177 shift; IFS=, read -a arg -r <<< "$1"
178 makepkg_args+=("${arg[@]}")
179 makechrootpkg_makepkg_args+=("${arg[@]}") ;;
180 # repo-add options
181 -v|--verify)
182 repo_add_args+=(-v) ;;
183 -R|--remove)
184 repo_add_args+=(-R) ;;
185 --new)
186 repo_add_args+=(-n) ;;
187 --prevent-downgrade)
188 repo_add_args+=(-p) ;;
189 # other options
190 --results)
191 shift; results_file=$1 ;;
192 --results-append)
193 shift; results_file=$1; truncate=0 ;;
194 -z|--null)
195 read_args+=(-d $'\0') ;;
196 --dump-options)
197 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
198 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
199 exit ;;
200 --) shift; break ;;
201 esac
202 shift
203 done
205 # Assign environment variables
206 : "${db_ext=$AUR_DBEXT}" "${db_root=$AUR_DBROOT}" "${db_repo=$AUR_REPO}"
208 # shellcheck disable=SC2174
209 mkdir -pm 0700 -- "${TMPDIR:-/tmp}/aurutils-$UID"
210 tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXXX")
212 # shellcheck disable=SC2174
213 mkdir -pm 0700 -- "${TMPDIR:-/var/tmp}/aurutils-$UID"
214 var_tmp=$(mktemp -d --tmpdir="${TMPDIR:-/var/tmp/}" "aurutils-$UID/$argv0.XXXXXXXX")
216 trap 'trap_exit' EXIT
217 trap 'exit' INT
219 # XXX: default paths can be set through the Makefile
220 if (( chroot )); then
221 # The pacman configuration in the chroot may contain a local repository that
222 # is not configured on the host. Therefore, $db_name is only used for the
223 # default paths below when specified on the command-line or through `AUR_REPO`.
224 if [[ $db_name ]]; then
225 default_pacman_paths=("/etc/aurutils/pacman-$db_name.conf"
226 "/etc/aurutils/pacman-$machine.conf")
227 default_makepkg_paths=("/etc/aurutils/makepkg-$db_name.conf"
228 "/etc/aurutils/makepkg-$machine.conf")
229 else
230 default_pacman_paths=("/etc/aurutils/pacman-$machine.conf")
231 default_makepkg_paths=("/etc/aurutils/makepkg-$machine.conf")
234 # Change the default /usr/share/devtools/pacman-extra.conf in aur-chroot to
235 # /etc/aurutils/pacman-<repo>.conf or /etc/aurutils/pacman-<uname>.conf in
236 # aur-build, and pass it on to aur-chroot (#824, #846)
237 for def in "${default_pacman_paths[@]}"; do
238 if [[ -f $def ]] && [[ ! -v pacman_conf ]]; then
239 pacman_conf=$def
240 break
242 done
244 # The same as above but for /etc/aurutils/makepkg-<repo>.conf or
245 # /etc/aurutils/makepkg-<uname>.conf. If the file is not found, fallback to
246 # makepkg.conf files in /usr/share/devtools.
247 for def in "${default_makepkg_paths[@]}"; do
248 if [[ -f $def ]] && [[ ! -v makepkg_conf ]]; then
249 makepkg_conf=$def
250 break
252 done
254 # No pacman configuration is available for the container, or it points to a
255 # non-existing file. Print a matching diagnostic and exit.
256 if [[ ! -v pacman_conf ]]; then
257 diag_pacman_conf "${default_pacman_paths[@]}"
258 exit 2
260 elif [[ ! -f $pacman_conf ]]; then
261 diag_pacman_conf "$pacman_conf"
262 exit 2
264 chroot_args+=(--pacman-conf "$pacman_conf")
266 # The default path is /usr/share/devtools/makepkg-<uname.conf>, which is
267 # copied to <container path>/etc/makepkg.conf by arch-nspawn.
268 if [[ -v makepkg_conf ]]; then
269 chroot_args+=(--makepkg-conf "$makepkg_conf")
270 else
271 # When `makechrootpkg` calls `makepkg` inside the container, it uses the devtools
272 # `makepkg.conf` for most variables including PKGEXT. (`makepkg --packagelist`)
273 makepkg_conf=$(aur chroot --path "${chroot_args[@]}")/etc/makepkg.conf
274 unset PKGEXT
277 # Propagate makepkg/makechrootpkg arguments to aur-chroot
278 if (( ${#makechrootpkg_args[@]} )); then
279 chroot_args+=(--cargs "$(args_csv "${makechrootpkg_args[@]}")")
281 if (( ${#makechrootpkg_makepkg_args[@]} )); then
282 chroot_args+=(--margs "$(args_csv "${makechrootpkg_makepkg_args[@]}")")
286 # Propagate makepkg and pacman configuration to other tools. This must be done
287 # BEFORE retrieving the local repository name/root. It should be done AFTER
288 # retrieving the container configuration, as it may contain (local) repositories
289 # not enabled on the host.
290 if [[ -v pacman_conf ]]; then
291 pacconf_args+=(--config "$pacman_conf")
294 if [[ -v makepkg_conf ]]; then
295 makepkg_common_args+=(--config "$makepkg_conf")
296 pkglist_args+=(--config "$makepkg_conf")
299 # Automatically choose the local repository based on the pacman configuration.
300 if [[ $db_name ]] && [[ $db_root ]]; then
301 db_path=$db_root/$db_name.${db_ext:-db}
302 db_path=$(realpath -- "$db_path")
303 else
304 { IFS=: read -r _ db_name
305 IFS=: read -r _ db_root
306 IFS=: read -r _ db_path # canonicalized
307 } < <(aur repo --status "${repo_args[@]}" "${pacconf_args[@]}")
308 wait "$!"
310 db_root=$(realpath -- "$db_root")
312 # Check that a valid database extension was retrieved (#700, #1038)
313 if [[ -z $AUR_DBEXT ]] && [[ $db_path =~ \.db$ ]]; then
314 printf >&2 '%s: %s does not have a valid database archive extension\n' "$argv0" "$db_path"
315 # TODO: this usually happens on file systems not supporting symbolic links
316 # (SMB/CIFS). Add a diagnostic to point towards AUR_DBEXT in this case
317 exit 2
320 # File permission checks
321 if [[ ! -f $db_path ]]; then
322 printf >&2 '%s: %s: not a regular file\n' "$argv0" "$db_path"
323 exit 2
324 elif [[ ! -w $db_path ]]; then
325 printf >&2 '%s: %s: permission denied\n' "$argv0" "$db_path"
326 exit 13
327 elif [[ -v pacman_conf ]] && [[ ! -r $pacman_conf ]]; then
328 printf >&2 '%s: %s: permission denied\n' "$argv0" "$pacman_conf"
329 exit 13
330 elif [[ -v makepkg_conf ]] && [[ ! -r $makepkg_conf ]]; then
331 printf >&2 '%s: %s: permission denied\n' "$argv0" "$makepkg_conf"
332 exit 13
335 # Write successfully built packages to file (#437, #980)
336 if [[ -v results_file ]]; then
337 results_file=$(realpath -- "$results_file")
338 (( truncate )) && true | tee "$results_file"
341 if (( chroot )); then
342 # Update pacman and makepkg configuration for the chroot build
343 # queue. A full system upgrade is run on the /root container to
344 # avoid lenghty upgrades for makechrootpkg -u.
345 aur chroot --create --update "${chroot_args[@]}"
348 if [[ -v queue ]]; then
349 exec {fd}< "$queue"
350 else
351 exec {fd}< <(echo "$startdir")
354 # Early consistency check for signed database
355 if (( ! sign_pkg )); then
356 db_sigs=("$db_root/$db_name".sig "$db_root/$db_name".files.sig)
358 if [[ -f ${db_sigs[0]} ]]; then
359 printf >&2 '%s: database signature found, but signing is disabled\n' "$argv0"
361 printf '%q\n' >&2 "${db_sigs[@]}"
362 exit 1
365 elif [[ -v GPGKEY ]]; then
366 #shellcheck disable=SC2086
367 ${AUR_GPG:-gpg} --list-keys "$GPGKEY"
368 gpg_args+=(-u "$GPGKEY")
371 while IFS= read "${read_args[@]}" -ru "$fd" path; do
372 cd -- "$startdir"
373 [[ $path ]] && cd -- "$path"
375 # Allow running repo-add(8) on existing packages (#839)
376 create_package=1
377 pkglist=()
379 # Run pkgver function before --packagelist (#500)
380 if (( run_pkgver )); then
381 #shellcheck disable=SC2086
382 ${AUR_MAKEPKG:-makepkg} -od "${makepkg_common_args[@]}" >&2
385 # Retrieve list of potential package paths. This is used to (optionally)
386 # check if package paths are already available in the local repository
387 # before builds. If so, the build is skipped and the path is passed to
388 # repo-add (create_package=0). If no paths are available, the package is
389 # assumed to not exist and the build proceeds as usual (create_package=1).
390 if (( ! overwrite )); then
391 exists=()
392 #shellcheck disable=SC2086
393 while read -r pkgpath; do
394 [[ -f $pkgpath ]] && exists+=("$pkgpath")
395 done < <(PKGDEST="$db_root" ${AUR_BUILD_PKGLIST:-aur build--pkglist} "${pkglist_args[@]}")
397 if ! wait "$!"; then
398 printf >&2 '%s: warning: failed to retrieve package list\n' "$argv0"
401 if (( ${#exists[@]} )); then
402 printf >&2 '%s: warning: skipping existing package (use -f to overwrite)\n' "$argv0"
403 create_package=0
405 printf '%q\n' >&2 "${exists[@]}"
406 pkglist=("${exists[@]}")
409 if (( ${#exists[@]} )) && [[ -v results_file ]]; then
410 printf "exist:file://%s\n" "${exists[@]}" | tee -a "$results_file" >/dev/null
414 if (( create_package )); then
415 if (( chroot )); then
416 PKGDEST="$var_tmp" aur chroot --build "${chroot_args[@]}"
417 else
418 #shellcheck disable=SC2086
419 PKGDEST="$var_tmp" LOGDEST="${LOGDEST:-$PWD}" \
420 ${AUR_MAKEPKG:-makepkg} "${makepkg_common_args[@]}" "${makepkg_args[@]}"
423 cd -- "$var_tmp"
424 pkglist=(!(*.sig)) # discard makepkg --sign from package list (#410)
425 else
426 cd -- "$var_tmp"
427 # pkglist has paths to $db_root/<pkg>
430 # Sign any packages without signatures, even if the packages are existing.
431 siglist=()
433 for p in "${pkglist[@]}"; do
434 # Package basename (equals $p if create_package=1)
435 p_base=${p##*/}
437 # Signature from makepkg --sign
438 if [[ -f $p_base.sig ]]; then
439 siglist+=("$p_base".sig)
441 # Skipped package build with signature
442 elif [[ -f $db_root/$p_base.sig ]] && [[ ! -f $p_base ]]; then
443 printf >&2 '%s: existing signature file %q\n' "$argv0" "$db_root/$p_base.sig"
445 # No candidate signature, generate one
446 elif (( sign_pkg )); then
447 #shellcheck disable=SC2086
448 ${AUR_GPG:-gpg} "${gpg_args[@]}" --output "$p_base".sig "$p"
450 printf >&2 '%s: created signature file %q\n' "$argv0" "$p_base".sig
451 siglist+=("$p_base".sig)
453 done
455 if (( create_package )); then
456 mv -f "${siglist[@]}" "${pkglist[@]}" "$db_root"
458 if [[ -v results_file ]]; then
459 printf "build:file://$db_root/%s\n" "${pkglist[@]}" | tee -a "$results_file" >/dev/null
462 elif (( ${#siglist[@]} )); then
463 mv -f "${siglist[@]}" "$db_root"
466 # Update database
467 #shellcheck disable=SC2086
468 env -C "$db_root" LANG=C ${AUR_REPO_ADD:-repo-add} "${repo_add_args[@]}" "$db_path" "${pkglist[@]}"
470 if (( chroot )) || (( no_sync )); then
471 continue
472 else
473 #shellcheck disable=SC2086
474 ${AUR_PACMAN_AUTH:-sudo} pacsync "$db_name"
475 #shellcheck disable=SC2086
476 ${AUR_PACMAN_AUTH:-sudo} pacsync "$db_name" --dbext=.files
478 # Retrieve upgrade targets in local repository. May error in case of
479 # conflicts or dependency errors.
480 mapfile -t targets < <(get_local_upgrades "$db_name")
481 wait "$!"
483 if (( ${#targets[@]} )); then
484 printf >&2 "%s: upgrading packages in repository '%s'\n" "$argv0" "$db_name"
485 #shellcheck disable=SC2086
486 printf '%s\n' "${targets[@]}" | ${AUR_PACMAN_AUTH:-sudo} pacman -S --noconfirm -
489 done
491 exec {fd}<&-
493 # vim: set et sw=4 sts=4 ft=sh: