Pragmatism in the real world

Cross-platform Makefile for Swift

I’m mostly building Swift applications for deployment to Linux, but sometimes it’s easier to build and test directly on OS X rather than spinning up a VM. To facilitate this, I use a Makefile that means that I don’t have to remember the compiler switches.

It looks like this:

# This Makefile assumes that you have swiftenv installed
# To get going, start with `make init`

SWIFT_VERSION = DEVELOPMENT-SNAPSHOT-2016-05-03-a

# OS specific differences
UNAME = ${shell uname}
ifeq ($(UNAME), Darwin)
SWIFTC_FLAGS =
LINKER_FLAGS = -Xlinker -L/usr/local/lib
endif
ifeq ($(UNAME), Linux)
SWIFTC_FLAGS = -Xcc -fblocks
LINKER_FLAGS = -Xlinker -rpath -Xlinker .build/debug
PATH_TO_SWIFT = /home/vagrant/swiftenv/versions/$(SWIFT_VERSION)
endif


build:
	swift build $(SWIFTC_FLAGS) $(LINKER_FLAGS)

test: build
	swift test

clean:
	swift build --clean

distclean:
	rm -rf Packages
	swift build --clean

init:
	- swiftenv install $(SWIFT_VERSION)
	swiftenv local $(SWIFT_VERSION)
ifeq ($(UNAME), Linux)
	cd /vagrant && \
	  git clone --recursive -b experimental/foundation https://github.com/apple/swift-corelibs-libdispatch.git && \
	  cd swift-corelibs-libdispatch && \
	  sh ./autogen.sh && \
	  ./configure --with-swift-toolchain=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr \
	    --prefix=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr && \
	  make && make install
endif


.PHONY: build test distclean init

Let’s go through it.

SWIFT_VERSION = DEVELOPMENT-SNAPSHOT-2016-05-03-a

This simply sets which version of Swift this project uses. It’s up the top as this is the only line that I change on a regular basis.

# OS specific differences
UNAME = ${shell uname}
ifeq ($(UNAME), Darwin)
SWIFTC_FLAGS =
LINKER_FLAGS = -Xlinker -L/usr/local/lib
endif
ifeq ($(UNAME), Linux)
SWIFTC_FLAGS = -Xcc -fblocks
LINKER_FLAGS = -Xlinker -rpath -Xlinker .build/debug
endif

This section allows for different compiler and linker switches for each operating system.

We’re compiling libdispatch on Liux, so we need to enable the blocks language feature in clang. This is done by using the -Xcc switch to tell the compiler to pass the next switch (-fblocks) to clang.

For linking on OS X, I nee to pick up the Homebrew Mysql library in /usr/local/lib. This isn’t needed on Linux as apt puts the relevant library in the right place. However, on Linux we need to link against a library in our own .build/debug directory, so we pass the switches for that. In the same way as -Xcc, -Xlinker passes the next parameter to the linker (ld). We need to pass -rpath .build/debug, but as we can only pass one argument at a time with -Xlinker, we do it twice.

build:
	swift build $(SWIFTC_FLAGS) $(LINKER_FLAGS)

test: build
	swift test

clean:
	swift build --clean

distclean:
	rm -rf Packages
	swift build --clean

These are the standard make targets for building, testing and cleaning up intermediate files. By using the standard names, working on different projects is very easy and predictable as the same make commands work everywhere.

init:
	- swiftenv install $(SWIFT_VERSION)
	swiftenv local $(SWIFT_VERSION)
ifeq ($(UNAME), Linux)
	cd /vagrant && \
	  git clone --recursive -b experimental/foundation https://github.com/apple/swift-corelibs-libdispatch.git && \
	  cd swift-corelibs-libdispatch && \
	  sh ./autogen.sh && \
	  ./configure --with-swift-toolchain=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr \
	    --prefix=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr && \
	  make && make install
endif

The init target is specific to my Swift projects. It sets the correct local Swift version for this project using swiftenv. The - before the swiftenv install command ensures that make continues even if this command fails (which it will if the version is already installed).

We then do something that’s specific to Linux and install lib-dispatch which we need for GCD. It’s included already in OS X, which is why this is guarded by the ifeq ($(UNAME), Linux).

That’s it. This is a simple Makefile which leaves me to think entirely about my code, rather than my build system.

One thought on “Cross-platform Makefile for Swift

Comments are closed.