summaryrefslogtreecommitdiff
path: root/src/chroot-tools/libremakepkg
blob: 945b8aec3d8a36a54783ec00d9de97041415b06e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#!/usr/bin/env bash
set -euE
# libremakepkg

# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
# Copyright (C) 2012-2015, 2017-2018 Luke Shumaker <lukeshu@parabola.nu>
#
# License: GNU GPLv2+
#
# This file is part of Parabola.
#
# Parabola is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Parabola is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Parabola. If not, see <http://www.gnu.org/licenses/>.


source "$(librelib conf)"
source "$(librelib messages)"
source "$(librelib chroot/makechrootpkg)"

set -o pipefail
shopt -s nullglob
umask 0022

# Global variables:
readonly _indent="$(librelib chroot/indent)"
readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false)
SANE=true  # can be changed with the -I flag
NONET=true # can be changed with the -N flag
# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf
# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf
# LIBREUSER, LIBREHOME set by conf.sh
librechroot_flags=()

# Hooks ########################################################################

hook_pre_build=(:)
hook_post_build=(:)
hook_check_pkgbuild=(:)
hook_check_pkg=(:)
source "$(librelib chroot/hooks-chcleanup.sh)"
source "$(librelib chroot/hooks-check.sh)"
source "$(librelib chroot/hooks-distcc.sh)"

# Boring/mundane functions #####################################################

indent() {
  "$_indent" ' |  '
}

# Usage: exit_copy $copydir $src_owner
# End immediately, but copy log files out
exit_copy() {
  local copydir=$1
  local src_owner=$2
  if ! $INCHROOT; then
    msg "Copying log and package files out of the chroot..."
    move_products "$copydir" "$src_owner"
  fi
}

# Usage; run_hook $hookname $args...
run_hook() {
  local hookname=$1; shift
  local hookvar="hook_${hookname}[@]"
  local fails=()

  for hook in "${!hookvar}"; do
    # The "& wait $!" trick prevents "||" from disabling "set -e"
    { "$hook" "$@" |& indent; } &
    wait $! || fails+=("$hook")
  done

  if [[ ${#fails[@]} -gt 0 ]]; then
    error "Failure(s) in %s: %s" "$hookname" "${fails[*]}"
    return $EXIT_FAILURE
  else
    return $EXIT_SUCCESS
  fi
}

# Usage: add_to_local_repo $copydir $pkgfiles...
add_to_local_repo() {
  local copydir=$1; shift
  mkdir -p "$copydir/repo"
  local pkgfile
  for pkgfile in "$@"; do
    cp "$pkgfile" "$copydir/repo"
    pushd "$copydir/repo" >/dev/null
    repo-add repo.db.tar.gz "${pkgfile##*/}"
    popd >/dev/null
  done
}

hook_post_build+=('chroot_cleanup')
chroot_cleanup() {
  local copydir=$1
  rm -f -- "$copydir"/chrootbuild
}

build() (
DBG "libremakepkg::build() IN"

  local copydir=$1
  local srcpkg=$2
  local repack=$3
  local makepkg_args=("${@:4}")

  local startdir
  startdir=$(mktemp -d)
  chown "$LIBREUSER:" "$startdir"
  trap "rm -rf -- ${startdir@Q}" EXIT
  sudo -u "$LIBREUSER" bsdtar -xf "$srcpkg" -C "$startdir" --strip-components 1

  local run_ynet=()
  local run_nnet=()
  if $INCHROOT; then
    local _run=(sh -c "mount --bind -o ro -- ${startdir@Q} ${startdir@Q} && cd ${startdir@Q} && \$@" --)
    run_ynet=(unshare --mount       -- "${_run[@]}")
    run_nnet=(unshare --mount --net -- "${_run[@]}")
  else
    librechroot_flags+=(-r "$startdir:/startdir")
    run_ynet=(librechroot "${librechroot_flags[@]}"    run)
    run_nnet=(librechroot "${librechroot_flags[@]}" -N run)
  fi
  $NONET || run_nnet=("${run_ynet[@]}")

DBG "libremakepkg::build() prepare_chroot"

  prepare_chroot "$copydir" "$LIBREHOME" "$repack" false # generates 'chrootbuild'

DBG "libremakepkg::build() pre_build run_nnet=${run_nnet[*]}"

  run_hook pre_build "$copydir"
  trap "run_hook post_build ${copydir@Q}; rm -rf -- ${startdir@Q}" EXIT
  "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" < /dev/null |& indent

DBG "libremakepkg::build() OUT"
)

# The main program #############################################################

usage() {
  local mount_msg="$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')"

  print "Usage: %s [options]" "${0##*/}"
  print 'This program will build your package.'
  echo
  prose 'If run from outside of a chroot, this command will make the
         following configuration changes in the chroot:'
  bullet 'whatever changes `librechroot` makes'
  bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`'
  bullet 'set `MAKEFLAGS` and `PACKAGER` in `/etc/makepkg.conf` to reflect
          the value outside of the chroot'
  bullet 'create a `builduser` with the same numeric UID as $LIBREUSER'
  bullet 'let this `builduser` call `sudo pacman` without a password'
  bullet 'set `keyserver-options` in `~builduser/.gnupg/gpg.conf`'
  bullet 'copy the GPG pubring of the host $LIBREUSER,
          as the GPG pubring of the chroot `builduser`'
  bullet 'add a pacman repositoriy of locally built packages'
  prose '$LIBREUSER is determined at runtime, per /usr/lib/libretools/conf.sh.
         $LIBREUSER is normally the login of the invoking user, per $SUDO_USER;
         but may be over-ridden by setting $LIBREUSER
         in the `configuration` section of conf.sh.'
  echo
  prose 'This command is configured both with `chroot.conf` (either in
         `/etc/libretools.d/` or `$XDG_CONFIG_HOME/libretools/`), and with
         makepkg.conf(5).  However, similarly to makepkg(8), it lets
         environment variables for {SRC,SRCPKG,PKG,LOG}DEST, MAKEFLAGS and
         PACKAGER override the settings in makepkg.conf(5).'
  echo
  prose 'The `-n` and `-l` options are passed to `librechroot`
        (see the librechroot documentation).'
  echo
  flag 'Options (librechroot):'                                                        \
       "-n <$(_ NAME)>"    'Name of the chroot to use'                                 \
       "-l <$(_ COPY)>"    'Name of, or absolute path to, the chroot copy to use'      \
       '-I'                "Allow packages older than their database entries to remain
                            installed, after updating the database manually.
                            This option can lead to an insane system configuaration.
                            Replacement packages will still be applied, for example,
                            unless they are helpd back explcitly; and there may be
                            dependency conflicts or shared library mis-matches.
                            Use this only in extreme circumstances; such as if you
                            need to enable [testing] without synchronizing afterward." \
       "-w <${mount_msg}>" 'Bind mount a file or directory, read/write'                \
       "-r <${mount_msg}>" 'Bind mount a file or directory, read-only'
  flag 'Options (libremakepkg):'                                               \
       '-N'             "Don't disable networking during prepare(),
                         build(), and package().  PLEASE don't use
                         this unless you have a special reason, its
                         use is a violation of Parabola policy."               \
       '-R'             'Repackage contents of the package without rebuilding' \
       "-S <$(_ FILE)>" 'Use an existing --allsource source-package (not-yet-implemented)'           \
       '-h'             'Show this message'
}

# Convenience method for use in option parsing
err_chflag() {
  local flag=$1
  error 'The -%s flag does not make sense inside of a chroot' "$flag"
  exit $EXIT_INVALIDARGUMENT
}

main() {
  # Initial variable values ##############################################
  local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER")
  local makepkg_args=(--syncdeps --noconfirm --log --holdver --skipinteg)
  local repack=false
  local chroot=''
  local srcpkg=''

  # Parse command line options ###########################################
  while getopts 'n:l:w:r:INRS:h' flag ; do
    case "${flag}" in
      n  ) chroot=$OPTARG                        ; ! $INCHROOT || err_chflag "$flag";;
      l  ) copy=$OPTARG                          ; ! $INCHROOT || err_chflag "$flag";;
      w|r) librechroot_flags+=(-$flag "$OPTARG") ; ! $INCHROOT || err_chflag "$flag";;
      I  ) SANE=false;;
      N  ) NONET=false;;
      R  ) repack=true; makepkg_args+=(-R);;
      S  ) srcpkg='';; # srcpkg=$OPTARG;; TODO: not yet implemented
      h  ) usage; exit $EXIT_SUCCESS;;
      *  ) usage >&2; exit $EXIT_INVALIDARGUMENT;;
    esac
  done
  shift $((OPTIND - 1))
  if [[ $# != 0 ]]; then
    error 'Extra arguments: %s' "$*"
    exit $EXIT_INVALIDARGUMENT
  fi

  # Resolve the chroot path ##############################################
  local copydir
  if $INCHROOT; then
    copydir='/'
  else
    load_conf chroot.conf CHROOTDIR CHROOT || exit
    [[ -z ${chroot} ]] || CHROOT=$chroot
    if [[ ${copy:0:1} = / ]]; then
      copydir=$copy
    else
      copydir="${CHROOTDIR}/${CHROOT}/${copy}"
    fi
    unset CHROOTDIR CHROOTEXTRAPKG
  fi
  unset chroot

  # Load makepkg configuration ###########################################
  # Note that all of these are globals
  PKGDEST="$(get_var makepkg PKGDEST "$PWD")"
  SRCDEST="$(get_var makepkg SRCDEST "$PWD")"
  SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")"
  LOGDEST="$(get_var makepkg LOGDEST "$PWD")"
  MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')"
  PACKAGER="$(get_var makepkg PACKAGER '')"

  # Quick sanity check ###################################################

  if (( EUID )); then
    error "This program must be run as root"
    exit $EXIT_NOPERMISSION
  fi

  if [[ -n $srcpkg ]]; then
    if [[ ! -f $srcpkg ]]; then
      error 'Source package does not exist: %s' "$srcpkg"
      exit $EXIT_INVALIDARGUMENT
    fi
  else
    if [[ ! -f PKGBUILD ]]; then
      error "No PKGBUILD exists in the present directory."
      exit $EXIT_FAILURE
    fi
  fi

  # Make sure that the various *DEST directories exist
  sudo -u "$LIBREUSER" mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST"


  # OK, we are starting now ##############################################

  if $INCHROOT; then
    lock 9 "/build/.lock" \
      "Waiting for existing lock on build directory to be released"
  else
    librechroot_flags+=(
      -n "$CHROOT"
      -l "$copy"
    )

    # Obtain a lock on the chroot
    lock 9 "$copydir.lock" \
      "Waiting for existing lock on chroot copy to be released: [%s]" "$copy"
    # Create the chroot if it does not exist
    msg 'Initializing the chroot...'
    librechroot "${librechroot_flags[@]}" make |& indent
  fi
DBG "libremakepkg::main() GUARD whoami=$(whoami) id=$(id)" ; return 1

  # Set target CARCH
  # note that we waited until after locking/creating the chroot to do this
  CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)"
  export CARCH

  # Pre-build
  msg 'Starting pre-build activities...'
  run_hook check_pkgbuild

  if [[ -n $srcpkg ]]; then
    msg 'Using existing source package %s' "$srcpkg"
    # TODO: symlink $srcpkg to ${SRCPKGDEST}/${pkgbase}-${evr}-${CARCH}${SRCEXT}
  else
    local srcpkgdest="$(mktemp -d)"
    chown "$LIBREUSER:" "$srcpkgdest"
#    trap "rm -rf -- ${srcpkgdest@Q}" EXIT
trap "echo TRAP ${srcpkgdest@Q} ; ls ${srcpkgdest@Q} ; rm -rf -- ${srcpkgdest@Q}" EXIT
DBG "libremakepkg::main() ->download_sources IN srcpkgdest='$srcpkgdest'"
    msg 'Downloading sources...'
    SRCPKGDEST="$srcpkgdest" download_sources "$copydir" "$LIBREUSER" |& indent
    srcpkg=("$srcpkgdest"/*)
DBG "libremakepkg::main() ->download_sources OUT srcpkg=${srcpkg[@]}" ; ls "$srcpkgdest" ;
    if (( ${#srcpkg[@]} != 1 )); then
      error 'Something went funny with makepkg --allsource'
      return $EXIT_FAILURE
    fi

    # We want to inject "-$pkgarch" in to srcpkg's filename, right before $SRCEXT
    local srcext pkgarch srcpkg_filename
    srcext="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg SRCEXT)"
    if [[ "$(bsdtar xfO "$srcpkg" --include='*/.SRCINFO' | grep $'\tarch =')" = $'\tarch = any' ]]; then
      pkgarch=any
    else
      pkgarch=$CARCH
    fi
    srcpkg_filename=${srcpkg##*/}
    srcpkg_filename=${srcpkg_filename%"${srcext}"}-${pkgarch}${srcext}
    mv -T -- "$srcpkg" "$SRCPKGDEST/${srcpkg_filename}"
    srcpkg="$SRCPKGDEST/${srcpkg_filename}"
    rmdir -- "${srcpkgdest}"
    trap EXIT
  fi

DBG "libremakepkg::main() build \"$copydir\" \"$srcpkg\" \"$repack\" \"${makepkg_args[@]}\""

  # Build
  msg 'Starting to build the package...'
  trap "exit_copy '$copydir' '$LIBREUSER'" EXIT
  build "$copydir" "$srcpkg" "$repack" "${makepkg_args[@]}"

  # Post-build
  msg 'Starting post-build activities...'
  run_hook check_pkg
  add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent
}


main "$@"