APPLIANCE ?= base VABUILDER_OUTPUT := $(CURDIR) CHROOT = $(VABUILDER_OUTPUT)/build/$(APPLIANCE) PKGDIR = $(VABUILDER_OUTPUT)/packages DISTDIR = $(CURDIR)/distfiles PORTAGE_DIR = $(CURDIR)/portage HOSTNAME = $(APPLIANCE) IMAGES = $(VABUILDER_OUTPUT)/images RAW_IMAGE = $(IMAGES)/$(APPLIANCE).img QCOW_IMAGE = $(IMAGES)/$(APPLIANCE).qcow VMDK_IMAGE = $(IMAGES)/$(APPLIANCE).vmdk XVA_IMAGE = $(IMAGES)/$(APPLIANCE).xva LST_FILE = $(IMAGES)/$(APPLIANCE)-packages.lst CHECKSUMS = $(IMAGES)/SHA256SUMS STAGE3 = $(CHROOT)/var/tmp/stage3 COMPILE_OPTIONS = $(CHROOT)/var/tmp/compile_options SOFTWARE = $(CHROOT)/var/tmp/software KERNEL = $(CHROOT)/var/tmp/kernel GRUB = $(CHROOT)/var/tmp/grub PREPROOT = $(CHROOT)/var/tmp/preproot SYSTOOLS = $(CHROOT)/var/tmp/systools STAGE4_TARBALL = $(VABUILDER_OUTPUT)/images/$(APPLIANCE).tar.xz VIRTIO = NO TIMEZONE = UTC DISK_SIZE = 6.0G SWAP_SIZE = 30 SWAP_FILE = $(CHROOT)/.swap ARCH = amd64 KERNEL_CONFIG = configs/kernel.config.$(ARCH) MAKEOPTS = -j5 -l5.64 ENABLE_SSHD = NO CHANGE_PASSWORD = YES HEADLESS = NO EXTERNAL_KERNEL = NO PKGLIST = 0 ACCEPT_KEYWORDS = amd64 DASH = NO LOCALE ?= en_US.utf8 M4 = m4 EMERGE = /usr/bin/emerge --jobs=4 M4_DEFS = -D HOSTNAME=$(HOSTNAME) M4C = $(M4) $(M4_DEFS) USEPKG = --usepkg --binpkg-respect-use=y RSYNC_MIRROR = rsync://rsync.gtlib.gatech.edu/gentoo/ KERNEL_PKG = gentoo-sources PACKAGE_FILES = $(wildcard appliances/$(APPLIANCE)/package.*) WORLD = appliances/$(APPLIANCE)/world EXTRA_WORLD = # Allow appliance to override variables -include appliances/$(APPLIANCE)/$(APPLIANCE).cfg # Allow user to override variables -include $(profile).cfg ifneq ($(profile),) container = $(profile)-$(APPLIANCE)-build else container = $(APPLIANCE)-build endif inroot := systemd-nspawn --quiet \ --directory=$(CHROOT) \ --machine=$(container) \ --bind=$(PORTAGE_DIR):/usr/portage \ --bind=$(PKGDIR):/usr/portage/packages \ --bind=$(DISTDIR):/usr/portage/distfiles ifeq ($(ARCH),x86) inroot := linux32 $(inroot) endif stage4-exists := $(wildcard $(STAGE4_TARBALL)) COPY_ARGS = --exclude-from=configs/rsync-excludes ifeq ($(CHANGE_PASSWORD),YES) ifdef ROOT_PASSWORD change_password = $(inroot) usermod -p '$(ROOT_PASSWORD)' root else change_password = $(inroot) passwd --delete --expire root endif endif gcc_config = $(inroot) gcc-config 1 export APPLIANCE ACCEPT_KEYWORDS CHROOT EMERGE HEADLESS M4 M4C inroot export HOSTNAME MAKEOPTS TIMEZONE USEPKG WORLD export USEPKG RSYNC_MIRROR unexport PKGDIR ARCH all: stage4 image: $(RAW_IMAGE) sync_portage: $(PORTAGE_DIR) @scripts/echo Grabbing latest portage git -C $(PORTAGE_DIR) pull touch $(PORTAGE_DIR) $(PORTAGE_DIR): @scripts/echo Grabbing the portage tree git clone --depth=1 git://github.com/gentoo/gentoo.git $(PORTAGE_DIR) $(PREPROOT): $(STAGE3) $(PORTAGE_DIR) configs/fstab mkdir -p $(PKGDIR) $(DISTDIR) cp configs/fstab $(CHROOT)/etc/fstab ifeq ($(VIRTIO),YES) sed -i 's/sda/vda/' $(CHROOT)/etc/fstab endif ifneq ($(SWAP_SIZE),0) @scripts/echo Creating swap file: `basename $(SWAP_FILE)` dd if=/dev/zero of=$(SWAP_FILE) bs=1M count=$(SWAP_SIZE) /sbin/mkswap $(SWAP_FILE) else sed -i '/swap/d' $(CHROOT)/etc/fstab endif rm -f $(CHROOT)/etc/resolv.conf cp -L /etc/resolv.conf $(CHROOT)/etc/resolv.conf touch $(PREPROOT) stage3-$(ARCH)-latest.tar.bz2: @scripts/echo You do not have a stage3 tarball. Consider \"make sync_stage3\" @exit 1 sync_stage3: ./scripts/fetch-stage3 --specialty=systemd --outfile=stage3-$(ARCH)-latest.tar.bz2 $(ARCH) $(STAGE3): stage3-$(ARCH)-latest.tar.bz2 mkdir -p $(CHROOT) ifdef stage4-exists @scripts/echo Using stage4 tarball: `basename $(STAGE4_TARBALL)` tar xpf "$(STAGE4_TARBALL)" -C $(CHROOT) else @scripts/echo Using stage3 tarball tar xpf stage3-$(ARCH)-latest.tar.bz2 -C $(CHROOT) endif rm -f $(CHROOT)/etc/localtime touch $(STAGE3) $(COMPILE_OPTIONS): $(STAGE3) $(PORTAGE_DIR) configs/make.conf.$(ARCH) configs/locale.gen $(PACKAGE_FILES) cp configs/make.conf.$(ARCH) $(CHROOT)/etc/portage/make.conf echo ACCEPT_KEYWORDS=$(ACCEPT_KEYWORDS) >> $(CHROOT)/etc/portage/make.conf -[ -f "appliances/$(APPLIANCE)/make.conf" ] && cat "appliances/$(APPLIANCE)/make.conf" >> $(CHROOT)/etc/portage/make.conf $(inroot) eselect profile set 1 cp configs/locale.gen $(CHROOT)/etc/locale.gen $(inroot) locale-gen for f in $(PACKAGE_FILES); do \ base=`basename $$f` ; \ mkdir -p $(CHROOT)/etc/portage/$$base; \ cp $$f $(CHROOT)/etc/portage/$$base/virtual-appliance-$$base; \ done touch $(COMPILE_OPTIONS) $(KERNEL): $(COMPILE_OPTIONS) $(KERNEL_CONFIG) scripts/kernel.sh ifneq ($(EXTERNAL_KERNEL),YES) @scripts/echo Configuring kernel cp $(KERNEL_CONFIG) $(CHROOT)/root/kernel.config cp scripts/kernel.sh $(CHROOT)/root/kernel.sh $(inroot) --setenv=KERNEL=$(KERNEL_PKG) \ --setenv=EMERGE="$(EMERGE)" \ --setenv=USEPKG="$(USEPKG)" \ --setenv=MAKEOPTS="$(MAKEOPTS)" \ /bin/sh /root/kernel.sh rm -f $(CHROOT)/root/kernel.sh endif touch $(KERNEL) $(SYSTOOLS): $(PREPROOT) $(COMPILE_OPTIONS) @scripts/echo Installing standard system tools -$(inroot) $(EMERGE) --unmerge sys-fs/eudev $(inroot) $(EMERGE) $(USEPKG) --noreplace --oneshot sys-apps/systemd $(inroot) systemd-firstboot \ --timezone=$(TIMEZONE) \ --hostname=$(HOSTNAME) \ --root-password= $(inroot) eselect locale set $(LOCALE) ifeq ($(DASH),YES) if ! test -e "$(STAGE4_TARBALL)"; \ then $(inroot) $(EMERGE) --noreplace $(USEPKG) app-shells/dash; \ echo /bin/dash >> $(CHROOT)/etc/shells; \ $(inroot) chsh -s /bin/sh root; \ fi $(inroot) ln -sf dash /bin/sh endif touch $(SYSTOOLS) $(GRUB): $(PREPROOT) configs/grub.conf $(KERNEL) scripts/grub-headless.sed ifneq ($(EXTERNAL_KERNEL),YES) @scripts/echo Installing Grub $(inroot) $(EMERGE) -nN $(USEPKG) sys-boot/grub-static cp configs/grub.conf $(CHROOT)/boot/grub/grub.conf ifeq ($(VIRTIO),YES) sed -i 's/sda/vda/' $(CHROOT)/boot/grub/grub.conf endif ifeq ($(HEADLESS),YES) sed -i -f scripts/grub-headless.sed $(CHROOT)/boot/grub/grub.conf endif endif $(inroot) ln -nsf /run/systemd/resolve/resolv.conf /etc/resolv.conf touch $(GRUB) $(SOFTWARE): $(STAGE3) $(SYSTOOLS) configs/eth.network configs/issue $(WORLD) @scripts/echo Building $(APPLIANCE)-specific software $(MAKE) -C appliances/$(APPLIANCE) preinstall cp $(WORLD) $(CHROOT)/var/tmp/world $(inroot) xargs -a/var/tmp/world -d'\n' -r $(EMERGE) $(USEPKG) --update --newuse --deep -$(gcc_config) @scripts/echo Running @preserved-rebuild $(inroot) $(EMERGE) --usepkg=n @preserved-rebuild cp configs/issue $(CHROOT)/etc/issue -$(gcc_config) $(inroot) $(EMERGE) $(USEPKG) --update --newuse --deep world $(inroot) $(EMERGE) --depclean --with-bdeps=n -$(gcc_config) $(inroot) --setenv EDITOR=/usr/bin/nano etc-update $(MAKE) -C appliances/$(APPLIANCE) postinstall cp configs/eth.network $(CHROOT)/etc/systemd/network/eth.network $(inroot) systemctl enable systemd-networkd.service $(inroot) systemctl enable systemd-resolved.service ifeq ($(ENABLE_SSHD),YES) $(inroot) systemctl enable sshd.service endif $(change_password) ifeq ($(DASH),YES) $(inroot) $(EMERGE) --depclean app-shells/bash endif ifneq ($(PKGLIST),0) echo \# > $(LST_FILE) echo \# Gentoo Virtual Appliance \"$(APPLIANCE)\" package list >> $(LST_FILE) echo \# Generated `date -u` >> $(LST_FILE) echo \# >> $(LST_FILE) (cd "$(CHROOT)"/var/db/pkg ; /bin/ls -1d */* | grep -v '^virtual/') >> $(LST_FILE) endif touch $(SOFTWARE) $(RAW_IMAGE): $(STAGE4_TARBALL) scripts/grub.shell scripts/motd.sh @scripts/echo Installing files to `basename $(RAW_IMAGE)` qemu-img create -f raw $(RAW_IMAGE).tmp $(DISK_SIZE) parted -s $(RAW_IMAGE).tmp mklabel gpt parted -s $(RAW_IMAGE).tmp mkpart primary 1 $(DISK_SIZE) parted -s $(RAW_IMAGE).tmp set 1 boot on sync losetup --show --find --partscan $(RAW_IMAGE).tmp > partitions mkfs.ext4 -O sparse_super,^has_journal -L "$(APPLIANCE)"_root -m 0 `cat partitions`p1 mkdir $(CHROOT) mount -o noatime `cat partitions`p1 $(CHROOT) tar -xf $(STAGE4_TARBALL) --numeric-owner $(COPY_ARGS) -C $(CHROOT) scripts/motd.sh $(EXTERNAL_KERNEL) $(VIRTIO) $(DISK_SIZE) $(SWAP_SIZE) $(DASH) $(ARCH) > $(CHROOT)/etc/motd ifneq ($(EXTERNAL_KERNEL),YES) echo '(hd0) ' $(RAW_IMAGE).tmp > device-map $(CHROOT)/sbin/grub --device-map=device-map --no-floppy --batch < scripts/grub.shell endif umount $(CHROOT) rmdir $(CHROOT) sync losetup --detach `cat partitions` rm -f partitions device-map mv $(RAW_IMAGE).tmp $(RAW_IMAGE) $(QCOW_IMAGE): $(RAW_IMAGE) @scripts/echo Creating `basename $(QCOW_IMAGE)` qemu-img convert -f raw -O qcow2 -c $(RAW_IMAGE) $(QCOW_IMAGE).tmp mv $(QCOW_IMAGE).tmp $(QCOW_IMAGE) qcow: $(QCOW_IMAGE) $(XVA_IMAGE): $(RAW_IMAGE) @scripts/echo Creating `basename $(XVA_IMAGE)` xva.py --disk=$(RAW_IMAGE) --is-hvm --memory=256 --vcpus=1 --name=$(APPLIANCE) \ --filename=$(XVA_IMAGE).tmp mv $(XVA_IMAGE).tmp $(XVA_IMAGE) xva: $(XVA_IMAGE) $(VMDK_IMAGE): $(RAW_IMAGE) @scripts/echo Creating `basename $(VMDK_IMAGE)` qemu-img convert -f raw -O vmdk $(RAW_IMAGE) $(VMDK_IMAGE).tmp mv $(VMDK_IMAGE).tmp $(VMDK_IMAGE) vmdk: $(VMDK_IMAGE) $(STAGE4_TARBALL): $(PORTAGE_DIR) stage3-$(ARCH)-latest.tar.bz2 appliances/$(APPLIANCE) configs/rsync-excludes $(MAKE) $(STAGE3) $(MAKE) $(PREPROOT) $(MAKE) $(SOFTWARE) $(MAKE) $(KERNEL) $(MAKE) $(GRUB) @scripts/echo Creating stage4 tarball: `basename $(STAGE4_TARBALL)` mkdir -p $(IMAGES) tar -acf "$(STAGE4_TARBALL).tmp.xz" --numeric-owner $(COPY_ARGS) -C $(CHROOT) --one-file-system . mv "$(STAGE4_TARBALL).tmp.xz" "$(STAGE4_TARBALL)" $(MAKE) clean stage4: $(STAGE4_TARBALL) eclean: $(COMPILE_OPTIONS) $(inroot) $(EMERGE) $(USEPKG) --oneshot --noreplace app-portage/gentoolkit $(inroot) eclean-pkg $(inroot) eclean-dist $(inroot) $(EMERGE) --depclean app-portage/gentoolkit $(MAKE) clean clean: rm -f partitions device-map $(IMAGES)/*.tmp rm -rf --one-file-system -- $(CHROOT) realclean: clean ${RM} $(RAW_IMAGE) $(QCOW_IMAGE) $(VMDK_IMAGE) distclean: rm -f -- *.qcow *.img *.vmdk rm -f latest-stage3.txt stage3-*-latest.tar.bz2 rm -f portage-snapshot.tar.bz2 appliance-list: @scripts/echo 'Available appliances:' @/bin/ls -1 appliances checksums: @scripts/echo 'Calculating checksums' $(RM) $(CHECKSUMS) cd $(IMAGES) && sha256sum --binary * > $(CHECKSUMS).tmp mv $(CHECKSUMS).tmp $(CHECKSUMS) shell: $(PREPROOT) @scripts/echo 'Entering interactive shell for the $(APPLIANCE) build.' @scripts/echo 'Type "exit" or "^D" to leave' @scripts/echo @$(inroot) @rm -f $(CHROOT)/root/.bash_history help: @scripts/echo 'Help targets (this is not a comprehensive list)' @echo @echo 'sync_portage - Download the latest portage snapshot' @echo 'sync_stage3 - Download the latest stage3 tarball' @echo 'stage4 - Build a stage4 tarball' @echo 'clean - Unmount chroot and clean directory' @echo 'eclean - Clean outdated packages and distfiles' @echo 'realclean - Clean and remove image files' @echo 'shell - Enter a shell in the build environment' @scripts/echo 'Images' @echo 'image - Build a raw VM image from stage4' @echo 'qcow - Build a qcow VM image from a raw image' @echo 'vmdk - Build a vmdk image from a raw image' @echo 'xva - Build an xva image from a raw image' @echo 'appliance-list - List built-in appliances' @echo 'help - Show this help' @scripts/echo 'Variables' @echo 'APPLIANCE= - The appliance to build' @echo 'HOSTNAME= - Hostname to give appliance' @echo 'TIMEZONE= - Timezone to set for the appliance' @echo 'CHROOT= - The directory to build the chroot' @echo 'DISK_SIZE= - Size of the disk image' @echo 'SWAP_SIZE= - Size of the swap file' @echo 'ARCH= - Architecture to build for (x86 or amd64)' @echo 'VIRTIO=YES - Configure the stage2/image to use virtio' @echo 'EXTERNAL_KERNEL=YES - Do not build a kernel in the image' @echo 'HEADLESS=YES - Build a headless (serial console) image.' @echo 'ENABLE_SSHD=YES - Enable sshd to start automatically in the image' @echo @scripts/echo 'Example' @echo 'make APPLIANCE=mongodb HEADLESS=YES VIRTIO=YES stage4 qcow clean' .PHONY: qcow vmdk clean realclean distclean stage4 image stage4 help appliance-list eclean sync_portage sync_stage3 checksums