Skip to content

Commit

Permalink
Support generating CMakeLists.txt files (#238)
Browse files Browse the repository at this point in the history
Fixes #229
  • Loading branch information
tristanlabelle authored Aug 19, 2024
1 parent 5e1db60 commit 7d367c9
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 41 deletions.
5 changes: 5 additions & 0 deletions Generator/Sources/CodeWriters/CMake/CMakeLibraryType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public enum CMakeLibraryType: String {
case `static` = "STATIC"
case shared = "SHARED"
case interface = "INTERFACE"
}
38 changes: 38 additions & 0 deletions Generator/Sources/CodeWriters/CMake/CMakeListsWriter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
public final class CMakeListsWriter {
private let output: IndentedTextOutputStream

public init(output: some TextOutputStream) {
self.output = .init(inner: output)
}

public func writeAddLibrary(_ name: String, _ type: CMakeLibraryType? = nil, _ sources: [String] = []) {
let typeSuffix = type.map { " \($0)" } ?? ""
output.writeIndentedBlock(grouping: .never, header: "add_library(\(name)\(typeSuffix)", footer: ")") {
for source in sources {
output.writeFullLine(source)
}
}
}

public func writeTargetIncludeDirectories(_ target: String, _ visibility: CMakeVisibility, _ directories: [String]) {
guard !directories.isEmpty else { return }
output.writeIndentedBlock(grouping: .never, header: "target_include_directories(\(target) \(visibility)", footer: ")") {
for directory in directories {
output.writeFullLine(directory)
}
}
}

public func writeTargetLinkLibraries(_ target: String, _ visibility: CMakeVisibility, _ libraries: [String]) {
guard !libraries.isEmpty else { return }
output.writeIndentedBlock(grouping: .never, header: "target_link_libraries(\(target) \(visibility)", footer: ")") {
for library in libraries {
output.writeFullLine(library)
}
}
}

public func writeAddSubdirectory(_ subdirectory: String) {
output.writeFullLine(grouping: .withName("add_subdirectory"), "add_subdirectory(\(subdirectory))")
}
}
5 changes: 5 additions & 0 deletions Generator/Sources/CodeWriters/CMake/CMakeVisibility.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public enum CMakeVisibility: String {
case `public` = "PUBLIC"
case `private` = "PRIVATE"
case `interface` = "INTERFACE"
}
2 changes: 2 additions & 0 deletions Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum SupportModules {

extension SupportModules.COM {
public static var moduleName: String { "COM" }
public static var abiModuleName: String { "COM_ABI" }

public static var guid: SwiftType { .chain(moduleName, "GUID") }

Expand Down Expand Up @@ -57,6 +58,7 @@ extension SupportModules.COM {

extension SupportModules.WinRT {
public static var moduleName: String { "WindowsRuntime" }
public static var abiModuleName: String { "WindowsRuntime_ABI" }

public static var char16: SwiftType { .chain(moduleName, "Char16") }

Expand Down
3 changes: 3 additions & 0 deletions Generator/Sources/SwiftWinRT/CommandLineArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ 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

@Option(name: .customLong("out-manifest"), help: .init("Path to generate an embeddable exe manifest file to.", valueName: "file"))
var exeManifestPath: String? = nil
Expand Down
12 changes: 11 additions & 1 deletion Generator/Sources/SwiftWinRT/Writing/ABIModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ import DotNetMetadata
import ProjectionModel
import WindowsMetadata

internal func writeABIModule(_ module: SwiftProjection.Module, toPath directoryPath: String) throws {
internal func writeABIModule(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
let includeDirectoryPath = "\(directoryPath)\\include"

try writeABIFile(module: module, toPath: "\(includeDirectoryPath)\\ABI.h")
// FIXME: Support transitive references?
for referencedModule in module.references {
guard !referencedModule.isEmpty else { continue }
try writeABIFile(module: referencedModule, toPath: "\(includeDirectoryPath)\\\(referencedModule.name).h")
}

if generateCMakeLists {
let cmakeListsWriter = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt", directoryCreation: .ancestors))
cmakeListsWriter.writeAddLibrary(module.abiModuleName, .interface)
cmakeListsWriter.writeTargetIncludeDirectories(module.abiModuleName, .public, ["include"])
cmakeListsWriter.writeTargetLinkLibraries(module.abiModuleName, .public,
[ SupportModules.WinRT.abiModuleName ] + module.references.map { $0.abiModuleName })
}
}

fileprivate func writeABIFile(module: SwiftProjection.Module, toPath path: String) throws {
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 @@ -28,7 +28,9 @@ do {
commandLineArguments: commandLineArguments,
projectionConfig: projectionConfig,
assemblyLoadContext: context)
try writeProjectionFiles(projection, commandLineArguments: commandLineArguments)
try writeProjectionFiles(projection,
directoryPath: commandLineArguments.outputDirectoryPath,
generateCMakeLists: commandLineArguments.generateCMakeLists)

if commandLineArguments.generatePackageDotSwift {
writeSwiftPackageFile(
Expand Down
143 changes: 104 additions & 39 deletions Generator/Sources/SwiftWinRT/writeProjectionFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,125 @@ import Foundation
import ProjectionModel
import WindowsMetadata

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

let moduleRootPath = "\(commandLineArguments.outputDirectoryPath)\\\(module.name)"
if generateCMakeLists {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
for module in projection.modulesByName.values {
guard !module.isEmpty else { continue }
writer.writeAddSubdirectory(module.name)
}
}
}

try writeABIModule(module, toPath: "\(moduleRootPath)\\ABI")
fileprivate func writeModuleFiles(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
try writeABIModule(module, directoryPath: "\(directoryPath)\\ABI", generateCMakeLists: generateCMakeLists)
try writeAssemblyModuleFiles(module, directoryPath: "\(directoryPath)\\Assembly", generateCMakeLists: generateCMakeLists)
if !module.flattenNamespaces {
try writeNamespaceModuleFiles(module, directoryPath: "\(directoryPath)\\Namespaces", generateCMakeLists: generateCMakeLists)
}

// Write the assembly module and namespace modules
let assemblyModuleDirectoryPath = "\(moduleRootPath)\\Assembly"
if generateCMakeLists {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
writer.writeAddSubdirectory("ABI")
writer.writeAddSubdirectory("Assembly")
if !module.flattenNamespaces {
writer.writeAddSubdirectory("Namespaces")
}
}
}

for typeDefinition in module.typeDefinitions + Array(module.genericInstantiationsByDefinition.keys) {
guard try hasSwiftDefinition(typeDefinition) else { continue }
fileprivate func writeAssemblyModuleFiles(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
var cmakeSources: [String] = []
for typeDefinition in module.typeDefinitions + Array(module.genericInstantiationsByDefinition.keys) {
guard try hasSwiftDefinition(typeDefinition) else { continue }

let compactNamespace = SwiftProjection.toCompactNamespace(typeDefinition.namespace!)
let assemblyNamespaceDirectoryPath = "\(assemblyModuleDirectoryPath)\\\(compactNamespace)"
let compactNamespace = SwiftProjection.toCompactNamespace(typeDefinition.namespace!)
let assemblyNamespaceDirectoryPath = "\(directoryPath)\\\(compactNamespace)"

if module.hasTypeDefinition(typeDefinition) {
let typeName = try projection.toTypeName(typeDefinition)
try writeTypeDefinitionFile(typeDefinition, module: module, toPath: "\(assemblyNamespaceDirectoryPath)\\\(typeName).swift")
if module.hasTypeDefinition(typeDefinition) {
let typeName = try module.projection.toTypeName(typeDefinition)
cmakeSources.append("\(compactNamespace)/\(typeName).swift")
try writeTypeDefinitionFile(typeDefinition, module: module, toPath: "\(assemblyNamespaceDirectoryPath)\\\(typeName).swift")

if let extensionFileBytes = try getExtensionFileBytes(typeDefinition: typeDefinition) {
try Data(extensionFileBytes).write(to: URL(fileURLWithPath:
"\(assemblyNamespaceDirectoryPath)\\\(typeName)+extras.swift",
isDirectory: false))
}
if let extensionFileBytes = try getExtensionFileBytes(typeDefinition: typeDefinition) {
cmakeSources.append("\(compactNamespace)/\(typeName)+extras.swift")
try Data(extensionFileBytes).write(to: URL(fileURLWithPath:
"\(assemblyNamespaceDirectoryPath)\\\(typeName)+extras.swift",
isDirectory: false))
}
}

if (typeDefinition as? ClassDefinition)?.isStatic != true,
!typeDefinition.isValueType || SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) != .definitionAndProjection {
let typeName = try projection.toTypeName(typeDefinition)
try writeABIProjectionConformanceFile(typeDefinition, module: module,
toPath: "\(assemblyNamespaceDirectoryPath)\\Projections\\\(typeName)+Projection.swift")
}
if (typeDefinition as? ClassDefinition)?.isStatic != true,
!typeDefinition.isValueType || SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) != .definitionAndProjection {
let typeName = try module.projection.toTypeName(typeDefinition)
cmakeSources.append("\(compactNamespace)/Projections/\(typeName)+Projection.swift")
try writeABIProjectionConformanceFile(typeDefinition, module: module,
toPath: "\(assemblyNamespaceDirectoryPath)\\Projections\\\(typeName)+Projection.swift")
}
}

for abiType in try getABITypes(module: module) {
guard let namespace = abiType.definition.namespace else { continue }
let compactNamespace = SwiftProjection.toCompactNamespace(namespace)
let mangledName = try CAbi.mangleName(type: abiType)
cmakeSources.append("\(compactNamespace)/COMInterop/\(mangledName).swift")
try writeCOMInteropExtensionFile(abiType: abiType, module: module,
toPath: "\(directoryPath)\\\(compactNamespace)\\COMInterop\\\(mangledName).swift")
}

if generateCMakeLists {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
cmakeSources.sort()
writer.writeAddLibrary(module.name, .static, cmakeSources)
writer.writeTargetLinkLibraries(
module.name, .public,
[ SupportModules.WinRT.moduleName, module.abiModuleName ] + module.references.map { $0.name })
}
}

for abiType in try getABITypes(module: module) {
guard let namespace = abiType.definition.namespace else { continue }
let compactNamespace = SwiftProjection.toCompactNamespace(namespace)
let mangledName = try CAbi.mangleName(type: abiType)
try writeCOMInteropExtensionFile(abiType: abiType, module: module,
toPath: "\(assemblyModuleDirectoryPath)\\\(compactNamespace)\\COMInterop\\\(mangledName).swift")
fileprivate func writeNamespaceModuleFiles(_ module: SwiftProjection.Module, directoryPath: String, generateCMakeLists: Bool) throws {
let typeDefinitionsByNamespace = Dictionary(grouping: module.typeDefinitions, by: { $0.namespace })

var compactNamespaces: [String] = []
for (namespace, typeDefinitions) in typeDefinitionsByNamespace {
let typeDefinitions = try typeDefinitions.filter(hasSwiftDefinition)
guard !typeDefinitions.isEmpty else { continue }
guard let namespace else { continue }

let compactNamespace = SwiftProjection.toCompactNamespace(namespace)
compactNamespaces.append(compactNamespace)
let namespaceAliasesPath = "\(directoryPath)\\\(compactNamespace)\\Aliases.swift"
try writeNamespaceAliasesFile(typeDefinitions: typeDefinitions, module: module, toPath: namespaceAliasesPath)

if generateCMakeLists {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\\(compactNamespace)\\CMakeLists.txt",
directoryCreation: .ancestors))
let namespaceModuleName = module.getNamespaceModuleName(namespace: namespace)
writer.writeAddLibrary(namespaceModuleName, .static, ["Aliases.swift"])
writer.writeTargetLinkLibraries(namespaceModuleName, .public, [module.name])
}
}

if !module.flattenNamespaces {
let typeDefinitionsByNamespace = Dictionary(grouping: module.typeDefinitions, by: { $0.namespace })
for (namespace, typeDefinitions) in typeDefinitionsByNamespace {
let typeDefinitions = try typeDefinitions.filter(hasSwiftDefinition)
guard !typeDefinitions.isEmpty else { continue }

let compactNamespace = SwiftProjection.toCompactNamespace(namespace!)
let namespaceAliasesPath = "\(moduleRootPath)\\Namespaces\\\(compactNamespace)\\Aliases.swift"
try writeNamespaceAliasesFile(typeDefinitions: typeDefinitions, module: module, toPath: namespaceAliasesPath)
}
if generateCMakeLists, !compactNamespaces.isEmpty {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
for compactNamespace in compactNamespaces {
writer.writeAddSubdirectory(compactNamespace)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions InteropTests/BuildWinRTComponent.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ $WindowsSDKVersion = $env:WindowsSDKVersion -replace "\\",""
--winsdk $WindowsSDKVersion `
--reference "$TestComponentDir\WinRTComponent.winmd" `
--spm `
--cmakelists `
--support "..\.." `
--out "$PSScriptRoot\Generated" `
--out-manifest "$PSScriptRoot\Generated\WinRTComponent.manifest"
Expand Down

0 comments on commit 7d367c9

Please sign in to comment.