Skip to content
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
9 changes: 8 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,14 @@ let package = Package(
swiftSettings: swiftSettings(languageMode: .v6)),
.target(
name: "SWBUniversalPlatform",
dependencies: ["SWBCore", "SWBMacro", "SWBUtil"],
dependencies: [
"SWBCore",
"SWBMacro",
"SWBUtil",
"SWBTaskConstruction",
"SWBTaskExecution",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
exclude: ["CMakeLists.txt"],
resources: [.process("Specs")],
swiftSettings: swiftSettings(languageMode: .v6)),
Expand Down
4 changes: 4 additions & 0 deletions Sources/SWBCore/Settings/BuiltinMacros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ public final class BuiltinMacros {
public static let GENERATE_MASTER_OBJECT_FILE = BuiltinMacros.declareBooleanMacro("GENERATE_MASTER_OBJECT_FILE")
public static let GENERATE_PKGINFO_FILE = BuiltinMacros.declareBooleanMacro("GENERATE_PKGINFO_FILE")
public static let GENERATE_RESOURCE_ACCESSORS = BuiltinMacros.declareBooleanMacro("GENERATE_RESOURCE_ACCESSORS")
public static let GENERATE_TEST_ENTRY_POINT = BuiltinMacros.declareBooleanMacro("GENERATE_TEST_ENTRY_POINT")
public static let GENERATED_TEST_ENTRY_POINT_PATH = BuiltinMacros.declarePathMacro("GENERATED_TEST_ENTRY_POINT_PATH")
public static let GENERATE_TEXT_BASED_STUBS = BuiltinMacros.declareBooleanMacro("GENERATE_TEXT_BASED_STUBS")
public static let GENERATE_INTERMEDIATE_TEXT_BASED_STUBS = BuiltinMacros.declareBooleanMacro("GENERATE_INTERMEDIATE_TEXT_BASED_STUBS")
public static let GLOBAL_API_NOTES_PATH = BuiltinMacros.declareStringMacro("GLOBAL_API_NOTES_PATH")
Expand Down Expand Up @@ -1757,6 +1759,8 @@ public final class BuiltinMacros {
GENERATE_MASTER_OBJECT_FILE,
GENERATE_PKGINFO_FILE,
GENERATE_RESOURCE_ACCESSORS,
GENERATE_TEST_ENTRY_POINT,
GENERATED_TEST_ENTRY_POINT_PATH,
GENERATE_TEXT_BASED_STUBS,
GENERATE_INTERMEDIATE_TEXT_BASED_STUBS,
GID,
Expand Down
2 changes: 2 additions & 0 deletions Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
SWIFT_FORCE_STATIC_LINK_STDLIB = NO;
// Avoid warning for executable types
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
GENERATE_TEST_ENTRY_POINT = YES;
GENERATED_TEST_ENTRY_POINT_PATH = "$(DERIVED_SOURCES_DIR)/test_entry_point.swift";
};
PackageTypes = (
com.apple.package-type.mach-o-executable // default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,10 @@ final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBase
result.append((embedInCodeAccessorResult.fileToBuild, embedInCodeAccessorResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}

if scope.evaluate(BuiltinMacros.GENERATE_TEST_ENTRY_POINT) {
result.append((scope.evaluate(BuiltinMacros.GENERATED_TEST_ENTRY_POINT_PATH), context.lookupFileType(fileName: "sourcecode.swift")!, /* shouldUsePrefixHeader */ false))
}

return result
}())

Expand Down
8 changes: 7 additions & 1 deletion Sources/SWBUniversalPlatform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ add_library(SWBUniversalPlatform STATIC
CppTool.swift
DiffTool.swift
LexCompiler.swift
TestEntryPointGenerationTaskAction.swift
TestEntryPointGenerationTool.swift
TestEntryPointTaskProducer.swift
YaccCompiler.swift
Plugin.swift)
target_link_libraries(SWBUniversalPlatform PUBLIC
SWBCore
SWBMacro
SWBUtil)
SWBUtil
SWBTaskConstruction
SWBTaskExecution
ArgumentParser)
target_sources(SWBUniversalPlatform PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/resource_bundle_accessor.swift")
37 changes: 37 additions & 0 deletions Sources/SWBUniversalPlatform/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
public import SWBUtil
import SWBCore
import Foundation
import SWBTaskConstruction
import SWBTaskExecution

@PluginExtensionSystemActor public func initializePlugin(_ manager: PluginManager) {
manager.register(UniversalPlatformSpecsExtension(), type: SpecificationsExtensionPoint.self)
manager.register(UniversalPlatformTaskProducerExtension(), type: TaskProducerExtensionPoint.self)
manager.register(UniversalPlatformTaskActionExtension(), type: TaskActionExtensionPoint.self)
}

struct UniversalPlatformSpecsExtension: SpecificationsExtension {
Expand All @@ -26,6 +30,7 @@ struct UniversalPlatformSpecsExtension: SpecificationsExtension {
CppToolSpec.self,
LexCompilerSpec.self,
YaccCompilerSpec.self,
TestEntryPointGenerationToolSpec.self,
]
}

Expand All @@ -44,3 +49,35 @@ struct UniversalPlatformSpecsExtension: SpecificationsExtension {
findResourceBundle(nameWhenInstalledInToolchain: "SwiftBuild_SWBUniversalPlatform", resourceSearchPaths: resourceSearchPaths, defaultBundle: Bundle.module)?.resourceURL.map { [$0] } ?? []
}
}

struct UniversalPlatformTaskProducerExtension: TaskProducerExtension {
func createPreSetupTaskProducers(_ context: SWBTaskConstruction.TaskProducerContext) -> [any SWBTaskConstruction.TaskProducer] {
[]
}

struct TestEntryPointTaskProducerFactory: TaskProducerFactory {
var name: String {
"TestEntryPointTaskProducerFactory"
}

func createTaskProducer(_ context: SWBTaskConstruction.TargetTaskProducerContext, startPhaseNodes: [SWBCore.PlannedVirtualNode], endPhaseNode: SWBCore.PlannedVirtualNode) -> any SWBTaskConstruction.TaskProducer {
TestEntryPointTaskProducer(context, phaseStartNodes: startPhaseNodes, phaseEndNode: endPhaseNode)
}
}

var setupTaskProducers: [any SWBTaskConstruction.TaskProducerFactory] {
[TestEntryPointTaskProducerFactory()]
}

var unorderedPostSetupTaskProducers: [any SWBTaskConstruction.TaskProducerFactory] { [] }

var unorderedPostBuildPhasesTaskProducers: [any SWBTaskConstruction.TaskProducerFactory] { [] }

var globalTaskProducers: [any SWBTaskConstruction.GlobalTaskProducerFactory] { [] }
}

struct UniversalPlatformTaskActionExtension: TaskActionExtension {
var taskActionImplementations: [SWBUtil.SerializableTypeCode : any SWBUtil.PolymorphicSerializable.Type] {
[41: TestEntryPointGenerationTaskAction.self]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

(
{ Identifier = "org.swift.test-entry-point-generator";
Type = Compiler;
Name = "Generate Test Entry Point";
Description = "Generates the entry point for a test target.";
CommandLine = "builtin-generateTestEntryPoint [options] --output $(OutputPath)";
RuleName = "GenerateTestEntryPoint $(OutputPath)";
ExecDescription = "Generate entry point for $(PRODUCT_NAME)";
Outputs = (
"$(OutputPath)"
);
Options = (
);
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SWBUtil
import SWBCore
import SWBTaskExecution
import ArgumentParser

class TestEntryPointGenerationTaskAction: TaskAction {
override class var toolIdentifier: String {
"TestEntryPointGenerationTaskAction"
}

override func performTaskAction(_ task: any ExecutableTask, dynamicExecutionDelegate: any DynamicTaskExecutionDelegate, executionDelegate: any TaskExecutionDelegate, clientDelegate: any TaskExecutionClientDelegate, outputDelegate: any TaskOutputDelegate) async -> CommandResult {
do {
let options = try Options.parse(Array(task.commandLineAsStrings.dropFirst()))
try executionDelegate.fs.write(options.output, contents: #"""
#if canImport(Testing)
import Testing
#endif

@main
@available(macOS 10.15, iOS 11, watchOS 4, tvOS 11, visionOS 1, *)
@available(*, deprecated, message: "Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which test deprecated functionality) without warnings")
struct Runner {
private static func testingLibrary() -> String {
var iterator = CommandLine.arguments.makeIterator()
while let argument = iterator.next() {
if argument == "--testing-library", let libraryName = iterator.next() {
return libraryName.lowercased()
}
}

// Fallback if not specified: run XCTest (legacy behavior)
return "xctest"
}

#if os(Linux)
@_silgen_name("$ss13_runAsyncMainyyyyYaKcF")
private static func _runAsyncMain(_ asyncFun: @Sendable @escaping () async throws -> ())

static func main() {
let testingLibrary = Self.testingLibrary()
#if canImport(Testing)
if testingLibrary == "swift-testing" {
_runAsyncMain {
await Testing.__swiftPMEntryPoint() as Never
}
}
#endif
}
#else
static func main() async {
let testingLibrary = Self.testingLibrary()
#if canImport(Testing)
if testingLibrary == "swift-testing" {
await Testing.__swiftPMEntryPoint() as Never
}
#endif
}
#endif
}
"""#)
return .succeeded
} catch {
outputDelegate.emitError("\(error)")
return .failed
}
}
}

private struct Options: ParsableArguments {
@Option var output: Path
}
23 changes: 23 additions & 0 deletions Sources/SWBUniversalPlatform/TestEntryPointGenerationTool.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SWBUtil
import SWBMacro
import SWBCore

final class TestEntryPointGenerationToolSpec: GenericCommandLineToolSpec, SpecIdentifierType, @unchecked Sendable {
static let identifier = "org.swift.test-entry-point-generator"

override func createTaskAction(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) -> (any PlannedTaskAction)? {
TestEntryPointGenerationTaskAction()
}
}
35 changes: 35 additions & 0 deletions Sources/SWBUniversalPlatform/TestEntryPointTaskProducer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SWBCore
import SWBTaskConstruction

class TestEntryPointTaskProducer: PhasedTaskProducer, TaskProducer {
func generateTasks() async -> [any PlannedTask] {
var tasks: [any PlannedTask] = []
if context.settings.globalScope.evaluate(BuiltinMacros.GENERATE_TEST_ENTRY_POINT) {
await self.appendGeneratedTasks(&tasks) { delegate in
let scope = context.settings.globalScope
let outputPath = scope.evaluate(BuiltinMacros.GENERATED_TEST_ENTRY_POINT_PATH)
let cbc = CommandBuildContext(producer: context, scope: scope, inputs: [], outputs: [outputPath])
await context.testEntryPointGenerationToolSpec.constructTasks(cbc, delegate)
}
}
return tasks
}
}

extension TaskProducerContext {
var testEntryPointGenerationToolSpec: TestEntryPointGenerationToolSpec {
return workspaceContext.core.specRegistry.getSpec(TestEntryPointGenerationToolSpec.identifier, domain: domain) as! TestEntryPointGenerationToolSpec
}
}
Loading