xref: /freebsd/tests/ci/Makefile (revision 2c6e4aed07b268f9ddb33950dc117c21b021e390)
1# SPDX-License-Identifier: BSD-2-Clause
2#
3# Copyright (c) 2024 The FreeBSD Foundation
4#
5# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
6# under sponsorship from the FreeBSD Foundation.
7#
8# Makefile for CI testing.
9#
10# User-driven targets:
11#  ci: Run CI tests
12#  ci-smoke: Run smoke tests which is simply booting the image
13#  ci-full: Run full tests
14#
15# Variables affecting the build process:
16#  TARGET/TARGET_ARCH: architecture of built release (default: same as build host)
17#  KERNELCONF: kernel configuration to use
18#  USE_QEMU: Use QEMU for testing rather than bhyve
19#
20
21WORLDDIR?=	${.CURDIR}/../..
22RELEASEDIR=	${WORLDDIR}/release
23MAKECONF?=	/dev/null
24SRCCONF?=	/dev/null
25_MEMORY!=sysctl -n hw.physmem 2>/dev/null
26PARALLEL_JOBS!=sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null
27TOTAL_MEMORY!=expr ${_MEMORY} / 1073741824
28KERNCONF?=	GENERIC
29LOCALBASE?=	/usr/local
30EXTRA_MAKE_FLAGS?=
31
32.if !defined(TARGET) || empty(TARGET)
33TARGET=		${MACHINE}
34.endif
35.if !defined(TARGET_ARCH) || empty(TARGET_ARCH)
36.if ${TARGET} == ${MACHINE}
37TARGET_ARCH=	${MACHINE_ARCH}
38.else
39TARGET_ARCH=	${TARGET}
40.endif
41.endif
42IMAKE=		${MAKE} TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}
43
44.if defined(CROSS_TOOLCHAIN) || !empty(CROSS_TOOLCHAIN)
45CROSS_TOOLCHAIN_PARAM=	"CROSS_TOOLCHAIN=${CROSS_TOOLCHAIN}"
46.endif
47
48# Define OSRELEASE by using newvers.sh
49.if !defined(OSRELEASE) || empty(OSRELEASE)
50.for _V in TYPE BRANCH REVISION
51. if !defined(${_V}) || empty(${_V})
52${_V}!=	eval $$(awk '/^${_V}=/{print}' ${.CURDIR}/../../sys/conf/newvers.sh); echo $$${_V}
53. endif
54.endfor
55.for _V in ${TARGET_ARCH}
56.if !empty(TARGET:M${_V})
57OSRELEASE=	${TYPE}-${REVISION}-${BRANCH}-${TARGET}
58VOLUME_LABEL=	${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET}
59.else
60OSRELEASE=	${TYPE}-${REVISION}-${BRANCH}-${TARGET}-${TARGET_ARCH}
61VOLUME_LABEL=	${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET_ARCH}
62.endif
63.endfor
64.endif
65
66.if exists(${.CURDIR}/tools/ci.conf) && !defined(CICONF)
67CICONF?=	${.CURDIR}/tools/ci.conf
68.endif
69SWAPSIZE?=	1g
70VMFS?=		ufs
71FORMAT=		raw
72CIIMAGE=	ci-${OSRELEASE}-${GITREV}-${KERNCONF}.${FORMAT}
73CIDISK?=	${.OBJDIR}/${CIIMAGE}
74VMSIZE?=	6g
75CITYPE?=	full
76META_TAR!=mktemp /tmp/meta.XXXXXX
77META_DIR!=mktemp -d /tmp/meta.XXXXXX
78META_DIROUT!=mktemp -d /tmp/meta.XXXXXX
79DISC_CAM!=truncate -s 128m /tmp/disk-cam
80EXTRA_DISK_NUM?=5
81DISK_NUMBERS!=jot - 1 ${EXTRA_DISK_NUM}
82BHYVE_EXTRA_DISK_PARAM?=
83BHYVE_EXTRA_DISK_PARAM+=-s 4:0,ahci-hd,/tmp/disk-cam
84.for i in ${DISK_NUMBERS}
85NEW_DISK!=truncate -s 128m /tmp/disk${i}
86BHYVE_EXTRA_DISK_PARAM+=-s $$((${i} + 4)):0,virtio-blk,/tmp/disk${i}
87CLEANFILES+=/tmp/disk${i}
88.endfor
89TEST_VM_NAME=	ci-${OSRELEASE}-${GITREV}-${KERNCONF}
90.if ${TOTAL_MEMORY} >= 16
91VM_MEM=8
92.elif ${TOTAL_MEMORY} >=4
93VM_MEM=${TOTAL_MEMORY}
94.else
95echo "Please increase the memory to at least 4GB"
96exit 0
97.endif
98VM_MEM_SIZE?=${VM_MEM}g
99TIMEOUT_MS?=5400000
100TIMEOUT=$$((${TIMEOUT_MS} / 1000))
101TIMEOUT_EXPECT=$$((${TIMEOUT} - 60))
102TIMEOUT_VM=$$((${TIMEOUT_EXPECT} - 120))
103.if exists(${.CURDIR}/Makefile.${TARGET_ARCH})
104.	include "${.CURDIR}/Makefile.${TARGET_ARCH}"
105.endif
106.if ${TARGET_ARCH} != ${MACHINE_ARCH}
107.if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" )
108QEMUSTATIC=/usr/local/bin/qemu-${QEMU_ARCH}-static
109QEMUTGT=portinstall-qemu
110.endif
111.endif
112QEMUTGT?=
113QEMU_DEVICES?=-device virtio-blk,drive=hd0
114QEMU_EXTRA_PARAM?=
115QEMU_MACHINE?=virt
116QEMUBIN=/usr/local/bin/qemu-system-${QEMU_ARCH}
117.if ${PARALLEL_JOBS} >= ${QEMU_MAX_CPU_COUNT}
118QEMU_CPU_COUNT=${QEMU_MAX_CPU_COUNT}
119.else
120QEMU_CPU_COUNT=${PARALLEL_JOBS}
121.endif
122.if ${VM_MEM} >= ${QEMU_MAX_MEM_SIZE}
123VM_MEM_SIZE=${QEMU_MAX_MEM_SIZE}g
124.else
125VM_MEM_SIZE=${VM_MEM}g
126.endif
127VMGUEST!=sysctl -n kern.vm_guest 2>/dev/null || true
128.if ${VMGUEST} != "none"
129USE_QEMU?=1
130.endif
131KLDFILEMONISLOADED!=kldload -q -n filemon 2>/dev/null && echo "1" || echo "0"
132.if ${KLDFILEMONISLOADED} == "1"
133METAMODE?=-DWITH_META_MODE
134.endif
135
136CLEANFILES+=	${.OBJDIR}/${CIIMAGE} ${.OBJDIR}/ci.img ${META_TAR}
137CLEANDIRS+=	${.OBJDIR}/ci-buildimage
138
139portinstall: portinstall-pkg portinstall-qemu portinstall-expect portinstall-${TARGET_ARCH:tl} .PHONY
140
141portinstall-pkg: .PHONY
142.if !exists(/usr/local/sbin/pkg-static)
143	env ASSUME_ALWAYS_YES=yes pkg bootstrap
144.endif
145
146portinstall-qemu: portinstall-pkg .PHONY
147.if !exists(/usr/local/bin/qemu-${TARGET_ARCH}-static)
148	env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu-user-static
149.endif
150.if !exists(/usr/local/bin/qemu-system-${QEMU_ARCH})
151	env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu@nox11
152.endif
153
154portinstall-expect: portinstall-pkg .PHONY
155.if !exists(/usr/local/bin/expect)
156	env ASSUME_ALWAYS_YES=yes pkg install lang/expect
157.endif
158
159beforeclean: .PHONY
160	chflags -R noschg ${.OBJDIR}/${.TARGET}
161
162.include <bsd.obj.mk>
163clean: beforeclean .PHONY
164
165cleandir: beforeclean .PHONY
166
167ci-buildworld: .PHONY
168	@echo "Building world for ${TARGET_ARCH}"
169	${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \
170		${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \
171		${EXTRA_MAKE_FLAGS} buildworld > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
172		(echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
173
174
175ci-buildkernel: ci-buildworld-${TARGET_ARCH:tl} .PHONY
176	@echo "Building kernel for ${TARGET_ARCH}"
177	${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \
178		${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \
179		${EXTRA_MAKE_FLAGS} KERNCONF=${KERNCONF} \
180		buildkernel > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
181		(echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
182
183ci-buildimage: ${QEMUTGT} ci-buildkernel-${TARGET_ARCH:tl} .PHONY
184	@echo "Building ci image for ${TARGET_ARCH}"
185	mkdir -p ${.OBJDIR}/${.TARGET}
186	env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \
187		QEMUSTATIC=${QEMUSTATIC} CITYPE=${CITYPE} \
188		${RELEASEDIR}/scripts/mk-vmimage.sh \
189		-C ${RELEASEDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${VMFS} \
190		-i ${.OBJDIR}/ci.img -s ${VMSIZE} -f ${FORMAT} \
191		-S ${WORLDDIR} -o ${.OBJDIR}/${CIIMAGE} -c ${CICONF} \
192		> ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
193		(echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
194	touch ${.TARGET}
195
196ci-set-smoke-var: .PHONY
197CITYPE=smoke
198
199ci-set-full-var: .PHONY
200CITYPE=full
201
202ci-create-meta: .PHONY
203	truncate -s 512M ${META_TAR}
204	tar rvf ${META_TAR} -C ${META_DIR} .
205
206ci-extract-meta: .PHONY
207	tar xfv ${META_TAR} -C ${META_DIROUT}
208	@echo "Extracted kyua reports to ${META_DIROUT}"
209
210ci-runtest: ci-buildimage-${TARGET_ARCH:tl} portinstall .PHONY
211.if ${MACHINE} == "amd64" && ( ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" ) && ( !defined(USE_QEMU) || empty(USE_QEMU) )
212	/usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy || true
213	/usr/sbin/bhyveload -c stdio -m ${VM_MEM_SIZE} -d ${CIDISK} ${TEST_VM_NAME}
214	expect -c "set timeout ${TIMEOUT_EXPECT}; \
215		spawn /usr/bin/timeout -k 60 ${TIMEOUT_VM} /usr/sbin/bhyve \
216		-c ${PARALLEL_JOBS} -m ${VM_MEM_SIZE} -A -H -P \
217		-s 0:0,hostbridge \
218		-s 1:0,lpc \
219		-s 2:0,virtio-blk,${CIDISK} \
220		-s 3:0,virtio-blk,${META_TAR} \
221		${BHYVE_EXTRA_DISK_PARAM} \
222		-l com1,stdio \
223		${TEST_VM_NAME}; \
224		expect { eof }"
225	/usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy
226.else
227	timeout -k 60 ${TIMEOUT_VM} ${QEMUBIN} \
228		-machine ${QEMU_MACHINE} \
229		-smp ${QEMU_CPU_COUNT} \
230		-m ${VM_MEM_SIZE} \
231		-nographic \
232		-no-reboot \
233		${QEMU_EXTRA_PARAM} \
234		-drive if=none,file=${CIDISK},format=raw,id=hd0 \
235		-drive if=none,file=${META_TAR},format=raw,id=hd1 \
236		${QEMU_DEVICES}
237.endif
238
239ci-checktarget: .PHONY
240.if ${TARGET_ARCH} != "aarch64" && \
241	${TARGET_ARCH} != "amd64" && \
242	${TARGET_ARCH} != "armv7" && \
243	${TARGET_ARCH} != "powerpc64" && \
244	${TARGET_ARCH} != "powerpc64le" && \
245	${TARGET_ARCH} != "riscv64"
246	@false
247.ERROR:
248	@echo "Error: ${TARGET_ARCH} is not supported on ${TYPE} ${REVISION} ${BRANCH}"
249.endif
250
251ci-smoke: ci-set-smoke-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .PHONY
252
253ci-full: ci-set-full-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} ci-extract-meta .PHONY
254
255ci: ci-${CITYPE:tl} .PHONY
256
257.include "${RELEASEDIR}/Makefile.inc1"
258