Recursive .headerSearchPath

Hi!
I am developing the ObjC library for iOS and I want to migrate to SwiftPM from Xcode Project. The source code files of my library are grouped into folders by their functions.

-|Sources/MyLibrary
 |   -|include/MyLibrary
 |    | MyLibrary.h
 |    | PublicHeader1.h
 |    | PublicHeader2.h
 |    ----
 |   -|Feature
 |    | FeatureClass.h
 |    | FeatureClass.m
 |    | -|Subfeature
 |    |  | SubfeautureClass.h
 |    |  | SubfeautureClass.m
 |    |  ----
 |    ----
 |    Class.h
 |    Class.m
 ----

But there is a problem that SPM requires all source code files to be in a flat structure (as far as I understood). There is a way to specify each folder in .headerSearchPath() in cSettings:. But my project has a lot of folders, so that's not an option. So I came up with the idea of using recursive search paths as in the Xcode project Build settings. So I added the following parameter to my SPM target:

cSettings: [
    .headerSearchPath("./**")
]

This solution works well with xcodebuild, but not with swift build.
It seems that xcodebuild understands that it must expose all folders from Sources/MyLibrary/** and inserts the following parameters in the Clang invocation:

clang -ISources/MyLibrary/Feature -ISources/MyLibrary/Feature/Subfeature

But swift build does not do this. It invokes Clang as follows:

clang -I/Path/To/Project/Sources/MyLibrary/**

Clang doesn't understand this search path and gives an error saying it can't find the required .h file.
I think swiftbuild should have the same behavior as xcodebuild.

I think the Xcode behavior is technically a bug here, the package manifest model currently does not define support for globbing. What's happening is likely that the string value is directly put into Xcode's native HEADER_SEARCH_PATHS build setting which does support this type of globbing.

1 Like

Well, I expected that

So is there any other solution for nested folders?

In Xcode 15 Beta 5, this workaround no longer functions. Despite the fact that xcodebuild generates all the -I parameters as it previously did, it has ceased to work. Now, specifying a full search path is required for proper operation.

Just noticed this in Xcode 15 Beta 5 as well. It looks like the glob pattern works, but the -I parameters come in as relative paths (-ISources/MyTargetDir/MyTargetSubDir) when the public include directories use a full path (-I/Users/me/Work/MySPMProject/Sources/MyTargetDir/include). I'm assuming this relative path is what is causing the headers to not be found.

It'd be good to understand if this feature is expected to be supported or not. Works great in Xcode 14, but no longer in the Xcode 15 beta.

See my reply above, this was never intended to work.

Awesome, thanks for confirming

In Xcode 15 release, the recursive header search paths seem to do work just fine. I've been trying .headerSearchPath("./**") and got exactly what I wanted. Is this an accident or is this "works as intended" now?

Fwiw, I think this behavior should be officially supported. In Obj-C projects, it's common to put headers and implementations side-by-side and not have dedicated include directories.

And so, while I'm not minding a different syntax, being able to just cross-reference any header in a package is important. It also matches the behavior of Obj-C sources in a Xcode project, or Swift sources in a target – everything can see everything else in scope. (I'd even argue this should be the default in Obj-C targets in SPM, with just an option to disable it...)

1 Like