Sub modules in Framework

Hi team,


I am creating a framework. And in our framework, we want to create sub-modules which will be private to the main framework, just like Public and Private frameworks in iOS SDKs.


So our framework's private modules(sub-modules) will communicate with each other independently, and our frameworks public header files need to provide very limited access, for the applications to use them.


I am currently trying it with Clang ModuleMap and Private ModuleMap, but it seems like private modulemap is not working properly. module.private.map file.

Replies

A few questions:


  1. Are all your sub-modules part of the primary library, or are you trying to create an "umbrella framework" with dynamic libraries contained within the sub-frameworks?
  2. Are you just trying to separate your public and private headers from each other?
  3. What do your module maps look like?


I've managed to do this fairly successfully on several frameworks I've developed, and reading the LLVM Modules documentation is definitely a great start, though they're difficult to troubleshoot: http://clang.llvm.org/docs/Modules.html


It's also worth noting that Xcode is very aggressive in its caching of module information, so I find it's best to 1) Quit Xcode, 2) Delete the ModuleCache (and really everything else) inside DerivedData, 3) restart Xcode, between all changes to your module maps. Otherwise Xcode may not (e.g. will never) pick up any further changes you make.

Could you expound on you experiences with module mapping?

My apologies for not responding sooner.


There are several benefits to custom modulemaps, the simplest being that you can define which other frameworks your library links to so your users don't have to manually link to to system libraries themselves. (see also: http://clang.llvm.org/docs/Modules.html#link-declaration)


In general the default auto-generated modulemap is useful for flat, or simpler frameworks. But when different framework components are produced by different teams, or a framework is consuming another internally-produced dependency, using custom module maps can be useful I find.


In my project, my teams are producing multiple frameworks that all depend on a common "Core" framework. This core framework itself is composed of two different libraries, because one of them is reused within different projects at my company (and not just created solely for this project). However, we don't want to expose it to the users of our public framework. For example:


  • ServiceCore.framework
    • ServiceCommon <Public APIs>

      Private <Private APIs exposed internally only>

    • OfflineCaching <Private APIs exposed internally only>
  • ServiceKnowledge.framework <Depends upon ServiceCore.framework>
    • KnowledgeCore <Public APIs>

      Private <Private APIs>

    • KnowledgeUI <Public APIs>

      Private <Private APIs>

  • Etc...


The private capabilities of the core framework are used directly not only by the other frameworks that depend upon it, but by our internal testing infrastructure. Furthermore, the frameworks that depend upon the Core functionality also use submodules for two main reasons:

  1. Forces our developers to separate out core API-only functionality from UI functionality to make the interfaces cleaner;
  2. Exposing private frameworks again for use in testing, and for interaction between the UI and Core submodules.


As a result, our public modulemap file looks something like this:

framework module ServiceCore {
    umbrella header "ServiceCore.h"
    export *
    module * { export * }

    framework module ServiceCommon {
        umbrella header "ServiceCommon.h"
        export *
        module * { export * }
    }

    link framework "UIKit"
    link framework "SystemConfiguration"
    link framework "CoreGraphics"
    link "System"
    link "sqlite3"
    link "z"

    use AnalyticsCommon
}


You can see how the submodule exposes its umbrella header. Since they're exported, a simple "@import ServiceCore" will also import the submodules. Also, you can see how it indicates which system frameworks it uses, so they will automatically be imported and linked by Xcode for you. Additionally, you can add "use" declarations to tell Xcode which associated frameworks are "safe" to cross-reference, without getting cross-framework import errors.


The private modulemap file looks something like this:

explicit framework module ServiceCore.ServiceCommon.Private {
    umbrella header "ServiceCommon/Private.h"
    export *
    module * { export * }
}

explicit framework module ServiceCore.OfflineCaching {
    umbrella header "OfflineCaching/OfflineCaching.h"
    export *
    module * { export * }
}


Those are marked as explicit framework modules, meaning they have to be manually imported to be accessible. Before shipping the frameworks, we strip these modulemap and private header files, but while used internally, it allows our developers to reference dependent classes safely.


I hope that helps.

Hi!


Thanks for complete information!

I would like to know about modules separation on folder level.


I have complex framework which I want to separate into modules.

My example framework would be somekind of:



framework module ServiceCore {
  umbrella header "ServiceCore.h"
  export *
  module * { export * }


  framework module Toolbox {
  umbrella header "ServiceToolbox.h"
  export *
  module * { export * }
  }

  framework module Random {
  umbrella header "ServiceRandom.h"
  export *
  module * { export * }
  }
}



All of the headers are a part of ServiceCore target. ( ServiceCore framework target )


ServiceCore.h takes care of core headers.
ServiceToolbox takes care of toolbox headers ( which are placed somewhere ).

ServiceRandom takes care of misc items over random numbers generation.


All umbrella headers are placed into folder FrameworkSupplement/.


However, after that setup Xcode can't find umbrella headers as ServiceToolbox and ServiceRandom.


My main goal is to import items as part of ServiceCore.


@import ServiceCore; // import Core part.
@import ServiceCore.Toolbox; // import ServiceToolbox.h
@import ServiceCore.Random; // import ServiceRandom.h


Thanks for help!