Sometimes you want to compile code without Xcode and all it's glory. This can be advantageous in some respects, so let's dig into the different ways we can compile some Swift code and project types from the command-line using only
make
and swiftc
.
Project Types
Currently, as of Xcode 6.1.1, Swift is only officially supported in essentially two types of targets:
- An executable target.
- A dynamic library (Xcode only supports a framework project).
Also, if we take a look at the output of swiftc -help
, we can see:
OVERVIEW: Swift compiler
USAGE: swiftc [options] <inputs>
MODES:
-dump-ast Parse and type-check input file(s) and dump AST(s)
-dump-parse Parse input file(s) and dump AST(s)
-emit-assembly Emit assembly file(s) (-S)
-emit-bc Emit LLVM BC file(s)
-emit-executable Emit a linked executable
-emit-ir Emit LLVM IR file(s)
-emit-library Emit a linked library
-emit-object Emit object file(s) (-c)
-emit-silgen Emit raw SIL file(s)
-emit-sil Emit canonical SIL file(s)
-parse Parse input file(s)
-print-ast Parse and type-check input file(s) and pretty print AST(s)
The import items from above are -emit-executable
and -emit-library
. Those are the ones that will get us where we want to go.
Executable Target
For the executable, all you need is a main.swift
file stored in a src
directory.
main.swift
println("Building with make!");
And the Makefile
looks like this:
# A simple build script for building projects.
#
# usage: make [CONFIG=debug|release]
MODULE_NAME = tool
SDK = macosx
ARCH = x86_64
CONFIG ?= debug
ROOT_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
OUTPUT_DIR = $(ROOT_DIR)/bin
TARGET_DIR = $(OUTPUT_DIR)/$(SDK)/$(CONFIG)
SRC_DIR = $(ROOT_DIR)/src
ifeq ($(CONFIG), debug)
CFLAGS=-Onone -g
else
CFLAGS=-O3
endif
SWIFTC = $(shell xcrun -f swiftc)
CLANG = $(shell xcrun -f clang)
SDK_PATH = $(shell xcrun --show-sdk-path --sdk $(SDK))
SWIFT_FILES = $(wildcard $(SRC_DIR)/*.swift)
build:
mkdir -p $(TARGET_DIR)
$(SWIFTC) $(SWIFT_FILES) -emit-executable -sdk $(SDK_PATH) -module-name $(MODULE_NAME) -emit-module -emit-module-path $(TARGET_DIR)/$(MODULE_NAME).swiftmodule -o $(TARGET_DIR)/$(MODULE_NAME)
clean:
rm -rf $(TARGET_DIR)
nuke:
rm -rf $(OUTPUT_DIR)
This is a fairly simple Makefile
that supports debug and release configurations, as well as cleaning or completely removing all of the built output.
This Makefile
will produce three things:
- The executable binary for the tool.
- A
swiftmodule
file. - A
swiftdoc
file.
All of the intermediate files are placed into temp directories. If you would like to see those, pass -v
into swiftc
.
Library Target
The library target is identical except for changing -emit-executable
with
-emit-library
.
Notice that once you make this change, main.swift
will no longer compile properly. So change the file to this:
public func message() -> String {
return "Building with make!"
}
Since the above method is public
, when you consume this library, and because we are generating the swiftmodule
file, the function will be available for you to call.
Summary
You'll probably not always want to use this technique and simply opt for the comfort of Xcode and its project configuration settings. However, if you need to setup a simple project and just want to use your favorite editor, copy this
Makefile
, make a few adjustments, and you're good to go.