########################## Usage ########################### define USAGE ======================================== USAGE: make TARGET=musl-toolchain [ARCH=cpu-arch] [program ...] The TARGET value must be a musl-cross-make toolchain target. The optional ARCH value must be a valid GCC -march CPU type. Examples targets: TARGET=arm-linux-musleabi TARGET=arm-linux-musleabihf ARCH=armv7-a+fp TARGET=mips-linux-musl TARGET=mipsel-linux-muslsf TARGET=x86_64-linux-musl ... For additional targets, consult the musl manual: https://musl.libc.org/doc/1.1.24/manual.html Goals: all Builds all available programs: $(sort $(ALL_PROGRAMS)) default Builds default subset of programs: $(sort $(DEFAULT_PROGRAMS)) musl Builds the cross-compiler toolchain for TARGET. archlist Shows available CPU architectures for TARGET. env Shows shell commands to activate TARGET toolchain. usage Shows this message. mostlyclean Removes source code and temporary objects. clean Removes cross-compiler toolchain, sources, and objects. ======================================== endef required_features := else-if order-only second-expansion target-specific missing_features := $(filter-out $(.FEATURES),$(required_features)) ifneq (,$(missing_features)) $(error This version of make is missing required features: $(required_features)) endif ########################## Flags ########################### # We need access to `command -v` to check if programs exist. SHELL := /bin/bash CFLAGS = -g0 -Os CXXFLAGS = $(CFLAGS) # Just in case the user forgets that we're doing static builds. override LDFLAGS += -static override CFLAGS += -static $(if $(ARCH),-march=$(ARCH)) override CXXFLAGS += -static $(if $(ARCH),-march=$(ARCH)) # Some builds need to be explicitly given these paths. override LDFLAGS += -L$(SYSROOT)/lib override CFLAGS += -I$(SYSROOT)/include override CXXFLAGS += -I$(SYSROOT)/include # Attempt to make builds reproducible. # For most builds, this will get you a byte-for-byte identical output # regardless of which machine you cross-compiled from. Failing that, # two builds from the same build machine are identical. ifneq (0,$(REPRODUCIBLE)) export SOURCE_DATE_EPOCH := 0 override CFLAGS += -ffile-prefix-map=$(MAKEFILE_DIR)=. override CXXFLAGS += -ffile-prefix-map=$(MAKEFILE_DIR)=. endif # Intermediate files will be larger and build times will be slightly longer # but the final binary can sometimes be much smaller. ifneq (0,$(EXTRA_SMALL)) override LDFLAGS += -Wl,--gc-sections override CFLAGS += -ffunction-sections -fdata-sections override CXXFLAGS += -ffunction-sections -fdata-sections endif # The download command should take two extra arguments: OUTPUT_FILE URL ifneq (,$(shell command -v curl)) DOWNLOAD := curl --silent --show-error -L -o else ifneq (,$(shell command -v wget)) DOWNLOAD := wget --no-verbose -c -O else $(error No curl or wget detected, please manually specify the DOWNLOAD command.) endif # LibreSSL is a drop-in replacement for OpenSSL that's smaller and easier to build. OPENSSL := libressl # OPENSSL := openssl BUILD_TRIPLE := $(shell $(CC) -dumpmachine 2>/dev/null) CONFIGURE_DEFAULTS = --build="$(BUILD_TRIPLE)" --host="$(TARGET)" --prefix="$(SYSROOT)" ########################## Paths ########################### # NOTE: these paths need to be absolute. # All other paths are built from these, including the toolchain binaries. # A relative path would be useless once we `cd` into a source code directory. MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) MAKEFILE_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) SOURCE_ROOT := $(MAKEFILE_DIR)/sources WORK_ROOT := $(MAKEFILE_DIR)/work OUTPUT_ROOT := $(MAKEFILE_DIR)/output TOOLCHAIN_ROOT := $(MAKEFILE_DIR)/sysroot SYSROOT := $(TOOLCHAIN_ROOT)/$(TARGET) OUTPUT := $(OUTPUT_ROOT)/$(TARGET) PKG_CONFIG_PATH := $(TOOLCHAIN_ROOT)/lib/pkgconfig CMAKE_DEFAULTS = -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=$(SYSROOT) -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_FIND_ROOT_PATH=$(TOOLCHAIN_ROOT) -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY # Having whitespace in our build paths _will_ result in failures. # In addition to failures, a path containing whitespace may cause an # improperly quoted $(RM) to delete things outside of the build directory. ifneq (1,$(words $(MAKEFILE_DIR))) $(error Whitespace detected in build path. This _will_ result in build failures.) endif ######################## Functions ######################### # These "activate" functions are meant to be used with $(eval $(call ...)) define activate_paths $(1): export SYSROOT=$(SYSROOT) $(1): export PREFIX=$(SYSROOT) $(1): export PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) endef define activate_toolchain $(call activate_paths,$(1)) $(1): export AR=$(SYSROOT)/bin/$(TARGET)-ar $(1): export AS=$(SYSROOT)/bin/$(TARGET)-as $(1): export CC=$(SYSROOT)/bin/$(TARGET)-cc $(1): export CXX=$(SYSROOT)/bin/$(TARGET)-g++ $(1): export LD=$(SYSROOT)/bin/$(TARGET)-ld $(1): export NM=$(SYSROOT)/bin/$(TARGET)-nm $(1): export OBJCOPY=$(SYSROOT)/bin/$(TARGET)-objcopy $(1): export OBJDUMP=$(SYSROOT)/bin/$(TARGET)-objdump $(1): export RANLIB=$(SYSROOT)/bin/$(TARGET)-ranlib $(1): export READELF=$(SYSROOT)/bin/$(TARGET)-readelf $(1): export STRIP=$(SYSROOT)/bin/$(TARGET)-strip endef # Downloads and unpacks a tar file. define untar_to_dir mkdir -p "$(dir $(2))" $(DOWNLOAD) "$(2).tgz" "$(1)" mkdir -p "$(2).tmp" tar xf "$(2).tgz" --strip-components=1 -C "$(2).tmp" $(RM) "$(2).tgz" mv "$(2).tmp" "$(2)" endef # Creates variables based on library names. # This makes it easier for packages to depend on libraries # that will be created by other packages. # For example $(eval $(call export_library,/path/to/libsomething.a)) # will set libsomething := /path/to/libsomething.a define export_library $(basename $(notdir $(1))) := $(1) endef # Creates generic recipe chains for a package's binaries and libraries. # This is where the magic happens! # This would have been much cleaner to create with $(file &1 | grep -F "valid arguments" || true -@ "$(CC)" --target-help 2>&1 | sed -n '/Known.*-march/,/^$$/p' || true # Cleans all sources except for musl. .PHONY: mostlyclean mostlyclean: - $(RM) -r \ $(filter-out $(MUSL_SRC),$(wildcard $(SOURCE_ROOT)/*-*)) \ "$(SOURCE_ROOT)/"*.tgz \ "$(SOURCE_ROOT)/"*.tmp # Cleans all compiled results. .PHONY: clean clean: mostlyclean ifneq (,$(TARGET)) - $(RM) -r "$(OUTPUT)" - $(RM) -r "$(SYSROOT)" - $(RM) -r "$(MAKEFILE_DIR)/docker_context" else - $(RM) -r "$(OUTPUT_ROOT)" - $(RM) -r "$(TOOLCHAIN_ROOT)" endif # Cleans musl toolchain artifacts. .PHONY: distclean distclean: clean - $(RM) -r "$(SOURCE_ROOT)" # Dumps the toolchain variables for use in shell environments. # Meant to be used as: eval "$(make --silent TARGET=toolchain env)" .PHONY: env env: ifeq (,$(TARGET)) $(error TARGET is required to dump environment variables) endif $(info $(subst : ,,$(call activate_toolchain))) $(info LDFLAGS='$(LDFLAGS)') $(info CFLAGS='$(CFLAGS)') $(info CXXFLAGS='$(CXXFLAGS)')