script/makeworld: support detached prepare/clean and deferred cleaning to reduce...
[CDN/Mosi.git] / script / makeworld
1 #!/bin/sh
2
3 # Makeworld dir structure should contain, at minimum:
4 # script
5 #  makeworld (this file)
6 # worlds
7 #  seed
8 #   base
9 #    <ARCH> (arch of build host: i386, amd64, etc)
10 #     base.* (virgin base tree of same arch as host, used to seed chroot for clean build)
11 #  <TARGET> (i386, amd64, etc)
12 #   <CONFIG> (GENERIC, SABA, SS4200, etc)
13 #    config
14 #     make.conf
15 #     src.conf
16 #     CONFIG (Matches config name: GENERIC, SABA, SS4200, etc)
17
18 # Load shlib and modules
19 _root="$(dirname "${0}")"; . "${_root}/lib/env.sh"
20 want log
21
22 pebkac() {
23   [ "${*}" ] && printf "%s\n\n" "${*}"
24   echo "Usage: $(basename "${0}") -m <makeopts> -t <target> -c <config> <target, ...>"
25   echo "  -m <makeopts>   Provide additional flags to make"
26   echo "  -t <target>     Target architecture (i386, amd64)"
27   echo "  -c <config>     Target configuration (in worlds/<target>/<config>)"
28   echo "  -h              Help!"
29   echo ""
30   echo "If, for some reason, you wish to spread out builds across multiple"
31   echo "invocations, these might come in handy:"
32   echo "  -p              Prepare chroot environment"
33   echo "  -d              Don't prepare or clean up chroot during run"
34   echo "  -q              Clean up chroot environment"
35   echo ""
36   echo "Available make targets:"
37   for target in ${make_tgts}
38   do
39     echo "  ${target}"
40   done
41   exit 1
42 }
43
44 # Prepare chroot for build
45 prepare() {
46   [ "${CHROOT_DIRTY}" -a ! "${CHROOT_PREPARE}" ] && return 0
47   # Verify environment sanity
48   [ -d "${build}" ] && omg "${build}: directory exists; purging" && cleanup
49   mount | grep -q "${build}" && wtf "Stuff is mounted under ${build}; cannot continue"
50   ls -1 "${seed}"/base.?? >/dev/null 2>&1 || wtf "Populate seed directory ${seed} first"
51   [ -f /usr/src/sys/conf/newvers.sh ] || wtf "Need sources in /usr/src to build"
52
53   # Cleanup trap here, so that an abort during prepare can clean up properly
54   trap "cleanup" exit hup int term kill
55
56   meh "Preparing build chroot"
57   [ -d "${build}" ] && wtf "${build}: directory exists"
58   mkdir -p "${build}" || wtf
59   cat "${seed}"/base.?? | tar xCf "${build}" - || wtf
60   mkdir -p "${build}/usr/obj" || wtf
61
62   meh "Mounting chroot filesystems"
63   mkdir -p "${world}/obj"
64   mkdir -p "${world}/root"
65   mount -t devfs devfs "${build}/dev" || wtf
66   # Mount /usr/src as a union, so that changes to it will not affect the underlying tree
67   # unionfs suffers from deadlocks; don't use it
68   mount -t nullfs -r /usr/src "${build}/usr/src" || wtf
69   mount -t nullfs "${world}/obj" "${build}/usr/obj" || wtf
70   mount -t nullfs "${world}/root" "${build}/mnt" || wtf
71
72   if [ -d "${world}/config" ]
73   then
74     meh "Installing build-time configuration"
75     [ -f "${world}/config/${CONFIG}" ] && cp "${world}/config/${CONFIG}" "/usr/src/sys/${TARGET}/conf/"
76     [ -f "${world}/config/make.conf" ] && cp "${world}/config/make.conf" "${build}/etc/"
77     [ -f "${world}/config/src.conf" ] && cp "${world}/config/src.conf" "${build}/etc/"
78   fi
79   return 0
80 }
81
82 # Cleanup chroot
83 cleanup() {
84   [ "${CHROOT_DIRTY}" -a ! "${CHROOT_CLEAN}" ] && return 0
85   meh "Cleaning up"
86   umount -f "${build}/mnt"
87   umount -f "${build}/usr/obj"
88   umount -f "${build}/usr/src"
89   umount -f "${build}/dev"
90   mount | grep -q "${build}" && wtf "Stuff is still mounted under ${build}; not removing"
91   chflags -R noschg "${build}"
92   rm -Rf "${build}"
93   trap "" exit hup int term kill
94   return 0
95 }
96
97 # Root directory of makeworld
98 ROOT="$(realpath "$(dirname "${0}")/../worlds")"
99
100 # Compute make -j<cpus*2>
101 make_cpus="$(sysctl -n hw.ncpu)"
102 make_jobs="$(( ${make_cpus} * 2 ))"
103 make_tgts="buildworld buildkernel distrib-dirs installworld installkernel distribution"
104
105 # Defaults
106 TARGET="i386"
107 CONFIG="GENERIC"
108 MAKEOPTS="-j${make_jobs}"
109
110 while getopts "m:t:c:hpdq" opt
111 do
112   case "${opt}" in
113     m) MAKEOPTS="${MAKEOPTS} ${OPTARG}" ;;
114     t) TARGET="${OPTARG}" ;;
115     c) CONFIG="${OPTARG}" ;;
116     p) CHROOT_PREPARE="TRUE" ;;
117     d) CHROOT_DIRTY="TRUE" ;;
118     q) CHROOT_CLEAN="TRUE" ;;
119     h) pebkac ;;
120     [?]) pebkac "Unrecognized option ${opt}" ;;
121   esac
122 done
123 shift $(( $OPTIND - 1 ))
124
125 # Should be root after this point
126 want root
127
128 # Build make target sequence
129 sequence="${*:-${make_tgts}}"
130
131 # Target world directory
132 world="${ROOT}/${TARGET}/${CONFIG}"
133 # Source chroot seed directory
134 seed="${ROOT}/seed/base/$(uname -m)"
135 # Root directory for chroot
136 build="${ROOT}/seed/chroot-${TARGET}-${CONFIG}"
137
138 # Environment for chroot build
139 env="
140 USER=root
141 HOME=/root
142 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
143 SHELL=/bin/sh
144 "
145
146 date="$(date +%Y%m%d)"
147
148 # Check if target config exists
149 [ -d "${world}" ] || wtf "${TARGET}/${CONFIG} doesn't exist"
150
151 if [ "${CHROOT_PREPARE}" ]
152 then
153   prepare
154   trap "" exit
155   meh "Chroot prepared"
156   exit
157 fi
158
159 if [ "${CHROOT_CLEAN}" ]
160 then
161   cleanup
162   meh "Chroot cleaned"
163   exit
164 fi
165
166 meh "Making world for ${TARGET}/${CONFIG}"
167
168 prepare
169
170 meh "Seed: ${seed}"
171 meh "Config: ${TARGET}/${CONFIG}"
172 meh "Builddir: ${build}"
173 meh "make ${MAKEOPTS}"
174 meh "DESTDIR=${world}/root"
175
176 for step in ${sequence}
177 do
178   meh "==> Phase: ${step}"
179   script "${world}/${date}-${step}.log" env -i ${env} chroot "${build}" sh -c \
180     "cd /usr/src; time make ${MAKEOPTS} ${step} TARGET=${TARGET} KERNCONF=${CONFIG} DESTDIR=/mnt" || wtf "chroot-cmd ${phase}"
181 done
182
183 # Copy the config files into the target, to keep a record of the build options
184 [ -f "${world}/config/${CONFIG}" ] && cp "${world}/config/${CONFIG}" "${build}/boot/kernel/"
185 [ -f "${world}/config/make.conf" ] && cp "${world}/config/make.conf" "${build}/etc/"
186 [ -f "${world}/config/src.conf" ] && cp "${world}/config/src.conf" "${build}/etc/"