I’ve been working on support for Swift in the Meson build system. For a while now, I have had support for mixed-source (i.e. Swift + C/C++/Obj-C) targets in my tree, and support for consuming a Swift library from another non-Swift target.
Of course, both of these involves generating the exported header, and the way this was done until now is by running essentially swiftc -parse -emit-objc-header -emit-objc-header-path Module-Swift.h Module.swiftmodule (plus potential extra flags for C++ interop and whatnot, depending on what the target wants). With Swift 6.3, this seems to have broken and this command now errors with error: unexpected input file: Module.swiftmodule.
What’s the intended way to do this, now that this no longer works? Also, I suppose I should ask since it’s related, on which side of the library boundary should this export header be generated? On behalf of the consuming library from the installed .swiftmodule or as part of the exported interface in addition to the .swiftmodule and other headers? Of course, if this is all done within the same project where all dependencies are built from source it doesn’t matter much but I do eventually want to add an install interface so that Meson can install and import built Swift modules in which case it would matter.
I'm surprised that ever worked, to be honest. AFAIK the -parse flag is just meant to verify that the files parse correctly and is mainly only used by the compiler's own tests. If you were passing a .swiftmodule file to it and other frontend interactions caused it to load that module and then also emit supplemental outputs based on it, that feels like an unintended interaction, but someone else more familiar with the frontend would have to say for sure.
The supported way to do this is to emit it on the library side when you compile the sources. You can do it in the same driver invocation that you use to emit the .swiftmodule file.
Ah okay, so I gather you should do everything in one command – compile object files, produce the .swiftmodule, and produce the export header? Right now these are three separate ninja targets but I’ve been wanting to combine at least the first two anyway so that swiftc doesn’t need to parse all the source files for the module twice. (Ninja targets for object files and .swiftmodule were separated like that before my changes already because at some point swiftc didn’t support making both at once according to the comment above that code.)
The -parse I just figured out works to suppress compilation altogether in this case, which iirc would fail anyway if I just pass it a .swiftmodule, and only have it output the header :^)
I guess now the time has come to finally change how this works. Thank you!
Strange, it's been possible to output both the module and the object files in the same driver invocation as long as I can remember.
However, depending on your needs and the capabilities of your build system, it might still be worthwhile to separate those actions. In general, performing the full codegen pipeline to produce object files takes longer than just what the compiler needs to do to produce the .swiftmodule (type-checking and SILGen for anything that's inlinable). Since compiling Swift only depends on the modules and not the object files from incoming edges, if your tooling does the right scheduling based on its input dependencies, then you can speed up end-to-end builds by splitting those outputs into separate actions so that downstream compiles start sooner.
Good point. Meson does indeed construct build dependencies correctly/in that amount of detail so I kept a separate target for building the object files and merged the generation for .swiftmodule and the C header into one target now (+ potentially .swiftinterface in the future as well).
And I removed that comment (which was from 2015 for what it’s worth) so that nobody will trip over it in the future :^)