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.
2016 and no thanks yet!
Here are my thanks for this Howto