diff --git a/CHANGELOG.md b/CHANGELOG.md index 823b4e423de..e97c318d52d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,13 +27,18 @@ Swift 6.0 `// swift-tools-version:` can now be specified on subsequent lines of `Package.swift`, for example when first few lines are required to contain a licensing comment header. +* [#7118] + + Macros cross-compiled by SwiftPM with Swift SDKs are now correctly built, loaded, and evaluated for the host triple. + Swift 5.10 ----------- + * [#7010] On macOS, `swift build` and `swift run` now produce binaries that allow backtraces in debug builds. Pass `SWIFT_BACKTRACE=enable=yes` environment variable to enable backtraces on such binaries when running them. -* [7101] +* [#7101] Binary artifacts are now cached along side repository checkouts so they do not need to be re-downloaded across projects. diff --git a/Sources/Basics/Collections/IdentifiableSet.swift b/Sources/Basics/Collections/IdentifiableSet.swift index c1007329e53..46383085b2b 100644 --- a/Sources/Basics/Collections/IdentifiableSet.swift +++ b/Sources/Basics/Collections/IdentifiableSet.swift @@ -47,13 +47,22 @@ public struct IdentifiableSet: Collection { } public subscript(id: Element.ID) -> Element? { - self.storage[id] + get { + self.storage[id] + } + set { + self.storage[id] = newValue + } } public func index(after i: Index) -> Index { Index(storageIndex: self.storage.elements.index(after: i.storageIndex)) } + public mutating func insert(_ element: Element) { + self.storage[element.id] = element + } + public func union(_ otherSequence: some Sequence) -> Self { var result = self for element in otherSequence { diff --git a/Sources/Basics/Errors.swift b/Sources/Basics/Errors.swift index c0900f565d7..f075978da86 100644 --- a/Sources/Basics/Errors.swift +++ b/Sources/Basics/Errors.swift @@ -21,8 +21,7 @@ public struct InternalError: Error { private let description: String public init(_ description: String) { assertionFailure(description) - self - .description = + self.description = "Internal error. Please file a bug at https://github.com/apple/swift-package-manager/issues with this info. \(description)" } } diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index e821ffa2331..5c68bc5c2d5 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -53,7 +53,7 @@ public final class ClangTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { - guard !resources.isEmpty else { + guard !self.resources.isEmpty else { return .none } @@ -133,7 +133,7 @@ public final class ClangTargetBuildDescription { self.target = target self.toolsVersion = toolsVersion self.buildParameters = buildParameters - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(buildParameters) self.derivedSources = Sources(paths: [], root: tempsPath.appending("DerivedSources")) // We did not use to apply package plugins to C-family targets in prior tools-versions, this preserves the behavior. @@ -225,7 +225,7 @@ public final class ClangTargetBuildDescription { if self.buildParameters.triple.isDarwin() { args += ["-fobjc-arc"] } - args += try buildParameters.targetTripleArgs(for: target) + args += try self.buildParameters.tripleArgs(for: target) args += optimizationArguments args += activeCompilationConditions diff --git a/Sources/Build/BuildDescription/PluginDescription.swift b/Sources/Build/BuildDescription/PluginDescription.swift index ed6f648ef0c..e7259ec3d45 100644 --- a/Sources/Build/BuildDescription/PluginDescription.swift +++ b/Sources/Build/BuildDescription/PluginDescription.swift @@ -29,6 +29,9 @@ public final class PluginDescription: Codable { /// the plugin). public let targetName: String + /// The language-level target name. + public let targetC99Name: String + /// The names of any plugin products in that package that vend the plugin /// to other packages. public let productNames: [String] @@ -56,6 +59,7 @@ public final class PluginDescription: Codable { self.package = package.identity self.targetName = target.name + self.targetC99Name = target.c99name self.productNames = products.map(\.name) self.toolsVersion = toolsVersion self.sources = target.sources diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 3dda78588cf..8f7c33daa2e 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -322,7 +322,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // setting is the package-level right now. We might need to figure out a better // answer for libraries if/when we support specifying deployment target at the // target-level. - args += try self.buildParameters.targetTripleArgs(for: self.product.targets[self.product.targets.startIndex]) + args += try self.buildParameters.tripleArgs(for: self.product.targets[self.product.targets.startIndex]) // Add arguments from declared build settings. args += self.buildSettingsFlags @@ -357,7 +357,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // Library search path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if product.type == .macro { - args += try ["-L", buildParameters.toolchain.hostLibDir.pathString] + args += try ["-L", defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif diff --git a/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift b/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift new file mode 100644 index 00000000000..7701ea32e28 --- /dev/null +++ b/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2015-2023 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 struct Basics.AbsolutePath +import struct PackageGraph.ResolvedModule + +import SPMBuildCore + +extension ResolvedModule { + func tempsPath(_ buildParameters: BuildParameters) -> AbsolutePath { + let suffix = buildParameters.suffix + return buildParameters.buildPath.appending(component: "\(self.c99name)\(suffix).build") + } +} diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index efb6a474906..79d11929e5f 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -43,7 +43,7 @@ public final class SwiftTargetBuildDescription { /// a target is built. public let toolsVersion: ToolsVersion - /// The build parameters. + /// The build parameters for this target. let buildParameters: BuildParameters /// Path to the temporary directory for this target. @@ -63,9 +63,10 @@ public final class SwiftTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { if let bundleName = target.underlying.potentialBundleName, needsResourceBundle { - return self.buildParameters.bundlePath(named: bundleName) + let suffix = self.buildParameters.suffix + return self.buildParameters.bundlePath(named: bundleName + suffix) } else { - return .none + return nil } } @@ -112,7 +113,8 @@ public final class SwiftTargetBuildDescription { } var modulesPath: AbsolutePath { - return self.buildParameters.buildPath.appending(component: "Modules") + let suffix = self.buildParameters.suffix + return self.buildParameters.buildPath.appending(component: "Modules\(suffix)") } /// The path to the swiftmodule file after compilation. @@ -121,7 +123,7 @@ public final class SwiftTargetBuildDescription { let triple = buildParameters.triple let allowLinkingAgainstExecutables = (triple.isDarwin() || triple.isLinux() || triple.isWindows()) && self.toolsVersion >= .v5_5 let dirPath = (target.type == .executable && !allowLinkingAgainstExecutables) ? self.tempsPath : self.modulesPath - return dirPath.appending(component: self.target.c99name + ".swiftmodule") + return dirPath.appending(component: "\(self.target.c99name).swiftmodule") } /// The path to the wrapped swift module which is created using the modulewrap tool. This is required @@ -131,7 +133,7 @@ public final class SwiftTargetBuildDescription { self.tempsPath.appending(component: self.target.c99name + ".swiftmodule.o") } - /// The path to the swifinterface file after compilation. + /// The path to the swiftinterface file after compilation. var parseableModuleInterfaceOutputPath: AbsolutePath { self.modulesPath.appending(component: self.target.c99name + ".swiftinterface") } @@ -233,7 +235,7 @@ public final class SwiftTargetBuildDescription { public let prebuildCommandResults: [PrebuildCommandResult] /// Any macro products that this target requires to build. - public let requiredMacroProducts: [ResolvedProduct] + public let requiredMacroProducts: [ProductBuildDescription] /// ObservabilityScope with which to emit diagnostics private let observabilityScope: ObservabilityScope @@ -242,7 +244,7 @@ public final class SwiftTargetBuildDescription { private let shouldGenerateTestObservation: Bool /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// Create a new target description with target and build parameters. init( @@ -253,10 +255,10 @@ public final class SwiftTargetBuildDescription { buildParameters: BuildParameters, buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], prebuildCommandResults: [PrebuildCommandResult] = [], - requiredMacroProducts: [ResolvedProduct] = [], + requiredMacroProducts: [ProductBuildDescription] = [], testTargetRole: TestTargetRole? = nil, shouldGenerateTestObservation: Bool = false, - disableSandbox: Bool, + shouldDisableSandbox: Bool, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -269,6 +271,7 @@ public final class SwiftTargetBuildDescription { self.target = target self.toolsVersion = toolsVersion self.buildParameters = buildParameters + // Unless mentioned explicitly, use the target type to determine if this is a test target. if let testTargetRole { self.testTargetRole = testTargetRole @@ -278,13 +281,13 @@ public final class SwiftTargetBuildDescription { self.testTargetRole = nil } - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(self.buildParameters) self.derivedSources = Sources(paths: [], root: self.tempsPath.appending("DerivedSources")) self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults self.requiredMacroProducts = requiredMacroProducts self.shouldGenerateTestObservation = shouldGenerateTestObservation - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = shouldDisableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope @@ -292,7 +295,7 @@ public final class SwiftTargetBuildDescription { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: buildParameters, + buildParameters: self.buildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults, prebuildCommandResults: prebuildCommandResults, observabilityScope: observabilityScope @@ -332,7 +335,10 @@ public final class SwiftTargetBuildDescription { return } - guard self.buildParameters.triple.isDarwin(), self.buildParameters.testingParameters.experimentalTestOutput else { + guard + self.buildParameters.triple.isDarwin() && + self.buildParameters.testingParameters.experimentalTestOutput + else { return } @@ -412,21 +418,25 @@ public final class SwiftTargetBuildDescription { #if BUILD_MACROS_AS_DYLIBS self.requiredMacroProducts.forEach { macro in - args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.buildParameters.binaryPath(for: macro).pathString] + args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", macro.binaryPath.pathString] } #else try self.requiredMacroProducts.forEach { macro in - if let macroTarget = macro.targets.first { - let executablePath = try self.buildParameters.binaryPath(for: macro).pathString + if let macroTarget = macro.product.targets.first { + let executablePath = try macro.binaryPath.pathString args += ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", "\(executablePath)#\(macroTarget.c99name)"] } else { - throw InternalError("macro product \(macro.name) has no targets") // earlier validation should normally catch this + throw InternalError("macro product \(macro.product.name) has no targets") // earlier validation should normally catch this } } #endif - if self.disableSandbox { - let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags(flags: ["-disable-sandbox"], toolchain: self.buildParameters.toolchain, fileSystem: fileSystem) + if self.shouldDisableSandbox { + let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags( + flags: ["-disable-sandbox"], + toolchain: self.buildParameters.toolchain, + fileSystem: fileSystem + ) if toolchainSupportsDisablingSandbox { args += ["-disable-sandbox"] } else { @@ -443,7 +453,7 @@ public final class SwiftTargetBuildDescription { /// The arguments needed to compile this target. public func compileArguments() throws -> [String] { var args = [String]() - args += try self.buildParameters.targetTripleArgs(for: self.target) + args += try self.buildParameters.tripleArgs(for: self.target) // pass `-v` during verbose builds. if self.buildParameters.outputParameters.isVerbose { @@ -818,7 +828,7 @@ public final class SwiftTargetBuildDescription { // Include path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if target.type == .macro { - flags += try ["-I", self.buildParameters.toolchain.hostLibDir.pathString] + flags += try ["-I", self.defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift index 70ae4ee508a..3f23cf1cbc3 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift @@ -93,7 +93,7 @@ extension LLBuildManifestBuilder { let additionalInputs = try addBuildToolPlugins(.clang(target)) // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) + let targetName = target.llbuildTargetName let output: Node = .virtual(targetName) self.manifest.addNode(output, toTarget: targetName) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift index 3061b25ee7b..d95e5b46662 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift @@ -11,11 +11,14 @@ //===----------------------------------------------------------------------===// import struct Basics.AbsolutePath +import struct Basics.InternalError import struct LLBuildManifest.Node +import struct SPMBuildCore.BuildParameters +import struct PackageGraph.ResolvedProduct extension LLBuildManifestBuilder { func createProductCommand(_ buildProduct: ProductBuildDescription) throws { - let cmdName = try buildProduct.product.getCommandName(config: buildProduct.buildParameters.buildConfig) + let cmdName = try buildProduct.commandName // Add dependency on Info.plist generation on Darwin platforms. let testInputs: [AbsolutePath] @@ -34,7 +37,7 @@ extension LLBuildManifestBuilder { } // Create a phony node to represent the entire target. - let targetName = try buildProduct.product.getLLBuildTargetName(config: buildProduct.buildParameters.buildConfig) + let targetName = try buildProduct.llbuildTargetName let output: Node = .virtual(targetName) let finalProductNode: Node @@ -85,7 +88,7 @@ extension LLBuildManifestBuilder { outputPath: plistPath ) - let cmdName = try buildProduct.product.getCommandName(config: buildProduct.buildParameters.buildConfig) + let cmdName = try buildProduct.commandName let codeSigningOutput = Node.virtual(targetName + "-CodeSigning") try self.manifest.addShellCmd( name: "\(cmdName)-entitlements", @@ -120,3 +123,48 @@ extension LLBuildManifestBuilder { ) } } + +extension ProductBuildDescription { + package var llbuildTargetName: String { + get throws { + try self.product.getLLBuildTargetName(buildParameters: self.buildParameters) + } + } + + package var commandName: String { + get throws { + try "C.\(self.llbuildTargetName)\(self.buildParameters.suffix)" + } + } +} + +extension ResolvedProduct { + public func getLLBuildTargetName(buildParameters: BuildParameters) throws -> String { + let triple = buildParameters.triple.tripleString + let config = buildParameters.buildConfig + let suffix = buildParameters.suffix + let potentialExecutableTargetName = "\(name)-\(triple)-\(config)\(suffix).exe" + let potentialLibraryTargetName = "\(name)-\(triple)-\(config)\(suffix).dylib" + + switch type { + case .library(.dynamic): + return potentialLibraryTargetName + case .test: + return "\(name)-\(triple)-\(config)\(suffix).test" + case .library(.static): + return "\(name)-\(triple)-\(config)\(suffix).a" + case .library(.automatic): + throw InternalError("automatic library not supported") + case .executable, .snippet: + return potentialExecutableTargetName + case .macro: + #if BUILD_MACROS_AS_DYLIBS + return potentialLibraryTargetName + #else + return potentialExecutableTargetName + #endif + case .plugin: + throw InternalError("unexpectedly asked for the llbuild target name of a plugin product") + } + } +} diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift index 599c7c435d5..79917f4eb43 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift @@ -45,7 +45,7 @@ extension LLBuildManifestBuilder { outputs.append(output) } - let cmdName = target.target.getLLBuildResourcesCmdName(config: target.buildParameters.buildConfig) + let cmdName = target.llbuildResourcesCmdName self.manifest.addPhonyCmd(name: cmdName, inputs: outputs, outputs: [.virtual(cmdName)]) return .virtual(cmdName) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index a82da020b8c..bf113cf6f81 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -191,9 +191,8 @@ extension LLBuildManifestBuilder { public func addTargetsToExplicitBuildManifest() throws { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. - let nodes: [ResolvedModule.Dependency] = try self.plan.targetMap.keys.compactMap { - guard let target = self.plan.graph.allTargets[$0] else { throw InternalError("unknown target \($0)") } - return ResolvedModule.Dependency.target(target, conditions: []) + let nodes = self.plan.targets.compactMap { + ResolvedModule.Dependency.target($0.target, conditions: []) } let allPackageDependencies = try topologicalSort(nodes, successors: { $0.dependencies }) // Instantiate the inter-module dependency oracle which will cache commonly-scanned @@ -376,7 +375,7 @@ extension LLBuildManifestBuilder { cmdOutputs: [Node] ) throws { let isLibrary = target.target.type == .library || target.target.type == .test - let cmdName = target.target.getCommandName(config: target.buildParameters.buildConfig) + let cmdName = target.getCommandName() self.manifest.addWriteSourcesFileListCommand(sources: target.sources, sourcesFileListPath: target.sourcesFileListPath) self.manifest.addSwiftCmd( @@ -431,14 +430,10 @@ extension LLBuildManifestBuilder { // Depend on the binary for executable targets. if target.type == .executable { // FIXME: Optimize. - let product = try plan.graph.allProducts.first { - try $0.type == .executable && $0.executableTarget.id == target.id - } - if let product { - guard let planProduct = plan.productMap[product.id] else { - throw InternalError("unknown product \(product)") - } - try inputs.append(file: planProduct.binaryPath) + if let productDescription = try plan.productMap.values.first(where: { + try $0.product.type == .executable && $0.product.executableTarget.id == target.id + }) { + try inputs.append(file: productDescription.binaryPath) } return } @@ -494,7 +489,7 @@ extension LLBuildManifestBuilder { // Depend on any required macro product's output. try target.requiredMacroProducts.forEach { macro in - try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.buildParameters.buildConfig))) + try inputs.append(.virtual(macro.llbuildTargetName)) } return inputs + additionalInputs @@ -503,7 +498,7 @@ extension LLBuildManifestBuilder { /// Adds a top-level phony command that builds the entire target. private func addTargetCmd(_ target: SwiftTargetBuildDescription, cmdOutputs: [Node]) { // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) + let targetName = target.getLLBuildTargetName() let targetOutput: Node = .virtual(targetName) self.manifest.addNode(targetOutput, toTarget: targetName) @@ -528,7 +523,7 @@ extension LLBuildManifestBuilder { "-modulewrap", target.moduleOutputPath.pathString, "-o", target.wrappedModuleOutputPath.pathString, ] - moduleWrapArgs += try target.buildParameters.targetTripleArgs(for: target.target) + moduleWrapArgs += try target.buildParameters.tripleArgs(for: target.target) self.manifest.addShellCmd( name: target.wrappedModuleOutputPath.pathString, description: "Wrapping AST for \(target.target.name) for debugging", @@ -609,3 +604,13 @@ extension Driver { } } } + +extension SwiftTargetBuildDescription { + public func getCommandName() -> String { + "C." + self.getLLBuildTargetName() + } + + public func getLLBuildTargetName() -> String { + self.target.getLLBuildTargetName(buildParameters: self.buildParameters) + } +} diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 77c79da1915..61f8ee4c805 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -316,49 +316,21 @@ extension TargetBuildDescription { } } -extension ResolvedModule { - public func getCommandName(config: String) -> String { - "C." + self.getLLBuildTargetName(config: config) - } - - public func getLLBuildTargetName(config: String) -> String { - "\(name)-\(config).module" - } - - public func getLLBuildResourcesCmdName(config: String) -> String { - "\(name)-\(config).module-resources" +extension TargetBuildDescription { + package var llbuildResourcesCmdName: String { + "\(self.target.name)-\(self.buildParameters.triple.tripleString)-\(self.buildParameters.buildConfig)\(self.buildParameters.suffix).module-resources" } } -extension ResolvedProduct { - public func getLLBuildTargetName(config: String) throws -> String { - let potentialExecutableTargetName = "\(name)-\(config).exe" - let potentialLibraryTargetName = "\(name)-\(config).dylib" - - switch type { - case .library(.dynamic): - return potentialLibraryTargetName - case .test: - return "\(name)-\(config).test" - case .library(.static): - return "\(name)-\(config).a" - case .library(.automatic): - throw InternalError("automatic library not supported") - case .executable, .snippet: - return potentialExecutableTargetName - case .macro: - #if BUILD_MACROS_AS_DYLIBS - return potentialLibraryTargetName - #else - return potentialExecutableTargetName - #endif - case .plugin: - throw InternalError("unexpectedly asked for the llbuild target name of a plugin product") - } +extension ClangTargetBuildDescription { + package var llbuildTargetName: String { + self.target.getLLBuildTargetName(buildParameters: self.buildParameters) } +} - public func getCommandName(config: String) throws -> String { - try "C." + self.getLLBuildTargetName(config: config) +extension ResolvedModule { + public func getLLBuildTargetName(buildParameters: BuildParameters) -> String { + "\(self.name)-\(buildParameters.triple.tripleString)-\(buildParameters.buildConfig)\(buildParameters.suffix).module" } } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index b478e20567d..eb98ec1cad1 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -52,6 +52,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS /// the plugin configuration for build plugins let pluginConfiguration: PluginConfiguration? + /// The path to scratch space (.build) directory. + let scratchDirectory: AbsolutePath + /// The llbuild build delegate reference. private var buildSystemDelegate: BuildOperationBuildSystemDelegateHandler? @@ -114,6 +117,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS cacheBuildManifest: Bool, packageGraphLoader: @escaping () throws -> ModulesGraph, pluginConfiguration: PluginConfiguration? = .none, + scratchDirectory: AbsolutePath, additionalFileRules: [FileRuleDescription], pkgConfigDirectories: [AbsolutePath], dependenciesByRootPackageIdentity: [PackageIdentity: [PackageIdentity]], @@ -136,6 +140,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS self.packageGraphLoader = packageGraphLoader self.additionalFileRules = additionalFileRules self.pluginConfiguration = pluginConfiguration + self.scratchDirectory = scratchDirectory self.pkgConfigDirectories = pkgConfigDirectories self.dependenciesByRootPackageIdentity = dependenciesByRootPackageIdentity self.rootPackageIdentityByTargetName = (try? Dictionary(throwingUniqueKeysWithValues: targetsByRootPackageIdentity.lazy.flatMap { e in e.value.map { ($0, e.key) } })) ?? [:] @@ -269,7 +274,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } // TODO: Currently this function will only match frameworks. - internal func detectUnexpressedDependencies( + func detectUnexpressedDependencies( availableLibraries: [LibraryMetadata], targetDependencyMap: [String: [String]]? ) { @@ -295,7 +300,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } let usedSDKDependencies: [String] = Set(possibleTempsPaths).flatMap { possibleTempsPath in - guard let contents = try? self.fileSystem.readFileContents(possibleTempsPath.appending(component: "\(c99name).d")) else { + guard let contents = try? self.fileSystem.readFileContents( + possibleTempsPath.appending(component: "\(c99name).d") + ) else { return [String]() } @@ -371,9 +378,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS let subsetDescriptor: String? switch subset { - case .product(let productName): + case .product(let productName, _): subsetDescriptor = "product '\(productName)'" - case .target(let targetName): + case .target(let targetName, _): subsetDescriptor = "target: '\(targetName)'" case .allExcludingTests, .allIncludingTests: subsetDescriptor = nil @@ -426,10 +433,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS case .allExcludingTests, .allIncludingTests: pluginsToCompile = allPlugins continueBuilding = true - case .product(let productName): + case .product(let productName, _): pluginsToCompile = allPlugins.filter{ $0.productNames.contains(productName) } continueBuilding = pluginsToCompile.isEmpty - case .target(let targetName): + case .target(let targetName, _): pluginsToCompile = allPlugins.filter{ $0.targetName == targetName } continueBuilding = pluginsToCompile.isEmpty } @@ -515,17 +522,57 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS return LLBuildManifestBuilder.TargetKind.main.targetName case .allIncludingTests: return LLBuildManifestBuilder.TargetKind.test.targetName - default: + case .product(let productName, let destination): // FIXME: This is super unfortunate that we might need to load the package graph. let graph = try getPackageGraph() - if let result = subset.llbuildTargetName( - for: graph, - config: self.productsBuildParameters.configuration.dirname, - observabilityScope: self.observabilityScope - ) { - return result + + let product = graph.product( + for: productName, + destination: destination == .host ? .tools : .destination + ) + + guard let product else { + observabilityScope.emit(error: "no product named '\(productName)'") + throw Diagnostics.fatalError } - throw Diagnostics.fatalError + + let buildParameters = if product.buildTriple == .tools { + self.toolsBuildParameters + } else { + self.productsBuildParameters + } + + // If the product is automatic, we build the main target because automatic products + // do not produce a binary right now. + if product.type == .library(.automatic) { + observabilityScope.emit( + warning: + "'--product' cannot be used with the automatic product '\(productName)'; building the default target instead" + ) + return LLBuildManifestBuilder.TargetKind.main.targetName + } + return try product.getLLBuildTargetName(buildParameters: buildParameters) + case .target(let targetName, let destination): + // FIXME: This is super unfortunate that we might need to load the package graph. + let graph = try getPackageGraph() + + let target = graph.target( + for: targetName, + destination: destination == .host ? .tools : .destination + ) + + guard let target else { + observabilityScope.emit(error: "no target named '\(targetName)'") + throw Diagnostics.fatalError + } + + let buildParameters = if target.buildTriple == .tools { + self.toolsBuildParameters + } else { + self.productsBuildParameters + } + + return target.getLLBuildTargetName(buildParameters: buildParameters) } } @@ -538,17 +585,21 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Invoke any build tool plugins in the graph to generate prebuild commands and build commands. if let pluginConfiguration, !self.productsBuildParameters.shouldSkipBuilding { // Hacky workaround for rdar://120560817, but it replicates precisely enough the original behavior before - // products/tools build parameters were split. Ideally we want to have specify the correct path at the time + // products/tools build parameters were split. Ideally we want to specify the correct path at the time // when `toolsBuildParameters` is initialized, but we have too many places in the codebase where that's // done, which makes it hard to realign them all at once. var pluginsBuildParameters = self.toolsBuildParameters pluginsBuildParameters.dataPath = pluginsBuildParameters.dataPath.parentDirectory.appending(components: ["plugins", "tools"]) + + var targetBuildParameters = pluginsBuildParameters + targetBuildParameters.destination = .target + let buildOperationForPluginDependencies = BuildOperation( - // FIXME: this doesn't maintain the products/tools split cleanly - productsBuildParameters: pluginsBuildParameters, + productsBuildParameters: targetBuildParameters, toolsBuildParameters: pluginsBuildParameters, cacheBuildManifest: false, - packageGraphLoader: { return graph }, + packageGraphLoader: { graph }, + scratchDirectory: pluginsBuildParameters.dataPath, additionalFileRules: self.additionalFileRules, pkgConfigDirectories: self.pkgConfigDirectories, dependenciesByRootPackageIdentity: [:], @@ -558,6 +609,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) + buildToolPluginInvocationResults = try graph.invokeBuildToolPlugins( outputDir: pluginConfiguration.workDirectory.appending("outputs"), buildParameters: pluginsBuildParameters, @@ -568,15 +620,16 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS observabilityScope: self.observabilityScope, fileSystem: self.fileSystem ) { name, path in - try buildOperationForPluginDependencies.build(subset: .product(name)) - if let builtTool = try buildOperationForPluginDependencies.buildPlan.buildProducts.first(where: { $0.product.name == name}) { + try buildOperationForPluginDependencies.build(subset: .product(name, for: .host)) + if let builtTool = try buildOperationForPluginDependencies.buildPlan.buildProducts.first(where: { + $0.product.name == name && $0.buildParameters.destination == .host + }) { return try builtTool.binaryPath } else { return nil } } - // Surface any diagnostics from build tool plugins. var succeeded = true for (_, (target, results)) in buildToolPluginInvocationResults { @@ -615,7 +668,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Emit warnings about any unhandled files in authored packages. We do this after applying build tool plugins, once we know what files they handled. // rdar://113256834 This fix works for the plugins that do not have PreBuildCommands. - let targetsToConsider: [ResolvedTarget] + let targetsToConsider: [ResolvedModule] if let subset = subset, let recursiveDependencies = try subset.recursiveDependencies(for: graph, observabilityScope: observabilityScope) { targetsToConsider = recursiveDependencies @@ -656,7 +709,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Create the build plan based, on the graph and any information from plugins. let plan = try BuildPlan( - productsBuildParameters: self.productsBuildParameters, + destinationBuildParameters: self.productsBuildParameters, toolsBuildParameters: self.toolsBuildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -720,7 +773,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS ) self.buildSystemDelegate = buildSystemDelegate - let databasePath = self.productsBuildParameters.dataPath.appending("build.db").pathString + let databasePath = self.scratchDirectory.appending("build.db").pathString let buildSystem = SPMLLBuild.BuildSystem( buildFile: self.productsBuildParameters.llbuildManifest.pathString, databaseFile: databasePath, @@ -883,55 +936,26 @@ extension BuildSubset { return Array(graph.reachableTargets) case .allExcludingTests: return graph.reachableTargets.filter { $0.type != .test } - case .product(let productName): - guard let product = graph.allProducts.first(where: { $0.name == productName }) else { + case .product(let productName, let destination): + guard let product = graph.product( + for: productName, + destination: destination == .host ? .tools : .destination + ) else { observabilityScope.emit(error: "no product named '\(productName)'") return nil } return try product.recursiveTargetDependencies() - case .target(let targetName): - guard let target = graph.allTargets.first(where: { $0.name == targetName }) else { + case .target(let targetName, let destination): + guard let target = graph.target( + for: targetName, + destination: destination == .host ? .tools : .destination + ) else { observabilityScope.emit(error: "no target named '\(targetName)'") return nil } return try target.recursiveTargetDependencies() } } - - /// Returns the name of the llbuild target that corresponds to the build subset. - func llbuildTargetName(for graph: ModulesGraph, config: String, observabilityScope: ObservabilityScope) - -> String? - { - switch self { - case .allExcludingTests: - return LLBuildManifestBuilder.TargetKind.main.targetName - case .allIncludingTests: - return LLBuildManifestBuilder.TargetKind.test.targetName - case .product(let productName): - guard let product = graph.allProducts.first(where: { $0.name == productName }) else { - observabilityScope.emit(error: "no product named '\(productName)'") - return nil - } - // If the product is automatic, we build the main target because automatic products - // do not produce a binary right now. - if product.type == .library(.automatic) { - observabilityScope.emit( - warning: - "'--product' cannot be used with the automatic product '\(productName)'; building the default target instead" - ) - return LLBuildManifestBuilder.TargetKind.main.targetName - } - return observabilityScope.trap { - try product.getLLBuildTargetName(config: config) - } - case .target(let targetName): - guard let target = graph.allTargets.first(where: { $0.name == targetName }) else { - observabilityScope.emit(error: "no target named '\(targetName)'") - return nil - } - return target.getLLBuildTargetName(config: config) - } - } } extension Basics.Diagnostic.Severity { diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 74080c912f0..40944f43f30 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -386,29 +386,29 @@ public struct BuildDescription: Codable { .explicitTargetDependencyImportCheckingMode self.targetDependencyMap = try plan.targets.reduce(into: [TargetName: [TargetName]]()) { partial, targetBuildDescription in let deps = try targetBuildDescription.target.recursiveDependencies( - satisfying: plan.buildParameters(for: targetBuildDescription.target).buildEnvironment + satisfying: targetBuildDescription.buildParameters.buildEnvironment ) .compactMap(\.target).map(\.c99name) partial[targetBuildDescription.target.c99name] = deps } var targetCommandLines: [TargetName: [CommandLineFlag]] = [:] var generatedSourceTargets: [TargetName] = [] - for (targetID, description) in plan.targetMap { - guard case .swift(let desc) = description, let target = plan.graph.allTargets[targetID] else { + for description in plan.targets { + guard case .swift(let desc) = description else { continue } - let buildParameters = plan.buildParameters(for: target) - targetCommandLines[target.c99name] = + let buildParameters = description.buildParameters + targetCommandLines[desc.target.c99name] = try desc.emitCommandLine(scanInvocation: true) + [ "-driver-use-frontend-path", buildParameters.toolchain.swiftCompilerPath.pathString ] if case .discovery = desc.testTargetRole { - generatedSourceTargets.append(target.c99name) + generatedSourceTargets.append(desc.target.c99name) } } generatedSourceTargets.append( - contentsOf: plan.graph.allTargets.filter { $0.type == .plugin } - .map(\.c99name) + contentsOf: plan.pluginDescriptions + .map(\.targetC99Name) ) self.swiftTargetScanArgs = targetCommandLines self.generatedSourceTargetSet = Set(generatedSourceTargets) diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index 5ba6f56dceb..a0cb3600bb9 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -81,7 +81,7 @@ extension BuildPlan { switch target.underlying { case is SwiftTarget: // Swift targets are guaranteed to have a corresponding Swift description. - guard case .swift(let description) = targetMap[target.id] else { + guard case .swift(let description) = self.targetMap[target.id] else { throw InternalError("unknown target \(target)") } @@ -103,13 +103,13 @@ extension BuildPlan { buildProduct.staticTargets = dependencies.staticTargets buildProduct.dylibs = try dependencies.dylibs.map { - guard let product = productMap[$0.id] else { + guard let product = self.productMap[$0.id] else { throw InternalError("unknown product \($0)") } return product } buildProduct.objects += try dependencies.staticTargets.flatMap { targetName -> [AbsolutePath] in - guard let target = targetMap[targetName.id] else { + guard let target = self.targetMap[targetName.id] else { throw InternalError("unknown target \(targetName)") } return try target.objects @@ -231,9 +231,11 @@ extension BuildPlan { if product.targets.contains(id: target.id) { staticTargets.append(target) } - // Library targets should always be included. + // Library targets should always be included for the same build triple. case .library: - staticTargets.append(target) + if target.buildTriple == product.buildTriple { + staticTargets.append(target) + } // Add system target to system targets array. case .systemModule: systemModules.append(target) diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 0d3263f0f01..33b4cc140d6 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -16,6 +16,7 @@ import struct Basics.AbsolutePath import struct LLBuildManifest.TestDiscoveryTool import struct LLBuildManifest.TestEntryPointTool import struct PackageGraph.ModulesGraph +import struct PackageGraph.ResolvedPackage import struct PackageGraph.ResolvedProduct import struct PackageGraph.ResolvedModule import struct PackageModel.Sources @@ -26,15 +27,16 @@ import protocol TSCBasic.FileSystem extension BuildPlan { static func makeDerivedTestTargets( - _ buildParameters: BuildParameters, - _ graph: ModulesGraph, - _ disableSandbox: Bool, + testProducts: [(product: ResolvedProduct, buildDescription: ProductBuildDescription)], + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, + shouldDisableSandbox: Bool, _ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { - guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = - buildParameters.testingParameters.testProductStyle + guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = + destinationBuildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") } @@ -43,15 +45,14 @@ extension BuildPlan { var isDiscoveryEnabledRedundantly = explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly var result: [(ResolvedProduct, SwiftTargetBuildDescription?, SwiftTargetBuildDescription)] = [] - for testProduct in graph.allProducts where testProduct.type == .test { - guard let package = graph.package(for: testProduct) else { - throw InternalError("package not found for \(testProduct)") - } + for (testProduct, testBuildDescription) in testProducts { + let package = testBuildDescription.package + isDiscoveryEnabledRedundantly = isDiscoveryEnabledRedundantly && nil == testProduct.testEntryPointTarget // If a non-explicitly specified test entry point file exists, prefer that over test discovery. // This is designed as an escape hatch when test discovery is not appropriate and for backwards // compatibility for projects that have existing test entry point files (e.g. XCTMain.swift, LinuxMain.swift). - let toolsVersion = graph.package(for: testProduct)?.manifest.toolsVersion ?? .v5_5 + let toolsVersion = package.manifest.toolsVersion // If `testProduct.testEntryPointTarget` is non-nil, it may either represent an `XCTMain.swift` (formerly `LinuxMain.swift`) file // if such a file is located in the package, or it may represent a test entry point file at a path specified by the option @@ -68,7 +69,7 @@ extension BuildPlan { /// Generates test discovery targets, which contain derived sources listing the discovered tests. func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedModule, buildDescription: SwiftTargetBuildDescription) { let discoveryTargetName = "\(package.manifest.displayName)PackageDiscoveredTests" - let discoveryDerivedDir = buildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") + let discoveryDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") let discoveryMainFile = discoveryDerivedDir.appending(component: TestDiscoveryTool.mainFileName) var discoveryPaths: [AbsolutePath] = [] @@ -84,7 +85,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls by default testDiscoverySrc: Sources(paths: discoveryPaths, root: discoveryDerivedDir) ) - let discoveryResolvedTarget = ResolvedModule( + var discoveryResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: discoveryTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) }, @@ -92,13 +93,15 @@ extension BuildPlan { supportedPlatforms: testProduct.supportedPlatforms, platformVersionProvider: testProduct.platformVersionProvider ) + discoveryResolvedTarget.buildTriple = testProduct.buildTriple + let discoveryTargetBuildDescription = try SwiftTargetBuildDescription( package: package, target: discoveryResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + buildParameters: testBuildDescription.buildParameters, testTargetRole: .discovery, - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -112,8 +115,8 @@ extension BuildPlan { swiftTargetDependencies: [Target.Dependency], resolvedTargetDependencies: [ResolvedModule.Dependency] ) throws -> SwiftTargetBuildDescription { - let entryPointDerivedDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived") - let entryPointMainFileName = TestEntryPointTool.mainFileName(for: buildParameters.testingParameters.library) + let entryPointDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(testProduct.name).derived") + let entryPointMainFileName = TestEntryPointTool.mainFileName(for: destinationBuildParameters.testingParameters.library) let entryPointMainFile = entryPointDerivedDir.appending(component: entryPointMainFileName) let entryPointSources = Sources(paths: [entryPointMainFile], root: entryPointDerivedDir) @@ -124,7 +127,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls testEntryPointSources: entryPointSources ) - let entryPointResolvedTarget = ResolvedModule( + var entryPointResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: entryPointTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) } + resolvedTargetDependencies, @@ -132,13 +135,15 @@ extension BuildPlan { supportedPlatforms: testProduct.supportedPlatforms, platformVersionProvider: testProduct.platformVersionProvider ) + entryPointResolvedTarget.buildTriple = testProduct.buildTriple + return try SwiftTargetBuildDescription( package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + buildParameters: testBuildDescription.buildParameters, testTargetRole: .entryPoint(isSynthesized: true), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -148,7 +153,7 @@ extension BuildPlan { let swiftTargetDependencies: [Target.Dependency] let resolvedTargetDependencies: [ResolvedModule.Dependency] - switch buildParameters.testingParameters.library { + switch destinationBuildParameters.testingParameters.library { case .xctest: discoveryTargets = try generateDiscoveryTargets() swiftTargetDependencies = [.target(discoveryTargets!.target, conditions: [])] @@ -181,9 +186,9 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + buildParameters: destinationBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -203,9 +208,9 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + buildParameters: destinationBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 8ff9dd79baa..120a12c29db 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -128,12 +128,13 @@ extension BuildParameters { } /// Computes the target triple arguments for a given resolved target. - public func targetTripleArgs(for target: ResolvedModule) throws -> [String] { + public func tripleArgs(for target: ResolvedModule) throws -> [String] { + // confusingly enough this is the triple argument, not the target argument var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. if self.triple.isDarwin() { - let platform = buildEnvironment.platform + let platform = self.buildEnvironment.platform let supportedPlatform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) args += [self.triple.tripleString(forPlatformVersion: supportedPlatform.version.versionString)] } else { @@ -185,16 +186,6 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Build parameters used for tools. public let toolsBuildParameters: BuildParameters - /// Triple for which this target is compiled. - private func buildTriple(for target: ResolvedModule) -> Basics.Triple { - self.buildParameters(for: target).triple - } - - /// Triple for which this product is compiled. - private func buildTriple(for product: ResolvedProduct) -> Basics.Triple { - self.buildParameters(for: product).triple - } - /// The package graph. public let graph: ModulesGraph @@ -231,14 +222,14 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() - /// Cache for library information. + /// Cache for library information. private var externalLibrariesCache = [BinaryTarget: [LibraryInfo]]() - /// Cache for tools information. + /// Cache for tools information. var externalExecutablesCache = [BinaryTarget: [ExecutableInfo]]() /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// The filesystem to operate on. let fileSystem: any FileSystem @@ -246,31 +237,27 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// ObservabilityScope with which to emit diagnostics let observabilityScope: ObservabilityScope - @available(*, deprecated, renamed: "init(productsBuildParameters:toolsBuildParameters:graph:)") + @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:fileSystem:observabilityScope:)") public convenience init( - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, graph: ModulesGraph, - additionalFileRules: [FileRuleDescription] = [], - buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], - prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { try self.init( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + destinationBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, graph: graph, - additionalFileRules: additionalFileRules, - buildToolPluginInvocationResults: buildToolPluginInvocationResults, - prebuildCommandResults: prebuildCommandResults, fileSystem: fileSystem, observabilityScope: observabilityScope ) } - /// Create a build plan with a package graph and explicitly distinct build parameters for products and tools. + /// Create a build plan with a package graph and explicitly distinct build parameters for destination platform and + /// tools platform. public init( - productsBuildParameters: BuildParameters, + destinationBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], @@ -280,16 +267,17 @@ public class BuildPlan: SPMBuildCore.BuildPlan { fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { - self.destinationBuildParameters = productsBuildParameters + self.destinationBuildParameters = destinationBuildParameters self.toolsBuildParameters = toolsBuildParameters self.graph = graph self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = disableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope.makeChildScope(description: "Build Plan") - var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = [:] + var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = + [:] // Create product description for each product we have in the package graph that is eligible. for product in graph.allProducts where product.shouldCreateProductDescription { let buildParameters: BuildParameters @@ -297,7 +285,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } guard let package = graph.package(for: product) else { @@ -335,7 +323,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } // Validate the product dependencies of this target. @@ -370,7 +358,15 @@ public class BuildPlan: SPMBuildCore.BuildPlan { let requiredMacroProducts = try target.recursiveTargetDependencies() .filter { $0.underlying.type == .macro } - .compactMap { macroProductsByTarget[$0.id] } + .compactMap { + guard let product = macroProductsByTarget[$0.id], + let description = productMap[product.id] else + { + throw InternalError("macro product not found for \($0)") + } + + return description.buildDescription + } var generateTestObservation = false if target.type == .test && shouldGenerateTestObservation { @@ -389,7 +385,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { prebuildCommandResults: prebuildCommandResults[target.id] ?? [], requiredMacroProducts: requiredMacroProducts, shouldGenerateTestObservation: generateTestObservation, - disableSandbox: self.disableSandbox, + shouldDisableSandbox: self.shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -441,19 +437,23 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Plan the derived test targets, if necessary. - if productsBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { + if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { let derivedTestTargets = try Self.makeDerivedTestTargets( - productsBuildParameters, - graph, - self.disableSandbox, + testProducts: productMap.values.filter { + $0.product.type == .test + }, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, + shouldDisableSandbox: self.shouldDisableSandbox, self.fileSystem, self.observabilityScope ) for item in derivedTestTargets { var derivedTestTargets = [item.entryPointTargetBuildDescription.target] - targetMap[item.entryPointTargetBuildDescription.target.id] = - .swift(item.entryPointTargetBuildDescription) + targetMap[item.entryPointTargetBuildDescription.target.id] = .swift( + item.entryPointTargetBuildDescription + ) if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription { targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription) @@ -582,7 +582,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { arguments.append("-l" + replProductName) // The graph should have the REPL product. - assert(self.graph.allProducts.first(where: { $0.name == replProductName }) != nil) + assert(self.graph.product(for: replProductName, destination: .destination) != nil) // Add the search path to the directory containing the modulemap file. for target in self.targets { @@ -687,7 +687,7 @@ extension Basics.Diagnostic { extension BuildParameters { /// Returns a named bundle's path inside the build directory. func bundlePath(named name: String) -> AbsolutePath { - buildPath.appending(component: name + self.triple.nsbundleExtension) + self.buildPath.appending(component: name + self.triple.nsbundleExtension) } } diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index fcc51aed76d..9412aa38590 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(Build BuildDescription/ClangTargetBuildDescription.swift BuildDescription/PluginDescription.swift BuildDescription/ProductBuildDescription.swift + BuildDescription/ResolvedModule+BuildDescription.swift BuildDescription/SwiftTargetBuildDescription.swift BuildDescription/TargetBuildDescription.swift BuildManifest/LLBuildManifestBuilder.swift diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index 0d080d7d3c3..81b5e5ac3ba 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -72,6 +72,7 @@ struct DumpSymbolGraph: SwiftCommand { let result = try symbolGraphExtractor.extractSymbolGraph( module: target, buildPlan: buildPlan, + buildParameters: buildPlan.destinationBuildParameters, outputRedirection: .collect(redirectStderr: true), outputDirectory: symbolGraphDirectory, verboseOutput: swiftCommandState.logLevel <= .info diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 6f7c95bce4c..fedf4df39d6 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -14,7 +14,9 @@ import ArgumentParser import Basics import CoreCommands import Dispatch + import PackageGraph + import PackageModel import enum TSCBasic.ProcessEnv @@ -320,11 +322,11 @@ struct PluginCommand: SwiftCommand { let buildSystem = try swiftCommandState.createBuildSystem( explicitBuildSystem: .native, cacheBuildManifest: false, - // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan - // knows to compile build tool plugin dependencies for the host but does not do the same for command - // plugins. - productsBuildParameters: buildParameters + productsBuildParameters: swiftCommandState.productsBuildParameters, + toolsBuildParameters: buildParameters, + packageGraphLoader: { packageGraph } ) + let accessibleTools = try plugin.processAccessibleTools( packageGraph: packageGraph, fileSystem: swiftCommandState.fileSystem, @@ -332,8 +334,10 @@ struct PluginCommand: SwiftCommand { for: try pluginScriptRunner.hostTriple ) { name, _ in // Build the product referenced by the tool, and add the executable to the tool map. Product dependencies are not supported within a package, so if the tool happens to be from the same package, we instead find the executable that corresponds to the product. There is always one, because of autogeneration of implicit executables with the same name as the target if there isn't an explicit one. - try buildSystem.build(subset: .product(name)) - if let builtTool = try buildSystem.buildPlan.buildProducts.first(where: { $0.product.name == name }) { + try buildSystem.build(subset: .product(name, for: .host)) + if let builtTool = try buildSystem.buildPlan.buildProducts.first(where: { + $0.product.name == name && $0.buildParameters.destination == .host + }) { return try builtTool.binaryPath } else { return nil diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index 72f1afa1017..05e8f7dac06 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -96,7 +96,7 @@ struct SnippetCard: Card { let buildSystem = try swiftCommandState.createBuildSystem(explicitProduct: snippet.name) try buildSystem.build(subset: .product(snippet.name)) let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: snippet.name) - if let exampleTarget = try buildSystem.getPackageGraph().allTargets.first(where: { $0.name == snippet.name }) { + if let exampleTarget = try buildSystem.getPackageGraph().target(for: snippet.name, destination: .destination) { try ProcessEnv.chdir(exampleTarget.sources.paths[0].parentDirectory) } try exec(path: executablePath.pathString, args: []) diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index 145bfd93af4..eeec6f315c1 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -218,8 +218,11 @@ public struct SwiftRunCommand: AsyncSwiftCommand { /// Returns the path to the correct executable based on options. private func findProductName(in graph: ModulesGraph) throws -> String { if let executable = options.executable { - let executableExists = graph.allProducts.contains { ($0.type == .executable || $0.type == .snippet) && $0.name == executable } - guard executableExists else { + // There should be only one product with the given name in the graph + // and it should be executable or snippet. + guard let product = graph.product(for: executable, destination: .destination), + product.type == .executable || product.type == .snippet + else { throw RunError.executableNotFound(executable) } return executable diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index b955cf4d7c4..7c946d00ae9 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -236,11 +236,11 @@ public struct SwiftTestCommand: AsyncSwiftCommand { throw TestError.xcodeNotInstalled } - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) // Remove test output from prior runs and validate priors. - if self.options.enableExperimentalTestOutput && buildParameters.triple.supportsTestSummary { - _ = try? localFileSystem.removeFileTree(buildParameters.testOutputPath) + if self.options.enableExperimentalTestOutput && productsBuildParameters.triple.supportsTestSummary { + _ = try? localFileSystem.removeFileTree(productsBuildParameters.testOutputPath) } let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .xctest) @@ -249,7 +249,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { try await runTestProducts( testProducts, additionalArguments: xctestArgs, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, swiftCommandState: swiftCommandState, library: .xctest ) @@ -276,7 +276,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(productsBuildParameters.codeCovPath) } // Run the tests using the parallel runner. @@ -286,7 +286,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { toolchain: toolchain, numJobs: options.numberOfWorkers ?? ProcessInfo.processInfo.activeProcessorCount, buildOptions: globalOptions.build, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, shouldOutputSuccess: swiftCommandState.logLevel <= .info, observabilityScope: swiftCommandState.observabilityScope ) @@ -295,7 +295,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { try generateXUnitOutputIfRequested(for: testResults, swiftCommandState: swiftCommandState) - // process code Coverage if request + // Process code coverage if requested if self.options.enableCodeCoverage, runner.ranSuccessfully { try await processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: .xctest) } @@ -305,7 +305,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { } if self.options.enableExperimentalTestOutput, !runner.ranSuccessfully { - try Self.handleTestOutput(buildParameters: buildParameters, packagePath: testProducts[0].packagePath) + try Self.handleTestOutput(productsBuildParameters: productsBuildParameters, packagePath: testProducts[0].packagePath) } } } @@ -366,13 +366,13 @@ public struct SwiftTestCommand: AsyncSwiftCommand { // MARK: - swift-testing private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) async throws { - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .swiftTesting) let additionalArguments = Array(CommandLine.arguments.dropFirst()) try await runTestProducts( testProducts, additionalArguments: additionalArguments, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, swiftCommandState: swiftCommandState, library: .swiftTesting ) @@ -408,20 +408,20 @@ public struct SwiftTestCommand: AsyncSwiftCommand { private func runTestProducts( _ testProducts: [BuiltTestProduct], additionalArguments: [String], - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library ) async throws { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(productsBuildParameters.codeCovPath) } let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + buildParameters: productsBuildParameters, sanitizers: globalOptions.build.sanitizers, library: library ) @@ -451,17 +451,17 @@ public struct SwiftTestCommand: AsyncSwiftCommand { } if self.options.enableExperimentalTestOutput, !ranSuccessfully { - try Self.handleTestOutput(buildParameters: buildParameters, packagePath: testProducts[0].packagePath) + try Self.handleTestOutput(productsBuildParameters: productsBuildParameters, packagePath: testProducts[0].packagePath) } } - private static func handleTestOutput(buildParameters: BuildParameters, packagePath: AbsolutePath) throws { - guard localFileSystem.exists(buildParameters.testOutputPath) else { + private static func handleTestOutput(productsBuildParameters: BuildParameters, packagePath: AbsolutePath) throws { + guard localFileSystem.exists(productsBuildParameters.testOutputPath) else { print("No existing test output found.") return } - let lines = try String(contentsOfFile: buildParameters.testOutputPath.pathString).components(separatedBy: "\n") + let lines = try String(contentsOfFile: productsBuildParameters.testOutputPath.pathString).components(separatedBy: "\n") let events = try lines.map { try JSONDecoder().decode(TestEventRecord.self, from: $0) } let caseEvents = events.compactMap { $0.caseEvent } @@ -505,10 +505,10 @@ public struct SwiftTestCommand: AsyncSwiftCommand { // Merge all the profraw files to produce a single profdata file. try mergeCodeCovRawDataFiles(swiftCommandState: swiftCommandState, library: library) - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) for product in testProducts { // Export the codecov data as JSON. - let jsonPath = buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) + let jsonPath = productsBuildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) try exportCodeCovAsJSON(to: jsonPath, testBinary: product.binaryPath, swiftCommandState: swiftCommandState, library: library) } } @@ -519,18 +519,18 @@ public struct SwiftTestCommand: AsyncSwiftCommand { let llvmProf = try swiftCommandState.getTargetToolchain().getLLVMProf() // Get the profraw files. - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) - let codeCovFiles = try swiftCommandState.fileSystem.getDirectoryContents(buildParameters.codeCovPath) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let codeCovFiles = try swiftCommandState.fileSystem.getDirectoryContents(productsBuildParameters.codeCovPath) // Construct arguments for invoking the llvm-prof tool. var args = [llvmProf.pathString, "merge", "-sparse"] for file in codeCovFiles { - let filePath = buildParameters.codeCovPath.appending(component: file) + let filePath = productsBuildParameters.codeCovPath.appending(component: file) if filePath.extension == "profraw" { args.append(filePath.pathString) } } - args += ["-o", buildParameters.codeCovDataFile.pathString] + args += ["-o", productsBuildParameters.codeCovDataFile.pathString] try TSCBasic.Process.checkNonZeroExit(arguments: args) } @@ -544,11 +544,11 @@ public struct SwiftTestCommand: AsyncSwiftCommand { ) throws { // Export using the llvm-cov tool. let llvmCov = try swiftCommandState.getTargetToolchain().getLLVMCov() - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) let args = [ llvmCov.pathString, "export", - "-instr-profile=\(buildParameters.codeCovDataFile)", + "-instr-profile=\(productsBuildParameters.codeCovDataFile)", testBinary.pathString ] let result = try TSCBasic.Process.popen(arguments: args) @@ -567,10 +567,11 @@ public struct SwiftTestCommand: AsyncSwiftCommand { swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library ) throws -> [BuiltTestProduct] { - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) return try Commands.buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, testProduct: self.options.sharedOptions.testProduct ) } @@ -620,8 +621,8 @@ extension SwiftTestCommand { guard let rootManifest = rootManifests.values.first else { throw StringError("invalid manifests at \(root.packages)") } - let buildParameters = try swiftCommandState.buildParametersForTest(enableCodeCoverage: true, library: .xctest) - print(buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName)) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(enableCodeCoverage: true, library: .xctest) + print(productsBuildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName)) } } @@ -632,7 +633,7 @@ extension SwiftTestCommand { func run(_ swiftCommandState: SwiftCommandState) throws { try SwiftTestCommand.handleTestOutput( - buildParameters: try swiftCommandState.productsBuildParameters, + productsBuildParameters: try swiftCommandState.productsBuildParameters, packagePath: localFileSystem.currentWorkingDirectory ?? .root // by definition runs in the current working directory ) } @@ -660,12 +661,16 @@ extension SwiftTestCommand { // MARK: - XCTest private func xctestRun(_ swiftCommandState: SwiftCommandState) throws { - let buildParameters = try swiftCommandState.buildParametersForTest( + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest( enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .xctest ) - let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, buildParameters: buildParameters) + let testProducts = try buildTestsIfNeeded( + swiftCommandState: swiftCommandState, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters + ) let testSuites = try TestingSupport.getTestSuites( in: testProducts, swiftCommandState: swiftCommandState, @@ -684,20 +689,21 @@ extension SwiftTestCommand { // MARK: - swift-testing private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) throws { - let buildParameters = try swiftCommandState.buildParametersForTest( + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest( enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .swiftTesting ) let testProducts = try buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters ) let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + buildParameters: productsBuildParameters, sanitizers: globalOptions.build.sanitizers, library: .swiftTesting ) @@ -737,11 +743,13 @@ extension SwiftTestCommand { private func buildTestsIfNeeded( swiftCommandState: SwiftCommandState, - buildParameters: BuildParameters + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters ) throws -> [BuiltTestProduct] { return try Commands.buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, testProduct: self.sharedOptions.testProduct ) } @@ -934,7 +942,7 @@ final class ParallelTestRunner { private let toolchain: UserToolchain private let buildOptions: BuildOptions - private let buildParameters: BuildParameters + private let productsBuildParameters: BuildParameters /// Number of tests to execute in parallel. private let numJobs: Int @@ -951,7 +959,7 @@ final class ParallelTestRunner { toolchain: UserToolchain, numJobs: Int, buildOptions: BuildOptions, - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, shouldOutputSuccess: Bool, observabilityScope: ObservabilityScope ) { @@ -978,7 +986,7 @@ final class ParallelTestRunner { } self.buildOptions = buildOptions - self.buildParameters = buildParameters + self.productsBuildParameters = productsBuildParameters assert(numJobs > 0, "num jobs should be > 0") } @@ -1008,7 +1016,7 @@ final class ParallelTestRunner { let testEnv = try TestingSupport.constructTestEnvironment( toolchain: self.toolchain, - buildParameters: self.buildParameters, + buildParameters: self.productsBuildParameters, sanitizers: self.buildOptions.sanitizers, library: .xctest // swift-testing does not use ParallelTestRunner ) @@ -1076,7 +1084,7 @@ final class ParallelTestRunner { // Print test results. for test in processedTests.get() { - if (!test.success || shouldOutputSuccess) && !buildParameters.testingParameters.experimentalTestOutput { + if (!test.success || shouldOutputSuccess) && !productsBuildParameters.testingParameters.experimentalTestOutput { // command's result output goes on stdout // ie "swift test" should output to stdout print(test.output) @@ -1295,7 +1303,7 @@ extension SwiftCommandState { func buildParametersForTest( options: TestCommandOptions, library: BuildParameters.Testing.Library - ) throws -> BuildParameters { + ) throws -> (productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters) { var result = try self.buildParametersForTest( enableCodeCoverage: options.enableCodeCoverage, enableTestability: options.enableTestableImports, @@ -1304,7 +1312,8 @@ extension SwiftCommandState { library: library ) if try options.testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: self) { - result.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] + result.productsBuildParameters.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] + result.toolsBuildParameters.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] } return result } @@ -1364,12 +1373,21 @@ private extension Basics.Diagnostic { /// - Returns: The paths to the build test products. private func buildTestsIfNeeded( swiftCommandState: SwiftCommandState, - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, testProduct: String? ) throws -> [BuiltTestProduct] { - let buildSystem = try swiftCommandState.createBuildSystem(productsBuildParameters: buildParameters) + let buildSystem = try swiftCommandState.createBuildSystem( + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters + ) + + let subset: BuildSubset = if let testProduct { + .product(testProduct) + } else { + .allIncludingTests + } - let subset = testProduct.map(BuildSubset.product) ?? .allIncludingTests try buildSystem.build(subset: subset) // Find the test product. diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index 2d88fbc471e..25dc4b99853 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -385,12 +385,23 @@ final class PluginDelegate: PluginInvocationDelegate { // Find the target in the build operation's package graph; it's an error if we don't find it. let packageGraph = try buildSystem.getPackageGraph() - guard let target = packageGraph.allTargets.first(where: { $0.name == targetName }) else { + guard let target = packageGraph.target(for: targetName, destination: .destination) else { throw StringError("could not find a target named “\(targetName)”") } + // FIXME: This is currently necessary because `target(for:destination:)` can + // produce a module that is targeting host when `targetName`` corresponds to + // a macro, plugin, or a test. Ideally we'd ask a build system for a`BuildSubset` + // and get the destination from there but there are other places that need + // refactoring in that way as well. + let buildParameters = if target.buildTriple == .tools { + try swiftCommandState.toolsBuildParameters + } else { + try swiftCommandState.productsBuildParameters + } + // Build the target, if needed. - try buildSystem.build(subset: .target(target.name)) + try buildSystem.build(subset: .target(target.name, for: buildParameters.destination)) // Configure the symbol graph extractor. var symbolGraphExtractor = try SymbolGraphExtract( @@ -419,7 +430,7 @@ final class PluginDelegate: PluginInvocationDelegate { guard let package = packageGraph.package(for: target) else { throw StringError("could not determine the package for target “\(target.name)”") } - let outputDir = try buildSystem.buildPlan.toolsBuildParameters.dataPath.appending( + let outputDir = try buildParameters.dataPath.appending( components: "extracted-symbols", package.identity.description, target.name @@ -430,6 +441,7 @@ final class PluginDelegate: PluginInvocationDelegate { let result = try symbolGraphExtractor.extractSymbolGraph( module: target, buildPlan: try buildSystem.buildPlan, + buildParameters: buildParameters, outputRedirection: .collect, outputDirectory: outputDir, verboseOutput: self.swiftCommandState.logLevel <= .info diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index 20f85ef6c16..6b8774078ea 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -56,17 +56,17 @@ public struct SymbolGraphExtract { public func extractSymbolGraph( module: ResolvedModule, buildPlan: BuildPlan, + buildParameters: BuildParameters, outputRedirection: TSCBasic.Process.OutputRedirection = .none, outputDirectory: AbsolutePath, verboseOutput: Bool ) throws -> ProcessResult { - let buildParameters = buildPlan.buildParameters(for: module) try self.fileSystem.createDirectory(outputDirectory, recursive: true) // Construct arguments for extracting symbols for a single target. var commandLine = [self.tool.pathString] commandLine += ["-module-name", module.c99name] - commandLine += try buildParameters.targetTripleArgs(for: module) + commandLine += try buildParameters.tripleArgs(for: module) commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true) commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString] if verboseOutput { diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 2b2e67ac120..d4314eb0727 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -36,7 +36,9 @@ enum TestingSupport { func findXCTestHelper(swiftBuildPath: AbsolutePath) -> AbsolutePath? { // XCTestHelper tool is installed in libexec. - let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending(components: "libexec", "swift", "pm", "swiftpm-xctest-helper") + let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending( + components: "libexec", "swift", "pm", "swiftpm-xctest-helper" + ) if swiftCommandState.fileSystem.isFile(maybePath) { return maybePath } else { @@ -54,7 +56,10 @@ enum TestingSupport { // This will be true during swiftpm development or when using swift.org toolchains. let xcodePath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcode-select", "--print-path").spm_chomp() - let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--find", "swift-build", environment: ["DEVELOPER_DIR": xcodePath]).spm_chomp() + let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit( + args: "/usr/bin/xcrun", "--find", "swift-build", + environment: ["DEVELOPER_DIR": xcodePath] + ).spm_chomp() if let xctestHelperPath = findXCTestHelper(swiftBuildPath: try AbsolutePath(validating: installedSwiftBuildPath)) { return xctestHelperPath } @@ -115,7 +120,7 @@ enum TestingSupport { shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, library: .xctest - ), + ).productsBuildParameters, sanitizers: sanitizers, library: .xctest ) @@ -131,7 +136,7 @@ enum TestingSupport { enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest - ), + ).productsBuildParameters, sanitizers: sanitizers, library: .xctest ) @@ -218,8 +223,35 @@ extension SwiftCommandState { shouldSkipBuilding: Bool = false, experimentalTestOutput: Bool = false, library: BuildParameters.Testing.Library - ) throws -> BuildParameters { - var parameters = try self.productsBuildParameters + ) throws -> (productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters) { + let productsBuildParameters = buildParametersForTest( + modifying: try productsBuildParameters, + enableCodeCoverage: enableCodeCoverage, + enableTestability: enableTestability, + shouldSkipBuilding: shouldSkipBuilding, + experimentalTestOutput: experimentalTestOutput, + library: library + ) + let toolsBuildParameters = buildParametersForTest( + modifying: try toolsBuildParameters, + enableCodeCoverage: enableCodeCoverage, + enableTestability: enableTestability, + shouldSkipBuilding: shouldSkipBuilding, + experimentalTestOutput: experimentalTestOutput, + library: library + ) + return (productsBuildParameters, toolsBuildParameters) + } + + private func buildParametersForTest( + modifying parameters: BuildParameters, + enableCodeCoverage: Bool, + enableTestability: Bool?, + shouldSkipBuilding: Bool, + experimentalTestOutput: Bool, + library: BuildParameters.Testing.Library + ) -> BuildParameters { + var parameters = parameters var explicitlyEnabledDiscovery = false var explicitlySpecifiedPath: AbsolutePath? diff --git a/Sources/Commands/Utilities/XCTEvents.swift b/Sources/Commands/Utilities/XCTEvents.swift index a264b205e3a..0ceedfce77f 100644 --- a/Sources/Commands/Utilities/XCTEvents.swift +++ b/Sources/Commands/Utilities/XCTEvents.swift @@ -237,12 +237,12 @@ extension TestErrorInfo { extension TestIssue { init(_ issue: XCTIssue) { self.init( - type: .init(destinationBuildParameters: issue.type), + type: .init(defaultBuildParameters: issue.type), compactDescription: issue.compactDescription, detailedDescription: issue.detailedDescription, - associatedError: issue.associatedError.map { .init(destinationBuildParameters: $0) }, - sourceCodeContext: .init(destinationBuildParameters: issue.sourceCodeContext), - attachments: issue.attachments.map { .init(destinationBuildParameters: $0) } + associatedError: issue.associatedError.map { .init(defaultBuildParameters: $0) }, + sourceCodeContext: .init(defaultBuildParameters: issue.sourceCodeContext), + attachments: issue.attachments.map { .init(defaultBuildParameters: $0) } ) } } @@ -275,8 +275,8 @@ extension TestLocation { extension TestSourceCodeContext { init(_ context: XCTSourceCodeContext) { self.init( - callStack: context.callStack.map { .init(destinationBuildParameters: $0) }, - location: context.location.map { .init(destinationBuildParameters: $0) } + callStack: context.callStack.map { .init(defaultBuildParameters: $0) }, + location: context.location.map { .init(defaultBuildParameters: $0) } ) } } @@ -285,8 +285,8 @@ extension TestSourceCodeFrame { init(_ frame: XCTSourceCodeFrame) { self.init( address: frame.address, - symbolInfo: (try? frame.symbolInfo()).map { .init(destinationBuildParameters: $0) }, - symbolicationError: frame.symbolicationError.map { .init(destinationBuildParameters: $0) } + symbolInfo: (try? frame.symbolInfo()).map { .init(defaultBuildParameters: $0) }, + symbolicationError: frame.symbolicationError.map { .init(defaultBuildParameters: $0) } ) } } @@ -296,7 +296,7 @@ extension TestSourceCodeSymbolInfo { self.init( imageName: symbolInfo.imageName, symbolName: symbolInfo.symbolName, - location: symbolInfo.location.map { .init(destinationBuildParameters: $0) } + location: symbolInfo.location.map { .init(defaultBuildParameters: $0) } ) } } diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 6106759a4e7..5d7d82e262f 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -49,6 +49,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { workDirectory: try self.swiftCommandState.getActiveWorkspace().location.pluginWorkingDirectory, disableSandbox: self.swiftCommandState.shouldDisableSandbox ), + scratchDirectory: self.swiftCommandState.scratchDirectory, additionalFileRules: FileRuleDescription.swiftpmFileTypes, pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories, dependenciesByRootPackageIdentity: rootPackageInfo.dependencies, diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 52b9d8575e1..0130a7bdcd6 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -726,7 +726,10 @@ public final class SwiftCommandState { when building on macOS. """ - private func _buildParams(toolchain: UserToolchain) throws -> BuildParameters { + private func _buildParams( + toolchain: UserToolchain, + destination: BuildParameters.Destination + ) throws -> BuildParameters { let triple = toolchain.targetTriple let dataPath = self.scratchDirectory.appending( @@ -738,6 +741,7 @@ public final class SwiftCommandState { } return try BuildParameters( + destination: destination, dataPath: dataPath, configuration: options.build.configuration, toolchain: toolchain, @@ -797,7 +801,7 @@ public final class SwiftCommandState { private lazy var _toolsBuildParameters: Result = { Result(catching: { - try _buildParams(toolchain: self.getHostToolchain()) + try _buildParams(toolchain: self.getHostToolchain(), destination: .host) }) }() @@ -809,7 +813,7 @@ public final class SwiftCommandState { private lazy var _productsBuildParameters: Result = { Result(catching: { - try _buildParams(toolchain: self.getTargetToolchain()) + try _buildParams(toolchain: self.getTargetToolchain(), destination: .target) }) }() diff --git a/Sources/PackageGraph/BuildTriple.swift b/Sources/PackageGraph/BuildTriple.swift index 4e121a2c7bb..a930d8255c0 100644 --- a/Sources/PackageGraph/BuildTriple.swift +++ b/Sources/PackageGraph/BuildTriple.swift @@ -10,13 +10,36 @@ // //===----------------------------------------------------------------------===// +import class PackageModel.Target +import class PackageModel.Product + /// Triple for which code should be compiled for. /// > Note: We're not using "host" and "target" triple terminology in this enum, as that clashes with build /// > system "targets" and can lead to confusion in this context. -public enum BuildTriple { +public enum BuildTriple: String { /// Triple for which build tools are compiled (the host triple). - case tools + case tools = "tools" /// Triple of the destination platform for which end products are compiled (the target triple). - case destination + case destination = "destination" +} + +extension Target { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } +} + +extension Product { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } } diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index 6a1695a0a4d..b95b1eb9feb 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -35,7 +35,7 @@ add_library(PackageGraph Resolution/PlatformVersionProvider.swift Resolution/ResolvedPackage.swift Resolution/ResolvedProduct.swift - Resolution/ResolvedTarget.swift + Resolution/ResolvedModule.swift Version+Extensions.swift VersionSetSpecifier.swift) target_link_libraries(PackageGraph PUBLIC diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index a324b283e95..c3df08e343a 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -197,16 +197,16 @@ private func checkAllDependenciesAreUsed( ) { for package in rootPackages { // List all dependency products dependent on by the package targets. - let productDependencies = IdentifiableSet(package.targets.flatMap({ target in - return target.dependencies.compactMap({ targetDependency in + let productDependencies = IdentifiableSet(package.targets.flatMap { target in + return target.dependencies.compactMap { targetDependency in switch targetDependency { case .product(let product, _): return product case .target: return nil } - }) - })) + } + }) for dependencyId in package.dependencies { guard let dependency = packages[dependencyId] else { @@ -239,7 +239,12 @@ private func checkAllDependenciesAreUsed( ) // Otherwise emit a warning if none of the dependency package's products are used. - let dependencyIsUsed = dependency.products.contains(where: { productDependencies.contains(id: $0.id) }) + let dependencyIsUsed = dependency.products.contains { product in + // Don't compare by product ID, but by product name to make sure both build triples as properties of + // `ResolvedProduct.ID` are allowed. + productDependencies.contains { $0.name == product.name } + } + if !dependencyIsUsed && !observabilityScope.errorsReportedInAnyScope { packageDiagnosticsScope.emit(.unusedDependency(dependency.identity.description)) } @@ -297,7 +302,10 @@ private func createResolvedPackages( // Resolve module aliases, if specified, for targets and their dependencies // across packages. Aliasing will result in target renaming. - let moduleAliasingUsed = try resolveModuleAliases(packageBuilders: packageBuilders, observabilityScope: observabilityScope) + let moduleAliasingUsed = try resolveModuleAliases( + packageBuilders: packageBuilders, + observabilityScope: observabilityScope + ) // Scan and validate the dependencies for packageBuilder in packageBuilders { @@ -662,7 +670,7 @@ private func createResolvedPackages( observabilityScope.emit( ModuleError.duplicateModule( targetName: entry.key, - packages: entry.value.map{ $0.identity }) + packages: entry.value.map { $0.identity }) ) } } @@ -1003,7 +1011,7 @@ private final class ResolvedTargetBuilder: ResolvedBuilder { } } - return ResolvedTarget( + return ResolvedModule( packageIdentity: self.packageIdentity, underlying: self.target, dependencies: dependencies, @@ -1089,13 +1097,17 @@ private final class ResolvedPackageBuilder: ResolvedBuilder { } override func constructImpl() throws -> ResolvedPackage { - return ResolvedPackage( + let products = try self.products.map { try $0.construct() } + var targets = products.reduce(into: IdentifiableSet()) { $0.formUnion($1.targets) } + try targets.formUnion(self.targets.map { try $0.construct() }) + + return try ResolvedPackage( underlying: self.package, defaultLocalization: self.defaultLocalization, supportedPlatforms: self.supportedPlatforms, dependencies: self.dependencies.map { $0.package.identity }, - targets: try self.targets.map{ try $0.construct() }, - products: try self.products.map{ try $0.construct() }, + targets: targets, + products: products, registryMetadata: self.registryMetadata, platformVersionProvider: self.platformVersionProvider ) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 2af4f421e54..8d0804aa79a 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -65,13 +65,13 @@ public struct ModulesGraph { public let packages: IdentifiableSet /// The list of all targets reachable from root targets. - public let reachableTargets: IdentifiableSet + public private(set) var reachableTargets: IdentifiableSet /// The list of all products reachable from root targets. - public let reachableProducts: IdentifiableSet + public private(set) var reachableProducts: IdentifiableSet /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. - public let allTargets: IdentifiableSet + public private(set) var allTargets: IdentifiableSet /// Returns all targets within the module graph in topological order, starting with low-level targets (that have no /// dependencies). @@ -82,8 +82,7 @@ public struct ModulesGraph { } /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. - - public let allProducts: IdentifiableSet + public private(set) var allProducts: IdentifiableSet /// Package dependencies required for a fully resolved graph. /// @@ -94,10 +93,14 @@ public struct ModulesGraph { /// Returns true if a given target is present in root packages and is not excluded for the given build environment. public func isInRootPackages(_ target: ResolvedModule, satisfying buildEnvironment: BuildEnvironment) -> Bool { // FIXME: This can be easily cached. - return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in + return rootPackages.reduce( + into: IdentifiableSet() + ) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in let allDependencies = package.targets.flatMap { $0.dependencies } let unsatisfiedDependencies = allDependencies.filter { !$0.satisfies(buildEnvironment) } - let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedModule.Dependency) -> ResolvedModule? in + let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { ( + dep: ResolvedModule.Dependency + ) -> ResolvedModule? in switch dep { case .target(let target, _): return target @@ -135,6 +138,60 @@ public struct ModulesGraph { package.dependencies.compactMap { self.package(for: $0) } } + public func product(for name: String, destination: BuildTriple) -> ResolvedProduct? { + func findProduct(name: String, destination: BuildTriple) -> ResolvedProduct? { + self.allProducts.first { $0.name == name && $0.buildTriple == destination } + } + + if let product = findProduct(name: name, destination: destination) { + return product + } + + // FIXME: This is a temporary workaround and needs to be handled by the callers. + + // It's possible to request a build of a macro, a plugin, or a test via `swift build` + // which won't have the right destination set because it's impossible to indicate it. + // + // Same happens with `--test-product` - if one of the test targets directly references + // a macro then all if its targets and the product itself become `host`. + if destination == .destination { + if let toolsProduct = findProduct(name: name, destination: .tools), + toolsProduct.type == .macro || toolsProduct.type == .plugin || toolsProduct.type == .test + { + return toolsProduct + } + } + + return nil + } + + public func target(for name: String, destination: BuildTriple) -> ResolvedModule? { + func findModule(name: String, destination: BuildTriple) -> ResolvedModule? { + self.allTargets.first { $0.name == name && $0.buildTriple == destination } + } + + if let module = findModule(name: name, destination: destination) { + return module + } + + // FIXME: This is a temporary workaround and needs to be handled by the callers. + + // It's possible to request a build of a macro, a plugin or a test via `swift build` + // which won't have the right destination set because it's impossible to indicate it. + // + // Same happens with `--test-product` - if one of the test targets directly references + // a macro then all if its targets and the product itself become `host`. + if destination == .destination { + if let toolsModule = findModule(name: name, destination: .tools), + toolsModule.type == .macro || toolsModule.type == .plugin || toolsModule.type == .test + { + return toolsModule + } + } + + return nil + } + /// All root and root dependency packages provided as input to the graph. public let inputPackages: [ResolvedPackage] @@ -155,29 +212,78 @@ public struct ModulesGraph { self.binaryArtifacts = binaryArtifacts self.packages = packages - let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedModule] in + var allTargets = IdentifiableSet() + var allProducts = IdentifiableSet() + for package in self.packages { + let targetsToInclude: [ResolvedModule] if rootPackages.contains(id: package.id) { - return package.targets + targetsToInclude = Array(package.targets) } else { // Don't include tests targets from non-root packages so swift-test doesn't // try to run them. - return package.targets.filter({ $0.type != .test }) + targetsToInclude = package.targets.filter { $0.type != .test } + } + + for target in targetsToInclude { + allTargets.insert(target) + + // Explicitly include dependencies of host tools in the maps of all targets or all products + if target.buildTriple == .tools { + for dependency in try target.recursiveDependencies() { + switch dependency { + case .target(let targetDependency, _): + allTargets.insert(targetDependency) + case .product(let productDependency, _): + allProducts.insert(productDependency) + } + } + } + + // Create a new executable product if plugin depends on an executable target. + // This is necessary, even though PackageBuilder creates one already, because + // that product is going to be built for `destination`, and this one has to + // be built for `tools`. + if target.underlying is PluginTarget { + for dependency in target.dependencies { + switch dependency { + case .product(_, conditions: _): + break + + case .target(let target, conditions: _): + if target.type != .executable { + continue + } + + var product = try ResolvedProduct( + packageIdentity: target.packageIdentity, + product: .init( + package: target.packageIdentity, + name: target.name, + type: .executable, + targets: [target.underlying] + ), + targets: IdentifiableSet([target]) + ) + product.buildTriple = .tools + + allProducts.insert(product) + } + } + } } - })) - let allProducts = IdentifiableSet(packages.flatMap({ package -> [ResolvedProduct] in if rootPackages.contains(id: package.id) { - return package.products + allProducts.formUnion(package.products) } else { - // Don't include tests products from non-root packages so swift-test doesn't + // Don't include test products from non-root packages so swift-test doesn't // try to run them. - return package.products.filter({ $0.type != .test }) + allProducts.formUnion(package.products.filter { $0.type != .test }) } - })) + } // Compute the reachable targets and products. - let inputTargets = inputPackages.flatMap { $0.targets } - let inputProducts = inputPackages.flatMap { $0.products } + let inputTargets = self.inputPackages.flatMap { $0.targets } + let inputProducts = self.inputPackages.flatMap { $0.products } let recursiveDependencies = try inputTargets.lazy.flatMap { try $0.recursiveDependencies() } self.reachableTargets = IdentifiableSet(inputTargets).union(recursiveDependencies.compactMap { $0.target }) diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedModule.swift similarity index 78% rename from Sources/PackageGraph/Resolution/ResolvedTarget.swift rename to Sources/PackageGraph/Resolution/ResolvedModule.swift index b8854a8ba3b..f218a35886d 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedModule.swift @@ -136,7 +136,7 @@ public struct ResolvedModule { public let underlying: Target /// The dependencies of this target. - public let dependencies: [Dependency] + public internal(set) var dependencies: [Dependency] /// The default localization for resources. public let defaultLocalization: String? @@ -147,7 +147,11 @@ public struct ResolvedModule { private let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. - public let buildTriple: BuildTriple + public package(set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// Create a resolved target instance. public init( @@ -164,7 +168,50 @@ public struct ResolvedModule { self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms self.platformVersionProvider = platformVersionProvider - self.buildTriple = .destination + + if underlying.type == .test { + // Make sure that test products are built for the tools triple if it has tools as direct dependencies. + // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros + // and SwiftSyntax to be built for the same triple as the tests. + // See https://github.com/apple/swift-package-manager/pull/7349 for more context. + var inferredBuildTriple = BuildTriple.destination + loop: for dependency in dependencies { + switch dependency { + case .target(let targetDependency, _): + if targetDependency.type == .macro { + inferredBuildTriple = .tools + break loop + } + case .product(let productDependency, _): + if productDependency.type == .macro { + inferredBuildTriple = .tools + break loop + } + } + } + self.buildTriple = inferredBuildTriple + } else { + self.buildTriple = underlying.buildTriple + } + self.updateBuildTriplesOfDependencies() + } + + mutating func updateBuildTriplesOfDependencies() { + if self.buildTriple == .tools { + for (i, dependency) in dependencies.enumerated() { + let updatedDependency: Dependency + switch dependency { + case .target(var target, let conditions): + target.buildTriple = self.buildTriple + updatedDependency = .target(target, conditions: conditions) + case .product(var product, let conditions): + product.buildTriple = self.buildTriple + updatedDependency = .product(product, conditions: conditions) + } + + dependencies[i] = updatedDependency + } + } } public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { @@ -178,13 +225,13 @@ public struct ResolvedModule { extension ResolvedModule: CustomStringConvertible { public var description: String { - return "" + return "" } } extension ResolvedModule.Dependency: CustomStringConvertible { public var description: String { - var str = " /// The products produced by the package. public let products: [ResolvedProduct] @@ -58,14 +58,14 @@ public struct ResolvedPackage { defaultLocalization: String?, supportedPlatforms: [SupportedPlatform], dependencies: [PackageIdentity], - targets: [ResolvedModule], + targets: IdentifiableSet, products: [ResolvedProduct], registryMetadata: RegistryReleaseMetadata?, platformVersionProvider: PlatformVersionProvider ) { self.underlying = underlying - self.targets = targets self.products = products + self.targets = targets self.dependencies = dependencies self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index c03e20fffa2..3217ef8d9ea 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -30,7 +30,7 @@ public struct ResolvedProduct { public let underlying: Product /// The top level targets contained in this product. - public let targets: IdentifiableSet + public internal(set) var targets: IdentifiableSet /// Executable target for test entry point file. public let testEntryPointTarget: ResolvedModule? @@ -44,7 +44,11 @@ public struct ResolvedProduct { public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved product should be compiled for. - public let buildTriple: BuildTriple + public internal(set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// The main executable target of product. /// @@ -63,7 +67,11 @@ public struct ResolvedProduct { } } - public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { + public init( + packageIdentity: PackageIdentity, + product: Product, + targets: IdentifiableSet + ) { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product @@ -97,7 +105,41 @@ public struct ResolvedProduct { ) } - self.buildTriple = .destination + if product.type == .test { + // Make sure that test products are built for the tools triple if it has tools as direct dependencies. + // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros + // and SwiftSyntax to be built for the same triple as the tests. + // See https://github.com/apple/swift-package-manager/pull/7349 for more context. + var inferredBuildTriple = BuildTriple.destination + targetsLoop: for target in targets { + for dependency in target.dependencies { + switch dependency { + case .target(let targetDependency, _): + if targetDependency.type == .macro { + inferredBuildTriple = .tools + break targetsLoop + } + case .product(let productDependency, _): + if productDependency.type == .macro { + inferredBuildTriple = .tools + break targetsLoop + } + } + } + } + self.buildTriple = inferredBuildTriple + } else { + self.buildTriple = product.buildTriple + } + self.updateBuildTriplesOfDependencies() + } + + mutating func updateBuildTriplesOfDependencies() { + self.targets = IdentifiableSet(self.targets.map { + var target = $0 + target.buildTriple = self.buildTriple + return target + }) } /// True if this product contains Swift targets. @@ -151,7 +193,7 @@ public struct ResolvedProduct { extension ResolvedProduct: CustomStringConvertible { public var description: String { - "" + "" } } @@ -166,13 +208,13 @@ extension ResolvedProduct { extension ResolvedProduct: Identifiable { /// Resolved target identity that uniquely identifies it in a resolution graph. public struct ID: Hashable { - public let targetName: String + public let productName: String let packageIdentity: PackageIdentity - public let buildTriple: BuildTriple + public var buildTriple: BuildTriple } public var id: ID { - ID(targetName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) + ID(productName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) } } diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index c49b93ba142..b66e5159fa7 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -131,7 +131,7 @@ public class Target: PolymorphicCodableProtocol { /// The name of the target. /// /// NOTE: This name is not the language-level target (i.e., the importable - /// name) name in many cases, instead use c99name if you need uniqueness. + /// name) name in many cases, instead use ``Target/c99name`` if you need uniqueness. public private(set) var name: String /// Module aliases needed to build this target. The key is an original name of a diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index e81e99e242f..4d51ec354c5 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -26,6 +26,18 @@ public struct BuildParameters: Encodable { case auto } + /// The destination for which code should be compiled for. + public enum Destination: Encodable { + /// The destination for which build tools are compiled. + case host + + /// The destination for which end products are compiled. + case target + } + + /// The destination these parameters are going to be used for. + public var destination: Destination + /// The path to the data directory. public var dataPath: AbsolutePath @@ -118,6 +130,7 @@ public struct BuildParameters: Encodable { public var testingParameters: Testing public init( + destination: Destination, dataPath: AbsolutePath, configuration: BuildConfiguration, toolchain: Toolchain, @@ -144,6 +157,7 @@ public struct BuildParameters: Encodable { omitFramePointers: nil ) + self.destination = destination self.dataPath = dataPath self.configuration = configuration self._toolchain = _Toolchain(toolchain: toolchain) @@ -243,18 +257,18 @@ public struct BuildParameters: Encodable { /// Returns the path to the dynamic library of a product for the current build parameters. func potentialDynamicLibraryPath(for product: ResolvedProduct) throws -> RelativePath { - try RelativePath(validating: "\(self.triple.dynamicLibraryPrefix)\(product.name)\(self.triple.dynamicLibraryExtension)") + try RelativePath(validating: "\(self.triple.dynamicLibraryPrefix)\(product.name)\(self.suffix)\(self.triple.dynamicLibraryExtension)") } /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. public func binaryRelativePath(for product: ResolvedProduct) throws -> RelativePath { - let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.triple.executableExtension)") + let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.suffix)\(self.triple.executableExtension)") switch product.type { case .executable, .snippet: return potentialExecutablePath case .library(.static): - return try RelativePath(validating: "lib\(product.name)\(self.triple.staticLibraryExtension)") + return try RelativePath(validating: "lib\(product.name)\(self.suffix)\(self.triple.staticLibraryExtension)") case .library(.dynamic): return try potentialDynamicLibraryPath(for: product) case .library(.automatic), .plugin: @@ -329,3 +343,11 @@ extension Triple { return !self.isWindows() } } + +extension BuildParameters { + /// Suffix appended to build manifest nodes to distinguish nodes created for tools from nodes created for + /// end products, i.e. nodes for host vs target triples. + package var suffix: String { + if destination == .host { "-tool" } else { "" } + } +} diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 9576c25a8aa..280d7f1954a 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -25,10 +25,10 @@ public enum BuildSubset { case allIncludingTests /// Represents a specific product. - case product(String) + case product(String, for: BuildParameters.Destination = .target) /// Represents a specific target. - case target(String) + case target(String, for: BuildParameters.Destination = .target) } /// A protocol that represents a build system used by SwiftPM for all build operations. This allows factoring out the @@ -74,7 +74,7 @@ extension ProductBuildDescription { /// The path to the product binary produced. public var binaryPath: AbsolutePath { get throws { - return try self.buildParameters.binaryPath(for: product) + try self.buildParameters.binaryPath(for: product) } } } @@ -92,28 +92,6 @@ public protocol BuildPlan { func createREPLArguments() throws -> [String] } -extension BuildPlan { - /// Parameters used for building a given target. - public func buildParameters(for target: ResolvedModule) -> BuildParameters { - switch target.buildTriple { - case .tools: - return self.toolsBuildParameters - case .destination: - return self.destinationBuildParameters - } - } - - /// Parameters used for building a given product. - public func buildParameters(for product: ResolvedProduct) -> BuildParameters { - switch product.buildTriple { - case .tools: - return self.toolsBuildParameters - case .destination: - return self.destinationBuildParameters - } - } -} - public protocol BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 2c8562f6f4a..cdbb9735655 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -56,7 +56,7 @@ internal struct PluginContextSerializer { // Adds a target to the serialized structure, if it isn't already there and // if it is of a kind that should be passed to the plugin. If so, this func- // tion returns the target's wire ID. If not, it returns nil. - mutating func serialize(target: ResolvedTarget) throws -> WireInput.Target.Id? { + mutating func serialize(target: ResolvedModule) throws -> WireInput.Target.Id? { // If we've already seen the target, just return the wire ID we already assigned to it. if let id = targetsToWireIDs[target.id] { return id } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 235b89a116c..4c194395370 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -402,8 +402,8 @@ extension ModulesGraph { observabilityScope: ObservabilityScope, fileSystem: FileSystem, builtToolHandler: (_ name: String, _ path: RelativePath) throws -> AbsolutePath? = { _, _ in return nil } - ) throws -> [ResolvedTarget.ID: (target: ResolvedTarget, results: [BuildToolPluginInvocationResult])] { - var pluginResultsByTarget: [ResolvedTarget.ID: (target: ResolvedTarget, results: [BuildToolPluginInvocationResult])] = [:] + ) throws -> [ResolvedModule.ID: (target: ResolvedModule, results: [BuildToolPluginInvocationResult])] { + var pluginResultsByTarget: [ResolvedModule.ID: (target: ResolvedModule, results: [BuildToolPluginInvocationResult])] = [:] for target in self.allTargets.sorted(by: { $0.name < $1.name }) { // Infer plugins from the declared dependencies, and collect them as well as any regular dependencies. Although usage of build tool plugins is declared separately from dependencies in the manifest, in the internal model we currently consider both to be dependencies. var pluginTargets: [PluginTarget] = [] @@ -452,7 +452,7 @@ extension ModulesGraph { let toolPaths = accessibleTools.values.map { $0.path }.sorted() // Assign a plugin working directory based on the package, target, and plugin. - let pluginOutputDir = outputDir.appending(components: package.identity.description, target.name, pluginTarget.name) + let pluginOutputDir = outputDir.appending(components: package.identity.description, target.name, target.buildTriple.rawValue, pluginTarget.name) // Determine the set of directories under which plugins are allowed to write. We always include just the output directory, and for now there is no possibility of opting into others. let writableDirectories = [outputDir] @@ -603,7 +603,7 @@ extension ModulesGraph { } public static func computePluginGeneratedFiles( - target: ResolvedTarget, + target: ResolvedModule, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], buildParameters: BuildParameters, @@ -675,7 +675,7 @@ public extension PluginTarget { executableOrBinaryTarget = target case .product(let productRef, _): guard - let product = packageGraph.allProducts.first(where: { $0.name == productRef.name }), + let product = packageGraph.product(for: productRef.name, destination: .tools), let executableTarget = product.targets.map({ $0.underlying }).executables.spm_only else { throw StringError("no product named \(productRef.name)") @@ -749,7 +749,7 @@ public struct BuildToolPluginInvocationResult { public var package: ResolvedPackage /// The target in that package to which the plugin was applied. - public var target: ResolvedTarget + public var target: ResolvedModule /// If the plugin finished successfully. public var succeeded: Bool diff --git a/Sources/SPMTestSupport/MockArchiver.swift b/Sources/SPMTestSupport/MockArchiver.swift index cf1a84434df..296f0d413a9 100644 --- a/Sources/SPMTestSupport/MockArchiver.swift +++ b/Sources/SPMTestSupport/MockArchiver.swift @@ -12,7 +12,7 @@ import Basics -package class MockArchiver: Archiver { +package final class MockArchiver: Archiver { package typealias ExtractionHandler = ( MockArchiver, AbsolutePath, diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 83662600628..64b3b232592 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -15,6 +15,9 @@ import Basics @_spi(SwiftPMInternal) import Build +import struct PackageGraph.ModulesGraph +import struct PackageGraph.ResolvedModule +import struct PackageGraph.ResolvedProduct import PackageModel import SPMBuildCore import TSCUtility @@ -31,7 +34,7 @@ public struct MockToolchain: PackageModel.Toolchain { public let swiftCompilerPath = AbsolutePath("/fake/path/to/swiftc") public let includeSearchPaths = [AbsolutePath]() public let librarySearchPaths = [AbsolutePath]() - public let swiftResourcesPath: AbsolutePath? = nil + public let swiftResourcesPath: AbsolutePath? public let swiftStaticResourcesPath: AbsolutePath? = nil public let sdkRootPath: AbsolutePath? = nil public let extraFlags = PackageModel.BuildFlags() @@ -50,7 +53,9 @@ public struct MockToolchain: PackageModel.Toolchain { #endif } - public init() {} + public init(swiftResourcesPath: AbsolutePath? = nil) { + self.swiftResourcesPath = swiftResourcesPath + } } extension Basics.Triple { @@ -70,15 +75,16 @@ public let defaultTargetTriple: String = hostTriple.tripleString(forPlatformVers public let defaultTargetTriple: String = hostTriple.tripleString #endif -public func mockBuildParameters( - buildPath: AbsolutePath = "/path/to/build", +package func mockBuildParameters( + destination: BuildParameters.Destination, + buildPath: AbsolutePath? = nil, config: BuildConfiguration = .debug, toolchain: PackageModel.Toolchain = MockToolchain(), flags: PackageModel.BuildFlags = PackageModel.BuildFlags(), shouldLinkStaticSwiftStdlib: Bool = false, shouldDisableLocalRpath: Bool = false, canRenameEntrypointFunctionName: Bool = false, - targetTriple: Basics.Triple = hostTriple, + triple: Basics.Triple = hostTriple, indexStoreMode: BuildParameters.IndexStoreMode = .off, useExplicitModuleBuild: Bool = false, linkerDeadStrip: Bool = true, @@ -86,16 +92,17 @@ public func mockBuildParameters( omitFramePointers: Bool? = nil ) -> BuildParameters { try! BuildParameters( - dataPath: buildPath, + destination: destination, + dataPath: buildPath ?? AbsolutePath("/path/to/build").appending(triple.tripleString), configuration: config, toolchain: toolchain, - triple: targetTriple, + triple: triple, flags: flags, pkgConfigDirectories: [], workers: 3, indexStoreMode: indexStoreMode, debuggingParameters: .init( - triple: targetTriple, + triple: triple, shouldEnableDebuggingEntitlement: config == .debug, omitFramePointers: omitFramePointers ), @@ -112,7 +119,10 @@ public func mockBuildParameters( ) } -public func mockBuildParameters(environment: BuildEnvironment) -> BuildParameters { +public func mockBuildParameters( + destination: BuildParameters.Destination, + environment: BuildEnvironment +) -> BuildParameters { let triple: Basics.Triple switch environment.platform { case .macOS: @@ -127,24 +137,136 @@ public func mockBuildParameters(environment: BuildEnvironment) -> BuildParameter fatalError("unsupported platform in tests") } - return mockBuildParameters(config: environment.configuration ?? .debug, targetTriple: triple) + return mockBuildParameters( + destination: destination, + config: environment.configuration ?? .debug, + triple: triple + ) +} + +public func mockBuildPlan( + buildPath: AbsolutePath? = nil, + environment: BuildEnvironment, + toolchain: PackageModel.Toolchain = MockToolchain(), + graph: ModulesGraph, + commonFlags: PackageModel.BuildFlags = .init(), + indexStoreMode: BuildParameters.IndexStoreMode = .off, + omitFramePointers: Bool? = nil, + driverParameters: BuildParameters.Driver = .init(), + linkingParameters: BuildParameters.Linking = .init(), + targetSanitizers: EnabledSanitizers = .init(), + fileSystem fs: any FileSystem, + observabilityScope: ObservabilityScope +) throws -> Build.BuildPlan { + try mockBuildPlan( + buildPath: buildPath, + config: environment.configuration ?? .debug, + platform: environment.platform, + toolchain: toolchain, + graph: graph, + commonFlags: commonFlags, + indexStoreMode: indexStoreMode, + omitFramePointers: omitFramePointers, + driverParameters: driverParameters, + linkingParameters: linkingParameters, + targetSanitizers: targetSanitizers, + fileSystem: fs, + observabilityScope: observabilityScope + ) } +public func mockBuildPlan( + buildPath: AbsolutePath? = nil, + config: BuildConfiguration = .debug, + triple: Basics.Triple? = nil, + platform: PackageModel.Platform? = nil, + toolchain: PackageModel.Toolchain = MockToolchain(), + graph: ModulesGraph, + commonFlags: PackageModel.BuildFlags = .init(), + indexStoreMode: BuildParameters.IndexStoreMode = .off, + omitFramePointers: Bool? = nil, + driverParameters: BuildParameters.Driver = .init(), + linkingParameters: BuildParameters.Linking = .init(), + targetSanitizers: EnabledSanitizers = .init(), + fileSystem fs: any FileSystem, + observabilityScope: ObservabilityScope +) throws -> Build.BuildPlan { + let inferredTriple: Basics.Triple + if let platform { + precondition(triple == nil) + + inferredTriple = switch platform { + case .macOS: + Triple.x86_64MacOS + case .linux: + Triple.arm64Linux + case .android: + Triple.arm64Android + case .windows: + Triple.windows + default: + fatalError("unsupported platform in tests") + } + } else { + inferredTriple = triple ?? hostTriple + } + + let commonDebuggingParameters = BuildParameters.Debugging( + triple: inferredTriple, + shouldEnableDebuggingEntitlement: config == .debug, + omitFramePointers: omitFramePointers + ) + + var destinationParameters = mockBuildParameters( + destination: .target, + buildPath: buildPath, + config: config, + toolchain: toolchain, + flags: commonFlags, + triple: inferredTriple, + indexStoreMode: indexStoreMode + ) + destinationParameters.debuggingParameters = commonDebuggingParameters + destinationParameters.driverParameters = driverParameters + destinationParameters.linkingParameters = linkingParameters + destinationParameters.sanitizers = targetSanitizers + + var hostParameters = mockBuildParameters( + destination: .host, + buildPath: buildPath, + config: config, + toolchain: toolchain, + flags: commonFlags, + triple: inferredTriple, + indexStoreMode: indexStoreMode + ) + hostParameters.debuggingParameters = commonDebuggingParameters + hostParameters.driverParameters = driverParameters + hostParameters.linkingParameters = linkingParameters + + return try BuildPlan( + destinationBuildParameters: destinationParameters, + toolsBuildParameters: hostParameters, + graph: graph, + fileSystem: fs, + observabilityScope: observabilityScope + ) +} enum BuildError: Swift.Error { case error(String) } -public struct BuildPlanResult { - public let plan: Build.BuildPlan - public let targetMap: [String: TargetBuildDescription] - public let productMap: [String: Build.ProductBuildDescription] +package struct BuildPlanResult { + package let plan: Build.BuildPlan + package let targetMap: [ResolvedModule.ID: TargetBuildDescription] + package let productMap: [ResolvedProduct.ID: Build.ProductBuildDescription] public init(plan: Build.BuildPlan) throws { self.plan = plan self.productMap = try Dictionary( throwingUniqueKeysWithValues: plan.buildProducts .compactMap { $0 as? Build.ProductBuildDescription } - .map { ($0.product.name, $0) } + .map { ($0.product.id, $0) } ) self.targetMap = try Dictionary( throwingUniqueKeysWithValues: plan.targetMap.compactMap { @@ -154,7 +276,7 @@ public struct BuildPlanResult { else { throw BuildError.error("Target \($0) not found.") } - return (target.name, $1) + return (target.id, $1) } ) } @@ -167,17 +289,27 @@ public struct BuildPlanResult { XCTAssertEqual(self.plan.productMap.count, count, file: file, line: line) } - public func target(for name: String) throws -> TargetBuildDescription { - guard let target = targetMap[name] else { - throw BuildError.error("Target \(name) not found.") + package func target(for name: String) throws -> TargetBuildDescription { + let matchingIDs = targetMap.keys.filter({ $0.targetName == name }) + guard matchingIDs.count == 1, let target = targetMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + throw BuildError.error("Target \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return target } - public func buildProduct(for name: String) throws -> Build.ProductBuildDescription { - guard let product = productMap[name] else { - // Display the thrown error on macOS - throw BuildError.error("Product \(name) not found.") + package func buildProduct(for name: String) throws -> Build.ProductBuildDescription { + let matchingIDs = productMap.keys.filter({ $0.productName == name }) + guard matchingIDs.count == 1, let product = productMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + // Display the thrown error on macOS + throw BuildError.error("Product \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return product } diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 4ab9c0cde98..1833db62c82 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -16,19 +16,18 @@ import class Basics.ObservabilityScope import struct PackageGraph.ModulesGraph import class PackageModel.Manifest import struct PackageModel.ProductDescription +import enum PackageModel.ProductType import struct PackageModel.TargetDescription import protocol TSCBasic.FileSystem import class TSCBasic.InMemoryFileSystem -@_spi(SwiftPMInternal) -public typealias MockPackageGraph = ( +package typealias MockPackageGraph = ( graph: ModulesGraph, fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) -@_spi(SwiftPMInternal) -public func macrosPackageGraph() throws -> MockPackageGraph { +package func macrosPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem(emptyFiles: "/swift-firmware/Sources/Core/source.swift", "/swift-firmware/Sources/HAL/source.swift", @@ -41,7 +40,7 @@ public func macrosPackageGraph() throws -> MockPackageGraph { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -126,8 +125,136 @@ public func macrosPackageGraph() throws -> MockPackageGraph { return (graph, fs, observability.topScope) } -@_spi(SwiftPMInternal) -public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func macrosTestsPackageGraph() throws -> MockPackageGraph { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-mmio/Sources/MMIO/source.swift", + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-mmio/Sources/MMIOMacrosTests/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift", + "/swift-syntax/Sources/SwiftSyntaxMacrosTestSupport/source.swift", + "/swift-syntax/Sources/SwiftSyntaxMacros/source.swift", + "/swift-syntax/Sources/SwiftCompilerPlugin/source.swift", + "/swift-syntax/Sources/SwiftCompilerPluginMessageHandling/source.swift", + "/swift-syntax/Tests/SwiftSyntaxTests/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "MMIO", + type: .library(.automatic), + targets: ["MMIO"] + ) + ], + targets: [ + TargetDescription( + name: "MMIO", + dependencies: [.target(name: "MMIOMacros")] + ), + TargetDescription( + name: "MMIOMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + ], + type: .macro + ), + TargetDescription( + name: "MMIOMacrosTests", + dependencies: [ + .target(name: "MMIOMacros"), + .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax") + ], + type: .test + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntaxMacros", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ), + ProductDescription( + name: "SwiftSyntax", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ), + ProductDescription( + name: "SwiftSyntaxMacrosTestSupport", + type: .library(.automatic), + targets: ["SwiftSyntaxMacrosTestSupport"] + ), + ProductDescription( + name: "SwiftCompilerPlugin", + type: .library(.automatic), + targets: ["SwiftCompilerPlugin"] + ), + ProductDescription( + name: "SwiftCompilerPluginMessageHandling", + type: .library(.automatic), + targets: ["SwiftCompilerPluginMessageHandling"] + ), + ], + targets: [ + TargetDescription( + name: "SwiftSyntax", + dependencies: [] + ), + TargetDescription( + name: "SwiftSyntaxMacros", + dependencies: [.target(name: "SwiftSyntax")] + ), + TargetDescription( + name: "SwiftCompilerPlugin", + dependencies: [ + .target(name: "SwiftCompilerPluginMessageHandling"), + .target(name: "SwiftSyntaxMacros"), + ] + ), + TargetDescription( + name: "SwiftCompilerPluginMessageHandling", + dependencies: [ + .target(name: "SwiftSyntax"), + .target(name: "SwiftSyntaxMacros"), + ] + ), + TargetDescription( + name: "SwiftSyntaxMacrosTestSupport", + dependencies: [.target(name: "SwiftSyntax")] + ), + TargetDescription( + name: "SwiftSyntaxTests", + dependencies: ["SwiftSyntax"], + type: .test + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} + +package func trivialPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", @@ -137,7 +264,7 @@ public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackage ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -157,8 +284,7 @@ public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackage return (graph, fs, observability.topScope) } -@_spi(SwiftPMInternal) -public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func embeddedCxxInteropPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", @@ -168,7 +294,7 @@ public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -199,3 +325,66 @@ public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> return (graph, fs, observability.topScope) } + +package func toolsExplicitLibrariesGraph(linkage: ProductType.LibraryType) throws -> MockPackageGraph { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-mmio/Sources/MMIOMacrosTests/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + targets: [ + TargetDescription( + name: "MMIOMacros", + dependencies: [ + .product(name: "SwiftSyntax", package: "swift-syntax"), + ], + type: .macro + ), + TargetDescription( + name: "MMIOMacrosTests", + dependencies: [ + .target(name: "MMIOMacros"), + ], + type: .test + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntax", + type: .library(linkage), + targets: ["SwiftSyntax"] + ), + ], + targets: [ + TargetDescription( + name: "SwiftSyntax", + dependencies: [] + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index 97aa593a971..b5442d300b4 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 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 @@ -17,8 +17,8 @@ import struct Basics.IdentifiableSet import PackageModel import PackageGraph -public func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) -> Void) { - result(PackageGraphResult(graph)) +package func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) throws -> Void) rethrows { + try result(PackageGraphResult(graph)) } public final class PackageGraphResult { @@ -49,8 +49,8 @@ public final class PackageGraphResult { public func check(targets: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual( graph.allTargets - .filter{ $0.type != .test } - .map{ $0.name } + .filter { $0.type != .test } + .map { $0.name } .sorted(), targets.sorted(), file: file, line: line) } @@ -86,28 +86,45 @@ public final class PackageGraphResult { XCTAssertEqual(products, Set(reachableBuildProducts), file: file, line: line) } - public func checkTarget( + package func checkTarget( _ name: String, + destination: BuildTriple = .destination, file: StaticString = #file, line: UInt = #line, body: (ResolvedTargetResult) -> Void ) { - guard let target = find(target: name) else { + let target = graph.target(for: name, destination: destination) + + guard let target else { return XCTFail("Target \(name) not found", file: file, line: line) } + body(ResolvedTargetResult(target)) } + package func checkTargets( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: ([ResolvedTargetResult]) throws -> Void + ) rethrows { + try body(graph.allTargets.filter { $0.name == name }.map(ResolvedTargetResult.init)) + } + public func checkProduct( _ name: String, + destination: BuildTriple = .destination, file: StaticString = #file, line: UInt = #line, body: (ResolvedProductResult) -> Void ) { - guard let target = find(product: name) else { + let product = graph.product(for: name, destination: destination) + + guard let product else { return XCTFail("Product \(name) not found", file: file, line: line) } - body(ResolvedProductResult(target)) + + body(ResolvedProductResult(product)) } public func check(testModules: String..., file: StaticString = #file, line: UInt = #line) { @@ -118,19 +135,11 @@ public final class PackageGraphResult { .sorted(), testModules.sorted(), file: file, line: line) } - public func find(target: String) -> ResolvedTarget? { - return graph.allTargets.first(where: { $0.name == target }) - } - - public func find(product: String) -> ResolvedProduct? { - return graph.allProducts.first(where: { $0.name == product }) - } - public func find(package: PackageIdentity) -> ResolvedPackage? { return graph.packages.first(where: { $0.identity == package }) } - private func reachableBuildTargets(in environment: BuildEnvironment) throws -> IdentifiableSet { + private func reachableBuildTargets(in environment: BuildEnvironment) throws -> IdentifiableSet { let inputTargets = graph.inputPackages.lazy.flatMap { $0.targets } let recursiveBuildTargetDependencies = try inputTargets .flatMap { try $0.recursiveDependencies(satisfying: environment) } @@ -148,10 +157,10 @@ public final class PackageGraphResult { } } -public final class ResolvedTargetResult { - private let target: ResolvedTarget +package final class ResolvedTargetResult { + let target: ResolvedModule - init(_ target: ResolvedTarget) { + init(_ target: ResolvedModule) { self.target = target } @@ -159,7 +168,11 @@ public final class ResolvedTargetResult { XCTAssertEqual(Set(dependencies), Set(target.dependencies.map({ $0.name })), file: file, line: line) } - public func checkDependency( + package func check(dependencies: [String], file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(Set(dependencies), Set(target.dependencies.map({ $0.name })), file: file, line: line) + } + + package func checkDependency( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -175,8 +188,10 @@ public final class ResolvedTargetResult { XCTAssertEqual(type, target.type, file: file, line: line) } - public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) + package func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { + let targetPlatforms = Dictionary( + uniqueKeysWithValues: target.supportedPlatforms.map { ($0.platform.name, $0.version.versionString) } + ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } @@ -187,22 +202,30 @@ public final class ResolvedTargetResult { return self.target.getSupportedPlatform(for: platform, usingXCTest: self.target.type == .test) } let targetPlatforms = Dictionary( - uniqueKeysWithValues: derived - .map { ($0.platform.name, $0.version.versionString) } + uniqueKeysWithValues: derived.map { ($0.platform.name, $0.version.versionString) } ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { - let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) + package func checkDerivedPlatformOptions( + _ platform: PackageModel.Platform, + options: [String], + file: StaticString = #file, + line: UInt = #line + ) { + let platform = self.target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.target.buildTriple, buildTriple, file: file, line: line) + } } -public final class ResolvedTargetDependencyResult { - private let dependency: ResolvedTarget.Dependency +package final class ResolvedTargetDependencyResult { + private let dependency: ResolvedModule.Dependency - init(_ dependency: ResolvedTarget.Dependency) { + init(_ dependency: ResolvedModule.Dependency) { self.dependency = dependency } @@ -217,6 +240,28 @@ public final class ResolvedTargetDependencyResult { ) { XCTAssert(!dependency.conditions.allSatisfy({ $0.satisfies(environment) }), file: file, line: line) } + + public func checkTarget( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard case let .target(target, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a target", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } + + public func checkProduct( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedProductResult) -> Void + ) { + guard case let .product(product, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a product", file: file, line: line) + } + body(ResolvedProductResult(product)) + } } public final class ResolvedProductResult { @@ -252,10 +297,26 @@ public final class ResolvedProductResult { let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.product.buildTriple, buildTriple, file: file, line: line) + } + + package func checkTarget( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard let target = product.targets.first(where: { $0.name == name }) else { + return XCTFail("Target \(name) not found", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } } -extension ResolvedTarget.Dependency { - public var name: String { +extension ResolvedModule.Dependency { + package var name: String { switch self { case .target(let target, _): return target.name diff --git a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift index 0fa4d373a54..61ddb1cf7b8 100644 --- a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift +++ b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift @@ -13,14 +13,14 @@ import PackageGraph import PackageModel -extension ResolvedTarget { - public static func mock( +extension ResolvedModule { + package static func mock( packageIdentity: PackageIdentity, name: String, - deps: ResolvedTarget..., + deps: ResolvedModule..., conditions: [PackageCondition] = [] - ) -> ResolvedTarget { - ResolvedTarget( + ) -> ResolvedModule { + ResolvedModule( packageIdentity: packageIdentity, underlying: SwiftTarget( name: name, diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index c47613725f6..c86f3f75b44 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -382,9 +382,9 @@ extension PIFBuilderParameters { extension BuildSubset { var pifTargetName: String { switch self { - case .product(let name): + case .product(let name, _): PackagePIFProjectBuilder.targetName(for: name) - case .target(let name): + case .target(let name, _): name case .allExcludingTests: PIFBuilder.allExcludingTestsTargetName diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 03d505d269d..1f0da76b9ae 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -282,6 +282,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { ) let buildParameters = try BuildParameters( + destination: .target, dataPath: dataPath, configuration: configuration, toolchain: self.targetToolchain, @@ -319,6 +320,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { toolsBuildParameters: buildParameters, cacheBuildManifest: false, packageGraphLoader: packageGraphLoader, + scratchDirectory: scratchDirectory, additionalFileRules: [], pkgConfigDirectories: [], dependenciesByRootPackageIdentity: [:], diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 3f196dd41fa..b454d8fb0d0 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -14,32 +14,63 @@ @testable import PackageModel import Basics +import LLBuildManifest +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import PackageGraph +import SPMBuildCore import SPMTestSupport import XCTest import class TSCBasic.BufferedOutputByteStream import class TSCBasic.InMemoryFileSystem +private func mockBuildOperation( + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, + cacheBuildManifest: Bool = false, + packageGraphLoader: @escaping () -> ModulesGraph = { fatalError() }, + scratchDirectory: AbsolutePath, + fs: any Basics.FileSystem, + observabilityScope: ObservabilityScope +) -> BuildOperation { + return BuildOperation( + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, + cacheBuildManifest: cacheBuildManifest, + packageGraphLoader: packageGraphLoader, + scratchDirectory: scratchDirectory, + additionalFileRules: [], + pkgConfigDirectories: [], + dependenciesByRootPackageIdentity: [:], + targetsByRootPackageIdentity: [:], + outputStream: BufferedOutputByteStream(), + logLevel: .info, + fileSystem: fs, + observabilityScope: observabilityScope + ) +} + final class BuildOperationTests: XCTestCase { func testDetectUnexpressedDependencies() throws { + let scratchDirectory = AbsolutePath("/path/to/build") + let triple = hostTriple + let targetBuildParameters = mockBuildParameters( + destination: .target, + buildPath: scratchDirectory.appending(triple.tripleString), + shouldDisableLocalRpath: false, + triple: triple + ) + let fs = InMemoryFileSystem(files: [ - "/path/to/build/debug/Lunch.build/Lunch.d" : "/Best.framework" + "\(targetBuildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework" ]) let observability = ObservabilitySystem.makeForTesting() - let buildOp = BuildOperation( - productsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), - toolsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), - cacheBuildManifest: false, - packageGraphLoader: { fatalError() }, - additionalFileRules: [], - pkgConfigDirectories: [], - dependenciesByRootPackageIdentity: [:], - targetsByRootPackageIdentity: [:], - outputStream: BufferedOutputByteStream(), - logLevel: .info, - fileSystem: fs, - observabilityScope: observability.topScope + let buildOp = mockBuildOperation( + productsBuildParameters: targetBuildParameters, + toolsBuildParameters: mockBuildParameters(destination: .host, shouldDisableLocalRpath: false), + scratchDirectory: scratchDirectory, + fs: fs, observabilityScope: observability.topScope ) buildOp.detectUnexpressedDependencies( availableLibraries: [ @@ -60,4 +91,89 @@ final class BuildOperationTests: XCTestCase { ["target 'Lunch' has an unexpressed depedency on 'foo'"] ) } + + func testDetectProductTripleChange() throws { + let observability = ObservabilitySystem.makeForTesting() + let fs = InMemoryFileSystem( + emptyFiles: "/Pkg/Sources/ATarget/foo.swift" + ) + let packageGraph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + .createRootManifest( + displayName: "SwitchTriple", + path: "/Pkg", + targets: [ + TargetDescription(name: "ATarget"), + ] + ), + ], + observabilityScope: observability.topScope + ) + try withTemporaryDirectory { tmpDir in + let scratchDirectory = tmpDir.appending(".build") + let fs = localFileSystem + let triples = try [Triple("x86_64-unknown-linux-gnu"), Triple("wasm32-unknown-wasi")] + var llbuildManifestByTriple: [String: String] = [:] + + // Perform initial builds for each triple + for triple in triples { + let targetBuildParameters = mockBuildParameters( + destination: .target, + buildPath: scratchDirectory.appending(triple.tripleString), + config: .debug, + triple: triple + ) + let buildOp = mockBuildOperation( + productsBuildParameters: targetBuildParameters, + toolsBuildParameters: mockBuildParameters(destination: .host), + cacheBuildManifest: false, + packageGraphLoader: { packageGraph }, + scratchDirectory: scratchDirectory, + fs: fs, observabilityScope: observability.topScope + ) + // Generate initial llbuild manifest + let _ = try buildOp.getBuildDescription() + // Record the initial llbuild manifest as expected one + llbuildManifestByTriple[triple.tripleString] = try fs.readFileContents(targetBuildParameters.llbuildManifest) + } + + XCTAssertTrue(fs.exists(scratchDirectory.appending("debug.yaml"))) + // FIXME: There should be a build database with manifest cache after the initial build. + // The initial build usually triggered with `cacheBuildManifest=false` because llbuild + // manifest file and description.json are not found. However, with `cacheBuildManifest=false`, + // `BuildOperation` does not trigger "PackageStructure" build, thus the initial build does + // not record the manifest cache. So "getBuildDescription" doesn't create build.db for the + // initial planning and the second build always need full-planning. + // + // XCTAssertTrue(fs.exists(scratchDirectory.appending("build.db"))) + + // Perform incremental build several times and switch triple for each time + for _ in 0..<4 { + for triple in triples { + let targetBuildParameters = mockBuildParameters( + destination: .target, + buildPath: scratchDirectory.appending(triple.tripleString), + config: .debug, + triple: triple + ) + let buildOp = mockBuildOperation( + productsBuildParameters: targetBuildParameters, + toolsBuildParameters: mockBuildParameters(destination: .host), + cacheBuildManifest: true, + packageGraphLoader: { packageGraph }, + scratchDirectory: scratchDirectory, + fs: fs, observabilityScope: observability.topScope + ) + // Generate llbuild manifest + let _ = try buildOp.getBuildDescription() + + // Ensure that llbuild manifest is updated to the expected one + let actualManifest: String = try fs.readFileContents(targetBuildParameters.llbuildManifest) + let expectedManifest = try XCTUnwrap(llbuildManifestByTriple[triple.tripleString]) + XCTAssertEqual(actualManifest, expectedManifest) + } + } + } + } } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index e69f605adb6..50a50c0fca2 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -41,29 +41,6 @@ extension Build.BuildPlan { .configuration == .release ? "release" : "debug" return buildParameters.dataPath.appending(components: buildConfigurationComponent) } - - /// Create a build plan with a package graph and same build parameters for products and tools. Provided for - /// testing purposes only. - convenience init( - buildParameters: BuildParameters, - graph: ModulesGraph, - additionalFileRules: [FileRuleDescription] = [], - buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], - prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], - fileSystem: any FileSystem, - observabilityScope: ObservabilityScope - ) throws { - try self.init( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, - graph: graph, - additionalFileRules: additionalFileRules, - buildToolPluginInvocationResults: buildToolPluginInvocationResults, - prebuildCommandResults: prebuildCommandResults, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) - } } final class BuildPlanTests: XCTestCase { @@ -191,9 +168,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -314,9 +293,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -407,9 +388,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -482,9 +465,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -613,9 +598,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -756,9 +743,11 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let plan = try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope ) @@ -922,15 +911,14 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) do { - let plan = try BuildPlan( - buildParameters: mockBuildParameters( - buildPath: buildDirPath, - config: .release, - toolchain: UserToolchain.default, - targetTriple: UserToolchain.default.targetTriple, + let plan = try mockBuildPlan( + config: .release, + triple: UserToolchain.default.targetTriple, + toolchain: UserToolchain.default, + graph: graph, + driverParameters: .init( useExplicitModuleBuild: true ), - graph: graph, fileSystem: fs, observabilityScope: observability.topScope ) @@ -1033,11 +1021,11 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) do { - let plan = try BuildPlan( - buildParameters: mockBuildParameters(environment: BuildEnvironment( + let plan = try mockBuildPlan( + environment: BuildEnvironment( platform: .linux, configuration: .release - )), + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1075,11 +1063,11 @@ final class BuildPlanTests: XCTestCase { } do { - let plan = try BuildPlan( - buildParameters: mockBuildParameters(environment: BuildEnvironment( + let plan = try mockBuildPlan( + environment: BuildEnvironment( platform: .macOS, configuration: .debug - )), + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1150,18 +1138,17 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope )) - XCTAssertEqual(Set(result.productMap.keys), ["APackageTests"]) + XCTAssertEqual(Set(result.productMap.keys.map(\.productName)), ["APackageTests"]) #if os(macOS) - XCTAssertEqual(Set(result.targetMap.keys), ["ATarget", "BTarget", "ATargetTests"]) + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), ["ATarget", "BTarget", "ATargetTests"]) #else - XCTAssertEqual(Set(result.targetMap.keys), [ + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), [ "APackageTests", "APackageDiscoveredTests", "ATarget", @@ -1193,8 +1180,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(config: .release), + let result = try BuildPlanResult(plan: mockBuildPlan( + config: .release, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1284,9 +1271,12 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(config: .release, linkerDeadStrip: false), + let result = try BuildPlanResult(plan: mockBuildPlan( + config: .release, graph: graph, + linkingParameters: .init( + linkerDeadStrip: false + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -1397,8 +1387,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1511,7 +1500,13 @@ final class BuildPlanTests: XCTestCase { ]) #endif - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "Pkg", + buildTriple: .destination + )] + ) XCTAssertEqual(Array(buildProduct.objects), [ buildPath.appending(components: "exe.build", "main.c.o"), buildPath.appending(components: "extlib.build", "extlib.c.o"), @@ -1572,11 +1567,11 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) do { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: BuildEnvironment( + let result = try BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment( platform: .linux, configuration: .release - )), + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1591,11 +1586,11 @@ final class BuildPlanTests: XCTestCase { } do { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: BuildEnvironment( + let result = try BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment( platform: .macOS, configuration: .debug - )), + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1647,8 +1642,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1755,8 +1749,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1828,7 +1821,7 @@ final class BuildPlanTests: XCTestCase { "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", "-target", defaultTargetTriple, - "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.build/exe.swiftmodule", + "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/exe.build/exe.swiftmodule", "-g", ]) #elseif os(Windows) @@ -1884,8 +1877,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1895,8 +1887,8 @@ final class BuildPlanTests: XCTestCase { let lib = try result.target(for: "lib").clangTarget() XCTAssertEqual(try lib.objects, [ - AbsolutePath("/path/to/build/debug/lib.build/lib.S.o"), - AbsolutePath("/path/to/build/debug/lib.build/lib.c.o"), + AbsolutePath("/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/lib.build/lib.S.o"), + AbsolutePath("/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/lib.build/lib.c.o"), ]) } @@ -1947,8 +1939,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1999,8 +1990,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2135,8 +2125,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(config: .release), + let result = try BuildPlanResult(plan: mockBuildPlan( + config: .release, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2392,9 +2382,11 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2500,8 +2492,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2596,8 +2587,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - var result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + var result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2613,8 +2603,8 @@ final class BuildPlanTests: XCTestCase { #endif // Verify that `-lstdc++` is passed instead of `-lc++` when cross-compiling to Linux. - result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64Linux), + result = try BuildPlanResult(plan: mockBuildPlan( + triple: .arm64Linux, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2662,8 +2652,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: g, fileSystem: fs, observabilityScope: observability.topScope @@ -2798,8 +2787,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -2922,8 +2910,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3142,8 +3129,7 @@ final class BuildPlanTests: XCTestCase { graphResult.check(targets: "ATarget", "BTarget1", "BTarget2", "CTarget") #endif - let planResult = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let planResult = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3239,8 +3225,8 @@ final class BuildPlanTests: XCTestCase { try graphResult.check(reachableBuildProducts: "aexec", "BLibrary1", "BLibrary2", in: linuxDebug) try graphResult.check(reachableBuildTargets: "ATarget", "BTarget1", "BTarget2", in: linuxDebug) - let planResult = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: linuxDebug), + let planResult = try BuildPlanResult(plan: mockBuildPlan( + environment: linuxDebug, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3254,8 +3240,8 @@ final class BuildPlanTests: XCTestCase { try graphResult.check(reachableBuildProducts: "aexec", "BLibrary2", in: macosDebug) try graphResult.check(reachableBuildTargets: "ATarget", "BTarget2", "BTarget3", in: macosDebug) - let planResult = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: macosDebug), + let planResult = try BuildPlanResult(plan: mockBuildPlan( + environment: macosDebug, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3269,8 +3255,8 @@ final class BuildPlanTests: XCTestCase { try graphResult.check(reachableBuildProducts: "aexec", "CLibrary", in: androidRelease) try graphResult.check(reachableBuildTargets: "ATarget", "CTarget", in: androidRelease) - let planResult = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: androidRelease), + let planResult = try BuildPlanResult(plan: mockBuildPlan( + environment: androidRelease, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3300,8 +3286,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertThrows(BuildPlan.Error.noBuildableTarget) { - _ = try BuildPlan( - buildParameters: mockBuildParameters(), + _ = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3341,8 +3326,7 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) - _ = try BuildPlan( - buildParameters: mockBuildParameters(), + _ = try mockBuildPlan( graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3383,8 +3367,7 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) - _ = try BuildPlan( - buildParameters: mockBuildParameters(), + _ = try mockBuildPlan( graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3424,8 +3407,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .windows), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .windows, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3506,12 +3489,12 @@ final class BuildPlanTests: XCTestCase { ) func createResult(for triple: Basics.Triple) throws -> BuildPlanResult { - try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters( - canRenameEntrypointFunctionName: true, - targetTriple: triple - ), + try BuildPlanResult(plan: mockBuildPlan( + triple: triple, graph: graph, + driverParameters: .init( + canRenameEntrypointFunctionName: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3559,13 +3542,11 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) func check(for mode: BuildParameters.IndexStoreMode, config: BuildConfiguration) throws { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters( - config: config, - toolchain: try UserToolchain.default, - indexStoreMode: mode - ), + let result = try BuildPlanResult(plan: mockBuildPlan( + config: config, + toolchain: try UserToolchain.default, graph: graph, + indexStoreMode: mode, fileSystem: fs, observabilityScope: observability.topScope )) @@ -3631,8 +3612,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3705,8 +3685,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .init("arm64-apple-ios")), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .init("arm64-apple-ios"), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3782,8 +3762,8 @@ final class BuildPlanTests: XCTestCase { // Therefore, we expect no error, as the iOS version // constraints above are valid. XCTAssertNoThrow( - _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64iOS), + _ = try mockBuildPlan( + triple: .arm64iOS, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3792,8 +3772,8 @@ final class BuildPlanTests: XCTestCase { // For completeness, the invalid target should still throw an error. XCTAssertThrows(Diagnostics.fatalError) { - _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + _ = try mockBuildPlan( + triple: .x86_64MacOS, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3855,8 +3835,8 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertThrows(Diagnostics.fatalError) { - _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + _ = try mockBuildPlan( + triple: .x86_64MacOS, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -4021,8 +4001,8 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) func createResult(for dest: Basics.Triple) throws -> BuildPlanResult { - try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: dest), + try BuildPlanResult(plan: mockBuildPlan( + triple: dest, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4088,12 +4068,10 @@ final class BuildPlanTests: XCTestCase { // omit frame pointers explicitly set to true do { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, - omitFramePointers: true - ), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .x86_64Linux, graph: graph, + omitFramePointers: true, fileSystem: fs, observabilityScope: observability.topScope )) @@ -4145,12 +4123,10 @@ final class BuildPlanTests: XCTestCase { // omit frame pointers explicitly set to false do { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, - omitFramePointers: false - ), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .x86_64Linux, graph: graph, + omitFramePointers: false, fileSystem: fs, observabilityScope: observability.topScope )) @@ -4297,9 +4273,9 @@ final class BuildPlanTests: XCTestCase { var flags = BuildFlags() flags.linkerFlags = ["-L", "/path/to/foo", "-L/path/to/foo", "-rpath=foo", "-rpath", "foo"] - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(flags: flags), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + commonFlags: flags, fileSystem: fs, observabilityScope: observability.topScope )) @@ -4361,16 +4337,15 @@ final class BuildPlanTests: XCTestCase { ) ) let mockToolchain = try UserToolchain(swiftSDK: userSwiftSDK) - let extraBuildParameters = mockBuildParameters( - toolchain: mockToolchain, - flags: BuildFlags( - cCompilerFlags: ["-clang-command-line-flag"], - swiftCompilerFlags: ["-swift-command-line-flag"] - ) + let commonFlags = BuildFlags( + cCompilerFlags: ["-clang-command-line-flag"], + swiftCompilerFlags: ["-swift-command-line-flag"] ) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: extraBuildParameters, + + let result = try BuildPlanResult(plan: mockBuildPlan( + toolchain: mockToolchain, graph: graph, + commonFlags: commonFlags, fileSystem: fs, observabilityScope: observability.topScope )) @@ -4419,16 +4394,14 @@ final class BuildPlanTests: XCTestCase { .anySequence, ]) - let staticBuildParameters = { - var copy = extraBuildParameters - copy.linkingParameters.shouldLinkStaticSwiftStdlib = true - // pick a triple with support for static linking - copy.triple = .x86_64Linux - return copy - }() - let staticResult = try BuildPlanResult(plan: BuildPlan( - buildParameters: staticBuildParameters, + let staticResult = try BuildPlanResult(plan: mockBuildPlan( + triple: .x86_64Linux, + toolchain: mockToolchain, graph: graph, + commonFlags: commonFlags, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4503,19 +4476,16 @@ final class BuildPlanTests: XCTestCase { toolset: toolset ) let toolchain = try UserToolchain(swiftSDK: swiftSDK) - let buildParameters = mockBuildParameters( + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: targetTriple, toolchain: toolchain, - flags: BuildFlags( + graph: graph, + commonFlags: BuildFlags( cCompilerFlags: [cliFlag(tool: .cCompiler)], cxxCompilerFlags: [cliFlag(tool: .cxxCompiler)], swiftCompilerFlags: [cliFlag(tool: .swiftCompiler)], linkerFlags: [cliFlag(tool: .linker)] ), - targetTriple: targetTriple - ) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: buildParameters, - graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope )) @@ -4659,9 +4629,8 @@ final class BuildPlanTests: XCTestCase { ]) ) let toolchain = try UserToolchain(swiftSDK: swiftSDK) - let buildParameters = mockBuildParameters(toolchain: toolchain) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: buildParameters, + let result = try BuildPlanResult(plan: mockBuildPlan( + toolchain: toolchain, graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -4730,8 +4699,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4796,8 +4764,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4813,7 +4780,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4823,7 +4790,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(result.plan.destinationBuildParameters.triple)/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4833,12 +4800,12 @@ final class BuildPlanTests: XCTestCase { #if os(macOS) XCTAssertMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #else XCTAssertNoMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #endif @@ -4896,8 +4863,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4914,7 +4880,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4925,7 +4891,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4937,7 +4903,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -4946,7 +4912,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5006,8 +4972,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5028,7 +4993,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5039,7 +5004,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5051,7 +5016,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5060,7 +5025,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5109,8 +5074,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64Linux), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .x86_64Linux, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5194,8 +5159,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5214,12 +5178,13 @@ final class BuildPlanTests: XCTestCase { try llbuild.generateManifest(at: yaml) let contents: String = try fs.readFileContents(yaml) + let triple = result.plan.destinationBuildParameters.triple.tripleString if result.plan.destinationBuildParameters.triple.isWindows() { XCTAssertMatch( contents, .contains(""" - "C.rary-debug.a": + "C.rary-\(triple)-debug.a": tool: shell inputs: ["\( buildPath.appending(components: "rary.build", "rary.swift.o") @@ -5250,7 +5215,7 @@ final class BuildPlanTests: XCTestCase { contents, .contains( """ - "C.rary-debug.a": + "C.rary-\(triple)-debug.a": tool: shell inputs: ["\( buildPath.appending(components: "rary.build", "rary.swift.o") @@ -5278,7 +5243,7 @@ final class BuildPlanTests: XCTestCase { contents, .contains( """ - "C.rary-debug.a": + "C.rary-\(triple)-debug.a": tool: shell inputs: ["\( buildPath.appending(components: "rary.build", "rary.swift.o") @@ -5345,8 +5310,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5413,8 +5377,8 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .wasi), + let plan = try mockBuildPlan( + triple: .wasi, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5480,8 +5444,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + let plan = try mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5546,9 +5509,12 @@ final class BuildPlanTests: XCTestCase { let supportingTriples: [Basics.Triple] = [.x86_64Linux, .arm64Linux, .wasi] for triple in supportingTriples { - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, targetTriple: triple), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: triple, graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -5671,8 +5637,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: targetTriple, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5800,8 +5766,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: targetTriple, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5883,8 +5849,8 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let plan = try BuildPlan( - buildParameters: mockBuildParameters(buildPath: buildPath), + let plan = try mockBuildPlan( + buildPath: buildPath, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5937,15 +5903,15 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - // Unrealistic: we can't enable all of these at once on all platforms. - // This test codifies current behavior, not ideal behavior, and - // may need to be amended if we change it. - var parameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) - parameters.sanitizers = EnabledSanitizers([sanitizer]) - - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), + // Unrealistic: we can't enable all of these at once on all platforms. + // This test codifies current behavior, not ideal behavior, and + // may need to be amended if we change it. + targetSanitizers: EnabledSanitizers([sanitizer]), fileSystem: fs, observabilityScope: observability.topScope )) @@ -5991,13 +5957,12 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let toolchain = try UserToolchain.default - let buildParameters = mockBuildParameters( + let result = try BuildPlanResult(plan: mockBuildPlan( toolchain: toolchain, - linkTimeOptimizationMode: .full - ) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: buildParameters, graph: graph, + linkingParameters: .init( + linkTimeOptimizationMode: .full + ), fileSystem: fileSystem, observabilityScope: observability.topScope )) @@ -6064,17 +6029,23 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(environment: BuildEnvironment( + let result = try BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment( platform: .linux, configuration: .release - )), + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope )) - switch try XCTUnwrap(result.targetMap["ExtLib"]) { + switch try XCTUnwrap( + result.targetMap[.init( + targetName: "ExtLib", + packageIdentity: "ExtPkg", + buildTriple: .destination + )] + ) { case .swift(let swiftTarget): if #available(macOS 13, *) { // `.contains` is only available in macOS 13 or newer XCTAssertTrue(try swiftTarget.compileArguments().contains(["-user-module-version", "1.0.0"])) @@ -6108,9 +6079,11 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldDisableLocalRpath: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldDisableLocalRpath: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -6222,9 +6195,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -6232,7 +6207,13 @@ final class BuildPlanTests: XCTestCase { result.checkTargetsCount(3) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "FooLogging" }) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BarLogging" }) - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "thisPkg", + buildTriple: .destination + )] + ) let dylibs = Array(buildProduct.dylibs.map({$0.product.name})).sorted() XCTAssertEqual(dylibs, ["BarLogging", "FooLogging"]) } @@ -6268,8 +6249,7 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope ) - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + let result = try BuildPlanResult(plan: mockBuildPlan( graph: graph, fileSystem: fs, observabilityScope: observability.topScope diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index 7b8a01802d2..e50e536b4ee 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -44,8 +44,8 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { ) } - private func makeResolvedTarget() throws -> ResolvedTarget { - ResolvedTarget( + private func makeResolvedTarget() throws -> ResolvedModule { + ResolvedModule( packageIdentity: .plain("dummy"), underlying: try makeClangTarget(), dependencies: [], @@ -85,13 +85,14 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { defaultLocalization: nil, supportedPlatforms: [], dependencies: [], - targets: [target], + targets: .init([target]), products: [], registryMetadata: nil, platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)), target: target, toolsVersion: .current, buildParameters: buildParameters ?? mockBuildParameters( + destination: .target, toolchain: try UserToolchain.default, indexStoreMode: .on ), diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index 504ba051f59..d322aca607b 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -14,10 +14,14 @@ import struct Basics.AbsolutePath import class Basics.ObservabilitySystem import class Build.BuildPlan import class Build.ProductBuildDescription +import enum Build.TargetBuildDescription import class Build.SwiftTargetBuildDescription import struct Basics.Triple +import enum PackageGraph.BuildTriple import class PackageModel.Manifest import struct PackageModel.TargetDescription +import enum PackageModel.ProductType +import struct SPMBuildCore.BuildParameters import func SPMTestSupport.loadPackageGraph @_spi(SwiftPMInternal) @@ -25,10 +29,10 @@ import func SPMTestSupport.embeddedCxxInteropPackageGraph @_spi(SwiftPMInternal) import func SPMTestSupport.macrosPackageGraph - +import func SPMTestSupport.macrosTestsPackageGraph import func SPMTestSupport.mockBuildParameters - -@_spi(SwiftPMInternal) +import func SPMTestSupport.mockBuildPlan +import func SPMTestSupport.toolsExplicitLibrariesGraph import func SPMTestSupport.trivialPackageGraph import struct SPMTestSupport.BuildPlanResult @@ -40,14 +44,18 @@ import XCTest final class CrossCompilationBuildPlanTests: XCTestCase { func testEmbeddedWasmTarget() throws { - var (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: "/Pkg") + var (graph, fs, observabilityScope) = try trivialPackageGraph() let triple = try Triple("wasm32-unknown-none-wasm") - var parameters = mockBuildParameters(targetTriple: triple) - parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true - var result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, + + let linkingParameters = BuildParameters.Linking( + shouldLinkStaticSwiftStdlib: true + ) + + var result = try BuildPlanResult(plan: mockBuildPlan( + triple: triple, graph: graph, + linkingParameters: linkingParameters, fileSystem: fs, observabilityScope: observabilityScope )) @@ -71,11 +79,12 @@ final class CrossCompilationBuildPlanTests: XCTestCase { ] ) - (graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph(pkgRootPath: "/Pkg") + (graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph() - result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, + result = try BuildPlanResult(plan: mockBuildPlan( + triple: triple, graph: graph, + linkingParameters: linkingParameters, fileSystem: fs, observabilityScope: observabilityScope )) @@ -101,17 +110,16 @@ final class CrossCompilationBuildPlanTests: XCTestCase { } func testWasmTargetRelease() throws { - let pkgPath = AbsolutePath("/Pkg") + let (graph, fs, observabilityScope) = try trivialPackageGraph() - let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) - - var parameters = mockBuildParameters( - config: .release, targetTriple: .wasi, linkerDeadStrip: true - ) - parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, + let result = try BuildPlanResult(plan: mockBuildPlan( + config: .release, + triple: .wasi, graph: graph, + linkingParameters: .init( + linkerDeadStrip: true, + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observabilityScope )) @@ -136,13 +144,14 @@ final class CrossCompilationBuildPlanTests: XCTestCase { func testWASITarget() throws { let pkgPath = AbsolutePath("/Pkg") - let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + let (graph, fs, observabilityScope) = try trivialPackageGraph() - var parameters = mockBuildParameters(targetTriple: .wasi) - parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, + let result = try BuildPlanResult(plan: mockBuildPlan( + triple: .wasi, graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observabilityScope )) @@ -153,7 +162,12 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let buildPath = result.plan.productsBuildPath - let lib = try result.target(for: "lib").clangTarget() + let lib = try XCTUnwrap( + result.allTargets(named: "lib") + .map { try $0.clangTarget() } + .first { $0.target.buildTriple == .destination } + ) + XCTAssertEqual(try lib.basicArguments(isCXX: false), [ "-target", "wasm32-unknown-wasi", "-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1", @@ -212,4 +226,192 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let testPathExtension = try testBuildDescription.binaryPath.extension XCTAssertEqual(testPathExtension, "wasm") } + + func testMacros() throws { + let (graph, fs, scope) = try macrosPackageGraph() + + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters( + destination: .target, + shouldLinkStaticSwiftStdlib: true, + triple: destinationTriple + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + triple: toolsTriple + ), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(3) + result.checkTargetsCount(10) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "MMIO") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "Core") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "HAL") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let mmioTargets = try result.allTargets(named: "MMIO").map { try $0.swiftTarget() } + XCTAssertEqual(mmioTargets.count, 1) + let mmioTarget = try XCTUnwrap(mmioTargets.first) + let compileArguments = try mmioTarget.emitCommandLine() + XCTAssertMatch( + compileArguments, + [ + "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), + .anySequence, + "-Xfrontend", "-load-plugin-executable", + // Verify that macros are located in the tools triple directory. + "-Xfrontend", .contains(toolsTriple.tripleString) + ] + ) + } + + func testMacrosTests() throws { + let (graph, fs, scope) = try macrosTestsPackageGraph() + + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters( + destination: .target, + shouldLinkStaticSwiftStdlib: true, + triple: destinationTriple + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + triple: toolsTriple + ), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(2) + result.checkTargetsCount(15) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageDiscoveredTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "MMIO") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacrosTests") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let mmioTargets = try result.allTargets(named: "MMIO").map { try $0.swiftTarget() } + XCTAssertEqual(mmioTargets.count, 1) + let mmioTarget = try XCTUnwrap(mmioTargets.first) + let compileArguments = try mmioTarget.emitCommandLine() + XCTAssertMatch( + compileArguments, + [ + "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), + .anySequence, + "-Xfrontend", "-load-plugin-executable", + // Verify that macros are located in the tools triple directory. + "-Xfrontend", .contains(toolsTriple.tripleString) + ] + ) + } + + func testToolsExplicitLibraries() throws { + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + + for (linkage, productFileName) in [(ProductType.LibraryType.static, "libSwiftSyntax-tool.a"), (.dynamic, "libSwiftSyntax-tool.dylib")] { + let (graph, fs, scope) = try toolsExplicitLibrariesGraph(linkage: linkage) + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters( + destination: .target, + shouldLinkStaticSwiftStdlib: true, + triple: destinationTriple + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + triple: toolsTriple + ), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(4) + result.checkTargetsCount(6) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageDiscoveredTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacrosTests") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let swiftSyntaxProducts = result.allProducts(named: "SwiftSyntax") + XCTAssertEqual(swiftSyntaxProducts.count, 2) + let swiftSyntaxToolsProduct = try XCTUnwrap(swiftSyntaxProducts.first { $0.product.buildTriple == .tools }) + let archiveArguments = try swiftSyntaxToolsProduct.archiveArguments() + + // Verify that produced library file has a correct name + XCTAssertMatch(archiveArguments, [.contains(productFileName)]) + } + } +} + +extension BuildPlanResult { + func allTargets(named name: String) throws -> some Collection { + self.targetMap + .filter { $0.0.targetName == name } + .values + } + + func allProducts(named name: String) -> some Collection { + self.productMap + .filter { $0.0.productName == name } + .values + } + + func check( + buildTriple: BuildTriple, + triple: Triple, + for target: String, + file: StaticString = #file, + line: UInt = #line + ) throws { + let targets = self.targetMap.filter { + $0.key.targetName == target && $0.key.buildTriple == buildTriple + } + XCTAssertEqual(targets.count, 1, file: file, line: line) + + let target = try XCTUnwrap( + targets.first?.value, + file: file, + line: line + ).swiftTarget() + XCTAssertMatch(try target.emitCommandLine(), [.contains(triple.tripleString)], file: file, line: line) + } } diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index f66e0dc5e25..ecb3609606e 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -48,13 +48,11 @@ final class LLBuildManifestBuilderTests: XCTestCase { // macOS, release build - var buildParameters = mockBuildParameters(environment: BuildEnvironment( - platform: .macOS, - configuration: .release - )) - var plan = try BuildPlan( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + var plan = try mockBuildPlan( + environment: BuildEnvironment( + platform: .macOS, + configuration: .release + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -70,10 +68,10 @@ final class LLBuildManifestBuilderTests: XCTestCase { ) try llbuild.createProductCommand(buildProduct) - let basicReleaseCommandNames = [ - AbsolutePath("/path/to/build/release/exe.product/Objects.LinkFileList").pathString, - "", - "C.exe-release.exe", + var basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-\(plan.destinationBuildParameters.triple)-release.exe", ] XCTAssertEqual( @@ -83,13 +81,11 @@ final class LLBuildManifestBuilderTests: XCTestCase { // macOS, debug build - buildParameters = mockBuildParameters(environment: BuildEnvironment( - platform: .macOS, - configuration: .debug - )) - plan = try BuildPlan( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + plan = try mockBuildPlan( + environment: BuildEnvironment( + platform: .macOS, + configuration: .debug + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -98,20 +94,20 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) - let entitlementsCommandName = "C.exe-debug.exe-entitlements" - let basicDebugCommandNames = [ - AbsolutePath("/path/to/build/debug/exe.product/Objects.LinkFileList").pathString, - "", - "C.exe-debug.exe", + let entitlementsCommandName = "C.exe-\(plan.destinationBuildParameters.triple)-debug.exe-entitlements" + var basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-\(plan.destinationBuildParameters.triple)-debug.exe", ] XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), (basicDebugCommandNames + [ - AbsolutePath("/path/to/build/debug/exe-entitlement.plist").pathString, + AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe-entitlement.plist").pathString, entitlementsCommandName, ]).sorted() ) @@ -124,26 +120,24 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( entitlementsCommand.inputs, [ - .file("/path/to/build/debug/exe", isMutated: true), - .file("/path/to/build/debug/exe-entitlement.plist"), + .file("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe", isMutated: true), + .file("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe-entitlement.plist"), ] ) XCTAssertEqual( entitlementsCommand.outputs, [ - .virtual("exe-debug.exe-CodeSigning"), + .virtual("exe-\(plan.destinationBuildParameters.triple)-debug.exe-CodeSigning"), ] ) // Linux, release build - buildParameters = mockBuildParameters(environment: BuildEnvironment( - platform: .linux, - configuration: .release - )) - plan = try BuildPlan( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + plan = try mockBuildPlan( + environment: BuildEnvironment( + platform: .linux, + configuration: .release + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -155,6 +149,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-\(plan.destinationBuildParameters.triple)-release.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicReleaseCommandNames.sorted() @@ -162,13 +162,11 @@ final class LLBuildManifestBuilderTests: XCTestCase { // Linux, debug build - buildParameters = mockBuildParameters(environment: BuildEnvironment( - platform: .linux, - configuration: .debug - )) - plan = try BuildPlan( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + plan = try mockBuildPlan( + environment: BuildEnvironment( + platform: .linux, + configuration: .debug + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -177,12 +175,45 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-\(plan.destinationBuildParameters.triple)-debug.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicDebugCommandNames.sorted() ) } + + /// Verifies that two targets with the same name but different triples don't share same build manifest keys. + func testToolsBuildTriple() throws { + let (graph, fs, scope) = try macrosPackageGraph() + let productsTriple = Triple.x86_64MacOS + let toolsTriple = Triple.arm64Linux + + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters( + destination: .target, + shouldLinkStaticSwiftStdlib: true, + triple: productsTriple + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + triple: toolsTriple + ), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + + let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope) + let manifest = try builder.generateManifest(at: "/manifest") + + XCTAssertNotNil(manifest.commands["C.SwiftSyntax-aarch64-unknown-linux-gnu-debug-tool.module"]) + } } diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 52c15c327c7..8bd0a32f716 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -186,9 +186,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -388,9 +390,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -472,9 +476,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -598,9 +604,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -694,9 +702,11 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -724,33 +734,33 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } @@ -812,9 +822,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -842,23 +854,23 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(result.plan.destinationBuildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } @@ -986,9 +998,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -1315,9 +1329,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -1420,9 +1436,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -1621,9 +1639,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -1796,9 +1816,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2009,9 +2031,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2226,9 +2250,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2403,9 +2429,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2547,9 +2575,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2690,9 +2720,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2840,9 +2872,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -2992,9 +3026,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3105,9 +3141,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3216,9 +3254,11 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3294,9 +3334,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3440,9 +3482,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3614,9 +3658,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3747,9 +3793,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -3875,9 +3923,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4016,9 +4066,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4188,9 +4240,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4358,9 +4412,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4525,9 +4581,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) @@ -4620,9 +4678,11 @@ final class ModuleAliasingBuildTests: XCTestCase { observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) - let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + let result = try BuildPlanResult(plan: try mockBuildPlan( graph: graph, + linkingParameters: .init( + shouldLinkStaticSwiftStdlib: true + ), fileSystem: fs, observabilityScope: observability.topScope )) diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 892c3da6725..7b2a1a78378 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -48,8 +48,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")) + ) + ) } // When cross compiling the final product, plugin dependencies should still be built for the host @@ -57,8 +65,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["--triple", targetTriple, "-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")) + ) + ) } } } diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift index f07646f64a5..e416331dc29 100644 --- a/Tests/BuildTests/ProductBuildDescriptionTests.swift +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -53,7 +53,7 @@ final class ProductBuildDescriptionTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let id = ResolvedProduct.ID(targetName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) + let id = ResolvedProduct.ID(productName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) let package = try XCTUnwrap(graph.rootPackages.first) let product = try XCTUnwrap(graph.allProducts[id]) @@ -61,7 +61,7 @@ final class ProductBuildDescriptionTests: XCTestCase { package: package, product: product, toolsVersion: .v5_9, - buildParameters: mockBuildParameters(environment: .init(platform: .macOS)), + buildParameters: mockBuildParameters(destination: .target, environment: .init(platform: .macOS)), fileSystem: fs, observabilityScope: observability.topScope ) diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 83d53e15b28..fc83de68471 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -1840,9 +1840,15 @@ final class PackageCommandTests: CommandsTestCase { """ ) let hostTriple = try UserToolchain(swiftSDK: .hostSwiftSDK()).targetTriple - let hostTripleString = hostTriple.isDarwin() ? hostTriple.tripleString(forPlatformVersion: "") : hostTriple.tripleString - try localFileSystem.writeFileContents(packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), string: - """ + let hostTripleString = if hostTriple.isDarwin() { + hostTriple.tripleString(forPlatformVersion: "") + } else { + hostTriple.tripleString + } + + try localFileSystem.writeFileContents( + packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), + string: """ { "schemaVersion": "1.0", "artifacts": { "LocalBinaryTool": { @@ -1858,11 +1864,13 @@ final class PackageCommandTests: CommandsTestCase { } """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), string: - #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), + string: #"print("Hello")"# ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), + string: """ import PackagePlugin import Foundation @main @@ -1923,8 +1931,9 @@ final class PackageCommandTests: CommandsTestCase { ) // Create the sample vendored dependency package. - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), + string: """ // swift-tools-version: 5.5 import PackageDescription let package = Package( @@ -1950,9 +1959,25 @@ final class PackageCommandTests: CommandsTestCase { ) """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "HelperLibrary", "library.swift"), string: "public func Bar() { }" + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "HelperLibrary", + "library.swift" + ), + string: "public func Bar() { }" ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "RemoteBuiltTool", "main.swift"), string: #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "RemoteBuiltTool", + "main.swift" + ), + string: #"print("Hello")"# ) // Check that we can invoke the plugin with the "plugin" subcommand. @@ -3070,9 +3095,9 @@ final class PackageCommandTests: CommandsTestCase { let execProducts = context.package.products(ofType: ExecutableProduct.self) print("execProducts: \\(execProducts.map{ $0.name })") let swiftTargets = context.package.targets(ofType: SwiftSourceModuleTarget.self) - print("swiftTargets: \\(swiftTargets.map{ $0.name })") + print("swiftTargets: \\(swiftTargets.map{ $0.name }.sorted())") let swiftSources = swiftTargets.flatMap{ $0.sourceFiles(withSuffix: ".swift") } - print("swiftSources: \\(swiftSources.map{ $0.path.lastComponent })") + print("swiftSources: \\(swiftSources.map{ $0.path.lastComponent }.sorted())") if let target = target.sourceModule { print("Module kind of '\\(target.name)': \\(target.kind)") @@ -3136,8 +3161,8 @@ final class PackageCommandTests: CommandsTestCase { do { let (stdout, _) = try SwiftPM.Package.execute(["print-target-dependencies", "--target", "FifthTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("execProducts: [\"FifthTarget\"]")) - XCTAssertMatch(stdout, .contains("swiftTargets: [\"ThirdTarget\", \"TestTarget\", \"SecondTarget\", \"FourthTarget\", \"FirstTarget\", \"FifthTarget\"]")) - XCTAssertMatch(stdout, .contains("swiftSources: [\"library.swift\", \"tests.swift\", \"library.swift\", \"library.swift\", \"library.swift\", \"main.swift\"]")) + XCTAssertMatch(stdout, .contains("swiftTargets: [\"FifthTarget\", \"FirstTarget\", \"FourthTarget\", \"SecondTarget\", \"TestTarget\", \"ThirdTarget\"]")) + XCTAssertMatch(stdout, .contains("swiftSources: [\"library.swift\", \"library.swift\", \"library.swift\", \"library.swift\", \"main.swift\", \"tests.swift\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'FifthTarget': executable")) } diff --git a/Tests/CommandsTests/SwiftCommandStateTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift index cbd580fa1fc..e7204aad341 100644 --- a/Tests/CommandsTests/SwiftCommandStateTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -255,7 +255,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "dwarf"]) let explicitDwarf = try SwiftCommandState.makeMockState(options: explicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: explicitDwarf.productsBuildParameters, + destinationBuildParameters: explicitDwarf.productsBuildParameters, toolsBuildParameters: explicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -270,7 +270,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitCodeView = try SwiftCommandState.makeMockState(options: explicitCodeViewOptions) plan = try BuildPlan( - productsBuildParameters: explicitCodeView.productsBuildParameters, + destinationBuildParameters: explicitCodeView.productsBuildParameters, toolsBuildParameters: explicitCodeView.productsBuildParameters, graph: graph, fileSystem: fs, @@ -293,7 +293,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let implicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc"]) let implicitDwarf = try SwiftCommandState.makeMockState(options: implicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: implicitDwarf.productsBuildParameters, + destinationBuildParameters: implicitDwarf.productsBuildParameters, toolsBuildParameters: implicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -306,7 +306,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitNoDebugInfoOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "none"]) let explicitNoDebugInfo = try SwiftCommandState.makeMockState(options: explicitNoDebugInfoOptions) plan = try BuildPlan( - productsBuildParameters: explicitNoDebugInfo.productsBuildParameters, + destinationBuildParameters: explicitNoDebugInfo.productsBuildParameters, toolsBuildParameters: explicitNoDebugInfo.toolsBuildParameters, graph: graph, fileSystem: fs, diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 7abb495a067..0ec57d05101 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -225,7 +225,7 @@ class PluginTests: XCTestCase { try testWithTemporaryDirectory { tmpPath in let packageDir = tmpPath.appending(components: "MyPackage") - let pathOfGeneratedFile = packageDir.appending(components: [".build", "plugins", "outputs", "mypackage", "SomeTarget", "Plugin", "best.txt"]) + let pathOfGeneratedFile = packageDir.appending(components: [".build", "plugins", "outputs", "mypackage", "SomeTarget", "destination", "Plugin", "best.txt"]) try createPackageUnderTest(packageDir: packageDir, toolsVersion: .v5_9) let (_, stderr) = try executeSwiftBuild(packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index c688b1d5439..6073cc22896 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -164,9 +164,9 @@ final class PackageGraphPerfTests: XCTestCasePerf { } func testRecursiveDependencies() throws { - var resolvedTarget = ResolvedTarget.mock(packageIdentity: "pkg", name: "t0") + var resolvedTarget = ResolvedModule.mock(packageIdentity: "pkg", name: "t0") for i in 1..<1000 { - resolvedTarget = ResolvedTarget.mock(packageIdentity: "pkg", name: "t\(i)", deps: resolvedTarget) + resolvedTarget = ResolvedModule.mock(packageIdentity: "pkg", name: "t\(i)", deps: resolvedTarget) } let N = 10 diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift new file mode 100644 index 00000000000..4c9b083aeea --- /dev/null +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 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 +// +//===----------------------------------------------------------------------===// + +@testable +import SPMTestSupport + +@testable +import PackageGraph + +import XCTest + +final class CrossCompilationPackageGraphTests: XCTestCase { + func testTrivialPackage() throws { + let graph = try trivialPackageGraph().graph + try PackageGraphTester(graph) { result in + result.check(packages: "Pkg") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check(targets: "app", "lib") + result.check(testModules: "test") + result.checkTarget("app") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "lib") + } + try result.checkTargets("lib") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(dependencies: []) + } + result.checkTarget("test") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "lib") + } + } + } + + func testMacros() throws { + let graph = try macrosPackageGraph().graph + try PackageGraphTester(graph) { result in + result.check(packages: "swift-firmware", "swift-mmio", "swift-syntax") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check( + targets: "Core", + "HAL", + "MMIO", + "MMIOMacros", + "SwiftSyntax", + "SwiftSyntax" + ) + result.check(testModules: "CoreTests", "HALTests") + try result.checkTargets("Core") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(dependencies: "HAL") + } + try result.checkTargets("HAL") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(buildTriple: .destination) + result.check(dependencies: "MMIO") + } + try result.checkTargets("MMIO") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(buildTriple: .destination) + result.check(dependencies: "MMIOMacros") + } + try result.checkTargets("MMIOMacros") { results in + let result = try XCTUnwrap(results.first(where: { $0.target.buildTriple == .tools })) + result.check(buildTriple: .tools) + result.checkDependency("SwiftSyntax") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + result.checkTarget("SwiftSyntax") { result in + result.check(buildTriple: .tools) + } + } + } + } + + result.checkTargets("SwiftSyntax") { results in + XCTAssertEqual(results.count, 2) + + XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) + XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + } + } + } + + func testMacrosTests() throws { + let graph = try macrosTestsPackageGraph().graph + PackageGraphTester(graph) { result in + result.check(packages: "swift-mmio", "swift-syntax") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check( + targets: "MMIO", + "MMIOMacros", + "SwiftCompilerPlugin", + "SwiftCompilerPlugin", + "SwiftCompilerPluginMessageHandling", + "SwiftCompilerPluginMessageHandling", + "SwiftSyntax", + "SwiftSyntax", + "SwiftSyntaxMacros", + "SwiftSyntaxMacros", + "SwiftSyntaxMacrosTestSupport", + "SwiftSyntaxMacrosTestSupport" + ) + result.check(testModules: "MMIOMacrosTests") + result.checkTarget("MMIO") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "MMIOMacros") + } + result.checkTargets("MMIOMacros") { results in + XCTAssertEqual(results.count, 1) + } + result.checkTarget("MMIOMacrosTests", destination: .tools) { result in + result.check(buildTriple: .tools) + result.checkDependency("MMIOMacros") { result in + result.checkTarget { result in + result.check(buildTriple: .tools) + result.checkDependency("SwiftSyntaxMacros") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + } + } + result.checkDependency("SwiftCompilerPlugin") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + result.checkTarget("SwiftCompilerPlugin") { result in + result.check(buildTriple: .tools) + result.checkDependency("SwiftCompilerPluginMessageHandling") { result in + result.checkTarget { result in + result.check(buildTriple: .tools) + } + } + } + } + } + } + } + } + + result.checkTargets("SwiftSyntax") { results in + XCTAssertEqual(results.count, 2) + + XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) + XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + } + } + } +} diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index 98e76341893..e0017803f9b 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -81,12 +81,12 @@ final class ModulesGraphTests: XCTestCase { } let fooPackage = try XCTUnwrap(g.package(for: .plain("Foo"))) - let fooTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "Foo" }) - let fooDepTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "FooDep" }) + let fooTarget = try XCTUnwrap(g.target(for: "Foo", destination: .destination)) + let fooDepTarget = try XCTUnwrap(g.target(for: "FooDep", destination: .destination)) XCTAssertEqual(g.package(for: fooTarget)?.id, fooPackage.id) XCTAssertEqual(g.package(for: fooDepTarget)?.id, fooPackage.id) let barPackage = try XCTUnwrap(g.package(for: .plain("Bar"))) - let barTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "Bar" }) + let barTarget = try XCTUnwrap(g.target(for: "Bar", destination: .destination)) XCTAssertEqual(g.package(for: barTarget)?.id, barPackage.id) } diff --git a/Tests/PackageGraphTests/ResolvedTargetTests.swift b/Tests/PackageGraphTests/ResolvedTargetTests.swift index 5c10466603e..5546a9cc267 100644 --- a/Tests/PackageGraphTests/ResolvedTargetTests.swift +++ b/Tests/PackageGraphTests/ResolvedTargetTests.swift @@ -17,29 +17,29 @@ import PackageGraph import SPMTestSupport private func XCTAssertEqualTargetIDs( - _ lhs: [ResolvedTarget], - _ rhs: [ResolvedTarget], + _ lhs: [ResolvedModule], + _ rhs: [ResolvedModule], file: StaticString = #filePath, line: UInt = #line ) { XCTAssertEqual(lhs.map(\.id), rhs.map(\.id), file: file, line: line) } -final class ResolvedTargetDependencyTests: XCTestCase { +final class ResolvedModuleDependencyTests: XCTestCase { func test1() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) XCTAssertEqualTargetIDs(try t2.recursiveTargetDependencies(), [t1]) } func test2() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t2, t3, t1) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t2, t3, t1) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -47,10 +47,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test3() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t1, t2, t3) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t1, t2, t3) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -58,10 +58,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test4() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -69,12 +69,12 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test5() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) - let t5 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t5", deps: t2) - let t6 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t6", deps: t5, t4) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t5 = ResolvedModule.mock(packageIdentity: "pkg", name: "t5", deps: t2) + let t6 = ResolvedModule.mock(packageIdentity: "pkg", name: "t6", deps: t5, t4) // precise order is not important, but it is important that the following are true let t6rd = try t6.recursiveTargetDependencies().map(\.id) @@ -91,12 +91,12 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test6() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) - let t5 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t5", deps: t2) - let t6 = ResolvedTarget.mock( + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t5 = ResolvedModule.mock(packageIdentity: "pkg", name: "t5", deps: t2) + let t6 = ResolvedModule.mock( packageIdentity: "pkg", name: "t6", deps: t4, @@ -118,10 +118,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func testConditions() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t2NoConditions = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t2WithConditions = ResolvedTarget.mock( + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t2NoConditions = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t2WithConditions = ResolvedModule.mock( packageIdentity: "pkg", name: "t2", deps: t1, diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index ab2b46e8785..9f168c99452 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -33,11 +33,12 @@ final class PluginInvocationTests: XCTestCase { let fileSystem = InMemoryFileSystem(emptyFiles: "/Foo/Plugins/FooPlugin/source.swift", "/Foo/Sources/FooTool/source.swift", + "/Foo/Sources/FooToolLib/source.swift", "/Foo/Sources/Foo/source.swift", "/Foo/Sources/Foo/SomeFile.abc" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -64,9 +65,14 @@ final class PluginInvocationTests: XCTestCase { ), TargetDescription( name: "FooTool", - dependencies: [], + dependencies: ["FooToolLib"], type: .executable ), + TargetDescription( + name: "FooToolLib", + dependencies: [], + type: .regular + ), ] ) ], @@ -77,16 +83,25 @@ final class PluginInvocationTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) PackageGraphTester(graph) { graph in graph.check(packages: "Foo") - graph.check(targets: "Foo", "FooPlugin", "FooTool") + // "FooTool{Lib}" duplicated as it's present for both build tools and end products triples. + graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool", "FooToolLib", "FooToolLib") graph.checkTarget("Foo") { target in target.check(dependencies: "FooPlugin") } - graph.checkTarget("FooPlugin") { target in + graph.checkTarget("FooPlugin", destination: .tools) { target in target.check(type: .plugin) target.check(dependencies: "FooTool") } - graph.checkTarget("FooTool") { target in - target.check(type: .executable) + for destination: BuildTriple in [.tools, .destination] { + graph.checkTarget("FooTool", destination: destination) { target in + target.check(type: .executable) + target.check(buildTriple: destination) + target.checkDependency("FooToolLib") { dependency in + dependency.checkTarget { + $0.check(buildTriple: destination) + } + } + } } } @@ -191,13 +206,14 @@ final class PluginInvocationTests: XCTestCase { // Construct a canned input and run plugins using our MockPluginScriptRunner(). let outputDir = AbsolutePath("/Foo/.build") - let builtToolsDir = AbsolutePath("/path/to/build/debug") let pluginRunner = MockPluginScriptRunner() + let buildParameters = mockBuildParameters( + destination: .host, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) let results = try graph.invokeBuildToolPlugins( outputDir: outputDir, - buildParameters: mockBuildParameters( - environment: BuildEnvironment(platform: .macOS, configuration: .debug) - ), + buildParameters: buildParameters, additionalFileRules: [], toolSearchDirectories: [UserToolchain.default.swiftCompilerPath.parentDirectory], pkgConfigDirectories: [], @@ -205,11 +221,12 @@ final class PluginInvocationTests: XCTestCase { observabilityScope: observability.topScope, fileSystem: fileSystem ) + let builtToolsDir = AbsolutePath("/path/to/build/\(buildParameters.triple)/debug") // Check the canned output to make sure nothing was lost in transport. XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(results.count, 1) - let (evalTargetID, (evalTarget, evalResults)) = try XCTUnwrap(results.first) + let (_, (evalTarget, evalResults)) = try XCTUnwrap(results.first) XCTAssertEqual(evalTarget.name, "Foo") XCTAssertEqual(evalResults.count, 1) @@ -895,6 +912,7 @@ final class PluginInvocationTests: XCTestCase { let result = try packageGraph.invokeBuildToolPlugins( outputDir: outputDir, buildParameters: mockBuildParameters( + destination: .host, environment: BuildEnvironment(platform: .macOS, configuration: .debug) ), additionalFileRules: [], @@ -1084,7 +1102,6 @@ final class PluginInvocationTests: XCTestCase { } } - XCTAssertEqual(count, 2) } } @@ -1092,7 +1109,7 @@ final class PluginInvocationTests: XCTestCase { func checkParseArtifactsPlatformCompatibility( artifactSupportedTriples: [Triple], hostTriple: Triple - ) async throws -> [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] { + ) async throws -> [ResolvedModule.ID: [BuildToolPluginInvocationResult]] { // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") @@ -1240,6 +1257,7 @@ final class PluginInvocationTests: XCTestCase { return try packageGraph.invokeBuildToolPlugins( outputDir: outputDir, buildParameters: mockBuildParameters( + destination: .host, environment: BuildEnvironment(platform: .macOS, configuration: .debug) ), additionalFileRules: [], diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 29691c493d9..8ca021945c8 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -27,7 +27,7 @@ class SourceKitLSPAPITests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -43,22 +43,53 @@ class SourceKitLSPAPITests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let plan = try BuildPlan( - productsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), - toolsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + destinationBuildParameters: mockBuildParameters( + destination: .target, + shouldLinkStaticSwiftStdlib: true + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + shouldLinkStaticSwiftStdlib: true + ), graph: graph, fileSystem: fs, observabilityScope: observability.topScope ) let description = BuildDescription(buildPlan: plan) - try description.checkArguments(for: "exe", graph: graph, partialArguments: ["-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"], isPartOfRootPackage: true) - try description.checkArguments(for: "lib", graph: graph, partialArguments: ["-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"], isPartOfRootPackage: true) + try description.checkArguments( + for: "exe", + graph: graph, + partialArguments: [ + "-module-name", "exe", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(plan.destinationBuildParameters.triple)/debug/exe.build/exe.swiftmodule" + ], + isPartOfRootPackage: true + ) + try description.checkArguments( + for: "lib", + graph: graph, + partialArguments: [ + "-module-name", "lib", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(plan.destinationBuildParameters.triple)/debug/Modules/lib.swiftmodule" + ], + isPartOfRootPackage: true + ) } } extension SourceKitLSPAPI.BuildDescription { - @discardableResult func checkArguments(for targetName: String, graph: ModulesGraph, partialArguments: [String], isPartOfRootPackage: Bool) throws -> Bool { - let target = try XCTUnwrap(graph.allTargets.first(where: { $0.name == targetName })) + @discardableResult func checkArguments( + for targetName: String, + graph: ModulesGraph, + partialArguments: [String], + isPartOfRootPackage: Bool + ) throws -> Bool { + let target = try XCTUnwrap(graph.target(for: targetName, destination: .destination)) let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target, in: graph)) guard let file = buildTarget.sources.first else {