Skip to content

Commit

Permalink
Support CMake target & SPM library prefixes/suffixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Sep 23, 2024
1 parent 025ea5d commit 5c3e434
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ function(generate_projection)
--reference "${WINRTCOMPONENT_WINMD_NATIVE}"
--spm
--spm-support-package "${SPM_SUPPORT_PACKAGE_DIR_NATIVE}"
--cmakelists
--spm-library-prefix "Swift"
--cmake
--cmake-target-prefix "Swift"
--out "${PROJECTION_DIR_NATIVE}"
--out-manifest "${PROJECTION_DIR_NATIVE}\\WinRTComponent.manifest"
COMMAND_ERROR_IS_FATAL ANY)
Expand Down
36 changes: 36 additions & 0 deletions Generator/Sources/CodeWriters/CMake/CMakeCommandArgument.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
public struct CMakeCommandArgument: ExpressibleByStringLiteral {
public let value: String
public let quoted: Bool

public init(_ value: String, quoted: Bool) {
self.value = value
self.quoted = quoted
}

public init(autoquote value: String) {
assert(!value.contains("${"))
self.init(value, quoted: value.contains(" ") || value.contains(";"))
}

public init(stringLiteral value: String) {
self.init(autoquote: value)
}

public func write(to stream: inout some TextOutputStream) {
if quoted { stream.write("\"") }
stream.write(value.replacingOccurrences(of: "\\", with: "\\\\"))
if quoted { stream.write("\"") }
}

public static func autoquote(_ value: String) -> CMakeCommandArgument {
Self(autoquote: value)
}

public static func quoted(_ value: String) -> CMakeCommandArgument {
Self(value, quoted: true)
}

public static func unquoted(_ value: String) -> CMakeCommandArgument {
Self(value, quoted: false)
}
}
58 changes: 39 additions & 19 deletions Generator/Sources/CodeWriters/CMake/CMakeListsWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,54 @@ public final class CMakeListsWriter {
self.output = .init(inner: output)
}

public func writeAddLibrary(_ name: String, _ type: CMakeLibraryType? = nil, _ sources: [String] = []) {
let typeSuffix = type.map { " \($0.rawValue)" } ?? ""
output.writeIndentedBlock(grouping: .never, header: "add_library(\(name)\(typeSuffix)", footer: ")") {
for source in sources {
output.writeFullLine(source)
public func writeCommand(_ command: String, headerArguments: [CMakeCommandArgument] = [], multilineArguments: [CMakeCommandArgument] = []) {
var output = output // Safe because IndentedTextOutputStream is a class
output.beginLine(grouping: .withName(command))
output.write(command)
output.write("(")
for (index, argument) in headerArguments.enumerated() {
if index > 0 { output.write(" ") }
argument.write(to: &output)
}
if multilineArguments.isEmpty {
output.write(")", endLine: true)
}
else {
output.writeIndentedBlock {
for argument in multilineArguments {
output.beginLine()
argument.write(to: &output)
}
}
output.write(")", endLine: true)
}
}

public func writeTargetIncludeDirectories(_ target: String, _ visibility: CMakeVisibility, _ directories: [String]) {
public func writeSingleLineCommand(_ command: String, _ arguments: [CMakeCommandArgument]) {
writeCommand(command, headerArguments: arguments)
}

public func writeSingleLineCommand(_ command: String, _ arguments: CMakeCommandArgument...) {
writeCommand(command, headerArguments: arguments)
}

public func writeAddLibrary(_ name: CMakeCommandArgument, _ type: CMakeLibraryType? = nil, _ sources: [CMakeCommandArgument] = []) {
var headerArguments = [ name ]
if let type { headerArguments.append(.unquoted(type.rawValue)) }
writeCommand("add_library", headerArguments: headerArguments, multilineArguments: sources)
}

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

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

public func writeAddSubdirectory(_ sources: String) {
output.writeFullLine(grouping: .withName("add_subdirectory"), "add_subdirectory(\(sources))")
public func writeAddSubdirectory(_ source: CMakeCommandArgument, _ binary: CMakeCommandArgument? = nil) {
writeSingleLineCommand("add_subdirectory", binary.map { [source, $0] } ?? [source])
}
}
20 changes: 20 additions & 0 deletions Generator/Sources/SwiftWinRT/BuildSystemOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
struct SPMOptions {
public let libraryPrefix: String
public let librarySuffix: String
public let dynamicLibraries: Bool
public let excludeCMakeLists: Bool

public func getLibraryName(module: String) -> String {
"\(libraryPrefix)\(module)\(librarySuffix)"
}
}

struct CMakeOptions {
public let targetPrefix: String
public let targetSuffix: String
public let dynamicLibraries: Bool

public func getTargetName(module: String) -> String {
"\(targetPrefix)\(module)\(targetSuffix)"
}
}
14 changes: 13 additions & 1 deletion Generator/Sources/SwiftWinRT/CommandLineArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,24 @@ struct CommandLineArguments: ParsableCommand {
@Flag(name: .customLong("spm"), help: "Generate a package.swift file for building with SPM.")
var generatePackageDotSwift: Bool = false

@Option(name: .customLong("spm-library-prefix"), help: "A prefix to the module name when naming SPM libraries.")
var spmLibraryPrefix: String = ""

@Option(name: .customLong("spm-library-suffix"), help: "A suffix to the module name when naming SPM libraries.")
var spmLibrarySuffix: String = ""

@Option(name: .customLong("spm-support-package"), help: .init("The directory path or '<url>#branch=<branch>' of the support package to reference.", valueName: "dir-or-url"))
var spmSupportPackageReference: String = "https://github.com/tristanlabelle/swift-winrt.git#branch=main"

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

@Option(name: .customLong("cmake-target-prefix"), help: "A prefix to the module name when naming CMake targets.")
var cmakeTargetPrefix: String = ""

@Option(name: .customLong("cmake-target-suffix"), help: "A suffix to the module name when naming CMake targets.")
var cmakeTargetSuffix: String = ""

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

Expand Down
15 changes: 9 additions & 6 deletions Generator/Sources/SwiftWinRT/Writing/ABIModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ import DotNetMetadata
import ProjectionModel
import WindowsMetadata

internal func writeABIModule(_ module: Module, directoryPath: String, generateCMakeLists: Bool) throws {
internal func writeABIModule(_ module: Module, directoryPath: String, cmakeOptions: CMakeOptions?) throws {
let includeDirectoryPath = "\(directoryPath)\\include"
let includeSWRTDirectoryPath = "\(includeDirectoryPath)\\SWRT"

try writeABIFile(module: module, toPath: "\(includeSWRTDirectoryPath)\\\(module.name).h")

try writeModulemapFile(module: module, toPath: "\(includeDirectoryPath)\\module.modulemap")

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

Expand Down
14 changes: 6 additions & 8 deletions Generator/Sources/SwiftWinRT/Writing/SwiftPackageFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import DotNetMetadata
import ProjectionModel
import struct Foundation.URL

func writeSwiftPackageFile(
_ projection: Projection,
supportPackageReference: String,
excludeCMakeLists: Bool,
dynamicLibraries: Bool,
toPath path: String) {
func writeSwiftPackageFile(_ projection: Projection, spmOptions: SPMOptions, toPath path: String) {
var package = SwiftPackage(name: "Projection")
package.dependencies.append(getSupportPackageDependency(reference: supportPackageReference))

Expand Down Expand Up @@ -37,7 +32,10 @@ func writeSwiftPackageFile(
package.targets.append(projectionModuleTarget)

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

Expand Down Expand Up @@ -67,7 +65,7 @@ func writeSwiftPackageFile(
package.products.append(.library(name: module.abiModuleName, type: .static, targets: [abiModuleTarget.name]))
}

if excludeCMakeLists {
if spmOptions.excludeCMakeLists {
// Assume every target has a root CMakeLists.txt file
for targetIndex in package.targets.indices {
package.targets[targetIndex].exclude.append("CMakeLists.txt")
Expand Down
17 changes: 11 additions & 6 deletions Generator/Sources/SwiftWinRT/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,22 @@ do {
commandLineArguments: commandLineArguments,
projectionConfig: projectionConfig,
winMDLoadContext: context)
try writeBindingFiles(projection,

try writeProjectionFiles(projection,
directoryPath: commandLineArguments.outputDirectoryPath,
generateCMakeLists: commandLineArguments.generateCMakeLists,
dynamicLibraries: commandLineArguments.dynamicLibraries)
cmakeOptions: !generateCMakeLists ? nil : CMakeOptions(
targetPrefix: commandLineArguments.cmakeTargetPrefix,
targetSuffix: commandLineArguments.cmakeTargetSuffix,
dynamicLibraries: commandLineArguments.dynamicLibraries))

if commandLineArguments.generatePackageDotSwift {
writeSwiftPackageFile(
projection,
supportPackageReference: commandLineArguments.spmSupportPackageReference,
excludeCMakeLists: commandLineArguments.generateCMakeLists,
dynamicLibraries: commandLineArguments.dynamicLibraries,
spmOptions: SPMOptions(
libraryPrefix: commandLineArguments.spmLibraryPrefix,
librarySuffix: commandLineArguments.spmLibrarySuffix,
dynamicLibraries: commandLineArguments.dynamicLibraries,
excludeCMakeLists: commandLineArguments.generateCMakeLists),
toPath: "\(commandLineArguments.outputDirectoryPath)\\Package.swift")
}

Expand Down
Loading

0 comments on commit 5c3e434

Please sign in to comment.