Swift/Ninja: Split compilation model
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