Skip to content

Instantly share code, notes, and snippets.

@briancroom
Last active February 6, 2024 18:26
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save briancroom/5d0f1b966fa9ef0ae4950e97f9d76f77 to your computer and use it in GitHub Desktop.
Save briancroom/5d0f1b966fa9ef0ae4950e97f9d76f77 to your computer and use it in GitHub Desktop.
How to create a Swift modular library

I am trying to determine if it is possible to build a Swift dynamic library which is itself composed of one of more private modules, without needing to expose to that fact to outside users. My hope was that I could build the private module as a static library, which would be linked into the primary (dynamic) library. The dylib could then be deployed together with its swiftmodule and swiftdoc and be imported, with the private module and its symbols not being exposed at all.

Unfortunately, what I'm currently observing seems to indicate that the private module's swiftmodule also has to be available for the primary library to be successfully imported.

This can be reproduced as follows. I have the following directory structure:

./Greeter/Logger/Logger.swift:

public func log(_ message: String) {
    print(fullLogString(message))
}

internal func fullLogString(_ message: String) -> String {
    return "(Logger): \(message)"
}

./Greeter/Greeter.swift:

import Logger

public func greet(_ name: String) {
    log("Hello \(name)!")
}

./main.swift:

import Greeter

greet("Swift")

I am building these as follows:

$ cd Greeter/Logger
$ swiftc -emit-object -emit-module -force-single-frontend-invocation -parse-as-library Logger.swift
$ ar -r libLogger.a Logger.o
$ cd ../
$ swiftc -emit-library -emit-module -parse-as-library -ILogger -lLogger -Xlinker -LLogger -Xlinker -install_name -Xlinker @rpath/libGreeter.dylib Greeter.swift
$ cd ../
$ swiftc -emit-executable -IGreeter -IGreeter/Logger -lGreeter -Xlinker -LGreeter -Xlinker -rpath -Xlinker Greeter main.swift

On the final command, if I omit the -IGreeter/Logger flag, compilation fails with the message:

main.swift:1:8: error: missing required module 'Logger'

So I'm wondering...

  1. Why is the Logger.swiftmodule necessary to complete the build, even though main.swift does not import it or use any symbols from it?
  2. Is there a way I could perform the build differently such that it wouldn't be needed?
  3. Where could I have started looking to figure out these answers on my own?
@Pitometsu
Copy link

@briancroom, thank you for sharing. Any news yet?
I'm trying get SPM load in swift repl: profburke/swiftreplmadness#1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment