heads/Makefile
Youness Alaoui e7d4b88d39
Add ability to specify a series of patches per module
If a file $module_$version.series exists, it will be used to
specify a list of patch files to apply to the module.
This is becoming necessary for coreboot which has an increasing
amount of patches required and which makes it hard to maintain
all in one file.
2018-03-14 16:27:21 -04:00

544 lines
14 KiB
Makefile

all:
-include .config
modules-y :=
pwd := $(shell pwd)
packages ?= $(pwd)/packages
build := $(pwd)/build
config := $(pwd)/config
INSTALL := $(pwd)/install
log_dir := $(build)/log
BOARD ?= qemu-coreboot
CONFIG := $(pwd)/boards/$(BOARD).config
ifneq "y" "$(shell [ -r '$(CONFIG)' ] && echo y)"
$(error $(CONFIG): board configuration does not exist)
endif
include $(CONFIG)
# Unless otherwise specified, we are building for heads
CONFIG_HEADS ?= y
# Controls how many parallel jobs are invoked in subshells
CPUS := $(shell nproc)
MAKE_JOBS ?= -j$(CPUS) --max-load 16
# Create the log directory if it doesn't already exist
BUILD_LOG := $(shell mkdir -p "$(log_dir)" "$(build)/$(BOARD)" )
# Some things want usernames, we use the current checkout
# so that they are reproducible
GIT_HASH := $(shell git rev-parse HEAD)
# Timestamps should be in ISO format
DATE=`date --rfc-3339=seconds`
# If V is set in the environment, do not redirect the tee
# command to /dev/null.
ifeq "$V" ""
VERBOSE_REDIRECT := > /dev/null
# Not verbose, so we only show the header
define do =
@echo "$(DATE) $1 $(2:$(pwd)/%=%)"
@$3
endef
else
# Verbose, so we display what we are doing
define do =
@echo "$(DATE) $1 $(2:$(pwd)/%=%)"
$3
endef
endif
# Check that we have a correct version of make
LOCAL_MAKE_VERSION := $(shell $(MAKE) --version | head -1 | cut -d' ' -f3)
include modules/make
ifeq "$(LOCAL_MAKE_VERSION)" "$(make_version)"
# Create a temporary directory for the initrd
initrd_dir := $(shell mktemp -d)
initrd_lib_dir := $(initrd_dir)/lib
initrd_bin_dir := $(initrd_dir)/bin
$(shell mkdir -p "$(initrd_lib_dir)" "$(initrd_bin_dir)")
# We are running our own version of make,
# proceed with the build.
# Force pipelines to fail if any of the commands in the pipe fail
SHELL := /bin/bash
.SHELLFLAGS := -o pipefail -c
# If musl-libc is being used in the initrd, set the heads_cc
# variable to point to it.
musl_dep := musl
heads_cc := $(INSTALL)/bin/musl-gcc \
-fdebug-prefix-map=$(pwd)=heads \
-gno-record-gcc-switches \
-D__MUSL__ \
CROSS := $(build)/../crossgcc/x86_64-linux-musl/bin/x86_64-musl-linux-
CROSS_TOOLS_NOCC := \
AR="$(CROSS)ar" \
LD="$(CROSS)ld" \
STRIP="$(CROSS)strip" \
NM="$(CROSS)nm" \
OBJCOPY="$(CROSS)objcopy" \
OBJDUMP="$(CROSS)objdump" \
CROSS_TOOLS := \
CC="$(heads_cc)" \
$(CROSS_TOOLS_NOCC) \
ifeq "$(CONFIG_COREBOOT)" "y"
all: $(build)/$(BOARD)/coreboot.rom
else ifeq "$(CONFIG_LINUXBOOT)" "y"
all: $(build)/$(BOARD)/linuxboot.rom
else
$(error "$(BOARD): neither CONFIG_COREBOOT nor CONFIG_LINUXBOOT is set?")
endif
# helpful targets for common uses
linux: $(build)/$(BOARD)/bzImage
cpio: $(build)/$(BOARD)/initrd.cpio.xz
# Disable all built in rules
.SUFFIXES:
FORCE:
# Make helpers to operate on lists of things
define prefix =
$(foreach _, $2, $1$_)
endef
define map =
$(foreach _,$2,$(eval $(call $1,$_)))
endef
# Bring in all of the module definitions;
# these are the external pieces that will be downloaded and built
# as part of creating the Heads firmware image.
include modules/*
# These will be built via their intermediate targets
# This increases the build time, so it is commented out for now
#all: $(foreach m,$(modules-y),$m.intermediate)
define bins =
$(foreach m,$1,$(call prefix,$(build)/$($m_dir)/,$($m_output)))
endef
define libs =
$(foreach m,$1,$(call prefix,$(build)/$($m_dir)/,$($m_libraries)))
endef
define outputs =
$(foreach m,$1,\
$(call bins,$m)\
$(call libs,$m)\
)
endef
#
# Build a cpio from a directory
#
define do-cpio =
$(call do,CPIO,$1,\
( cd "$2"; \
find . \
| cpio \
--quiet \
-H newc \
-o \
) > "$1.tmp" \
)
@if ! cmp --quiet "$1.tmp" "$1" ; then \
mv "$1.tmp" "$1" ; \
else \
rm "$1.tmp" ; \
fi
endef
define do-copy =
$(call do,COPY,$1 => $2',\
sha256sum "$(1:$(pwd)/%=%)" ; \
if ! cmp --quiet "$1" "$2" ; then \
cp -a "$1" "$2"; \
fi
)
endef
#
# Generate the targets for a module.
#
# Special variables like $@ must be written as $$@ to avoid
# expansion during the first evaluation.
#
define define_module =
ifneq ("$($1_repo)","")
# Checkout the tree instead and touch the canary file so that we know
# that the files are all present. No signature hashes are checked in
# this case, since we don't have a stable version to compare against.
$(build)/$($1_dir)/.canary:
git clone $($1_repo) "$(build)/$($1_dir)"
if [ -r patches/$1.patch ]; then \
( cd $(build)/$($1_dir) ; patch -p1 ) \
< patches/$1.patch; \
fi
if [ -d patches/$1 ] && \
[ -r patches/$1 ] ; then \
for patch in patches/$1/*.patch ; do \
echo "Applying patch file : $$$$patch " ; \
( cd $(build)/$($1_dir) ; patch -p1 ) \
< $$$$patch ; \
done ; \
fi
@touch "$$@"
else
# Fetch and verify the source tar file
$(packages)/$($1_tar):
wget -O "$$@" $($1_url)
$(packages)/.$1-$($1_version)_verify: $(packages)/$($1_tar)
echo "$($1_hash) $$^" | sha256sum --check -
@touch "$$@"
# Unpack the tar file and touch the canary so that we know
# that the files are all present
$(build)/$($1_dir)/.canary: $(packages)/.$1-$($1_version)_verify
tar -xf "$(packages)/$($1_tar)" -C "$(build)"
if [ -r patches/$1-$($1_version).patch ]; then \
( cd $(build)/$($1_dir) ; patch -p1 ) \
< patches/$1-$($1_version).patch; \
fi
if [ -d patches/$1-$($1_version) ] && \
[ -r patches/$1-$($1_version) ] ; then \
for patch in patches/$1-$($1_version)/*.patch ; do \
echo "Applying patch file : $$$$patch " ; \
( cd $(build)/$($1_dir) ; patch -p1 ) \
< $$$$patch ; \
done ; \
fi
@touch "$$@"
endif
# Allow the module to override the destination configuration file
# via a relative path. Linux uses this to have a per-board build.
$(eval $1_config_file_path := $(build)/$($1_dir)/$(or $($1_config_file),.config))
ifeq "$($1_config)" ""
# There is no official .config file
$($1_config_file_path): $(build)/$($1_dir)/.canary
@mkdir -p $$(dir $$@)
@touch "$$@"
else
# Copy the stored config file into the unpacked directory
$($1_config_file_path): $($1_config) $(build)/$($1_dir)/.canary
@mkdir -p $$(dir $$@)
$(call do-copy,$($1_config),$$@)
endif
# Use the module's configure variable to build itself
$(dir $($1_config_file_path)).configured: \
$(build)/$($1_dir)/.canary \
$($1_config_file_path) \
$(foreach d,$($1_depends),$(call outputs,$d)) \
modules/$1
@echo "$(DATE) CONFIG $1"
@( \
cd "$(build)/$($1_dir)" ; \
echo "$($1_configure)"; \
$($1_configure) \
) \
< /dev/null \
2>&1 \
| tee "$(log_dir)/$1.configure.log" \
$(VERBOSE_REDIRECT)
@touch "$$@"
# All of the outputs should result from building the intermediate target
$(call outputs,$1): $1.intermediate
# Short hand target for the module
#$1: $(call outputs,$1)
# Target for all of the outputs, which depend on their dependent modules
$1.intermediate: \
$(foreach d,$($1_depends),$d.intermediate) \
$(foreach d,$($1_depends),$(call outputs,$d)) \
$(dir $($1_config_file_path)).configured \
@echo "$(DATE) MAKE $1"
@( \
echo "$(MAKE) \
-C \"$(build)/$($1_dir)\" \
$($1_target)" ; \
$(MAKE) \
-C "$(build)/$($1_dir)" \
$($1_target) \
) \
< /dev/null \
2>&1 \
| tee "$(log_dir)/$1.log" \
$(VERBOSE_REDIRECT) \
|| ( \
echo "tail $(log_dir)/$1.log"; \
echo "-----"; \
tail -20 "$(log_dir)/$1.log"; \
exit 1; \
)
@echo "$(DATE) DONE $1"
$1.clean:
-$(RM) "$(build)/$($1_dir)/.configured"
-$(MAKE) -C "$(build)/$($1_dir)" clean
endef
$(call map, define_module, $(modules-y))
# hack to force musl-cross to be built before musl
#$(build)/$(musl_dir)/.configured: $(build)/$(musl-cross_dir)/../../crossgcc/x86_64-linux-musl/bin/x86_64-linux-musl-gcc
#
# Install a file into the initrd, if it changed from
# the destination file.
#
define install =
@-mkdir -p "$(dir $2)"
$(call do,INSTALL,$2,cp -a "$1" "$2")
endef
#
# Files that should be copied into the initrd
# THis should probably be done in a more scalable manner
#
define initrd_bin_add =
$(initrd_bin_dir)/$(notdir $1): $1
$(call do,INSTALL-BIN,$$(<:$(pwd)/%=%),cp -a "$$<" "$$@")
@$(CROSS)strip --preserve-dates "$$@" 2>&-; true
initrd_bins += $(initrd_bin_dir)/$(notdir $1)
endef
define initrd_lib_add =
$(initrd_lib_dir)/$(notdir $1): $1
$(call do,INSTALL-LIB,$(1:$(pwd)/%=%),\
$(CROSS)strip --preserve-dates -o "$$@" "$$<")
initrd_libs += $(initrd_lib_dir)/$(notdir $1)
endef
# Only some modules have binaries that we install
# Shouldn't this be specified in the module file?
bin_modules-$(CONFIG_KEXEC) += kexec
bin_modules-$(CONFIG_TPMTOTP) += tpmtotp
bin_modules-$(CONFIG_PCIUTILS) += pciutils
bin_modules-$(CONFIG_FLASHROM) += flashrom
bin_modules-$(CONFIG_CRYPTSETUP) += cryptsetup
bin_modules-$(CONFIG_GPG) += gpg
bin_modules-$(CONFIG_LVM2) += lvm2
bin_modules-$(CONFIG_DROPBEAR) += dropbear
bin_modules-$(CONFIG_FLASHTOOLS) += flashtools
bin_modules-$(CONFIG_NEWT) += newt
$(foreach m, $(bin_modules-y), \
$(call map,initrd_bin_add,$(call bins,$m)) \
)
# Install the libraries for every module that we have built
$(foreach m, $(modules-y), \
$(call map,initrd_lib_add,$(call libs,$m)) \
)
# hack to install busybox into the initrd if busybox is configured
ifeq "$(CONFIG_BUSYBOX)" "y"
$(build)/$(BOARD)/heads.cpio: busybox.intermediate
initrd_bins += $(initrd_bin_dir)/busybox
endif
$(initrd_bin_dir)/busybox: $(build)/$(busybox_dir)/busybox
$(do,SYMLINK,$@,$(MAKE) \
-C $(build)/$(busybox_dir) \
CC="$(heads_cc)" \
CONFIG_PREFIX="$(pwd)/initrd" \
$(MAKE_JOBS) \
install \
)
#
# hack to build cbmem from coreboot
# this must be built *AFTER* musl, but since coreboot depends on other things
# that depend on musl it should be ok.
#
COREBOOT_UTIL_DIR=$(build)/$(coreboot_dir)/util
ifeq ($(CONFIG_COREBOOT),y)
$(eval $(call initrd_bin_add,$(COREBOOT_UTIL_DIR)/cbmem/cbmem))
#$(eval $(call initrd_bin_add,$(COREBOOT_UTIL_DIR)/superiotool/superiotool))
#$(eval $(call initrd_bin_add,$(COREBOOT_UTIL_DIR)/inteltool/inteltool))
endif
$(COREBOOT_UTIL_DIR)/cbmem/cbmem \
$(COREBOOT_UTIL_DIR)/superiotool/superiotool \
$(COREBOOT_UTIL_DIR)/inteltool/inteltool \
: $(build)/$(coreboot_dir)/.canary \
musl.intermediate
$(call do,MAKE,$(notdir $@),\
$(MAKE) -C "$(dir $@)" $(CROSS_TOOLS) \
)
# superio depends on zlib and pciutils
$(COREBOOT_UTIL_DIR)/superiotool/superiotool: zlib.intermediate pciutils.intermediate
#
# initrd image creation
#
# The initrd is constructed from various bits and pieces
# The cpio-clean program is used ensure that the files
# always have the same timestamp and appear in the same order.
#
# If there is no /dev/console, initrd can't startup.
# We have to force it to be included into the cpio image.
# Since we are picking up the system's /dev/console, there
# is a chance the build will not be reproducible (although
# unlikely that their device file has a different major/minor)
#
initrd-y += $(pwd)/blobs/dev.cpio
initrd-y += $(build)/$(BOARD)/modules.cpio
initrd-y += $(build)/$(BOARD)/tools.cpio
initrd-$(CONFIG_HEADS) += $(build)/$(BOARD)/heads.cpio
initrd.intermediate: $(build)/$(BOARD)/initrd.cpio.xz
$(build)/$(BOARD)/initrd.cpio.xz: $(initrd-y)
$(call do,CPIO-CLEAN,$@,\
$(pwd)/bin/cpio-clean \
$^ \
| xz \
--check=crc32 \
--lzma2=dict=1MiB \
-9 \
| dd bs=512 conv=sync > "$@" \
)
@sha256sum "$(@:$(pwd)/%=%)"
#
# The heads.cpio is built from the initrd directory in the
# Heads tree.
#
$(build)/$(BOARD)/heads.cpio: FORCE
$(call do-cpio,$@,$(pwd)/initrd)
#
# The tools initrd is made from all of the things that we've
# created during the submodule build.
#
$(build)/$(BOARD)/tools.cpio: \
$(initrd_bins) \
$(initrd_libs) \
$(call do,INSTALL,$(CONFIG), \
mkdir -p "$(initrd_dir)/etc" ; \
export \
| grep ' CONFIG_' \
| sed -e 's/^declare -x /export /' \
-e 's/\\\"//g' \
> "$(initrd_dir)/etc/config" \
)
$(call do-cpio,$@,$(initrd_dir))
@$(RM) -rf "$(initrd_dir)"
# This produces a ROM image that is written with the flashrom program
$(build)/$(BOARD)/coreboot.rom: $(build)/$(coreboot_dir)/$(BOARD)/coreboot.rom
"$(build)/$(coreboot_dir)/$(BOARD)/cbfstool" "$<" print
$(call do,EXTRACT,$@,mv "$<" "$@")
@sha256sum "$(@:$(pwd)/%=%)"
module_dirs := \
$(busybox_dir) \
$(cryptsetup_dir) \
$(dropbear_dir) \
$(flashrom_dir) \
$(gpg_dir) \
$(kexec_dir) \
$(libusb_dir) \
$(libusb-compat_dir) \
$(lvm2_dir) \
$(mbedtls_dir) \
$(pciutils_dir) \
$(popt_dir) \
$(qrencode_dir) \
$(tpmtotp_dir) \
$(util-linux_dir) \
$(zlib_dir) \
$(kernel-headers_dir) \
$(slang_dir) \
$(newt_dir) \
modules.clean:
for dir in $(module_dirs) \
; do \
$(MAKE) -C "build/$$dir" clean ; \
rm "build/$$dir/.configured" ; \
done
real.clean:
for dir in \
$(module_dirs) \
$(musl_dir) \
$(kernel_headers) \
; do \
if [ ! -z "$$dir" ]; then \
rm -rf "build/$$dir"; \
fi; \
done
rm -rf ./install
bootstrap:
$(MAKE) \
-j`nproc` \
musl-cross.intermediate \
$(build)/$(coreboot_dir)/util/crossgcc/xgcc/bin/i386-elf-gcc \
else
# Wrong make version detected -- build our local version
# and re-invoke the Makefile with it instead.
$(eval $(shell echo >&2 "$(DATE) Wrong make detected: $(LOCAL_MAKE_VERSION)"))
HEADS_MAKE := $(build)/$(make_dir)/make
# Once we have a proper Make, we can just pass arguments into it
all bootstrap linux cpio: $(HEADS_MAKE)
LANG=C MAKE=$(HEADS_MAKE) $(HEADS_MAKE) $@
%.clean %.intermediate %.vol: $(HEADS_MAKE)
LANG=C MAKE=$(HEADS_MAKE) $(HEADS_MAKE) $@
# How to download and build the correct version of make
$(HEADS_MAKE): $(build)/$(make_dir)/Makefile
make -C "$(dir $@)" $(MAKE_JOBS) \
2>&1 \
| tee "$(log_dir)/make.log" \
$(VERBOSE_REDIRECT)
$(build)/$(make_dir)/Makefile: $(packages)/$(make_tar)
tar xf "$<" -C build/
cd "$(dir $@)" ; ./configure \
2>&1 \
| tee "$(log_dir)/make.configure.log" \
$(VERBOSE_REDIRECT)
$(packages)/$(make_tar):
wget -O "$@" "$(make_url)"
if ! echo "$(make_hash) $@" | sha256sum --check -; then \
$(MV) "$@" "$@.failed"; \
false; \
fi
endif