21

I'm using this answer to create a module map to create a module for CommonCrypto so I can use it in a framework.

Doing this however means that any projects that I use this framework in have access to CommonCrypto with import CommonCrypto - and even worse, declaring CommonCrypto in another framework and importing this into the project results in Redefinition of module 'CommonCrypto' errors.

I.e. the following setup:

MainProject
    |--> import FrameworkA - module map for CommonCrypto
    |--> import FrameworkB - module map for CommonCrypto

Is there a way to create a module map but have it private to that Framework its created/used in? (Much like the internal access attribute in Swift for a Framework). The llvm Clang docs show a private attribute but I can't work out where to put this in my module map, and it might not even be for this purpose! There's also an export attribute but again I'm not entirely sure how to use this...!

This is my module map I'm using for CommonCrypto - the $(SDKROOT) gets swapped out in a build phase to the correct location (for iphoneos or iphonesimulator SDKs):

module CommonCrypto [system] [extern_c] {
    umbrella header "$(SDKROOT)/usr/include/CommonCrypto/CommonCrypto.h"
    export *
}

This works fine (except you can't "go to definition" but I don't mind that) for use in FrameworkA / FrameworkB.

Community
  • 1
  • 1
Rich
  • 8,108
  • 5
  • 46
  • 59
  • Hi, any updated on this? I have the same exact setup for libz :( – tmpz Feb 02 '16 at 17:41
  • 1
    Rich, could you please provide your build phase script that replaces $(SKROOT)? Thanks! – appleitung Feb 15 '16 at 10:02
  • 1
    @appleitung: Script is here: gist.github.com/rhodgkins/5eecee8bcbdb6021fc798247132e9fa7 and then set it up like this: postimg.org/image/fj7j9nsqp in a project. Don't forget to add the directory $(PROJECT_DIR)/$(TARGET_NAME)/ExternalFrameworks/ to the *Framework Search Paths build setting as well. Then all module maps should sit in the input directory folder - with the folder name containing the modulemap being the name. – Rich Apr 12 '16 at 09:32

1 Answers1

8

Disclaimer: I have not tried this for CommonCrypto but it works for my case with libz

A possible solution to this is to create a module.private.modulemap as described in the Clang documentation

So for example in FrameworkA you can write a module.modulemap file for FrameworkA like so:

module FrameworkACommon {
}

Then you would create a module.private.modulemap file like so

explicit  FrameworkACommon.Crypto [system] [extern_c] {
   header "/Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/usr/include/CommonCrypto/CommonCrypto.h"
    link "CommonCrypto"
    export *
}

Then repeat for FrameworkB.

Now CommonCrypto is a private module in both FrameworkA and FrameworkB and the names won't clash.

tmpz
  • 368
  • 3
  • 12
  • Nice! Thanks for sharing - I shall try it! – Rich Feb 03 '16 at 13:38
  • 1
    @tmpz I get lot of 'use of undeclared type ...'. Have you faced this ? – nsinvocation Feb 09 '16 at 23:23
  • You're probably getting undeclared type because your module is not exporting any headers. The `export *` will export headers to `FrameworkACommon` and to import it you have to write `FrameworkACommon.Crypto` but it will stop there. Exported headers won't be visible past FrameworkACommon, they are internal now. – tmpz Feb 09 '16 at 23:50
  • 2
    Is there any way to get this kind of thing to work using CocoaPods? I've tried a number of things with modulemaps, but I just can't seem to get it to work – cjwirth Feb 22 '16 at 07:15
  • @cjwirth You can specify a [module map](https://guides.cocoapods.org/syntax/podspec.html#module_map) for a CocoaPod spec. – Rich Sep 27 '16 at 18:47
  • Is that a typo: "in FrameworkA you can write a module.modulemap file for CommonCrypto like so". Is that meant to say "in FrameworkA you can write a module.modulemap file for *FrameworkA* like so"? – hnh Oct 13 '16 at 16:32
  • @hnh That is a typo. Fixed. – tmpz Oct 14 '16 at 12:30
  • Struggling to get this to work with Xcode 8 :-) The module maps you give, you don't just add those to the Swift import path, you also set them as the "Module Map File" and "Private Module Map File" in the target, right? Also, yours says "module FrameworkACommon {}", given the setup of the question, is that another typo and should be "module FrameworkA" and the "explicit module "FrameworkA.CommonCrypto"? – hnh Oct 14 '16 at 12:45
  • Can you please check this: https://stackoverflow.com/q/56769304/1364053 – nr5 Jun 26 '19 at 10:52
  • This answer no longer works, since the way private module maps work has changed in Clang. The root module of a private module map now needs to be named Foo_Private. See https://stackoverflow.com/a/65236670/1005419 for a complete way that works in December 2020. – Thomas McGuire Dec 10 '20 at 14:46