Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate flat modules #424

Merged
merged 6 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Generator/Sources/SwiftWinRT/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ file(GLOB_RECURSE SOURCES *.swift)
add_executable(SwiftWinRT ${SOURCES})
target_sources(SwiftWinRT PRIVATE "${PACKAGERESOURCES_SWIFT_FILE}")
target_link_libraries(SwiftWinRT PRIVATE
ArgumentParser Collections DotNetMetadata DotNetMetadataFormat WindowsMetadata
ArgumentParser Collections
DotNetMetadata DotNetMetadataFormat WindowsMetadata
CodeWriters ProjectionModel)

if(DEFINED SWIFTWINRT_EXE_STAGING_DIR)
Expand Down
2 changes: 1 addition & 1 deletion Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ fileprivate func writeOverrideSupport(
for interface in interfaces {
// if id == uuidof(SWRT_IFoo.self) {
let abiSwiftType = try projection.toABIType(interface.asBoundType)
try writer.writeBracedBlock("if id == uuidof(\(abiSwiftType).self)") { writer in
writer.writeBracedBlock("if id == uuidof(\(abiSwiftType).self)") { writer in
let outerPropertyName = SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix)

// _ifoo_outer.initEmbedder(self)
Expand Down
32 changes: 0 additions & 32 deletions Generator/Sources/SwiftWinRT/Writing/NamespaceAlias.swift

This file was deleted.

96 changes: 96 additions & 0 deletions Generator/Sources/SwiftWinRT/Writing/NamespaceModules.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import CodeWriters
import DotNetMetadata
import ProjectionModel

/// Writes a namespace module to a given directory.
/// A namespace module contains short name typealiases for all types in the projection module,
/// for example "public typealias MyType = MainModule.MyNamespace_MyType".
internal func writeNamespaceModule(moduleName: String, typeDefinitions: [TypeDefinition], module: Module, cmakeOptions: CMakeOptions?, directoryPath: String) throws {
let writer = SwiftSourceFileWriter(
output: FileTextOutputStream(path: "\(directoryPath)\\Aliases.swift", directoryCreation: .ancestors))
writeGeneratedCodePreamble(to: writer)
writer.writeImport(module: module.name)

for typeDefinition in typeDefinitions.sorted(by: { $0.fullName < $1.fullName }) {
try writeShortNameTypeAlias(typeDefinition, projection: module.projection, to: writer)
}

if let cmakeOptions {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
let targetName = cmakeOptions.getTargetName(moduleName: moduleName)
writer.writeAddLibrary(targetName, .static, ["Aliases.swift"])
if targetName != moduleName {
writer.writeSingleLineCommand(
"set_target_properties", .autoquote(targetName),
"PROPERTIES", "Swift_MODULE_NAME", .autoquote(moduleName))
}
writer.writeTargetLinkLibraries(targetName, .public, [ cmakeOptions.getTargetName(moduleName: module.name) ])
}
}

/// Writes the flat namespace module to a given directory.
/// The flat namespace reexports the types from all namespace modules,
/// provided unqualified name access to all types in the projection module.
internal func writeFlatNamespaceModule(moduleName: String, namespaceModuleNames: [String], cmakeOptions: CMakeOptions?, directoryPath: String) throws {
let writer = SwiftSourceFileWriter(
output: FileTextOutputStream(path: "\(directoryPath)\\Flat.swift", directoryCreation: .ancestors))
writeGeneratedCodePreamble(to: writer)

for namespaceModuleName in namespaceModuleNames {
writer.writeImport(exported: true, module: namespaceModuleName)
}

if let cmakeOptions {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
let targetName = cmakeOptions.getTargetName(moduleName: moduleName)
writer.writeAddLibrary(targetName, .static, ["Flat.swift"])
if targetName != moduleName {
writer.writeSingleLineCommand(
"set_target_properties", .autoquote(targetName),
"PROPERTIES", "Swift_MODULE_NAME", .autoquote(moduleName))
}
writer.writeTargetLinkLibraries(targetName, .public,
// Workaround CMake bug that doesn't always transitively inherit link libraries.
[ "WindowsRuntime" ] + namespaceModuleNames.map { cmakeOptions.getTargetName(moduleName: $0) })
}
}

/// Writes a typealias exposing a type from the projection module by its short name.
/// For example, "public typealias MyType = MainModule.MyNamespace_MyType".
fileprivate func writeShortNameTypeAlias(
_ typeDefinition: TypeDefinition,
projection: Projection,
to writer: SwiftSourceFileWriter) throws {
try writer.writeTypeAlias(
visibility: Projection.toVisibility(typeDefinition.visibility),
name: projection.toTypeName(typeDefinition, namespaced: false),
typeParams: typeDefinition.genericParams.map { $0.name },
target: SwiftType.identifier(
name: projection.toTypeName(typeDefinition),
genericArgs: typeDefinition.genericParams.map { SwiftType.identifier(name: $0.name) }))

if let interface = typeDefinition as? InterfaceDefinition {
// We can't typealias protocols, so we define a new one that inherits from the original.
// Compare `IFooProtocol` with `MyNamespace_IFooProtocol`:
// - When implementing `IFooProtocol`, we are implementing `MyNamespace_IFooProtocol`, so that works.
// - When using `any IFooProtocol`, we are using an incompatible type from `any MyNamespace_IFooProtocol`,
// however, the code should be using `IFoo`, which avoids this issue.
try writer.writeProtocol(
visibility: Projection.toVisibility(interface.visibility),
name: projection.toProtocolName(interface, namespaced: false),
typeParams: interface.genericParams.map { $0.name },
bases: [projection.toBaseProtocol(interface)]) { _ in }
}

if typeDefinition is InterfaceDefinition || typeDefinition is DelegateDefinition {
try writer.writeTypeAlias(
visibility: Projection.toVisibility(typeDefinition.visibility),
name: projection.toBindingTypeName(typeDefinition, namespaced: false),
target: SwiftType.identifier(
name: projection.toBindingTypeName(typeDefinition)))
}
}
6 changes: 6 additions & 0 deletions Generator/Sources/SwiftWinRT/Writing/SwiftPackageFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ func writeSwiftPackageFile(_ projection: Projection, spmOptions: SPMOptions, toP
package.targets.append(namespaceModuleTarget)
moduleProduct.targets.append(namespaceModuleTarget.name)
}

var flatModuleTarget: SwiftPackage.Target = .target(name: module.name + "_Flat")
flatModuleTarget.path = "\(module.name)/Namespaces/Flat"
flatModuleTarget.dependencies = namespaces.map { .target(name: module.getNamespaceModuleName(namespace: $0)) }
package.targets.append(flatModuleTarget)
moduleProduct.targets.append(flatModuleTarget.name)
}

// Create products for the projections and the ABI
Expand Down
70 changes: 35 additions & 35 deletions Generator/Sources/SwiftWinRT/writeProjectionFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fileprivate func writeModuleFiles(
directoryPath: "\(directoryPath)\\Projection")

if !module.flattenNamespaces {
try writeNamespaceModuleFiles(module, cmakeOptions: cmakeOptions, directoryPath: "\(directoryPath)\\Namespaces")
try writeNamespaceModules(module, cmakeOptions: cmakeOptions, directoryPath: "\(directoryPath)\\Namespaces")
}

if cmakeOptions != nil {
Expand Down Expand Up @@ -163,7 +163,22 @@ fileprivate func writeSwiftModuleFiles(
}
}

fileprivate func writeNamespaceModuleFiles(
/// Writes the directory structure containing namespace modules for a given module.
///
/// The structure looks like:
///
/// ```
/// <root module directory>
/// └── Namespaces
/// ├── WindowsFoundation
/// │ ├── Aliases.swift
/// │ └── CMakeLists.txt
/// ├── Flat
/// │ ├── Flat.swift
/// │ └── CMakeLists.txt
/// └── CMakeLists.txt
/// ```
fileprivate func writeNamespaceModules(
_ module: Module,
cmakeOptions: CMakeOptions?,
directoryPath: String) throws {
Expand All @@ -176,37 +191,34 @@ fileprivate func writeNamespaceModuleFiles(
guard let namespace else { continue }

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

if let cmakeOptions {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\\(compactNamespace)\\CMakeLists.txt",
directoryCreation: .ancestors))
let namespaceModuleName = module.getNamespaceModuleName(namespace: namespace)
let targetName = cmakeOptions.getTargetName(moduleName: namespaceModuleName)
writer.writeAddLibrary(targetName, .static, ["Aliases.swift"])
if targetName != namespaceModuleName {
writer.writeSingleLineCommand(
"set_target_properties", .autoquote(targetName),
"PROPERTIES", "Swift_MODULE_NAME", .autoquote(namespaceModuleName))
}
writer.writeTargetLinkLibraries(targetName, .public, [ cmakeOptions.getTargetName(moduleName: module.name) ])
}
cmakeOptions: cmakeOptions,
directoryPath: "\(directoryPath)\\\(compactNamespace)")

compactNamespaces.append(compactNamespace)
}

if cmakeOptions != nil, !compactNamespaces.isEmpty {
guard !compactNamespaces.isEmpty else { return }

compactNamespaces.sort()

try writeFlatNamespaceModule(
moduleName: module.name + "_Flat",
namespaceModuleNames: compactNamespaces.map { module.getNamespaceModuleName(namespace: $0) },
cmakeOptions: cmakeOptions,
directoryPath: "\(directoryPath)\\Flat")

if cmakeOptions != nil {
let writer = CMakeListsWriter(output: FileTextOutputStream(
path: "\(directoryPath)\\CMakeLists.txt",
directoryCreation: .ancestors))
compactNamespaces.sort()
for compactNamespace in compactNamespaces {
writer.writeAddSubdirectory(compactNamespace)
}
writer.writeAddSubdirectory("Flat")
}
}

Expand Down Expand Up @@ -284,15 +296,3 @@ fileprivate func getCOMInteropableTypes(typeDefinition: TypeDefinition, module:
if typeDefinition.namespace == "Windows.Foundation", typeDefinition.name == "IReference`1" { return [] }
return module.getTypeInstantiations(definition: typeDefinition)
}

internal func writeNamespaceAliasesFile(typeDefinitions: [TypeDefinition], module: Module, toPath path: String) throws {
let writer = SwiftSourceFileWriter(output: FileTextOutputStream(path: path, directoryCreation: .ancestors))
writeGeneratedCodePreamble(to: writer)
writer.writeImport(module: module.name)

for typeDefinition in typeDefinitions.sorted(by: { $0.fullName < $1.fullName }) {
guard typeDefinition.isPublic else { continue }

try writeNamespaceAlias(typeDefinition, projection: module.projection, to: writer)
}
}
7 changes: 3 additions & 4 deletions InteropTests/Tests/ByteBufferTests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import UWP_WindowsFoundation
import UWP_WindowsStorageStreams
import UWP
import WindowsRuntime
import WinRTComponent
import XCTest
Expand All @@ -19,13 +18,13 @@ class ByteBufferTests : XCTestCase {

public func testProduceMemoryBuffer() throws {
let bytes: [UInt8] = [1, 2, 3]
let roundtrippedBytes = try ByteBuffers.memoryBufferToArray(try MemoryBuffer(bytes))
let roundtrippedBytes = try ByteBuffers.memoryBufferToArray(try WindowsFoundation_MemoryBuffer(bytes))
XCTAssertEqual(roundtrippedBytes, bytes)
}

public func testProduceStorageBuffer() throws {
let bytes: [UInt8] = [1, 2, 3]
let roundtrippedBytes = try ByteBuffers.storageBufferToArray(try Buffer(bytes))
let roundtrippedBytes = try ByteBuffers.storageBufferToArray(try WindowsStorageStreams_Buffer(bytes))
XCTAssertEqual(roundtrippedBytes, bytes)
}
}
6 changes: 3 additions & 3 deletions InteropTests/Tests/DateTimeTests.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import XCTest
import UWP
import WinRTComponent
import UWP_WindowsFoundation
import struct Foundation.Date

class DateTimeTests: WinRTTestCase {
func testTimeSpan() throws {
XCTAssertEqual(try DateTimes.fromSeconds(42).timeInterval.rounded(), 42)
XCTAssertEqual(try DateTimes.roundToSeconds(TimeSpan(timeInterval: 42)), 42)
XCTAssertEqual(try DateTimes.roundToSeconds(WindowsFoundation_TimeSpan(timeInterval: 42)), 42)
}

func testDateTime() throws {
XCTAssertEqual(try DateTimes.fromUTCYearMonthDay(1970, 1, 1).foundationDate.timeIntervalSince1970, 0)
var year: Int32 = 0
var month: Int32 = 0
var day: Int32 = 0
try DateTimes.toUTCYearMonthDay(DateTime(foundationDate: Date(timeIntervalSince1970: 0)), &year, &month, &day)
try DateTimes.toUTCYearMonthDay(WindowsFoundation_DateTime(foundationDate: Date(timeIntervalSince1970: 0)), &year, &month, &day)
XCTAssertEqual(year, 1970)
XCTAssertEqual(month, 1)
XCTAssertEqual(day, 1)
Expand Down
6 changes: 6 additions & 0 deletions InteropTests/Tests/FlatModuleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import UWP_Flat

// Compile-time tests that the UWP_Flat flat module
// exposes short names for types in that module.
fileprivate typealias MemoryBufferExists = MemoryBuffer // From UWP_WindowsFoundation
fileprivate typealias IBufferExists = IBuffer // From UWP_WindowsStorageStreams
5 changes: 5 additions & 0 deletions InteropTests/Tests/NamespaceModuleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UWP_WindowsFoundation

// Compile-time tests that the UWP_WindowsFoundation namespace module
// exposes short names for types in that namespace.
fileprivate typealias MemoryBufferExists = MemoryBuffer
Loading