Separate custom module map path for Cxx targets

Hi All,

When working with Cxx targets you can provide your own module.modulemap file within the location specified by publicHeadersPath attribute. It’s not always the case the module map is included in the same location as the headers, especially in old code bases, or ones that are shared with other build/packaging systems. It will be beneficial to open up this restriction for them.

The pitch is to add a new target property called moduleMapPath for Cxx targets that gives an explicit location of the module map so that it can be placed independently of the headers and then included in the target module. This path will be to the module map file itself, so that it can be sent to clang using the -fmodule-map-file parameter.

How does this sound?

Chris

1 Like

This sounds reasonable to me and is inline with what we did in SE-0482 where we defined a custom moduleMapPath key in the artifact bundle manifest format

{
    "schemaVersion": "1.0",
    "artifacts": {
        "<identifier>": {
            "version": "<version number>",
            "type": "staticLibrary",
            "variants": [
                {
                    "path": "<relative-path-to-library-file>",
                    "supportedTriples": ["<triple1>", ... ],
                    "staticLibraryMetadata": {
                        "headerPaths": ["<relative-path-to-header-directory-1>, ...],
                        "moduleMapPath": "<path-to-module-map>"
                    }
                },
                ...
            ]
        },
        ...
    }
}

Since this changes the package manifest API though, it would need to go through formal Swift Evolution.

1 Like

I think the main complication to figure out here is how it works when users want to distribute the module content alongside binaries, and the module map is not colocated with the headers. If the modulemap has relative paths from its source location to the source headers, or absolute paths to the source headers, this will break when the two are colocated for distribution. Injecting a VFS or rewriting paths in the module map can work around this, but it gets unwieldy pretty fast. The VFS approach might be somewhat automatable though as it's similar to how we retroactively modularize GlibC on some platforms.

I was thinking of sidestepping this case by focusing on the source code targets, not the binary ones. Implementation-wise it can be a copy of the moduleMapPath to wherever SwiftPM currently generates a module map when it doesn’t detect one in the public headers directory.

It would be interesting to explore using VFS in a variety of use cases including this. We have a similar problem with plug-in generated header files and their modulemaps.

I think Owen was referring to the case where the user will publish the products from their package in a binary form for use by consumers who may not even be using SwiftPM. The modulemap location is delicate and why it’s recommended they be co-located with the header files.

Ah, thanks. Yes, I was thinking mostly on the source side, not the publishing side.

Is there something that would be different on the publishing side of SwiftPM if instead of generating a module map at the top of the public headers path the module map is copied there from a different location before build (and publish) time?

If the user is providing a module map, they need to make sure the paths to the headers get picked up both by the package build and after publishing, any consumer builds.

I think the way SwiftPM manages the module map has issues as it stands. Any time we make a package manifest change, we need to make sure we map out all the ways it can go wrong and have a strategy for it. At the very least we need to analyze why the module map is not with the public headers. Do you have examples you can share?

1 Like