Multi-target Makefiles

A colleague recently asked me whether or not I would recommend using CMake for a project that needed to target multiple architectures. While I appreciate that his instinct was not to reach for Autotools, I think CMake falls firmly in the same category as Autotools (though, to be fair, you're only introducing one new problem rather than four).

As an example, I present a complete Makefile to build cat on every platform for which Debian packages a GCC cross compiler. I know, cat is not a particularly interesting program, but the focus is on the Makefile.

.POSIX:

ARCHES= \
	aarch64-linux-gnu \
	arm-linux-gnueabi \
	arm-linux-gnueabihf \
	i686-linux-gnu \
	mips-linux-gnu \
	mips64el-linux-gnuabi64 \
	mipsel-linux-gnu \
	powerpc-linux-gnu \
	powerpc64le-linux-gnu \
	s390x-linux-gnu \
	x86_64-linux-gnu

CC=$(ARCH)-gcc

all: 
	for i in $(ARCHES); do make ARCH=$$i $$i; done

$(ARCH): $(ARCH)/cat

$(ARCH)/cat: $(ARCH)/cat.o
	$(CC) $(LDFLAGS) -s -o $@ $(ARCH)/cat.o

$(ARCH)/cat.o: cat.c
	mkdir -p $(ARCH)
	$(CC) $(CFLAGS) -c cat.c -o $@

This will cause make to loop through all the available architectures, outputting object files and linked binaries into a directory named for the target.

There's really no magic involved, though I understand that specifying a target that includes a macro name might seem so. Basically, the default target (all) executes a loop through the list of targets in $(ARCHES), setting ARCH to that target and building that same target (e.g. make ARCH=aarch64-linux-gnu aarch64-linux-gnu). This causes the $(ARCH) macro in each of the other targets to be replaced (in this case with aarch64-linux-gnu). The substitution also happens in the prerequisites and rules. So, on the first iteration, the child make process seems something more like (unchanged portions omitted for clarity):

.POSIX:

CC=aarch64-linux-gnu-gcc

aarch64-linux-gnu: aarch64-linux-gnu/cat

aarch64-linux-gnu/cat: aarch64-linux-gnu/cat.o
	$(CC) $(LDFLAGS) -s -o $@ aarch64-linux-gnu/cat.o

aarch64-linux-gnu/cat.o: cat.c
	mkdir -p aarch64-linux-gnu
	$(CC) $(CFLAGS) -c cat.c -o $@

This continues for each of the other architectures listed under ARCHES.

If you prefer to use Clang to GCC (and I won't blame you if you do), just change the CC macro:

CC=clang -triple $(ARCH)
Copyright © 2019 Jakob Kaivo <jakob@kaivo.net>