Skip to content

Commit

Permalink
Support generating projections that build as dynamic libraries (#268)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Sep 5, 2024
1 parent c228356 commit b335c4c
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 31 deletions.
6 changes: 6 additions & 0 deletions Generator/Sources/CodeWriters/Swift/SwiftPackage+write.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ extension SwiftPackage.Product {
fileprivate func write(to writer: IndentedTextOutputStream) {
writer.writeIndentedBlock(header: ".library(", footer: ")", endFooterLine: false) {
writer.write("name: \"\(escapeStringLiteral(name))\"")

if let type {
writer.write(",", endLine: true)
writer.write("type: .\(type.rawValue)")
}

if !targets.isEmpty {
writer.write(",", endLine: true)
writer.writeIndentedBlock(header: "targets: [", footer: "]", endFooterLine: false) {
Expand Down
22 changes: 10 additions & 12 deletions Generator/Sources/CodeWriters/Swift/SwiftPackage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ public struct SwiftPackage {

public struct Product {
public var name: String
public var type: ProductType?
public var targets: [String]

public static func library(name: String, targets: [String]) -> Product {
.init(name: name, targets: targets)
public static func library(name: String, type: ProductType? = nil, targets: [String]) -> Product {
.init(name: name, type: type, targets: targets)
}

public enum ProductType: String {
case `static`
case dynamic
}
}

Expand All @@ -51,19 +57,11 @@ public struct SwiftPackage {

public struct Target {
public var name: String
public var dependencies: [Dependency] = []
public var dependencies: [Dependency]
public var path: String?
public var exclude: [String] = []
public var exclude: [String]
public var cUnsafeFlags: [String]

public init(name: String, dependencies: [Dependency] = [], path: String? = nil, exclude: [String] = [], cUnsafeFlags: [String] = []) {
self.name = name
self.dependencies = dependencies
self.path = path
self.exclude = exclude
self.cUnsafeFlags = cUnsafeFlags
}

public static func target(name: String, dependencies: [Dependency] = [], path: String? = nil, exclude: [String] = [], cUnsafeFlags: [String] = []) -> Target {
.init(name: name, dependencies: dependencies, path: path, exclude: exclude, cUnsafeFlags: cUnsafeFlags)
}
Expand Down
5 changes: 4 additions & 1 deletion Generator/Sources/SwiftWinRT/CommandLineArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ struct CommandLineArguments: ParsableCommand {

@Option(name: .customLong("support"), help: .init("The directory path or url:branch or url@revision of the support package to use.", valueName: "dir-or-url"))
var supportPackageLocation: String = "https://github.com/tristanlabelle/swift-winrt.git:main"

@Flag(name: .customLong("cmakelists"), help: "Generate a CMakeLists.txt files for building with CMake.")
var generateCMakeLists: Bool = false

@Flag(name: .customLong("dylib"), help: "Makes SPM and CMake build definitions specify to build dynamic libraries.")
var dynamicLibraries: Bool = false

@Option(name: .customLong("out-manifest"), help: .init("Path to generate an embeddable exe manifest file to.", valueName: "file"))
var exeManifestPath: String? = nil
}
29 changes: 19 additions & 10 deletions Generator/Sources/SwiftWinRT/Writing/SwiftPackageFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@ import DotNetMetadata
import ProjectionModel
import struct Foundation.URL

func writeSwiftPackageFile(_ projection: SwiftProjection, supportPackageLocation: String, excludeCMakeLists: Bool, toPath path: String) {
func writeSwiftPackageFile(
_ projection: SwiftProjection,
supportPackageLocation: String,
excludeCMakeLists: Bool,
dynamicLibraries: Bool,
toPath path: String) {
var package = SwiftPackage(name: "Projection")
package.dependencies.append(getSupportPackageDependency(location: supportPackageLocation))

var productTargets = [String]()

for module in projection.modulesByName.values {
guard !module.isEmpty else { continue }

// ABI module
var abiModuleTarget = SwiftPackage.Target(name: module.abiModuleName, path: "\(module.name)/ABI")
var abiModuleTarget: SwiftPackage.Target = .target(name: module.abiModuleName, path: "\(module.name)/ABI")
abiModuleTarget.dependencies.append(.product(name: "WindowsRuntime_ABI", package: "swift-winrt"))
package.targets.append(abiModuleTarget)

// Assembly module
var assemblyModuleTarget = SwiftPackage.Target(name: module.name)
var assemblyModuleTarget: SwiftPackage.Target = .target(name: module.name)
assemblyModuleTarget.path = "\(module.name)/Assembly"
assemblyModuleTarget.dependencies.append(.product(name: "WindowsRuntime", package: "swift-winrt"))

Expand All @@ -31,7 +34,11 @@ func writeSwiftPackageFile(_ projection: SwiftProjection, supportPackageLocation
assemblyModuleTarget.dependencies.append(.target(name: module.abiModuleName))

package.targets.append(assemblyModuleTarget)
productTargets.append(assemblyModuleTarget.name)

// Define a product for the module
var moduleProduct: SwiftPackage.Product = .library(name: module.name, type: dynamicLibraries ? .dynamic : nil, targets: [])
moduleProduct.targets.append(assemblyModuleTarget.name)
moduleProduct.targets.append(abiModuleTarget.name)

// Namespace modules
if !module.flattenNamespaces {
Expand All @@ -44,15 +51,19 @@ func writeSwiftPackageFile(_ projection: SwiftProjection, supportPackageLocation
namespaces.sort()

for namespace in namespaces {
var namespaceModuleTarget = SwiftPackage.Target(
var namespaceModuleTarget: SwiftPackage.Target = .target(
name: module.getNamespaceModuleName(namespace: namespace))
let compactNamespace = SwiftProjection.toCompactNamespace(namespace)
namespaceModuleTarget.path = "\(module.name)/Namespaces/\(compactNamespace)"
namespaceModuleTarget.dependencies.append(.target(name: module.name))
package.targets.append(namespaceModuleTarget)
productTargets.append(namespaceModuleTarget.name)
moduleProduct.targets.append(namespaceModuleTarget.name)
}
}

// Create products for the projections and the ABI
package.products.append(moduleProduct)
package.products.append(.library(name: module.abiModuleName, type: .static, targets: [abiModuleTarget.name]))
}

if excludeCMakeLists {
Expand All @@ -62,8 +73,6 @@ func writeSwiftPackageFile(_ projection: SwiftProjection, supportPackageLocation
}
}

package.products.append(.library(name: "Projection", targets: productTargets))

package.write(version: "5.10", to: FileTextOutputStream(path: path, directoryCreation: .ancestors))
}

Expand Down
4 changes: 3 additions & 1 deletion Generator/Sources/SwiftWinRT/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ do {
assemblyLoadContext: context)
try writeProjectionFiles(projection,
directoryPath: commandLineArguments.outputDirectoryPath,
generateCMakeLists: commandLineArguments.generateCMakeLists)
generateCMakeLists: commandLineArguments.generateCMakeLists,
dynamicLibraries: commandLineArguments.dynamicLibraries)

if commandLineArguments.generatePackageDotSwift {
writeSwiftPackageFile(
projection,
supportPackageLocation: commandLineArguments.supportPackageLocation,
excludeCMakeLists: commandLineArguments.generateCMakeLists,
dynamicLibraries: commandLineArguments.dynamicLibraries,
toPath: "\(commandLineArguments.outputDirectoryPath)\\Package.swift")
}

Expand Down
27 changes: 21 additions & 6 deletions Generator/Sources/SwiftWinRT/writeProjectionFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import Foundation
import ProjectionModel
import WindowsMetadata

internal func writeProjectionFiles(_ projection: SwiftProjection, directoryPath: String, generateCMakeLists: Bool) throws {
internal func writeProjectionFiles(
_ projection: SwiftProjection,
directoryPath: String,
generateCMakeLists: Bool,
dynamicLibraries: Bool) throws {
for module in projection.modulesByName.values {
guard !module.isEmpty else { continue }
try writeModuleFiles(module,
directoryPath: "\(directoryPath)\\\(module.name)",
generateCMakeLists: generateCMakeLists)
generateCMakeLists: generateCMakeLists,
dynamicLibrary: dynamicLibraries)
}

if generateCMakeLists {
Expand All @@ -25,9 +30,17 @@ internal func writeProjectionFiles(_ projection: SwiftProjection, directoryPath:
}
}

fileprivate func writeModuleFiles(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
fileprivate func writeModuleFiles(
_ module: SwiftProjection.Module,
directoryPath: String,
generateCMakeLists: Bool,
dynamicLibrary: Bool) throws {
try writeABIModule(module, directoryPath: "\(directoryPath)\\ABI", generateCMakeLists: generateCMakeLists)
try writeAssemblyModuleFiles(module, directoryPath: "\(directoryPath)\\Assembly", generateCMakeLists: generateCMakeLists)

try writeAssemblyModuleFiles(
module, directoryPath: "\(directoryPath)\\Assembly",
generateCMakeLists: generateCMakeLists, dynamicLibrary: dynamicLibrary)

if !module.flattenNamespaces {
try writeNamespaceModuleFiles(module, directoryPath: "\(directoryPath)\\Namespaces", generateCMakeLists: generateCMakeLists)
}
Expand All @@ -44,7 +57,9 @@ fileprivate func writeModuleFiles(_ module: SwiftProjection.Module, directoryPat
}
}

fileprivate func writeAssemblyModuleFiles(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
fileprivate func writeAssemblyModuleFiles(
_ module: SwiftProjection.Module, directoryPath: String,
generateCMakeLists: Bool, dynamicLibrary: Bool) throws {
var cmakeSources: [String] = []
for typeDefinition in module.typeDefinitions + Array(module.genericInstantiationsByDefinition.keys) {
guard try hasSwiftDefinition(typeDefinition) else { continue }
Expand Down Expand Up @@ -88,7 +103,7 @@ fileprivate func writeAssemblyModuleFiles(_ module: SwiftProjection.Module, dire
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
cmakeSources.sort()
writer.writeAddLibrary(module.name, .static, cmakeSources)
writer.writeAddLibrary(module.name, dynamicLibrary ? .shared : .static, cmakeSources)
writer.writeTargetLinkLibraries(
module.name, .public,
[ SupportModules.WinRT.moduleName, module.abiModuleName ] + module.references.map { $0.name })
Expand Down
3 changes: 2 additions & 1 deletion InteropTests/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ let package = Package(
name: "Tests",
dependencies: [
.product(name: "WindowsRuntime", package: "swift-winrt"),
.product(name: "Projection", package: "Generated"),
.product(name: "UWP", package: "Generated"),
.product(name: "WinRTComponent", package: "Generated"),
],
path: "Tests",
// Workaround for SPM library support limitations causing "LNK4217: locally defined symbol imported" spew
Expand Down

0 comments on commit b335c4c

Please sign in to comment.