Using non-modular libraries in an audio-unit

I've got a bunch of Audio Units I've been developing over the last few months - in Xcode 14 based on the Audio Unit template that ships in Xcode.

The DSP heavy-lifting is all done in Rust libraries, which I build for Intel and Apple Silicon, merge using lipo and build XCFrameworks from. There are several of these - one with the DSP code, and several others used from the UI - a mix of SwiftUI and Cocoa.

Getting the integration of Rust, Objective C, C++ and Swift working and automated took a few weeks (my first foray into C++ since the 1990s), but has been solid, reliable and working well - everything loads in Logic Pro and Garage Band and works.

But now I'm attempting the (woefully underdocumented) process of making 13 audio unit projects able to be loaded in-process - which means moving all of the actual code into a Framework. And therein lies the problem:

None of the xcframeworks are modular, which leads to the dreaded "Include of non-modular header inside framework module". Imported directly into the app extension project with "Allow Non-modular Includes in Framework Modules" works fine - but in a framework this seems to be verboten.

So, the obvious solution would be to add a module map to each xcframework, and then, poof, they're modular.

BUT... due to a peculiar limitation of Xcode's build system I've spent days searching for a workaround for, it is only possible to have ONE xcframework containing a module.modulemap file in any project. More than that and xcodebuild will try to clobber them with each other and the build will fail. And there appears to be NO WAY to name the file anything other than module.modulemap inside an xcframework and have it be detected. So I cannot modularize my frameworks, because there is more than one of them.

How the heck does one work around this? A custom module map file somewhere (that the build should find and understand applies to four xcframeworks - how?)? Something else?

I've seen one dreadful workaround - https://medium.com/@florentmorin/integrating-swift-framework-with-non-modular-libraries-d18098049e18 - given that I'm generating a lot of the C and Objective C code for the audio in Rust, I suppose I could write a tool that parses the header files and generates Objective C code that imports each framework and declares one method for every single Rust call. But it seems to me there has to be a correct way to do this without jumping through such hoops.

Thoughts?

To update this with my attempts at workarounds - I've made what seems like some progress by:

  • Adding a Framework target to the audio unit project
  • Moving all sources to be compiled as part of that, and moving the library dependencies there
  • Adding a custom module map that includes all of the library headers (requires including a copy of the header files in my project [!] - no idea if it will link at runtime, but usages of the libraries are no longer errors)
  • Setting the framework project's custom module map to that

Since the original Audio Unit template makes liberal use of .h files, and these are verboten in a Framework without a module map, all of those have to be added as well (I suspect adding a bunch of stuff that has no business in the public API of my plugin into it) - I tried the private module map option, which did not work.

The result: The usages of my libraries are no longer errors. But imports from the C++ standard library now are - e.g.

#import <span> now results in 'span' file not found.

My god, what a mess this is, and why in the world is the Xcode template set up to make it as difficult as possible to turn the code it generates into something production-worthy?

Using non-modular libraries in an audio-unit
 
 
Q