script/makepkg: regenerate ld.so.hints on chroot startup to avoid ldconfig errors...
[CDN/Mosi.git] / script / makepkg
1 #!/bin/sh
2
3 # Load shlib and modules
4 _root="$(dirname "${0}")"; . "${_root}/lib/env.sh"
5 want log root
6
7 ########
8 #
9 # Chroot handling
10 #
11 ########
12
13 # Prepare a chroot for work
14 chprepare() {
15   # Verify environment sanity
16   [ -d "${chroot_dir}" ] && omg "${chroot_dir}: directory exists; purging" && chdestroy
17   mount | grep -q "${chroot_dir}" && wtf "Stuff is mounted under ${chroot_dir}; cannot continue"
18   [ -d "${seed_dir}" -a -f "${seed_dir}/COPYRIGHT" ] || wtf "Use 'makeworld' to create a root build first"
19   [ -f "/usr/ports/UPDATING" ] || wtf "Need ports tree in /usr/ports to build"
20   [ -f "/usr/src/sys/conf/newvers.sh" ] || omg "No base sourcetree in /usr/src"
21
22   meh "Setting up chroot tree"
23
24   # Just in case we're aborted partway through the prepare
25   trap "chdestroy" exit hup int term kill
26
27   # Make sure config dir exists for later save
28   [ -d "${conf_dir}" ] || mkdir -p "${conf_dir}"
29
30   # Populate initial seed
31   mkdir -p "$(dirname "${chroot_dir}")" || wtf "chroot path create failed"
32   cp -pR "${seed_dir}" "${chroot_dir}" || wtf "chroot seeding failed"
33
34   # Create distfile cache
35   mkdir -p "${dist_dir}" || wtf "mkdir ${dist_dir} failed"
36
37   # Create mountpoint directories
38   mkdir -p "${chroot_dir}/dev" || wtf "mkdir /dev failed"
39   mkdir -p "${chroot_dir}/usr/src" || wtf "mkdir /usr/src failed"
40   mkdir -p "${chroot_dir}/usr/obj" || wtf "mkdir /usr/obj failed"
41   mkdir -p "${chroot_dir}/usr/ports" || wtf "mkdir /usr/ports failed"
42   mkdir -p "${chroot_dir}/var/ports/distfiles" || wtf "mkdir /var/ports/distfiles failed"
43
44   # Create pkg directories
45   mkdir -p "${pkg_dir}" || wtf "mkdir ${chroot_pkg_dir} failed"
46   mkdir -p "${bdeps_dir}" || wtf "mkdir ${chroot_bdeps_dir} failed"
47
48   # Copy in cached configuration, if necessary
49   [ -f "${conf_dir}/make.conf" ] && cp -p "${conf_dir}/make.conf" "${chroot_dir}/etc/make.conf"
50
51   # If you have WITHOUT_INFO set in src.conf during buildworld/installworld, ports that need
52   # makeinfo and install-info will fail horribly; stub them
53   [ -f "${chroot_dir}/usr/bin/makeinfo" ] || ln -sf true "${chroot_dir}/usr/bin/makeinfo"
54   [ -f "${chroot_dir}/usr/bin/install-info" ] || ln -sf true "${chroot_dir}/usr/bin/install-info"
55
56   # Tweak make.conf to support read-only ports tree
57   cat <<EOF >> "${chroot_dir}/etc/make.conf"
58
59 # Read-only ports tree
60 DISTDIR=/var/ports/distfiles
61 PACKAGES=/var/ports/packages
62 WRKDIRPREFIX=/usr/obj
63 EOF
64
65   trap "" exit hup int term kill
66   meh "Chroot tree set up in ${chroot_dir}"
67
68   return 0
69 }
70
71 # Set up chroot mounts and runtime configuration
72 chstartup() {
73   [ -d "${chroot_dir}" -a -f "${chroot_dir}/COPYRIGHT" -a -d "${chroot_dir}/dev" ] || wtf "Chroot not prepared"
74   chup? && return 0
75
76   # Rollback if a problem occurs during startup
77   trap "chshutdown" exit hup int term kill
78
79   meh "Starting up chroot"
80
81   # Necessary mountpoints
82   mount -t devfs devfs "${chroot_dir}/dev" || wtf "mount /dev failed"
83   mount -t nullfs -r /usr/src "${chroot_dir}/usr/src" || wtf "mount /usr/src failed"
84   mount -t nullfs -r /usr/ports "${chroot_dir}/usr/ports" || wtf "mount /usr/ports failed"
85   mount -t nullfs -w "${dist_dir}" "${chroot_dir}/var/ports/distfiles" || wtf "mount /var/ports/distfiles failed"
86
87   # Chroot configuration
88   cp -f /etc/resolv.conf "${chroot_dir}/etc/resolv.conf" || wtf "seeding /etc/resolv.conf failed"
89
90   # Load port configuration, now that chroot is running
91   port_load_config
92
93   # Update ld.so.hints
94   cheval "/etc/rc.d/ldconfig start"
95
96   trap "" exit hup int term kill
97   meh "Chroot up and running in ${chroot_dir}"
98
99   return 0
100 }
101
102 # Check if the chroot is probably ready for use
103 chup?() {
104   [ -d "${chroot_dir}" -a -f "${chroot_dir}/COPYRIGHT" -a -c "${chroot_dir}/dev/null" ]
105   return $?
106 }
107
108 # Unmount all chroot directories
109 chshutdown() {
110   # Short-circuit if nothing is mounted
111   mount | grep -q "${chroot_dir}" || return 0
112   meh "Shutting down chroot"
113   umount "${chroot_dir}/var/ports/distfiles"
114   umount "${chroot_dir}/usr/ports"
115   umount "${chroot_dir}/usr/src"
116   umount "${chroot_dir}/dev"
117   return 0
118 }
119
120 # Remove all chroot contents
121 chdestroy() {
122   chshutdown
123   meh "Destroying chroot"
124   chflags -R noschg "${chroot_dir}" || wtf "noschg failed"
125   rm -Rf "${chroot_dir}" || wtf "chroot removal failed"
126   return 0
127 }
128
129 # Evaluate a command line within the chroot
130 cheval() {
131   chup? || wtf "Chroot not ready"
132   chroot "${chroot_dir}" env -i ${chroot_env} /bin/sh -c "cd ${chroot_pkg_dir}; ${*}"
133   return $?
134 }
135
136 # Run chrooted make
137 chmake() {
138   local port="/usr/ports/${1##/usr/ports/}"
139   shift
140   cheval "make -C ${port} ${*}"
141   return $?
142 }
143
144 ########
145 #
146 # Port Dependency Tracking
147 #
148 ########
149
150 # Translate port origin to package name
151 port2pkg() {
152   while [ "${1}" ]
153   do
154     chmake "${1}" -V PKGNAME
155     shift
156   done
157 }
158
159 # Build dependencies (shallow, recursive, package)
160 port_bdeps() {
161   while [ "${1}" ]
162   do
163     chmake "${1}" build-depends-list | sed -e 's#^/usr/ports/##'
164     shift
165   done
166 }
167 port_all_bdeps() {
168   # Clear cache if first value isn't '-r'
169   [ "${1}" = '-r' ] && shift || { : > "${_port_all_bdeps_cache}"; : > "${_port_all_rdeps_cache}"; }
170   # rdeps for rdeps are rdeps, bdeps for rdeps are bdeps, rdeps for bdeps are bdeps; thus:
171   (
172     port_bdeps "${@}" | while read port
173     do
174       [ "${VERBOSE_CACHE}" ] && logf "**** bdep cache %s: '%s'\n" "$(grep -q "${port}" \
175         "${_port_all_bdeps_cache}" && echo "hit" || echo "miss")"  "${port}" >&2
176       if ! grep -q "${port}" "${_port_all_bdeps_cache}"
177       then
178         echo "${port}" >> "${_port_all_bdeps_cache}"
179         echo "${port}"
180         port_all_bdeps -r "${port}"
181         port_all_rdeps -r "${port}"
182       fi
183     done
184     port_all_rdeps -r "${@}" | while read port
185     do
186       [ "${VERBOSE_CACHE}" ] && logf "**** bdep cache %s: '%s'\n" "$(grep -q "${port}" \
187         "${_port_all_bdeps_cache}" && echo "hit" || echo "miss")"  "${port}" >&2
188       if ! grep -q "${port}" "${_port_all_bdeps_cache}"
189       then
190         echo "${port}" >> "${_port_all_bdeps_cache}"
191         port_all_bdeps -r "${port}"
192       fi
193     done
194   ) | sort | uniq
195 }
196 pkg_bdeps() {
197   port2pkg $(port_all_bdeps "${@}")
198 }
199
200 # Runtime dependencies (shallow, recursive, package)
201 port_rdeps() {
202   while [ "${1}" ]
203   do
204     chmake "${1}" run-depends-list | sed -e 's#^/usr/ports/##'
205     shift
206   done
207 }
208 port_all_rdeps() {
209   # Clear cache if first value isn't '-r'
210   [ "${1}" = '-r' ] && shift || { : > "${_port_all_rdeps_cache}"; }
211   port_rdeps "${@}" | while read port
212   do
213     [ "${VERBOSE_CACHE}" ] && logf "**** rdep cache %s: '%s'\n" "$(grep -q "${port}" \
214       "${_port_all_rdeps_cache}" && echo "hit" || echo "miss")"  "${port}" >&2
215     if ! grep -q "${port}" "${_port_all_rdeps_cache}"
216     then
217       echo "${port}" >> "${_port_all_rdeps_cache}"
218       echo "${port}"
219       port_all_rdeps -r "${port}"
220     fi
221   done | sort | uniq
222 }
223 pkg_rdeps() {
224   port2pkg $(port_all_rdeps "${@}")
225 }
226
227 # All dependencies (shallow, recursive, package)
228 port_deps() {
229   while [ "${1}" ]
230   do
231     chmake "${1}" all-depends-list | sed -e 's#^/usr/ports/##'
232     shift
233   done
234 }
235 port_all_deps() {
236   port_deps "${@}" | while read port
237   do
238     echo "${port}"
239     port_deps "${port}"
240   done | sort | uniq
241 }
242 pkg_deps() {
243   port2pkg $(port_all_deps "${@}")
244 }
245
246 # Dump a list of leaf ports from a working system
247 # Leaf ports are ports that have nothing depending upon them
248 # These are generally the top-level ports; everything else should
249 # be pulled in by them; on a system that has had updates applied
250 # this may pick up orphaned packages as well, so try cutleaves?
251 leaf_ports() {
252   ( cd /var/db/pkg; ls -1 ) | while read pkg
253   do
254     pkg_info -Rq "${pkg}" | grep -q '' || pkg_info -oq "${pkg}"
255   done | sort
256 }
257
258 # Display a tree of all build dependencies (and
259 # any runtime dependencies those build dependencies
260 # depend upon). Individual ports may appear more than
261 # once, as it is a tree and not a list.
262 port_bdep_tree() {
263   port="${1##/usr/ports/}"
264   printf "%${2}s%s\n" "" "${port}"
265   
266   ( chmake "${port}" build-depends-list
267     [ "${2:-0}" -gt 0 ] && chmake "${port}" run-depends-list
268   ) | sort -u | while read port
269   do
270     port_bdep_tree "${port}" $(( ${2:-0} + 1 ))
271   done
272 }
273
274 # Display a tree of all runtime dependencies. Individual
275 # ports may appear more than once, as it is a tree and
276 # not a list.
277 port_rdep_tree() {
278   port="${1##/usr/ports/}"
279   printf "%${2}s%s\n" "" "${port}"
280   
281   chmake "${port}" run-depends-list | sort -u | while read port
282   do
283     port_rdep_tree "${port}" $(( ${2:-0} + 1 ))
284   done
285 }
286
287 ########
288 #
289 # Port Configuration Handling
290 #
291 ########
292
293 # Run make config on a list of ports
294 port_config() {
295   while [ "${1}" ]
296   do
297     meh "port config ${1}"
298     chmake "${1}" config < /dev/tty
299     shift
300   done
301 }
302
303 # Make config-conditional for a list of ports, and all dependencies
304 port_config_recursive() {
305   # Clear cache if first value isn't '-r'
306   [ "${1}" = '-r' ] && shift || _port_config_recursive_cache=""
307   while [ "${1}" ]
308   do
309     # Do not use config-recursive because it computes the depchain first;
310     # instead, manually compute the depchain after each config to ensure the
311     # proper depchain is followed. Use config-conditional to avoid dialog
312     # if the config is already complete. Also use a cache to avoid re-config
313     # and re-recurse on previously handled port branches.
314     if echo "${_port_config_recursive_cache}" | grep -qv " ${1} "
315     then
316       meh "port config-recursive ${1}"
317       chmake "${1}" config-conditional < /dev/tty
318       port_config_recursive -r $(port_deps "${1}")
319       _port_config_recursive_cache="${_port_config_recursive_cache} ${1} "
320     fi
321     shift
322   done
323 }
324 # Config cache
325 unset _port_config_recursive_cache
326
327 # Remove saved config for a list of ports
328 port_rmconfig() {
329   while [ "${1}" ]
330   do
331     meh "port rmconfig ${1}"
332     chmake "${1}" rmconfig
333     shift
334   done
335 }
336
337 # Remove saved config for a list of ports and all dependencies
338 port_rmconfig_recursive() {
339   meh "port rmconfig-recursive ${*}"
340   port_rmconfig $(echo "${@}" $(port_all_deps "${@}") | sort | uniq)
341 }
342
343 # Obliterate saved configuration for all ports
344 port_rmconfig_all() {
345   meh "port rmconfig-all"
346   cheval "cd /var/db/ports; find . -name 'options' -delete; find . -type d | xargs rmdir 2>/dev/null"
347 }
348
349 # Restore port build options from directory
350 port_load_config() {
351   [ -d "${conf_dir}/port.options" ] || return 0
352   meh "port load-config"
353   ( cd "${conf_dir}/port.options"; find . | cpio -oHnewc ) | cheval "cd /var/db/ports; cpio -i" || wtf "port load-config failed"
354 }
355
356 # Dump port build options to directory
357 port_save_config() {
358   meh "port save-config"
359   mkdir -p "${conf_dir}/port.options.tmp"
360   cheval "cd /var/db/ports; find . -type d -o -type f -name options | cpio -oHnewc" | ( cd "${conf_dir}/port.options.tmp"; cpio -i ) || wtf "port safe-config failed"
361   rm -Rf "${conf_dir}/port.options" && mv -f "${conf_dir}/port.options.tmp" "${conf_dir}/port.options" || wtf "port save-config atomic commit failed"
362 }
363
364 ########
365 #
366 # Port distfile handling
367 #
368 ########
369
370 # Recursively retrieve distfiles
371 port_fetch_recursive() {
372   while [ "${1}" ]
373   do
374     meh "fetch-recursive ${1}"
375     chmake "${1}" fetch-recursive
376     shift
377   done
378 }
379
380 ########
381 #
382 # Port building and packaging
383 #
384 ########
385
386 # Copy in and install dependency packages
387 port_load_deps() {
388   local port="${1}"
389   for pkg in $(port2pkg $(port_all_deps "${port}"))
390   do
391     cp -f "${final_bdeps_dir}/${pkg}.tbz" "${bdeps_dir}" 2>/dev/null && meh "Loading dependent ${pkg}"
392   done
393   if ls "${bdeps_dir}"/*.tbz >/dev/null 2>&1
394   then
395     meh "Installing dependencies"
396     cheval "cd ${chroot_bdeps_dir}; pkg_add -F *" || wtf "port_load_deps ${port} failed"
397   fi
398 }
399
400 # Build and install a port
401 port_build() {
402   local port="${1}"
403   meh "Building ${port}"
404   chmake "${port}" clean build install clean || wtf "port_build ${port} failed"
405 }
406
407 # Package a port
408 port_package() {
409   local port="${1}"
410   meh "Creating rdep package tree for ${port}"
411   cheval "pkg_create -Rvb $(port2pkg "${port}")" || wtf "port_package ${port} failed"
412 }
413
414 # Package port build dependencies, unless they're already run deps
415 port_stash_bdeps() {
416   meh "Stashing unsaved bdeps"
417   # This doesn't work well, because there can be bdeps that aren't listed as bdeps (bison)
418   #for pkg in $(pkg_bdeps "${1}")
419   #do
420   #  [ ! -f "${pkg_dir}/${pkg}.tbz" ] && cheval "cd ${chroot_bdeps_dir}; pkg_create -vb ${pkg}"
421   #done
422   #
423   # Instead, just save everything that's not already in rdeps as bdeps
424   for pkg in $(cheval pkg_info | awk '{print $1}')
425   do
426     if [ ! -f "${pkg_dir}/${pkg}.tbz" -a ! -f "${bdeps_dir}/${pkg}.tbz" ]
427     then
428       cheval "cd ${chroot_bdeps_dir}; pkg_create -vb ${pkg}" || wtf "port_stash_bdeps failed"
429     fi
430   done
431 }
432
433 # Copy generated packages out of tree
434 pkg_final() {
435   meh "Moving created packages to repo"
436   mkdir -p "${final_pkg_dir}"
437   ls "${pkg_dir}"/*.tbz >/dev/null 2>&1 && mv -f "${pkg_dir}"/*.tbz "${final_pkg_dir}"
438   mkdir -p "${final_bdeps_dir}"
439   ls "${bdeps_dir}"/*.tbz >/dev/null 2>&1 && mv -f "${bdeps_dir}"/*.tbz "${final_bdeps_dir}"
440   # link everything into ${bdeps_dir} so we can find it easily later
441   ( cd "${final_pkg_dir}"; find . -type f | cpio -plu --quiet "${final_bdeps_dir}" )
442 }
443
444 # Delete all installed packages (hope you saved them first)
445 pkg_delete_all() {
446   meh "Clearing out installed packages"
447   cheval "pkg_delete -f \*" || wtf "pkg_delete_all failed"
448 }
449
450 ########
451 #
452 # All of the above?
453 #
454 ########
455
456 # Execute a complete port build, using prebuilt packages to fulfill dependencies when available
457 # Be sure to chsetup and populate your config before running!
458 chport() {
459   while [ "${1}" ]
460   do
461     local port="${1}"
462     meh "config-recursive"
463     port_config_recursive "${port}"
464     meh "fetch-recursive"
465     port_fetch_recursive "${port}"
466     meh "load-deps"
467     port_load_deps "${port}"
468     meh "build"
469     port_build "${port}"
470     meh "package"
471     port_package "${port}"
472     meh "stash-deps"
473     port_stash_bdeps
474     meh "final"
475     pkg_final
476     meh "delete-all"
477     pkg_delete_all
478     shift
479   done
480 }
481
482
483 ########
484 #
485 # Configuration variable setup
486 #
487 ########
488
489 ARCH="${ARCH:-$(uname -m)}"
490 CONF="${CONF:-GENERIC}"
491
492 # Root directory of makepkg
493 ROOT="$(realpath "$(dirname "${0}")/..")"
494
495 # Location of targets
496 TARGETS="${ROOT}/targets"
497
498 # Base directory for selected target
499 base_dir="${TARGETS}/${ARCH}/${CONF}"
500
501 # Link to appropriate world
502 world_dir="${base_dir}/world"
503
504 # Verify that world points to a proper world
505 if [ ! -d "${world_dir}" ]
506 then
507   omg "World link is not appropriate; defaulting to ${ARCH}/GENERIC"
508   world_dir="${ROOT}/worlds/${ARCH}/GENERIC"
509 fi
510
511 # Directory holding configuration
512 conf_dir="${base_dir}/config"
513
514 # Root tree for chroot seeding
515 seed_dir="${world_dir}/root"
516
517 # Directory where distfiles will be stored between builds (common to all targets)
518 dist_dir="${ROOT}/seed/distfiles"
519
520 # Final directory for built packages (Outside chroot)
521 final_pkg_dir="${base_dir}/pkg"
522 final_bdeps_dir="${base_dir}/bdeps"
523
524 # Chroot directory
525 chroot_dir="${base_dir}/chroot"
526
527 # Package directories (must be under ${chroot_dir})
528 pkg_dir="${chroot_dir}/pkg"
529 bdeps_dir="${pkg_dir}/bdeps"
530
531 # Compute in-chroot pkg and bdeps dirs
532 chroot_pkg_dir="${pkg_dir##${chroot_dir}}"
533 chroot_bdeps_dir="${bdeps_dir##${chroot_dir}}"
534
535 # Cache files to speed up recursive bdep/rdep scanning
536 _port_all_bdeps_cache="${chroot_dir}/tmp/_port_all_bdeps_cache"
537 _port_all_rdeps_cache="${chroot_dir}/tmp/_port_all_rdeps_cache"
538
539 # Chroot environment
540 chroot_env="
541 USER=root
542 HOME=/root
543 LOGNAME=root
544 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
545 SHELL=/bin/sh
546 TERM=${TERM}
547 "
548
549 help() {
550   cat <<EOF
551 Configuration variables:
552
553 ARCH (${ARCH})
554 CONF (${CONF})
555
556 Functions:
557
558 Chroot Handling:
559 ------------------------------
560 chprepare      Prepare chroot
561 chstartup      Start up chroot
562 cheval         Evaluate a command string within chroot
563 chmake         Run port make within chroot
564 chshutdown     Shut down chroot
565 chdestroy      Destroy chroot
566
567 Port Dependency Tracking
568 ------------------------------
569 port2pkg       Translate a port dirname to a package name
570
571 port_bdeps     Compute port build dependencies
572 port_all_bdeps Recursively compute port build dependencies
573 pkg_bdeps      port2pkg port_all_bdeps
574
575 port_rdeps     Compute port run dependencies
576 port_all_rdeps Recursively compute port run dependencies
577 pkg_rdeps      port2pkg port_all_rdeps
578
579 port_deps      Compute port build and run dependencies
580 port_all_deps  Recursively compute port build and run dependencies
581 pkg_deps       port2pkg port_all_deps
582
583 leaf_ports     Dump a list of leaf ports (ports with nothing depending upon them) from a running system
584 port_bdep_tree Display a tree of all build dependencies
585 port_rdep_tree Display a tree of all run dependencies
586
587 Port Configuration Handling
588 ------------------------------
589 port_config              Make config for a port
590 port_config_recursive    Make config recursive, recomputing depchain for each
591 port_rmconfig            Remove saved configuration
592 port_rmconfig_recursive  Remove saved configuration for a port and all dependencies
593 port_rmconfig_all        Remove all saved configuration
594 port_load_config         Restore port configuration from cpio
595 port_save_config         Save port configuration to cpio
596
597 Port Distfile Handling
598 ------------------------------
599 port_fetch_recursive     fetch-recursive
600
601 Port Building And Packaging
602 ------------------------------
603 port_load_deps           
604 port_build               
605 port_package             
606 port_stash_bdeps         
607 pkg_final                
608 pkg_delete_all           
609
610 All Of The Above?
611 ------------------------------
612
613 chport <port>            
614 EOF
615 }
616
617 # Blind passthru for testing
618 [ "${#}" ] && "${@}"
619
620 # Todo: Clean up entrypoint to support proper options
621 # Todo: Add methods to autoprocess a ports.lst file to produce packages
622 # Todo: Unify chroot handling with makeworld
623