diff --git a/Sources/SWBCore/SpecImplementations/LinkerSpec.swift b/Sources/SWBCore/SpecImplementations/LinkerSpec.swift index 6149d4c4..a8d7df3f 100644 --- a/Sources/SWBCore/SpecImplementations/LinkerSpec.swift +++ b/Sources/SWBCore/SpecImplementations/LinkerSpec.swift @@ -89,11 +89,12 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable { /// The path to the privacy file, if one exists. public let privacyFile: Path? - public init(kind: Kind, path: Path, mode: Mode, useSearchPaths: Bool, swiftModulePaths: [String: Path], swiftModuleAdditionalLinkerArgResponseFilePaths: [String: Path], explicitDependencies: [Path] = [], topLevelItemPath: Path? = nil, dsymPath: Path? = nil, xcframeworkSourcePath: Path? = nil, privacyFile: Path? = nil) { + public let libPrefix: String? + + public init(kind: Kind, path: Path, mode: Mode, useSearchPaths: Bool, swiftModulePaths: [String: Path], swiftModuleAdditionalLinkerArgResponseFilePaths: [String: Path], prefix: String? = nil, explicitDependencies: [Path] = [], topLevelItemPath: Path? = nil, dsymPath: Path? = nil, xcframeworkSourcePath: Path? = nil, privacyFile: Path? = nil) { self.kind = kind self.path = path self.mode = mode - self.useSearchPaths = useSearchPaths self.swiftModulePaths = swiftModulePaths self.swiftModuleAdditionalLinkerArgResponseFilePaths = swiftModuleAdditionalLinkerArgResponseFilePaths self.explicitDependencies = explicitDependencies @@ -101,6 +102,10 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable { self.dsymPath = dsymPath self.xcframeworkSourcePath = xcframeworkSourcePath self.privacyFile = privacyFile + self.libPrefix = prefix + // Only use search paths when no prefix is required or when the prefix matches + let hasValidPrefix = libPrefix.map { path.basename.hasPrefix($0) } ?? true + self.useSearchPaths = hasValidPrefix && useSearchPaths } } diff --git a/Sources/SWBCore/SpecImplementations/Specs.swift b/Sources/SWBCore/SpecImplementations/Specs.swift index cb8cf844..8223ce77 100644 --- a/Sources/SWBCore/SpecImplementations/Specs.swift +++ b/Sources/SWBCore/SpecImplementations/Specs.swift @@ -301,6 +301,9 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable { /// Returns `true` if the `isWrapperFolder` value is set in the XCSpec for the file spec. public let isWrapper: Bool + /// Returns any common prefix this file may have (currently used when specifying searched libraries to linker) + public let prefix: String? + required init(_ parser: SpecParser, _ basedOnSpec: Spec?) { let basedOnFileTypeSpec = basedOnSpec as? FileTypeSpec ?? nil @@ -318,8 +321,8 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable { self.isEmbeddableInProduct = parser.parseBool("IsEmbeddable") ?? false self.validateOnCopy = parser.parseBool("ValidateOnCopy") ?? false self.codeSignOnCopy = parser.parseBool("CodeSignOnCopy") ?? false - self.isWrapper = parser.parseBool("IsWrapperFolder") ?? false + self.prefix = parser.parseString("Prefix") // Parse and ignore keys we have no use for. // @@ -358,7 +361,6 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable { parser.parseStringList("MIMETypes") parser.parseString("Permissions") parser.parseString("PlistStructureDefinition") - parser.parseStringList("Prefix") parser.parseBool("RemoveHeadersOnCopy") parser.parseBool("RequiresHardTabs") parser.parseString("UTI") diff --git a/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift b/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift index 414ec486..9cfc2d8c 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift @@ -1289,41 +1289,12 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec private static func computeLibraryArgs(_ libraries: [LibrarySpecifier], scope: MacroEvaluationScope) -> (args: [String], inputs: [Path]) { // Construct the library arguments. return libraries.compactMap { specifier -> (args: [String], inputs: [Path]) in - let basename = specifier.path.basename - - // FIXME: This isn't a good system, we need to redesign how we talk to the linker w.r.t. search paths and our notion of paths. switch specifier.kind { - case .static: - if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".a") { - return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(".a")), []) - } - return (specifier.absolutePathFlagsForLd(), [specifier.path]) - case .dynamic: - let suffix = ".\(scope.evaluate(BuiltinMacros.DYNAMIC_LIBRARY_EXTENSION))" - if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(suffix) { - return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(suffix)), []) - } - return (specifier.absolutePathFlagsForLd(), [specifier.path]) - case .textBased: - if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".tbd") { - // .merge and .reexport are not supported for text-based libraries. - return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(".tbd")), []) - } - return (specifier.absolutePathFlagsForLd(), [specifier.path]) - case .framework: - let frameworkName = Path(basename).withoutSuffix + case .static, .dynamic, .textBased, .framework: if specifier.useSearchPaths { - return (specifier.searchPathFlagsForLd(frameworkName), []) + return (specifier.searchPathFlagsForLd(), []) } - let absPathArgs = specifier.absolutePathFlagsForLd() - let returnPath: Path - if let pathArg = absPathArgs.last, Path(pathArg).basename == frameworkName { - returnPath = Path(pathArg) - } - else { - returnPath = specifier.path - } - return (absPathArgs, [returnPath]) + return (specifier.absolutePathFlagsForLd(), [specifier.path]) case .object: // Object files are added to linker inputs in the sources task producer. return ([], []) @@ -1559,35 +1530,47 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec /// Extensions to `LinkerSpec.LibrarySpecifier` specific to the dynamic linker. fileprivate extension LinkerSpec.LibrarySpecifier { - func searchPathFlagsForLd(_ name: String) -> [String] { + + func searchPathFlagsForLd() -> [String] { + precondition(useSearchPaths) + // Extract basename once to avoid redundant operations + let basename = path.basename + let basenameWithoutSuffix = Path(basename).withoutSuffix + // Strip the prefix if one exists and is present in the basename + let strippedName: String + if let prefix = libPrefix, basename.hasPrefix(prefix) { + strippedName = basenameWithoutSuffix.withoutPrefix(prefix) + } else { + strippedName = basenameWithoutSuffix + } switch (kind, mode) { case (.dynamic, .normal): - return ["-l" + name] + return ["-l" + strippedName] case (.dynamic, .reexport): - return ["-Xlinker", "-reexport-l" + name] + return ["-Xlinker", "-reexport-l" + strippedName] case (.dynamic, .merge): - return ["-Xlinker", "-merge-l" + name] + return ["-Xlinker", "-merge-l" + strippedName] case (.dynamic, .reexport_merge): - return ["-Xlinker", "-no_merge-l" + name] + return ["-Xlinker", "-no_merge-l" + strippedName] case (.dynamic, .weak): - return ["-weak-l" + name] + return ["-weak-l" + strippedName] case (.static, .weak), (.textBased, .weak): - return ["-weak-l" + name] + return ["-weak-l" + strippedName] case (.static, _), (.textBased, _): // Other modes are not supported for these kinds. - return ["-l" + name] + return ["-l" + strippedName] case (.framework, .normal): - return ["-framework", name] + return ["-framework", strippedName] case (.framework, .reexport): - return ["-Xlinker", "-reexport_framework", "-Xlinker", name] + return ["-Xlinker", "-reexport_framework", "-Xlinker", strippedName] case (.framework, .merge): - return ["-Xlinker", "-merge_framework", "-Xlinker", name] + return ["-Xlinker", "-merge_framework", "-Xlinker", strippedName] case (.framework, .reexport_merge): - return ["-Xlinker", "-no_merge_framework", "-Xlinker", name] + return ["-Xlinker", "-no_merge_framework", "-Xlinker", strippedName] case (.framework, .weak): - return ["-weak_framework", name] + return ["-weak_framework", strippedName] case (.object, _): // Object files are added to linker inputs in the sources task producer. return [] @@ -1729,15 +1712,17 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u delegate.warning("Product \(cbc.output.basename) cannot weak-link \(specifier.kind) \(basename)") } - if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".a") { + if specifier.useSearchPaths { // Locate using search paths: Add a -l option and *don't* add the path to the library as an input to the task. - return ["-l" + basename.withoutPrefix("lib").withoutSuffix(".a")] - } - else { - // Locate using an absolute path: Add the path as an option and as an input to the task. - inputPaths.append(specifier.path) - return [specifier.path.str] + let basename = specifier.path.basename + let expectedPrefix = specifier.libPrefix ?? "lib" + if basename.hasPrefix(expectedPrefix) { + return ["-l" + Path(basename).withoutSuffix.withoutPrefix(expectedPrefix)] + } } + // Locate using an absolute path: Add the path as an option and as an input to the task. + inputPaths.append(specifier.path) + return [specifier.path.str] case .object: // Object files are added to linker inputs in the sources task producer and so end up in the link-file-list. diff --git a/Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec b/Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec index 446d925b..0922b489 100644 --- a/Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec +++ b/Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec @@ -91,4 +91,14 @@ IconNamePrefix = "TargetPlugin"; DefaultTargetName = "Object File"; }, + { + Domain = generic-unix; + Type = FileType; + Identifier = compiled.mach-o.dylib; + BasedOn = compiled.mach-o; + Prefix = lib; + Extensions = (so); + IsLibrary = YES; + IsDynamicLibrary = YES; + } ) diff --git a/Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift b/Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift index c8e6296f..9488b8de 100644 --- a/Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift +++ b/Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift @@ -507,16 +507,25 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F useSearchPaths: useSearchPaths, swiftModulePaths: swiftModulePaths, swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths, + prefix: fileType.prefix, privacyFile: privacyFile ) } else if fileType.conformsTo(context.lookupFileType(identifier: "compiled.mach-o.dylib")!) { + let adjustedAbsolutePath: Path + // On Windows, ensure import libraries (.lib) are used instead of DLLs. + if context.sdkVariant?.llvmTargetTripleSys == "windows" && absolutePath.fileSuffix.lowercased() == ".dll" { + adjustedAbsolutePath = Path(absolutePath.withoutSuffix + ".lib") + } else { + adjustedAbsolutePath = absolutePath + } return LinkerSpec.LibrarySpecifier( kind: .dynamic, - path: absolutePath, + path: adjustedAbsolutePath, mode: linkageModeForDylib(), useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:], + prefix: fileType.prefix, privacyFile: privacyFile ) } else if fileType.conformsTo(context.lookupFileType(identifier: "sourcecode.text-based-dylib-definition")!) { @@ -527,17 +536,18 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:], + prefix: fileType.prefix, privacyFile: privacyFile ) } else if fileType.conformsTo(context.lookupFileType(identifier: "wrapper.framework")!) { - func kindFromSettings(_ settings: Settings) -> LinkerSpec.LibrarySpecifier.Kind? { + func kindFromSettings(_ settings: Settings) -> (kind: LinkerSpec.LibrarySpecifier.Kind, prefix: String?)? { switch settings.globalScope.evaluate(BuiltinMacros.MACH_O_TYPE) { case "staticlib": - return .static + return (.static, context.lookupFileType(identifier: "archive.ar")?.prefix) case "mh_dylib": - return .dynamic + return (.dynamic, context.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefix) case "mh_object": - return .object + return (.object, nil) default: return nil } @@ -547,9 +557,11 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F let path: Path let dsymPath: Path? let topLevelItemPath: Path? + let prefix: String? if let settingsForRef, let presumedKind = kindFromSettings(settingsForRef), !useSearchPaths { // If we have a Settings from a cross-project reference, use the _actual_ library path. This prevents downstream code from reconstituting the framework path by joining the framework path with the basename of the framework, which won't be correct for deep frameworks which also need the Versions/A path component. - kind = presumedKind + kind = presumedKind.kind + prefix = presumedKind.prefix path = settingsForRef.globalScope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(settingsForRef.globalScope.evaluate(BuiltinMacros.EXECUTABLE_PATH)).normalize() topLevelItemPath = absolutePath if shouldGenerateDSYM(settingsForRef.globalScope) { @@ -563,6 +575,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F path = absolutePath topLevelItemPath = nil dsymPath = nil + prefix = nil } return LinkerSpec.LibrarySpecifier( @@ -572,6 +585,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:], + prefix: prefix, topLevelItemPath: topLevelItemPath, dsymPath: dsymPath, privacyFile: privacyFile @@ -581,7 +595,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F kind: .object, path: absolutePath, mode: buildFile.shouldLinkWeakly ? .weak : .normal, - useSearchPaths: useSearchPaths, + useSearchPaths: false, swiftModulePaths: swiftModulePaths, swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths, privacyFile: privacyFile @@ -621,10 +635,16 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F } let libraryKind: LinkerSpec.LibrarySpecifier.Kind + let prefix: String? switch library.libraryType { - case .framework: libraryKind = .framework; break - case .dynamicLibrary: libraryKind = .dynamic; break - case .staticLibrary: libraryKind = .static; break + case .framework: libraryKind = .framework; prefix = nil + case .dynamicLibrary: + libraryKind = .dynamic; + prefix = context.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefix + case .staticLibrary: + libraryKind = .static + prefix = context.lookupFileType(identifier: "archive.ar")?.prefix + break case let .unknown(fileExtension): // An error of type this type should have already been manifested. assertionFailure("unknown xcframework type: \(fileExtension)") @@ -651,6 +671,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:], + prefix: prefix, explicitDependencies: outputFilePaths, xcframeworkSourcePath: xcframeworkPath, privacyFile: nil diff --git a/Sources/SWBTestSupport/RunDestinationTestSupport.swift b/Sources/SWBTestSupport/RunDestinationTestSupport.swift index fa29f90c..f535e7ba 100644 --- a/Sources/SWBTestSupport/RunDestinationTestSupport.swift +++ b/Sources/SWBTestSupport/RunDestinationTestSupport.swift @@ -319,7 +319,7 @@ extension RunDestinationInfo { /// An `Environment` object with `PATH` or `LD_LIBRARY_PATH` set appropriately pointing into the toolchain to be able to run a built Swift binary in tests. /// /// - note: On macOS, the OS provided Swift runtime is used, so `DYLD_LIBRARY_PATH` is never set for Mach-O destinations. - package func hostRuntimeEnvironment(_ core: Core, initialEnvironment: Environment = Environment()) -> Environment { + package func hostRuntimeEnvironment(_ core: Core, initialEnvironment: Environment = Environment()) throws -> Environment { var environment = initialEnvironment guard let toolchain = core.toolchainRegistry.defaultToolchain else { return environment @@ -328,6 +328,21 @@ extension RunDestinationInfo { case .elf: environment.prependPath(key: "LD_LIBRARY_PATH", value: toolchain.path.join("usr/lib/swift/\(platform)").str) case .pe: + if let path = core.platformRegistry.lookup(name: platform)?.platform?.path.join("Developer/Library") { + func matchesArch(_ path: Path) -> Bool { + switch Architecture.hostStringValue { + case "x86_64": + return path.basename == "bin64" + case "aarch64": + return path.basename == "bin64a" + default: + return false + } + } + for dir in try localFS.traverse(path, { $0 }).sorted() where localFS.isDirectory(dir) && matchesArch(dir) { + environment.prependPath(key: .path, value: dir.str) + } + } environment.prependPath(key: .path, value: core.developerPath.path.join("Runtimes").join(toolchain.version.description).join("usr/bin").str) case .macho: // Fall back to the OS provided Swift runtime diff --git a/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec b/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec index 6afa5833..13695671 100644 --- a/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec @@ -903,6 +903,7 @@ Class = PBXMachOFileType; BasedOn = compiled.mach-o; Extensions = (dylib); + Prefix = lib; IsLibrary = YES; IsDynamicLibrary = YES; CodeSignOnCopy = YES; @@ -939,6 +940,7 @@ Identifier = sourcecode.text-based-dylib-definition; BasedOn = sourcecode; Extensions = (tbd); + Prefix = lib; IsLibrary = YES; IsDynamicLibrary = YES; CodeSignOnCopy = YES; @@ -1471,7 +1473,7 @@ Identifier = archive.ar; BasedOn = archive; Extensions = (a); - Prefix = (lib); + Prefix = lib; IsLibrary = YES; IsStaticLibrary = YES; ContainsNativeCode = YES; diff --git a/Sources/SWBWindowsPlatform/Plugin.swift b/Sources/SWBWindowsPlatform/Plugin.swift index b3c095ef..c4e07e67 100644 --- a/Sources/SWBWindowsPlatform/Plugin.swift +++ b/Sources/SWBWindowsPlatform/Plugin.swift @@ -170,7 +170,7 @@ struct WindowsSDKRegistryExtension: SDKRegistryExtension { "GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO", "LIBRARY_SEARCH_PATHS": "$(inherited) $(SDKROOT)/usr/lib/swift/windows/$(CURRENT_ARCH)", - "TEST_LIBRARY_SEARCH_PATHS": .plString("\(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH)"), + "TEST_LIBRARY_SEARCH_PATHS": .plString("\(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/ \(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows"), "OTHER_SWIFT_FLAGS": "$(inherited) -libc $(DEFAULT_USE_RUNTIME)", "DEFAULT_USE_RUNTIME": "MD", diff --git a/Sources/SWBWindowsPlatform/Specs/Windows.xcspec b/Sources/SWBWindowsPlatform/Specs/Windows.xcspec index 3ed0fccb..c182b273 100644 --- a/Sources/SWBWindowsPlatform/Specs/Windows.xcspec +++ b/Sources/SWBWindowsPlatform/Specs/Windows.xcspec @@ -48,6 +48,7 @@ Identifier = com.apple.product-type.bundle.unit-test; BasedOn = com.apple.product-type.library.dynamic; DefaultBuildProperties = { + ENABLE_TESTING_SEARCH_PATHS = YES; // Index store data is required to discover XCTest tests COMPILER_INDEX_STORE_ENABLE = YES; SWIFT_INDEX_STORE_ENABLE = YES; @@ -87,6 +88,7 @@ BasedOn = default:com.apple.product-type.library.dynamic; HasInfoPlist = NO; DefaultBuildProperties = { + EXECUTABLE_PREFIX = ""; PUBLIC_HEADERS_FOLDER_PATH = ""; PRIVATE_HEADERS_FOLDER_PATH = ""; }; @@ -111,4 +113,25 @@ Identifier = org.swift.product-type.common.object; BasedOn = com.apple.product-type.library.static; }, + + { + Domain = windows; + Type = FileType; + Identifier = archive.ar; + BasedOn = archive; + Extensions = (lib); + IsLibrary = YES; + IsStaticLibrary = YES; + ContainsNativeCode = YES; + }, + + { + Domain = windows; + Type = FileType; + Identifier = compiled.mach-o.dylib; + BasedOn = compiled.mach-o; + Extensions = (dll); + IsLibrary = YES; + IsDynamicLibrary = YES; + } ) diff --git a/Tests/SWBBuildSystemTests/BuildOperationTests.swift b/Tests/SWBBuildSystemTests/BuildOperationTests.swift index 25f8c854..5e39b915 100644 --- a/Tests/SWBBuildSystemTests/BuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/BuildOperationTests.swift @@ -94,7 +94,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["dynamic library.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), TestStandardTarget( "staticlib", @@ -108,7 +109,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["static library.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), ]) let core = try await getCore() @@ -279,7 +281,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["dynamic.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), TestStandardTarget( "staticlib", @@ -293,7 +296,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["static.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), ]) let core = try await getCore() @@ -422,7 +426,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["dynamic.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), TestStandardTarget( "staticlib", @@ -436,7 +441,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], buildPhases: [ TestSourcesBuildPhase(["static.swift"]), - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)", ), ]) let core = try await getCore() @@ -546,7 +552,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { } } - @Test(.requireSDKs(.host), .skipHostOS(.macOS), .skipHostOS(.windows, "cannot find testing library")) + @Test(.requireSDKs(.host), .skipHostOS(.macOS)) func unitTestWithGeneratedEntryPoint() async throws { try await withTemporaryDirectory(removeTreeOnDeinit: false) { (tmpDir: Path) in let testProject = try await TestProject( @@ -582,7 +588,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildPhases: [ TestSourcesBuildPhase(), TestFrameworksBuildPhase([ - "MyTests.so" + TestBuildFile(.target("MyTests")) ]) ], dependencies: ["MyTests"] @@ -593,7 +599,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildConfigurations: [ TestBuildConfiguration("Debug", buildSettings: [ "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", - "LD_DYLIB_INSTALL_NAME": "MyTests.so" + "LD_DYLIB_INSTALL_NAME": "$(EXECUTABLE_NAME)" ]) ], buildPhases: [ @@ -604,7 +610,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], dependencies: [ "library" ], - productReferenceName: "MyTests.so" + productReferenceName: "$(EXECUTABLE_NAME)" ), TestStandardTarget( "library", @@ -612,16 +618,16 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildConfigurations: [ TestBuildConfiguration("Debug", buildSettings: [ "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", - "LD_DYLIB_INSTALL_NAME": "liblibrary.so", - // FIXME: Find a way to make these default "EXECUTABLE_PREFIX": "lib", "EXECUTABLE_PREFIX[sdk=windows*]": "", + "LD_DYLIB_INSTALL_NAME": "$(EXECUTABLE_NAME)", ]) ], buildPhases: [ TestSourcesBuildPhase(["library.swift"]), ], + productReferenceName: "$(EXECUTABLE_NAME)", ) ]) let core = try await getCore() @@ -656,21 +662,23 @@ fileprivate struct BuildOperationTests: CoreBasedTests { try await tester.checkBuild(runDestination: destination, persistent: true) { results in results.checkNoErrors() - let environment = destination.hostRuntimeEnvironment(core) + let environment = try destination.hostRuntimeEnvironment(core) do { let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: [], environment: environment) + #expect(executionResult.exitStatus == .exit(0)) #expect(String(decoding: executionResult.stdout, as: UTF8.self).contains("Executed 1 test")) } do { let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: ["--testing-library", "swift-testing"], environment: environment) + #expect(executionResult.exitStatus == .exit(0)) #expect(String(decoding: executionResult.stderr, as: UTF8.self).contains("Test run with 1 test ")) } } } } - @Test(.requireSDKs(.host), .skipHostOS(.macOS), .skipHostOS(.windows, "cannot find testing library")) + @Test(.requireSDKs(.host), .skipHostOS(.macOS)) func unitTestWithGeneratedEntryPoint_testabilityDisabled() async throws { try await withTemporaryDirectory(removeTreeOnDeinit: false) { (tmpDir: Path) in let testProject = try await TestProject( @@ -708,7 +716,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildPhases: [ TestSourcesBuildPhase(), TestFrameworksBuildPhase([ - "MyTests.so" + TestBuildFile(.target("MyTests")) ]) ], dependencies: ["MyTests"] @@ -719,7 +727,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildConfigurations: [ TestBuildConfiguration("Debug", buildSettings: [ "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", - "LD_DYLIB_INSTALL_NAME": "MyTests.so" + "LD_DYLIB_INSTALL_NAME": "$(EXECUTABLE_NAME)" ]) ], buildPhases: [ @@ -730,7 +738,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { ], dependencies: [ "library" ], - productReferenceName: "MyTests.so" + productReferenceName: "$(EXECUTABLE_NAME)" ), TestStandardTarget( "library", @@ -738,16 +746,16 @@ fileprivate struct BuildOperationTests: CoreBasedTests { buildConfigurations: [ TestBuildConfiguration("Debug", buildSettings: [ "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", - "LD_DYLIB_INSTALL_NAME": "liblibrary.so", - // FIXME: Find a way to make these default "EXECUTABLE_PREFIX": "lib", "EXECUTABLE_PREFIX[sdk=windows*]": "", + "LD_DYLIB_INSTALL_NAME": "$(EXECUTABLE_NAME)", ]) ], buildPhases: [ TestSourcesBuildPhase(["library.swift"]), ], + productReferenceName: "$(EXECUTABLE_NAME)", ) ]) let core = try await getCore() @@ -783,15 +791,21 @@ fileprivate struct BuildOperationTests: CoreBasedTests { results.checkWarning(.prefix("Skipping XCTest discovery for 'MyTests' because it was not built for testing")) results.checkNoErrors() - let environment = destination.hostRuntimeEnvironment(core) + let environment = try destination.hostRuntimeEnvironment(core) do { let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: [], environment: environment) + #expect(executionResult.exitStatus == .exit(0)) #expect(String(decoding: executionResult.stdout, as: UTF8.self).contains("Executed 0 tests")) } do { let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: ["--testing-library", "swift-testing"], environment: environment) - #expect(String(decoding: executionResult.stderr, as: UTF8.self).contains("Test run with 1 test ")) + withKnownIssue("On windows the test output indicates no tests ran, needs investigation") { + #expect(executionResult.exitStatus == .exit(0)) + #expect(String(decoding: executionResult.stderr, as: UTF8.self).contains("Test run with 1 test ")) + } when: { + core.hostOperatingSystem == .windows + } } } } diff --git a/Tests/SWBBuildSystemTests/CustomTaskBuildOperationTests.swift b/Tests/SWBBuildSystemTests/CustomTaskBuildOperationTests.swift index 736b48d2..e92f796c 100644 --- a/Tests/SWBBuildSystemTests/CustomTaskBuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/CustomTaskBuildOperationTests.swift @@ -29,7 +29,7 @@ fileprivate struct CustomTaskBuildOperationTests: CoreBasedTests { try await withTemporaryDirectory { tmpDir in let destination: RunDestinationInfo = .host let core = try await getCore() - let environment = destination.hostRuntimeEnvironment(core) + let environment = try destination.hostRuntimeEnvironment(core) let testProject = TestProject( "aProject", diff --git a/Tests/SWBBuildSystemTests/LinkerTests.swift b/Tests/SWBBuildSystemTests/LinkerTests.swift index 8991b51e..ed6fa8cd 100644 --- a/Tests/SWBBuildSystemTests/LinkerTests.swift +++ b/Tests/SWBBuildSystemTests/LinkerTests.swift @@ -223,7 +223,8 @@ fileprivate struct LinkerTests: CoreBasedTests { TestSourcesBuildPhase([ "library.swift" ]) - ] + ], + productReferenceName: "$(EXECUTABLE_NAME)" ), ]) let tester = try await BuildOperationTester(getCore(), testProject, simulated: false) diff --git a/Tests/SWBCoreTests/CommandLineSpecTests.swift b/Tests/SWBCoreTests/CommandLineSpecTests.swift index fa1c49e1..1a137783 100644 --- a/Tests/SWBCoreTests/CommandLineSpecTests.swift +++ b/Tests/SWBCoreTests/CommandLineSpecTests.swift @@ -1346,23 +1346,30 @@ import SWBMacro for useSearchPaths in [true, false] { let searchPathString = useSearchPaths ? "search" : "abs" for mode in LinkerSpec.LibrarySpecifier.Mode.allCases { + let prefix: String? let suffix = "_\(mode)_\(searchPathString)" let filePath: Path switch kind { case .static: filePath = Path.root.join("usr/lib/libfoo\(suffix).a") + prefix = producer.lookupFileType(identifier: "archive.ar")?.prefix case .dynamic: filePath = Path.root.join("usr/lib/libbar\(suffix).dylib") + prefix = producer.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefix case .textBased: filePath = Path.root.join("usr/lib/libbaz\(suffix).tbd") + prefix = producer.lookupFileType(identifier: "sourcecode.text-based-dylib-definition")?.prefix case .framework: filePath = Path.root.join("tmp/Foo\(suffix).framework") + prefix = nil case .object: + prefix = nil continue case .objectLibrary: + prefix = nil continue } - result.append(LinkerSpec.LibrarySpecifier(kind: kind, path: filePath, mode: mode, useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:])) + result.append(LinkerSpec.LibrarySpecifier(kind: kind, path: filePath, mode: mode, useSearchPaths: useSearchPaths, swiftModulePaths: [:], swiftModuleAdditionalLinkerArgResponseFilePaths: [:], prefix: prefix)) } } return result diff --git a/Tests/SWBTaskConstructionTests/UnitTestTaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/UnitTestTaskConstructionTests.swift index f40399a3..30160571 100644 --- a/Tests/SWBTaskConstructionTests/UnitTestTaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/UnitTestTaskConstructionTests.swift @@ -328,9 +328,7 @@ fileprivate struct UnitTestTaskConstructionTests: CoreBasedTests { "UnitTestRunner", type: .swiftpmTestRunner, buildConfigurations: [ - TestBuildConfiguration( - "Debug", - buildSettings: [:]) + TestBuildConfiguration("Debug") ], buildPhases: [ TestSourcesBuildPhase(), @@ -346,7 +344,11 @@ fileprivate struct UnitTestTaskConstructionTests: CoreBasedTests { buildConfigurations: [ TestBuildConfiguration( "Debug", - buildSettings: [:]) + buildSettings: [ + // FIXME: Find a way to make these default + "EXECUTABLE_PREFIX": "lib", + "EXECUTABLE_PREFIX[sdk=windows*]": "", + ]) ], buildPhases: [ TestSourcesBuildPhase([ @@ -355,7 +357,6 @@ fileprivate struct UnitTestTaskConstructionTests: CoreBasedTests { ]) ], dependencies: [], - productReferenceName: "$(EXCTABLE_NAME)" ), ]) let core = try await getCore() @@ -384,7 +385,7 @@ fileprivate struct UnitTestTaskConstructionTests: CoreBasedTests { ]) task.checkInputs([ .pathPattern(.suffix("UnitTestTarget.LinkFileList")), - .pathPattern(.or(.suffix("UnitTestTarget.so"), .suffix("UnitTestTarget.dll"))), + .pathPattern(.or(.suffix("/libUnitTestTarget.so"), .suffix("\\UnitTestTarget.dll"))), .namePattern(.any), .namePattern(.any), ])