Reading and Understanding the CMake in apple/swift

This article contains some content especially for patrons. Although it reads coherently as-is, to read the full article, please consider supporting me on Patreon, or click here if you are already a patron. $10/month gives you access to all content I will ever write on this website.

In my last post, I invoked cmake in order to build apple/swift. When I invoked cmake, I gave it the path to a source code directory. cmake then executes the CMakeLists.txt file in that directory.

In the case of apple/swift, that means the swift/CMakeLists.txt CMake script file is executed.

swift/CMakeLists.txt, in turn, calls the CMake function add_subdirectory() to include its child directories. The CMakeLists.txt files in those subdirectories then include their children, and so on:

~/local/Source/apple/swift/CMakeLists.txt
    lib/CMakeLists.txt
        Driver/CMakeLists.txt
            Action.cpp, Compilation.cpp, ...
        Frontend/CMakeLists.txt
            Frontend.cpp, FrontendOptions.cpp, ...
        ...
     tools/CMakeLists.txt
        ...
     test/CMakeLists.txt
        ...

Here's an animated "time-lapse" of this process.

This recursive execution of build system code is referred to as the recursive make pattern. The LLVM, Clang, and apple/swift projects all structure their CMake code following this pattern.

By convention, a CMakeLists.txt file in a directory describes how the source code in that directory is built. For example, to understand how the source code in swift/lib/Driver is compiled, I would first read the swift/lib/Driver/CMakeLists.txt file.

In addition, apple/swift defines helper CMake functions in the swift/cmake/modules directory. The top-level swift/CMakeLists.txt file includes these CMake functions by first appending the directory to the CMake module path…

swift/CMakeLists.txt

4  list(APPEND CMAKE_MODULE_PATH
5      "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")

…and then including them by name by using the CMake include() function:

swift/CMakeLists.txt

15  include(SwiftUtils)        # Loads swift/cmake/modules/SwiftUtils.cmake.
16  include(CheckSymbolExists) # Loads swift/cmake/modules/CheckSymbolExists.cmake.

Including the two files above makes functions defined therein available to all CMake scripts in the project. For example, swift/cmake/modules/SwiftUtils.cmake defines a function named precondition(), which halts CMake with an error at configuration time if a variable is not defined.

I find reading the CMake in apple/swift to be straightforward, because the order in which the CMake is executed is clear. All I have to do is read from the top of swift/CMakeLists.txt to the bottom, following any calls to include() or add_subdirectory().

Reading apple/swift CMake by example: the swift executable

The above description should be enough to determine how, for example, the swift compiler executable is configured and built. Here's how I found out:

$10+ patron-only content

A guide on how to search through the apple/swift CMake in order to find out about something you're interested in. In this specific case, I searched for where the swift compiler executable was described by apple/swift CMake.

I am a patronBecome a patron

A recap of how apple/swift CMake describes the swift executable

In summary, the story of how apple/swift CMake describes the swift executable is as follows:

$10+ patron-only content

Reading through the code that defines the swift compiler executable involved a lot of twists and turns. This summary breaks it down conceptually into three steps.

I am a patronBecome a patron


The challenge for a contributor who hopes to improve Swift's CMake isn't that the CMake behaves in a magical way, or does unintuitive things. Rather, the challenge is simply that there is a lot of CMake code: _add_swift_executable_single() alone is nearly 100 lines long.

The entire apple/swift project has 189 CMake files, with over 8,000 significant lines of code. It also makes use of CMake functions defined in LLVM, which itself has 347 CMake files that contain over 10,000 significant lines!

As a result, it's not practical for me to describe how every single component in apple/swift is configured. However, if you're interested in how, for example, the sil-opt executable is configured, this post has hopefully shown how you can find out for yourself.

In an upcoming post, I'll use the methods above to learn and write about how some of the main components of the apple/swift project are configured:

Afterwards, I'll also write about specific CMake configurations that allow me to build just what I'm working on, in order to iterate quicker on Swift compiler development.

If this sort of content interests you, please consider supporting me on Patreon, and look forward to the next post!