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