xref: /freebsd/tools/tools/sysbuild/sysbuild.sh (revision 315ee00f)
1#!/bin/sh
2#
3# Copyright (c) 1994-2009 Poul-Henning Kamp.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27#
28
29set -e
30
31exec < /dev/null
32
33if [ `uname -m` = "i386" -o `uname -m` = "amd64" ] ; then
34	TARGET_PART=`df / | sed '
35	1d
36	s/[    ].*//
37	s,/dev/,,
38	s,s1a,s3a,
39	s,s2a,s1a,
40	s,s3a,s2a,
41	'`
42
43	FREEBSD_PART=`sed -n	\
44		-e 's/#.*//'	\
45		-e '/[ 	]\/freebsd[ 	]/!d'	\
46		-e 's/[ 	].*//p'	\
47		/etc/fstab`
48
49	# Calculate a suggested gpart command
50	TARGET_DISK=`expr ${TARGET_PART} : '\(.*\)s[12]a$' || true`
51	TARGET_SLICE=`expr ${TARGET_PART} : '.*s\([12]\)a$' || true`
52	GPART_SUGGESTION="gpart set -a active -i $TARGET_SLICE /dev/$TARGET_DISK"
53	unset TARGET_DISK TARGET_SLICE
54else
55	TARGET_PART=unknown
56	FREEBSD_PART=unknown
57	GPART_SUGGESTION=unknown
58fi
59
60
61# Relative to /freebsd
62PORTS_PATH=ports
63SRC_PATH=src
64# OBJ_PATH=obj
65
66# Name of kernel
67KERNCONF=GENERIC
68
69# srcconf
70#SRCCONF="SRCCONF=/usr/src/src.conf"
71
72# -j arg to make(1)
73
74ncpu=`sysctl -n kern.smp.cpus`
75if [ $ncpu -gt 1 ] ; then
76	JARG="-j $ncpu"
77fi
78
79# serial console ?
80SERCONS=false
81
82PKG_DIR=/usr/ports/packages/All
83
84# Remotely mounted distfiles
85# REMOTEDISTFILES=fs:/rdonly/distfiles
86
87# Proxy
88#FTP_PROXY=http://127.0.0.1:3128/
89#HTTP_PROXY=http://127.0.0.1:3128/
90#export FTP_PROXY HTTP_PROXY
91
92PORTS_WE_WANT='
93'
94
95PORTS_OPTS="BATCH=YES A4=yes"
96
97PORTS_WITHOUT=""
98PORTS_WITH=""
99
100CONFIGFILES='
101'
102
103SBMNT="/mnt.sysbuild"
104
105cleanup() (
106)
107
108before_ports() (
109)
110
111before_ports_chroot() (
112)
113
114final_root() (
115)
116
117final_chroot() (
118)
119
120#######################################################################
121# -P is a pretty neat way to clean junk out from your ports dist-files:
122#
123#	mkdir /freebsd/ports/distfiles.old
124#	mv /freebsd/ports/distfiles/* /freebsd/ports/distfiles.old
125#	sh sysbuild.sh -c $yourconfig -P /freebsd/ports/distfiles.old
126#	rm -rf /freebsd/ports/distfiles.old
127#
128# Unfortunately bsd.ports.mk does not attempt to use a hard-link so
129# while this runs you need diskspace for both your old and your "new"
130# distfiles.
131#
132#######################################################################
133
134usage () {
135	(
136        echo "Usage: $0 [-b/-k/-w] [-c config_file]"
137        echo "  -b      suppress builds (both kernel and world)"
138        echo "  -k      suppress buildkernel"
139        echo "  -w      suppress buildworld"
140        echo "  -p      used cached packages"
141        echo "  -P <dir> prefetch ports"
142        echo "  -c      specify config file"
143        ) 1>&2
144        exit 2
145}
146
147#######################################################################
148#######################################################################
149
150if [ ! -f $0 ] ; then
151	echo "Must be able to access self ($0)" 1>&2
152	exit 1
153fi
154
155if grep -q 'Magic String: 0`0nQT40W%l,CX&' $0 ; then
156	true
157else
158	echo "self ($0) does not contain magic string" 1>&2
159	exit 1
160fi
161
162#######################################################################
163
164set -e
165
166log_it() (
167	a="$*"
168	set `cat /tmp/_sb_log`
169	TX=`date +%s`
170	echo "$1 $TX" > /tmp/_sb_log
171	DT=`expr $TX - $1 || true`
172	DL=`expr $TX - $2 || true`
173	echo -n "### `date +%H:%M:%S`"
174	printf " ### %5d ### %5d ### %s\n" $DT $DL "$a"
175)
176
177#######################################################################
178
179ports_make() {
180	make $* WITH="${PORTS_WITH}" WITHOUT="${PORTS_WITHOUT}" ${PORTS_OPTS}
181}
182
183ports_recurse() (
184	cd /usr/ports
185	t=$1
186	shift
187	if [ "x$t" = "x." ] ; then
188		true > /tmp/_.plist
189		true > /tmp/_.plist.tdone
190		echo 'digraph {' > /tmp/_.plist.dot
191	fi
192	if grep -q "^$t\$" /tmp/_.plist.tdone ; then
193		return
194	fi
195	echo "$t" >> /tmp/_.plist.tdone
196	for d
197	do
198		if [ "x$d" == "xpatch" ] ; then
199			continue
200		fi
201		fl=""
202		if [ ! -d $d ] ; then
203			fl=FLAVOR=`expr $d : '.*@\(.*\)'`
204			bd=`expr $d : '\(.*\)@.*'`
205			if [ ! -d "$bd" ] ; then
206				echo "Missing port $d ($t) (fl $fl) (bd $bd)" 1>&2
207				continue
208			fi
209			# echo "Flavored port $d ($t) (fl $fl) (bd $bd)" 1>&2
210			d=$bd
211		fi
212		d=`cd /usr/ports && cd $d && /bin/pwd`
213		if [ ! -f $d/Makefile ] ; then
214			echo "Missing port (Makefile) $d" 1>&2
215			continue
216		fi
217		if [ "x$t" != "x." ] ; then
218			echo "\"$t\" -> \"$d\"" >> /tmp/_.plist.dot
219		fi
220		if grep -q "^$d\$" /tmp/_.plist ; then
221			true
222		elif grep -q "^$d\$" /tmp/_.plist.tdone ; then
223			true
224		else
225			(
226			cd $d
227			l=""
228			for a in `ports_make -V _UNIFIED_DEPENDS $fl`
229			do
230				x=`expr "$a" : '.*:\(.*\)'`
231				l="${l} ${x}"
232			done
233			ports_recurse $d $l
234			)
235			echo "$d" >> /tmp/_.plist
236		fi
237	done
238	if [ "x$t" = "x." ] ; then
239		echo '}' >> /tmp/_.plist.dot
240	fi
241)
242
243ports_build() (
244
245	ports_recurse . $PORTS_WE_WANT
246
247	if [ "x${PKG_DIR}" != "x" ] ; then
248		mkdir -p ${PKG_DIR}
249	fi
250
251	pd=`cd /usr/ports && /bin/pwd`
252	# Now build & install them
253	for p in `cat /tmp/_.plist`
254	do
255		b=`echo $p | tr / _`
256		t=`echo $p | sed "s,${pd},,"`
257		pn=`cd $p && ports_make package-name`
258
259		if [ "x`basename $p`" == "xpkg" ] ; then
260			log_it "Very Special: $t ($pn)"
261
262			(
263			cd $p
264			ports_make clean all install
265			) > _.$b 2>&1 < /dev/null
266			continue
267		fi
268
269		if pkg info $pn > /dev/null 2>&1 ; then
270			log_it "Already installed: $t ($pn)"
271			continue
272		fi
273
274		if [ "x${PKG_DIR}" != "x" -a -f ${PKG_DIR}/$pn.txz ] ; then
275			if [ "x$use_pkg" = "x-p" ] ; then
276				log_it "Install $t ($pn)"
277				(
278				set +e
279				pkg add ${PKG_DIR}/$pn.txz || true
280				) > _.$b 2>&1 < /dev/null
281				continue
282			fi
283		fi
284
285		miss=`(cd $p ; ports_make missing) || true`
286
287		if [ "x${miss}" != "x" ] ; then
288			log_it "NB: MISSING for $p:" $miss
289		fi
290
291		log_it "build $pn ($p)"
292		(
293			set +e
294			cd $p
295			ports_make clean
296			if ports_make install ; then
297				if [ "x${PKG_DIR}" != "x" ] ; then
298					ports_make package
299				fi
300			else
301				log_it FAIL build $p
302			fi
303			ports_make clean
304
305		) > _.$b 2>&1 < /dev/null
306	done
307)
308
309ports_prefetch() (
310	(
311	set +x
312	ldir=$1
313	true > /${ldir}/_.prefetch
314	echo "Building /tmp/_.plist" >> /${ldir}/_.prefetch
315
316	ports_recurse . $PORTS_WE_WANT
317
318	echo "Completed /tmp/_.plist" >> /${ldir}/_.prefetch
319	# Now checksump/fetch them
320	for p in `cat /tmp/_.plist`
321	do
322		b=`echo $p | tr / _`
323		(
324			cd $p
325			if ports_make checksum ; then
326				rm -f /${ldir}/_.prefetch.$b
327				echo "OK $p" >> /${ldir}/_.prefetch
328				exit 0
329			fi
330			ports_make distclean
331			ports_make checksum || true
332
333			if ports_make checksum > /dev/null 2>&1 ; then
334				rm -f /${ldir}/_.prefetch.$b
335				echo "OK $p" >> /${ldir}/_.prefetch
336			else
337				echo "BAD $p" >> /${ldir}/_.prefetch
338			fi
339		) > /${ldir}/_.prefetch.$b 2>&1
340	done
341	echo "Done" >> /${ldir}/_.prefetch
342	)
343)
344
345#######################################################################
346
347do_world=true
348do_kernel=true
349do_prefetch=false
350use_pkg=""
351c_arg=""
352
353set +e
354args=`getopt bc:hkpP:w $*`
355if [ $? -ne 0 ] ; then
356	usage
357fi
358set -e
359
360set -- $args
361for i
362do
363	case "$i"
364	in
365	-b)
366		shift;
367		do_world=false
368		do_kernel=false
369		;;
370	-c)
371		c_arg=$2
372		if [ ! -f "$c_arg" ] ; then
373			echo "Cannot read $c_arg" 1>&2
374			usage
375		fi
376		. "$2"
377		shift
378		shift
379		;;
380	-h)
381		usage
382		;;
383	-k)
384		shift;
385		do_kernel=false
386		;;
387	-p)
388		shift;
389		use_pkg="-p"
390		;;
391	-P)
392		shift;
393		do_prefetch=true
394		distfile_cache=$1
395		shift;
396		;;
397	-w)
398		shift;
399		do_world=false
400		;;
401	--)
402		shift
403		break;
404		;;
405	esac
406done
407
408#######################################################################
409
410if [ "x$1" = "xchroot_script" ] ; then
411	set -e
412
413	shift
414
415	before_ports_chroot
416
417	ports_build
418
419	exit 0
420fi
421
422if [ "x$1" = "xfinal_chroot" ] ; then
423	final_chroot
424	exit 0
425fi
426
427if [ $# -gt 0 ] ; then
428        echo "$0: Extraneous arguments supplied"
429        usage
430fi
431
432#######################################################################
433
434T0=`date +%s`
435echo $T0 $T0 > /tmp/_sb_log
436
437[ ! -d ${SBMNT} ] && mkdir -p ${SBMNT}
438
439if $do_prefetch ; then
440	rm -rf /tmp/sysbuild/ports
441	mkdir -p /tmp/sysbuild/ports
442	ln -s ${distfile_cache} /tmp/sysbuild/ports/distfiles
443	export PORTS_OPTS=CD_MOUNTPTS=/tmp/sysbuild
444	ports_prefetch /tmp
445	exit 0
446fi
447
448log_it Unmount everything
449(
450	( cleanup )
451	umount /freebsd/distfiles || true
452	umount ${SBMNT}/freebsd/distfiles || true
453	umount ${FREEBSD_PART} || true
454	umount ${SBMNT}/freebsd || true
455	umount ${SBMNT}/dev || true
456	umount ${SBMNT} || true
457	umount /dev/${TARGET_PART} || true
458) # > /dev/null 2>&1
459
460log_it Prepare running image
461mkdir -p /freebsd
462mount ${FREEBSD_PART} /freebsd
463
464#######################################################################
465
466if [ ! -d /freebsd/${PORTS_PATH} ] ;  then
467	echo PORTS_PATH does not exist 1>&2
468	exit 1
469fi
470
471if [ ! -d /freebsd/${SRC_PATH} ] ;  then
472	echo SRC_PATH does not exist 1>&2
473	exit 1
474fi
475
476log_it TARGET_PART $TARGET_PART
477sleep 5
478
479rm -rf /usr/ports
480ln -s /freebsd/${PORTS_PATH} /usr/ports
481
482rm -rf /usr/src
483ln -s /freebsd/${SRC_PATH} /usr/src
484
485if $do_world ; then
486	if [ "x${OBJ_PATH}" != "x" ] ; then
487		rm -rf /usr/obj
488		(cd /freebsd && mkdir -p ${OBJ_PATH} && ln -s ${OBJ_PATH} /usr/obj)
489	else
490		rm -rf /usr/obj/*
491		mkdir -p /usr/obj
492	fi
493fi
494
495#######################################################################
496
497for i in ${PORTS_WE_WANT}
498do
499	(
500	cd /usr/ports
501	if [ ! -d $i ] ; then
502		fl=FLAVOR=`expr $i : '.*@\(.*\)'`
503		i=`expr $i : '\(.*\)@.*'`
504	fi
505	if [ ! -d $i ]  ; then
506		echo "Port $i not found" 1>&2
507		exit 2
508	fi
509	)
510done
511
512#export PORTS_WE_WANT
513#export PORTS_OPTS
514
515#######################################################################
516
517log_it Prepare destination partition
518newfs -t -E -O2 -U /dev/${TARGET_PART} > /dev/null
519mount /dev/${TARGET_PART} ${SBMNT}
520mkdir -p ${SBMNT}/dev
521mount -t devfs devfs ${SBMNT}/dev
522
523if [ "x${REMOTEDISTFILES}" != "x" ] ; then
524	rm -rf /freebsd/${PORTS_PATH}/distfiles
525	ln -s /freebsd/distfiles /freebsd/${PORTS_PATH}/distfiles
526	mkdir -p /freebsd/distfiles
527	mount  ${REMOTEDISTFILES} /freebsd/distfiles
528fi
529
530log_it copy ports config files
531(cd / ; find var/db/ports -print | cpio -dumpv ${SBMNT} > /dev/null 2>&1)
532
533log_it "Start prefetch of ports distfiles"
534ports_prefetch ${SBMNT} &
535
536if $do_world ; then
537	(
538	cd /usr/src
539	log_it "Buildworld"
540	make ${JARG} -s buildworld ${SRCCONF} > ${SBMNT}/_.bw 2>&1
541	)
542fi
543
544if $do_kernel ; then
545	(
546	cd /usr/src
547	log_it "Buildkernel"
548	make ${JARG} -s buildkernel KERNCONF=$KERNCONF > ${SBMNT}/_.bk 2>&1
549	)
550fi
551
552
553log_it Installworld
554(cd /usr/src && make ${JARG} installworld DESTDIR=${SBMNT} ${SRCCONF} ) \
555	> ${SBMNT}/_.iw 2>&1
556
557log_it distribution
558(cd /usr/src && make -m /usr/src/share/mk distribution DESTDIR=${SBMNT} ${SRCCONF} ) \
559	> ${SBMNT}/_.dist 2>&1
560
561log_it Installkernel
562(cd /usr/src && make ${JARG} installkernel DESTDIR=${SBMNT} KERNCONF=$KERNCONF ) \
563	> ${SBMNT}/_.ik 2>&1
564
565if [ "x${OBJ_PATH}" != "x" ] ; then
566	rmdir ${SBMNT}/usr/obj
567	( cd /freebsd && mkdir -p ${OBJ_PATH} && ln -s ${OBJ_PATH} ${SBMNT}/usr/obj )
568fi
569
570log_it Wait for ports prefetch
571log_it "(Tail ${SBMNT}/_.prefetch for progress)"
572wait
573
574log_it Move filesystems
575
576if [ "x${REMOTEDISTFILES}" != "x" ] ; then
577	umount /freebsd/distfiles
578fi
579umount ${FREEBSD_PART} || true
580mkdir -p ${SBMNT}/freebsd
581mount ${FREEBSD_PART} ${SBMNT}/freebsd
582if [ "x${REMOTEDISTFILES}" != "x" ] ; then
583	mount  ${REMOTEDISTFILES} ${SBMNT}/freebsd/distfiles
584fi
585
586rm -rf ${SBMNT}/usr/ports || true
587ln -s /freebsd/${PORTS_PATH} ${SBMNT}/usr/ports
588
589rm -rf ${SBMNT}/usr/src || true
590ln -s /freebsd/${SRC_PATH} ${SBMNT}/usr/src
591
592log_it Build and install ports
593
594# Make sure fetching will work in the chroot
595if [ -f /etc/resolv.conf ] ; then
596	log_it copy resolv.conf
597	cp /etc/resolv.conf ${SBMNT}/etc
598	chflags schg ${SBMNT}/etc/resolv.conf
599fi
600
601if [ -f /etc/localtime ] ; then
602	log_it copy localtime
603	cp /etc/localtime ${SBMNT}/etc
604	if [ -f /var/db/zoneinfo ] ; then
605		log_it copy zoneinfo
606		cp /var/db/zoneinfo ${SBMNT}/var/db
607	fi
608fi
609
610log_it ldconfig in chroot
611chroot ${SBMNT} sh /etc/rc.d/ldconfig start
612
613log_it before_ports
614(
615	before_ports
616)
617
618log_it fixing fstab
619sed "/[ 	]\/[ 	]/s;^[^ 	]*[ 	];/dev/${TARGET_PART}	;" \
620	/etc/fstab > ${SBMNT}/etc/fstab
621
622log_it build ports
623
624cp $0 ${SBMNT}/root
625cp /tmp/_sb_log ${SBMNT}/tmp
626b=`basename $0`
627if [ "x$c_arg" != "x" ] ; then
628	cp $c_arg ${SBMNT}/root
629	chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` $use_pkg chroot_script
630else
631	chroot ${SBMNT} sh /root/$0 $use_pkg chroot_script
632fi
633cp ${SBMNT}/tmp/_sb_log /tmp
634
635log_it create all mountpoints
636grep -v '^[ 	]*#' ${SBMNT}/etc/fstab |
637while read a b c
638do
639	mkdir -p ${SBMNT}/$b
640done
641
642if [ "x$SERCONS" != "xfalse" ] ; then
643	log_it serial console
644	echo " -h" > ${SBMNT}/boot.config
645	sed -i "" -e /ttyd0/s/off/on/ ${SBMNT}/etc/ttys
646	sed -i "" -e /ttyu0/s/off/on/ ${SBMNT}/etc/ttys
647	sed -i "" -e '/^ttyv[0-8]/s/	on/	off/' ${SBMNT}/etc/ttys
648fi
649
650log_it move dist config files "(expect warnings)"
651(
652	cd ${SBMNT}
653	mkdir root/configfiles_dist
654	find ${CONFIGFILES} -print | cpio -dumpv root/configfiles_dist
655)
656
657log_it copy live config files
658(cd / && find ${CONFIGFILES} -print | cpio -dumpv ${SBMNT})
659
660log_it final_root
661( final_root )
662log_it final_chroot
663cp /tmp/_sb_log ${SBMNT}/tmp
664if [ "x$c_arg" != "x" ] ; then
665	chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` final_chroot
666else
667	chroot ${SBMNT} sh /root/$0 final_chroot
668fi
669cp ${SBMNT}/tmp/_sb_log /tmp
670log_it "Check these messages (if any):"
671grep '^Stop' ${SBMNT}/_* || true
672log_it DONE
673echo "Now you probably want to:"
674echo "    $GPART_SUGGESTION"
675echo "    shutdown -r now"
676