Simplify Your iOS CI with Makefiles

Harness the power of the classic MAKE utility for efficient and portable iOS builds

Simplify Your iOS CI with Makefiles

An iOS project of any size can benefit from automating the build process. Before reaching for a more complex solution like Fastlane or GitHub Actions, consider starting with the humble MAKE utility. MAKE has been around since 1976 and is preinstalled on any UNIX-like OS, including macOS, making it easy to adopt.

The Elegant Simplicity of MAKE

MAKE is the inspiration for many modern build tools, including Fastlane, and is still an excellent option for many projects needing automation but don't want to invest in complicated build systems. Here are a few benefits of using Makefiles over some alternatives.

1. Simplicity: Makefiles use a straightforward syntax that's easy to read and write.

2. Portability: Since MAKE is preinstalled on most UNIX-like systems, there are no dependencies to install and maintain on CI servers or developer machines.

3. Consistency: They provide a single source of truth for build and test commands.

4. Version Control: Your build process is now versioned alongside your code.

Let's look at how you can implement a Makefile for your iOS CI process.

Implementing a Makefile for iOS CI

Here's a basic structure for your iOS project's Makefile:

PROJECT = $(shell ls -1 . | grep .xcodeproj)
SCHEME = ExampleProject
BUILD_DIR = .build

.DEFAULT_GOAL := help

help:
  @echo Goals:
  @echo "help - display this message"
  @echo "clean - clean the project"
  @echo "build - build the project"
  @echo "test - test the project"

clean:
  @echo Cleaning...
  xcodebuild clean \
    -project $(PROJECT) \
    -scheme $(SCHEME) \
    | xcbeautify
  @rm -rf $(BUILD_DIR)

build: clean
  @echo Building...
  xcodebuild build \
    -project $(PROJECT) \
    -scheme $(SCHEME) \
    -derivedDataPath $(BUILD_DIR) \
    | xcbeautify

test: clean
  @echo Testing...
  xcodebuild test \
    -project $(PROJECT) \
    -scheme $(SCHEME) \
    -derivedDataPath $(BUILD_DIR) 

.PHONY: help clean build test

Breaking Down the Makefile

This Makefile defines several goals: help, clean, build, and test. Let's examine each goal:

1. help: Is a convenient goal to print out the available goals.

2. clean: Cleans the build folder.

3. build: Builds the project using xcodebuild.

4. test: Runs the test suite.

This is a simple example Makefile that can easily be extended to include goals for deploying to TestFlight or running SwiftLint.

Define Prerequisites

Prerequisites of a goal are listed after the colon, ensuring that it executes the prereqs before executing the specified goal.

build: clean

Setting a Default

By convention, running make without additional parameters will execute the first goal in the file. As of MAKE 3.80, the default can be set explicitly using the .DEFAULT_GOAL variable, making it a bit clearer.

.DEFAULT_GOAL := help

Variables

Variable values can be passed in as command-line arguments to MAKE or as environment variables, adding additional flexibility in configuring a Makefile.

make build SCHEME=MyScheme

Why so .PHONY?

By default, Makefile goals are file targets — they are expected to produce a file with the same name as the goal. If that file already exists, it won't run the goal. Using .PHONY tells MAKE that a goal will not produce a file.

GitHub Actions

Turn the Makefile into a Continuous Integration process by integrating it with a GitHub Action workflow.

  • Keeps the GH Action workflow simple
  • Developers can use the same commands as CI to execute builds locally

When MAKE isn't enough

While MAKE is a straightforward solution, it can become more complex as a project grows. As a Makefile becomes more complex, there is a point of diminishing returns. To scale a build system for complex projects with many modules, consider alternatives like BUCK or BAZEL, which support concepts like remote build execution and remote build caches.

Wrapping Up

MAKE is a simple yet powerful tool that can bring consistency, efficiency, and clarity to your CI pipeline without hiding details away behind unnecessary abstractions.

As you integrate this approach into your workflow, consider:

  • How can you tailor the Makefile to your team's specific needs?
  • What other areas of your development process could benefit from similar standardization?
  • How might this approach evolve as your team and project grow?

Remember, the goal is to spend less time fighting with CI and more time building great iOS apps. Happy coding!

Further Reading


Subscribe to Invoke.dev

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe