Skip to content

Swift/Ninja: Split compilation model

Evan Wilde requested to merge etcwilde/cmake:ewilde/swiftly-remodelling into master

The current Swift compilation model doesn't fit into CMake terribly well. With C and C++, compilation and linking are separate targets, where the compiler emits object files, followed by linking the objects into static archives, executables, or dynamic libraries. With Swift today, the Swift source files are treated as "object" files, passed to the Swift "linker". In mixed-source targets, the objects for the C and C++ sources are generated and passed to the Swift linker for linking as well. The Swift driver then picks out the Swift sources files and schedules the frontend jobs to produce 1 or N objects depending on the compilation mode, and then schedules a link job to link all of the objects. This is not terribly ideal; we don't get object libraries or compile-commands for Swift.

This patch remodels the Swift build in CMake to generate objects in one step, followed by linking them into the final library, executable, static archive, or otherwise. With these changes, Swift gets compile-commands support for LSP and object libraries, in addition to the executables, dynamic libraries, and static archives from before.

Challenges to this approach: While this fits into the CMake build model better, it doesn't fit perfectly. CMake's WriteObjectBuildStatement operates per-source file, which is sufficient for C and C++ because a translation unit to the compiler is a single C/C++ source file, which produces a single object file. This isn't the case of a unit of work for Swift, where all files in the module must be passed to the compiler invocation at once. Refactoring WriteObjectBuildStatement to handle multiple source files at once would require a much larger change than the one in this patch. WriteSwiftObjectBuildStatement duplicates a fair bit of the logic from WriteObjectBuildStatement, but it operates on multiple Swift source files at once.

I'm also running into some troubles with WMO. Object library dependencies and the TARGET_OBJECTS generator expression both call through cmLocalGenerator::GetObjectFileNameWithoutTarget, which doesn't have a way to inspect the flags passed to the target to determine if -wmo or -whole-module-optimization was passed to the Swift compiler to figure out what the name of the object is from the dependent target. I might be able to intercept the check higher in the call stack in cmGeneratorTarget::GetObjectSources, where we still have the necessary information, and then use that to set the name of the object file.

I would like to get thoughts on how to approach the difference in a swift compilation module vs C/C++ translation units mapping single source files to objects, better approaches to tracking targets that have -wmo passed to the Swift compiler (The Xcode generator has the ExtractFlag, and IDEOptions has HasFlag, but it doesn't look like that's available to the makefile and ninja generators), and how best to approach the TARGET_OBJECTS generator expression evaluation for WMO builds.

Issue: #25308 (closed)
Topic-rename: swift-split-compilation-model

Edited by Brad King

Merge request reports