script/makeworld: option 'v' never added to getopts mask
[CDN/Mosi.git] / script / makeworld
1 #!/bin/sh
2
3 # Load shlib and modules
4 _root="$(dirname "${0}")"; . "${_root}/lib/env.sh"
5 want log
6
7 pebkac() {
8   [ "${*}" ] && printf "%s\n\n" "${*}"
9   echo "Usage: $(basename "${0}") -m <makeopts> -a <arch> -c <conf> <target, ...>"
10   echo "  -m <makeopts>   Provide additional flags to make"
11   echo "  -a <arch>       World architecture (i386, amd64)"
12   echo "  -c <conf>       World configuration (in worlds/<arch>/<conf>)"
13   echo "  -s <srctree>    Override source tree selection"
14   echo "  -h              Help!"
15   echo "  -v              Verbose (show build log in real time)"
16   echo ""
17   echo "If, for some reason, you wish to spread out builds across multiple"
18   echo "invocations, these might come in handy:"
19   echo "  -p              Prepare chroot environment"
20   echo "  -d              Don't prepare or clean up chroot during run"
21   echo "  -q              Clean up chroot environment"
22   echo ""
23   echo "Available make targets:"
24   for make_tgt in ${make_tgts}
25   do
26     echo "  ${make_tgt}"
27   done
28   exit 1
29 }
30
31 # Prepare chroot for build
32 prepare() {
33   [ "${CHROOT_DIRTY}" -a ! "${CHROOT_PREPARE}" ] && return 0
34   # Verify environment sanity
35   [ -d "${build}" ] && omg "${build}: directory exists; purging" && cleanup
36   mount | grep -q "${build}" && wtf "Stuff is mounted under ${build}; cannot continue"
37   ls -1 "${seed}"/base.?? >/dev/null 2>&1 || wtf "Populate seed directory ${seed} first"
38   [ -f "${SRCS}/sys/conf/newvers.sh" ] || wtf "Need sources in ${SRCS} to build"
39
40   # Cleanup trap here, so that an abort during prepare can clean up properly
41   trap "cleanup" exit hup int term kill
42
43   meh "Preparing build chroot"
44   [ -d "${build}" ] && wtf "${build}: directory exists"
45   mkdir -p "${build}" || wtf
46   cat "${seed}"/base.?? | tar xCf "${build}" - || wtf
47   mkdir -p "${build}/usr/obj" || wtf
48
49   meh "Mounting chroot filesystems"
50   mkdir -p "${world}/log"
51   mkdir -p "${world}/obj"
52   mkdir -p "${world}/root"
53   mount -t devfs devfs "${build}/dev" || wtf
54   # Mount /usr/src as a union, so that changes to it will not affect the underlying tree
55   # unionfs suffers from deadlocks; don't use it
56   mount -t nullfs -r "${SRCS}" "${build}/usr/src" || wtf
57   mount -t nullfs "${world}/obj" "${build}/usr/obj" || wtf
58   mount -t nullfs "${world}/root" "${build}/mnt" || wtf
59
60   if [ -d "${world}/config" ]
61   then
62     meh "Installing build-time configuration"
63     [ -f "${world}/config/${CONF}" ] && {
64       mkdir -p "${build}/usr/obj/conf" || wtf
65       cp "${world}/config/${CONF}" "${build}/usr/obj/conf" || wtf
66     }
67     [ -f "${world}/config/make.conf" ] && cp "${world}/config/make.conf" "${build}/etc/"
68     [ -f "${world}/config/src.conf" ] && cp "${world}/config/src.conf" "${build}/etc/"
69   fi
70   return 0
71 }
72
73 # Cleanup chroot
74 cleanup() {
75   [ "${BUILD_VERBOSE}" ] || exec 1>&3
76   [ "${CHROOT_DIRTY}" -a ! "${CHROOT_CLEAN}" ] && return 0
77   meh "Cleaning up"
78   umount -f "${build}/mnt"
79   umount -f "${build}/usr/obj"
80   umount -f "${build}/usr/src"
81   umount -f "${build}/dev"
82   mount | grep -q "${build}/" && wtf "Stuff is still mounted under ${build}; not removing"
83   chflags -R noschg "${build}"
84   rm -Rf "${build}"
85   trap "" exit hup int term kill
86   return 0
87 }
88
89 # Root directory of makeworld
90 ROOT="$(realpath "$(dirname "${0}")/..")"
91
92 # Location of worlds
93 WORLDS="${ROOT}/worlds"
94
95 # Compute make -j<cpus*2>
96 make_cpus="$(sysctl -n hw.ncpu)"
97 make_jobs="$(( ${make_cpus} * 2 ))"
98 make_tgts="buildworld buildkernel distrib-dirs installworld installkernel distribution"
99
100 # Defaults
101 ARCH="$(uname -m)"
102 CONF="GENERIC"
103 MAKEOPTS="-j${make_jobs}"
104 SRCS=""
105 CHROOT_PREPARE=""
106 CHROOT_DIRTY=""
107 CHROOT_CLEAN=""
108 BUILD_VERBOSE=""
109
110 while getopts "m:a:c:hpdqv" opt
111 do
112   case "${opt}" in
113     m) MAKEOPTS="${MAKEOPTS} ${OPTARG}" ;;
114     a) ARCH="${OPTARG}" ;;
115     c) CONF="${OPTARG}" ;;
116     s) SRCS="${OPTARG}" ;;
117     p) CHROOT_PREPARE="TRUE" ;;
118     d) CHROOT_DIRTY="TRUE" ;;
119     q) CHROOT_CLEAN="TRUE" ;;
120     v) BUILD_VERBOSE="TRUE" ;;
121     h) pebkac ;;
122     [?]) pebkac "Unrecognized option ${opt}" ;;
123   esac
124 done
125 shift $(( $OPTIND - 1 ))
126
127 # Should be root after this point
128 want root
129
130 # Build make target sequence
131 sequence="${*:-${make_tgts}}"
132
133 # Target world directory
134 world="${WORLDS}/${ARCH}/${CONF}"
135 # Source chroot seed directory
136 seed="${ROOT}/seed/base/$(uname -m)"
137 # Root directory for chroot
138 build="${world}/chroot"
139
140 if [ -z "${SRCS}" ]
141 then
142   # Locate a usable source tree
143   # Pick world/src over /usr/src if it exists at all, even if it is not usable, to provide warning of bad configs
144   SRCS="${world}/src"
145   [ -L "${SRCS}" -o -d "${SRCS}" ] || SRCS=/usr/src
146 fi
147 SRCS="$(realpath "${SRCS}")"
148
149 # If NO_KERNEL exists, don't bother building one (jails)
150 if [ -e "${world}/config/NO_KERNEL" ]
151 then
152   NO_KERNEL=yes
153   KERNCONFDIR=
154   KERNCONF=
155 else
156   # If kernel config matching CONF does not exist, assume GENERIC
157   if [ -e "${world}/config/${CONF}" ]
158   then
159     KERNCONFDIR=/usr/obj/conf
160     KERNCONF="${CONF}"
161   else
162     KERNCONFDIR=
163     KERNCONF=GENERIC
164   fi
165 fi
166
167 # Environment for chroot build
168 env="
169 USER=root
170 HOME=/root
171 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
172 SHELL=/bin/sh
173 DESTDIR=/mnt
174 "
175
176 date="$(date +%Y%m%d)"
177
178 # Check if target config exists
179 [ -d "${world}" ] || wtf "${ARCH}/${CONF} doesn't exist"
180
181 if [ "${CHROOT_PREPARE}" ]
182 then
183   prepare
184   trap "" exit
185   meh "Chroot prepared"
186   exit
187 fi
188
189 if [ "${CHROOT_CLEAN}" ]
190 then
191   cleanup
192   meh "Chroot cleaned"
193   exit
194 fi
195
196 meh "Making world for ${ARCH}/${CONF}"
197
198 meh "Targets: ${sequence}"
199
200 prepare
201
202 meh "Seed: ${seed}"
203 meh "Srctree: ${SRCS}"
204 meh "Config: ${ARCH}/${CONF}"
205 [ "${NO_KERNEL}" ] && meh "Kernel config: NO_KERNEL (will not build kernel)"
206 [ "${KERNCONF}" -a "${KERNCONF}" != "${CONF}" ] && meh "Kernel config: ${KERNCONF}"
207 meh "Builddir: ${build}"
208 meh "make ${MAKEOPTS}"
209 meh "DESTDIR=${world}/root"
210
211 for step in ${sequence}
212 do
213   [ "${NO_KERNEL}" -a "${step%%kernel}" != "${step}" ] && {
214     meh "Step: ${step} -> Skipped: NO_KERNEL"
215     continue
216   }
217   build_log="${world}/log/${date}-${step}.log"
218   meh "==> Step: ${step} -> ${build_log}"
219   [ "${BUILD_VERBOSE}" ] || exec 3>&1 > /dev/null
220   script "${build_log}" env -i ${env} chroot "${build}" sh -c \
221     "cd /usr/src; time make ${MAKEOPTS} ${step} TARGET=${ARCH} DESTDIR=/mnt " \
222     "${KERNCONFDIR:+KERNCONFDIR=${KERNCONFDIR}} ${KERNCONF:+KERNCONF=${KERNCONF}}"
223   res=$?
224   [ "${BUILD_VERBOSE}" ] || exec 1>&3
225   [ "${res}" -gt 0 ] && wtf "chroot-cmd ${step} failed; check log"
226 done
227
228 # Copy the config files into the target, to keep a record of the build options
229 [ -f "${world}/config/${CONF}" ] && cp "${world}/config/${CONF}" "${world}/root/boot/kernel/"
230 [ -f "${world}/config/make.conf" ] && cp "${world}/config/make.conf" "${world}/root/etc/"
231 [ -f "${world}/config/src.conf" ] && cp "${world}/config/src.conf" "${world}/root/etc/"