CMake gains native Swift support through Ninja
CMake can now describe Swift targets for its Ninja generator. The initial support helps existing CMake projects integrate Swift without moving their whole build.
Adding a Swift component to an existing CMake project creates a build-integration problem before it creates a language-design problem. The project already has targets and build configurations, and a new language has to participate in those relationships. Saleem Abdulrasool announced on May 23 that the initial upstream changes for Swift support through the Ninja generator have merged. The announcement expects them to appear in CMake 3.15.
That wording matters for anyone trying the support today. It requires CMake built from the current development sources or an appropriate nightly build, with a minimum version of 3.14.20190522. It also requires the Ninja generator. Support for the Makefile backend remains work that upstream is interested in receiving. A machine with a stable CMake package does not acquire the new behavior merely because it has a Swift compiler installed.
I welcome this because Swift build configuration should fit the project that contains it. Existing native projects have reasons to retain their build system. Requiring a wholesale change before one Swift component can join the build makes the language harder to introduce at a useful boundary.
Language support belongs in the target graph
Abdulrasool supplies a demonstration project as it stood on announcement day. Its top-level configuration declares Swift as a project language. It also chooses a directory for emitted Swift modules and includes subdirectories for the implementation and tests. Those declarations make the compiler outputs part of the project structure.
The following excerpt shows the relevant language and module-directory settings from that example.
cmake_minimum_required(VERSION 3.14.20190522)
project(SwiftDemo LANGUAGES Swift)
set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)
The module directory has a different purpose from the final executable output. A client target importing a Swift library needs the compiler-readable module information as well as the linked library. Keeping that output in a known build-tree location gives dependent targets a place to find it. It also avoids treating every Swift source file as an isolated compiler invocation with no import relationships.
The demonstration splits its program into a library and an executable. The executable links the library through the normal target relationship.
add_executable(HelloWorld
HelloWorld.swift)
target_link_libraries(HelloWorld PRIVATE
HelloWorldCore)
That relationship is more useful than a custom command that happens to run swiftc. It gives CMake a named dependency to connect to the executable target. The library configuration also exposes the module include directory through its interface properties. The consumer can therefore obtain information supplied by the dependency itself instead of separately repeating its output locations.
The generator is part of the supported configuration
CMake generates build rules for a selected backend. The announcement specifically integrates Swift with Ninja, so choosing that generator is a requirement of the initial implementation. It is not an interchangeable preference for this release of the work. A command that selects a different backend is testing a configuration outside the announced support.
The historical demonstration documents this build sequence. The commands assume execution from the demonstration source directory.
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_TESTING=YES
cd build
ninja
ninja test
RelWithDebInfo selects an optimized build with debug information. The example also supports debug, release, and size-oriented configurations through CMAKE_BUILD_TYPE. Those configurations give the surrounding native build a familiar way to select its intended compiler behavior, rather than requiring a separate Swift-only convention.
The compiler still has to be discoverable. The demonstration README says to supply CMAKE_Swift_COMPILER when swiftc is absent from the command search path. That is a toolchain-location setting. It does not provide the compiler, standard library, or any target runtime that the build requires.
This separation becomes especially relevant when a machine contains several toolchains. Successfully running some swiftc from an interactive shell does not establish which compiler CMake selected during configuration. A build directory represents a configured environment. Compiler selection belongs to that environment, alongside generator selection and the intended build configuration.
Tests require the correct library build
The demonstration includes a test executable that depends on the library and registers through add_test. Running the Ninja test target then invokes that registered test. The announcement also explicitly calls out support for configuring libraries for @testable builds. That qualification matters because test compilation can require information or access that an ordinary client does not use.
The useful relationship is between the library configuration and the tests that consume it. Enabling a test target cannot compensate for compiling its dependency with incompatible visibility settings. Conversely, always enabling test-oriented exports for every library build would make the test configuration dictate ordinary distribution. Build-system integration needs to represent that distinction explicitly.
I remain more interested in this coordination than in replacing one manifest syntax with another. Swift build times are already a cost I notice. A project with a single coherent set of dependencies can reason about which outputs each stage needs, even though this announcement supplies no general claim about faster compilation.
A test target also documents a dependency that must remain valid as the source tree changes. If the library is renamed or its outputs move, target relationships give the build description a common place to express that change. A collection of unrelated shell commands instead makes every caller responsible for rediscovering the same paths. The distinction becomes more consequential when tests and ordinary executables share the same library but use different compiler settings.
This does not make CMake a package manager for Swift dependencies. The demonstrated configuration describes how local targets relate and how the selected backend builds them. Resolving an external dependency and arranging its source or binary inputs remains another part of the surrounding project. Treating those as separate responsibilities makes the initial support easier to assess.
Cross-compilation still needs target inputs
Abdulrasool reports testing the demonstration on Linux, Windows, and macOS. He also says it can cross-compile when the standard library has been built for the foreign target. The condition is essential. A build system can arrange compiler invocations, but it cannot make missing target libraries appear.
That is the practical scope of the announcement for a project already using CMake. Swift can join its target definitions and test configuration through the supported Ninja backend. The project retains responsibility for selecting a usable compiler and providing the libraries for its destination. Existing CMake infrastructure becomes reusable, while the language-specific outputs and target requirements remain visible in the build description.