script/makeworld: take advantage of newly discovered KERNCONFDIR to avoid tampering...
[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:hpdq" 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 kernel config matching CONF does not exist, assume GENERIC
150 if [ -e "${world}/config/${CONF}" ]
151 then
152   KERNCONFDIR=/usr/obj/conf
153   KERNCONF="${CONF}"
154 else
155   KERNCONFDIR=
156   KERNCONF=GENERIC
157 fi
158
159 # Environment for chroot build
160 env="
161 USER=root
162 HOME=/root
163 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
164 SHELL=/bin/sh
165 "
166
167 date="$(date +%Y%m%d)"
168
169 # Check if target config exists
170 [ -d "${world}" ] || wtf "${ARCH}/${CONF} doesn't exist"
171
172 if [ "${CHROOT_PREPARE}" ]
173 then
174   prepare
175   trap "" exit
176   meh "Chroot prepared"
177   exit
178 fi
179
180 if [ "${CHROOT_CLEAN}" ]
181 then
182   cleanup
183   meh "Chroot cleaned"
184   exit
185 fi
186
187 meh "Making world for ${ARCH}/${CONF}"
188
189 meh "Targets: ${sequence}"
190
191 prepare
192
193 meh "Seed: ${seed}"
194 meh "Srctree: ${SRCS}"
195 meh "Config: ${ARCH}/${CONF}"
196 [ "${KERNCONF}" != "${CONF}" ] && meh "Kernel config: ${KERNCONF}"
197 meh "Builddir: ${build}"
198 meh "make ${MAKEOPTS}"
199 meh "DESTDIR=${world}/root"
200
201 for step in ${sequence}
202 do
203   build_log="${world}/log/${date}-${step}.log"
204   meh "==> Step: ${step} -> ${build_log}"
205   [ "${BUILD_VERBOSE}" ] || exec 3>&1 > /dev/null
206   script "${build_log}" env -i ${env} chroot "${build}" sh -c \
207     "cd /usr/src; time make ${MAKEOPTS} ${step} TARGET=${ARCH} " \
208     "${KERNCONFDIR:+KERNCONFDIR=${KERNCONFDIR}} KERNCONF=${KERNCONF} DESTDIR=/mnt"
209   res=$?
210   [ "${BUILD_VERBOSE}" ] || exec 1>&3
211   [ "${res}" -gt 0 ] && wtf "chroot-cmd ${step} failed; check log"
212 done
213
214 # Copy the config files into the target, to keep a record of the build options
215 [ -f "${world}/config/${CONF}" ] && cp "${world}/config/${CONF}" "${world}/root/boot/kernel/"
216 [ -f "${world}/config/make.conf" ] && cp "${world}/config/make.conf" "${world}/root/etc/"
217 [ -f "${world}/config/src.conf" ] && cp "${world}/config/src.conf" "${world}/root/etc/"