diff --git a/Package.swift b/Package.swift index e1638eb73c3..4a9ce128905 100644 --- a/Package.swift +++ b/Package.swift @@ -308,6 +308,14 @@ let package = Package( "SPMBuildCore", "SPMLLBuild", .product(name: "SwiftDriver", package: "swift-driver"), + "DriverSupport", + ], + exclude: ["CMakeLists.txt"] + ), + .target( + name: "DriverSupport", + dependencies: [ + .product(name: "SwiftDriver", package: "swift-driver"), ], exclude: ["CMakeLists.txt"] ), diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 21f6aad5cb8..a67cf26a99b 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +@_implementationOnly import DriverSupport import LLBuildManifest import PackageGraph import PackageLoading @@ -57,7 +58,17 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS public let cacheBuildManifest: Bool /// The build plan that was computed, if any. - public private(set) var buildPlan: BuildPlan? + public private(set) var _buildPlan: BuildPlan? + + public var buildPlan: SPMBuildCore.BuildPlan { + get throws { + if let buildPlan = _buildPlan { + return buildPlan + } else { + throw StringError("did not compute a build plan yet") + } + } + } /// The build description resulting from planing. private let buildDescription = ThreadSafeBox() @@ -166,7 +177,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS return } // Ensure the compiler supports the import-scan operation - guard SwiftTargetBuildDescription.checkSupportedFrontendFlags(flags: ["import-prescan"], fileSystem: localFileSystem) else { + guard DriverSupport.checkSupportedFrontendFlags(flags: ["import-prescan"], fileSystem: localFileSystem) else { return } @@ -457,7 +468,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) - self.buildPlan = plan + self._buildPlan = plan let (buildDescription, buildManifest) = try BuildDescription.create( with: plan, @@ -568,14 +579,14 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS public func provideBuildErrorAdvice(for target: String, command: String, message: String) -> String? { // Find the target for which the error was emitted. If we don't find it, we can't give any advice. - guard let _ = self.buildPlan?.targets.first(where: { $0.target.name == target }) else { return nil } + guard let _ = self._buildPlan?.targets.first(where: { $0.target.name == target }) else { return nil } // Check for cases involving modules that cannot be found. if let importedModule = try? RegEx(pattern: "no such module '(.+)'").matchGroups(in: message).first?.first { // A target is importing a module that can't be found. We take a look at the build plan and see if can offer any advice. // Look for a target with the same module name as the one that's being imported. - if let importedTarget = self.buildPlan?.targets.first(where: { $0.target.c99name == importedModule }) { + if let importedTarget = self._buildPlan?.targets.first(where: { $0.target.c99name == importedModule }) { // For the moment we just check for executables that other targets try to import. if importedTarget.target.type == .executable { return "module '\(importedModule)' is the main module of an executable, and cannot be imported by tests and other targets" diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 0f2e1a7392a..5ec977c23f7 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -354,7 +354,7 @@ public struct BuildDescription: Codable { self.builtTestProducts = plan.buildProducts.filter{ $0.product.type == .test }.map { desc in return BuiltTestProduct( productName: desc.product.name, - binaryPath: desc.binary + binaryPath: desc.binaryPath ) } self.pluginDescriptions = pluginDescriptions diff --git a/Sources/Build/BuildPlan.swift b/Sources/Build/BuildPlan.swift index 3067e11651f..30c47752dbf 100644 --- a/Sources/Build/BuildPlan.swift +++ b/Sources/Build/BuildPlan.swift @@ -851,16 +851,6 @@ public final class SwiftTargetBuildDescription { try self.fileSystem.writeIfChanged(path: path, bytes: stream.bytes) } - public static func checkSupportedFrontendFlags(flags: Set, fileSystem: FileSystem) -> Bool { - do { - let executor = try SPMSwiftDriverExecutor(resolver: ArgsResolver(fileSystem: fileSystem), fileSystem: fileSystem, env: [:]) - let driver = try Driver(args: ["swiftc"], executor: executor) - return driver.supportedFrontendFlags.intersection(flags) == flags - } catch { - return false - } - } - /// The arguments needed to compile this target. public func compileArguments() throws -> [String] { var args = [String]() @@ -1262,7 +1252,7 @@ public final class SwiftTargetBuildDescription { } /// The build description for a product. -public final class ProductBuildDescription { +public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription { /// The reference to the product. public let package: ResolvedPackage @@ -1276,12 +1266,7 @@ public final class ProductBuildDescription { public let toolsVersion: ToolsVersion /// The build parameters. - let buildParameters: BuildParameters - - /// The path to the product binary produced. - public var binary: AbsolutePath { - return buildParameters.binaryPath(for: product) - } + public let buildParameters: BuildParameters /// All object files to link into this product. /// @@ -1387,12 +1372,12 @@ public final class ProductBuildDescription { let librarian = buildParameters.toolchain.librarianPath.pathString let triple = buildParameters.triple if triple.isWindows(), librarian.hasSuffix("link") || librarian.hasSuffix("link.exe") { - return [librarian, "/LIB", "/OUT:\(binary.pathString)", "@\(linkFileListPath.pathString)"] + return [librarian, "/LIB", "/OUT:\(binaryPath.pathString)", "@\(linkFileListPath.pathString)"] } if triple.isDarwin(), librarian.hasSuffix("libtool") { - return [librarian, "-o", binary.pathString, "@\(linkFileListPath.pathString)"] + return [librarian, "-o", binaryPath.pathString, "@\(linkFileListPath.pathString)"] } - return [librarian, "crs", binary.pathString, "@\(linkFileListPath.pathString)"] + return [librarian, "crs", binaryPath.pathString, "@\(linkFileListPath.pathString)"] } /// The arguments to link and create this product. @@ -1416,7 +1401,7 @@ public final class ProductBuildDescription { } args += ["-L", buildParameters.buildPath.pathString] - args += ["-o", binary.pathString] + args += ["-o", binaryPath.pathString] args += ["-module-name", product.name.spm_mangledToC99ExtendedIdentifier()] args += dylibs.map({ "-l" + $0.product.name }) @@ -1639,7 +1624,7 @@ public final class PluginDescription: Codable { } /// A build plan for a package graph. -public class BuildPlan { +public class BuildPlan: SPMBuildCore.BuildPlan { public enum Error: Swift.Error, CustomStringConvertible, Equatable { /// There is no buildable target in the graph. @@ -1680,8 +1665,8 @@ public class BuildPlan { } /// The products in this plan. - public var buildProducts: AnySequence { - return AnySequence(productMap.values) + public var buildProducts: AnySequence { + return AnySequence(productMap.values.map { $0 as SPMBuildCore.ProductBuildDescription }) } /// The results of invoking any build tool plugins used by targets in this build. @@ -2055,7 +2040,7 @@ public class BuildPlan { // Plan products. for buildProduct in buildProducts { - try plan(buildProduct) + try plan(buildProduct as! ProductBuildDescription) } // FIXME: We need to find out if any product has a target on which it depends // both static and dynamically and then issue a suitable diagnostic or auto diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index 2399f929a83..53d87f840b3 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -11,7 +11,6 @@ add_library(Build BuildOperation.swift BuildPlan.swift LLBuildManifestBuilder.swift - SPMSwiftDriverExecutor.swift SwiftCompilerOutputParser.swift) target_link_libraries(Build PUBLIC TSCBasic @@ -21,6 +20,7 @@ target_link_libraries(Build PUBLIC SPMBuildCore SPMLLBuild) target_link_libraries(Build PRIVATE + DriverSupport SwiftDriver) # NOTE(compnerd) workaround for CMake not setting up include flags yet diff --git a/Sources/Build/LLBuildManifestBuilder.swift b/Sources/Build/LLBuildManifestBuilder.swift index 31d9b8e2af6..83530e24bae 100644 --- a/Sources/Build/LLBuildManifestBuilder.swift +++ b/Sources/Build/LLBuildManifestBuilder.swift @@ -15,6 +15,7 @@ import PackageGraph import PackageModel import LLBuildManifest import SPMBuildCore +@_implementationOnly import DriverSupport @_implementationOnly import SwiftDriver import TSCBasic @@ -559,7 +560,7 @@ extension LLBuildManifestBuilder { guard let planProduct = plan.productMap[product] else { throw InternalError("unknown product \(product)") } - inputs.append(file: planProduct.binary) + inputs.append(file: planProduct.binaryPath) } return } @@ -588,7 +589,7 @@ extension LLBuildManifestBuilder { throw InternalError("unknown product \(product)") } // Establish a dependency on binary of the product. - inputs.append(file: planProduct.binary) + inputs.append(file: planProduct.binaryPath) // For automatic and static libraries, and plugins, add their targets as static input. case .library(.automatic), .library(.static), .plugin: @@ -742,7 +743,7 @@ extension LLBuildManifestBuilder { throw InternalError("unknown product \(product)") } // Establish a dependency on binary of the product. - let binary = planProduct.binary + let binary = planProduct.binaryPath inputs.append(file: binary) case .library(.automatic), .library(.static), .plugin: @@ -894,20 +895,20 @@ extension LLBuildManifestBuilder { case .library(.static): manifest.addShellCmd( name: cmdName, - description: "Archiving \(buildProduct.binary.prettyPath())", + description: "Archiving \(buildProduct.binaryPath.prettyPath())", inputs: buildProduct.objects.map(Node.file), - outputs: [.file(buildProduct.binary)], + outputs: [.file(buildProduct.binaryPath)], arguments: try buildProduct.archiveArguments() ) default: - let inputs = buildProduct.objects + buildProduct.dylibs.map({ $0.binary }) + let inputs = buildProduct.objects + buildProduct.dylibs.map({ $0.binaryPath }) manifest.addShellCmd( name: cmdName, - description: "Linking \(buildProduct.binary.prettyPath())", + description: "Linking \(buildProduct.binaryPath.prettyPath())", inputs: inputs.map(Node.file), - outputs: [.file(buildProduct.binary)], + outputs: [.file(buildProduct.binaryPath)], arguments: try buildProduct.linkArguments() ) } @@ -919,7 +920,7 @@ extension LLBuildManifestBuilder { manifest.addNode(output, toTarget: targetName) manifest.addPhonyCmd( name: output.name, - inputs: [.file(buildProduct.binary)], + inputs: [.file(buildProduct.binaryPath)], outputs: [output] ) diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 0bcdaf70ccd..cd9d43d990c 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(SPMSQLite3) add_subdirectory(Basics) add_subdirectory(Build) add_subdirectory(Commands) +add_subdirectory(DriverSupport) add_subdirectory(LLBuildManifest) add_subdirectory(PackageCollections) add_subdirectory(PackageCollectionsModel) diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index 98b8f034e43..fcbe7daa771 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -7,7 +7,6 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_library(Commands - Options.swift Snippets/CardEvent.swift Snippets/Cards/SnippetCard.swift @@ -44,6 +43,7 @@ target_link_libraries(Commands PUBLIC Workspace XCBuildSupport) target_link_libraries(Commands PRIVATE + DriverSupport $<$>:FoundationXML>) # NOTE(compnerd) workaround for CMake not setting up include flags yet set_target_properties(Commands PROPERTIES diff --git a/Sources/Commands/Options.swift b/Sources/Commands/Options.swift index 0b73c305cb8..b548cf3df9b 100644 --- a/Sources/Commands/Options.swift +++ b/Sources/Commands/Options.swift @@ -15,7 +15,6 @@ import TSCBasic import PackageFingerprint import PackageModel import SPMBuildCore -import Build import struct TSCUtility.Triple @@ -346,9 +345,9 @@ struct BuildOptions: ParsableArguments { /// The build system to use. @Option(name: .customLong("build-system")) - var _buildSystem: BuildSystemKind = .native + var _buildSystem: BuildSystemProvider.Kind = .native - var buildSystem: BuildSystemKind { + var buildSystem: BuildSystemProvider.Kind { #if os(macOS) // Force the Xcode build system if we want to build more than one arch. return archs.count > 1 ? .xcode : self._buildSystem @@ -377,11 +376,6 @@ struct BuildOptions: ParsableArguments { case disableIndexStore } - enum BuildSystemKind: String, ExpressibleByArgument, CaseIterable { - case native - case xcode - } - enum TargetDependencyImportCheckingMode : String, Codable, ExpressibleByArgument { case none case warn @@ -462,3 +456,6 @@ extension Sanitizer { return Sanitizer.allCases.map(\.rawValue).joined(separator: ", ") } } + +extension BuildSystemProvider.Kind: ExpressibleByArgument { +} diff --git a/Sources/Commands/SwiftBuildTool.swift b/Sources/Commands/SwiftBuildTool.swift index 73ea08b128c..cb2081c1aba 100644 --- a/Sources/Commands/SwiftBuildTool.swift +++ b/Sources/Commands/SwiftBuildTool.swift @@ -105,7 +105,10 @@ public struct SwiftBuildTool: SwiftCommand { } if options.printManifestGraphviz { - let buildOperation = try swiftTool.createBuildOperation() + // FIXME: Doesn't seem ideal that we need an explicit build operation, but this concretely uses the `LLBuildManifest`. + guard let buildOperation = try swiftTool.createBuildSystem(explicitBuildSystem: .native) as? BuildOperation else { + throw StringError("asked for native build system but did not get it") + } let buildManifest = try buildOperation.getBuildManifest() var serializer = DOTManifestSerializer(manifest: buildManifest) // print to stdout diff --git a/Sources/Commands/SwiftPackageRegistryTool.swift b/Sources/Commands/SwiftPackageRegistryTool.swift index f9a34a7cd79..3c73a672fa8 100644 --- a/Sources/Commands/SwiftPackageRegistryTool.swift +++ b/Sources/Commands/SwiftPackageRegistryTool.swift @@ -14,12 +14,10 @@ import ArgumentParser import Basics import TSCBasic import SPMBuildCore -import Build import PackageModel import PackageLoading import PackageGraph import SourceControl -import XCBuildSupport import Workspace import Foundation import PackageRegistry diff --git a/Sources/Commands/SwiftPackageTool.swift b/Sources/Commands/SwiftPackageTool.swift index 6d87fc3ffe6..ce2d9b42b72 100644 --- a/Sources/Commands/SwiftPackageTool.swift +++ b/Sources/Commands/SwiftPackageTool.swift @@ -14,7 +14,6 @@ import ArgumentParser import Basics import TSCBasic import SPMBuildCore -import Build import PackageModel import PackageLoading import PackageGraph @@ -406,22 +405,22 @@ extension SwiftPackageTool { let baselineRevision = try repository.resolveRevision(identifier: treeish) // We turn build manifest caching off because we need the build plan. - let buildOp = try swiftTool.createBuildOperation(cacheBuildManifest: false) + let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) - let packageGraph = try buildOp.getPackageGraph() + let packageGraph = try buildSystem.getPackageGraph() let modulesToDiff = try determineModulesToDiff( packageGraph: packageGraph, observabilityScope: swiftTool.observabilityScope ) // Build the current package. - try buildOp.build() + try buildSystem.build() // Dump JSON for the baseline package. let baselineDumper = try APIDigesterBaselineDumper( baselineRevision: baselineRevision, packageRoot: swiftTool.getPackageRoot(), - buildParameters: buildOp.buildParameters, + buildParameters: try buildSystem.buildPlan.buildParameters, apiDigesterTool: apiDigesterTool, observabilityScope: swiftTool.observabilityScope ) @@ -436,7 +435,7 @@ extension SwiftPackageTool { let results = ThreadSafeArrayStore() let group = DispatchGroup() - let semaphore = DispatchSemaphore(value: Int(buildOp.buildParameters.jobs)) + let semaphore = DispatchSemaphore(value: Int(try buildSystem.buildPlan.buildParameters.jobs)) var skippedModules: Set = [] for module in modulesToDiff { @@ -452,7 +451,7 @@ extension SwiftPackageTool { if let comparisonResult = try apiDigesterTool.compareAPIToBaseline( at: moduleBaselinePath, for: module, - buildPlan: buildOp.buildPlan!, + buildPlan: try buildSystem.buildPlan, except: breakageAllowlistPath ) { results.append(comparisonResult) @@ -594,8 +593,8 @@ extension SwiftPackageTool { // Build the current package. // // We turn build manifest caching off because we need the build plan. - let buildOp = try swiftTool.createBuildOperation(cacheBuildManifest: false) - try buildOp.build() + let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) + try buildSystem.build() // Configure the symbol graph extractor. let symbolGraphExtractor = try SymbolGraphExtract( @@ -609,9 +608,9 @@ extension SwiftPackageTool { ) // Run the tool once for every library and executable target in the root package. - let buildPlan = buildOp.buildPlan! + let buildPlan = try buildSystem.buildPlan let symbolGraphDirectory = buildPlan.buildParameters.dataPath.appending(component: "symbolgraph") - let targets = buildPlan.graph.rootPackages.flatMap{ $0.targets }.filter{ $0.type == .library || $0.type == .executable } + let targets = try buildSystem.getPackageGraph().rootPackages.flatMap{ $0.targets }.filter{ $0.type == .library || $0.type == .executable } for target in targets { print("-- Emitting symbol graph for", target.name) try symbolGraphExtractor.extractSymbolGraph( @@ -666,14 +665,12 @@ extension SwiftPackageTool { func run(_ swiftTool: SwiftTool) throws { let graph = try swiftTool.loadPackageGraph() - let parameters = try PIFBuilderParameters(swiftTool.buildParameters()) - let builder = PIFBuilder( - graph: graph, - parameters: parameters, + let pif = try PIFBuilder.generatePIF( + buildParameters: swiftTool.buildParameters(), + packageGraph: graph, fileSystem: swiftTool.fileSystem, - observabilityScope: swiftTool.observabilityScope - ) - let pif = try builder.generatePIF(preservePIFModelStructure: preserveStructure) + observabilityScope: swiftTool.observabilityScope, + preservePIFModelStructure: preserveStructure) print(pif) } @@ -1083,13 +1080,13 @@ extension SwiftPackageTool { // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. var toolNamesToPaths: [String: AbsolutePath] = [:] for dep in try plugin.accessibleTools(packageGraph: packageGraph, fileSystem: swiftTool.fileSystem, environment: try swiftTool.buildParameters().buildEnvironment, for: try pluginScriptRunner.hostTriple) { - let buildOperation = try swiftTool.createBuildOperation(cacheBuildManifest: false) + let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) switch dep { case .builtTool(let name, _): // 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 buildOperation.build(subset: .product(name)) - if let builtTool = buildOperation.buildPlan?.buildProducts.first(where: { $0.product.name == name}) { - toolNamesToPaths[name] = builtTool.binary + try buildSystem.build(subset: .product(name)) + if let builtTool = try buildSystem.buildPlan.buildProducts.first(where: { $0.product.name == name}) { + toolNamesToPaths[name] = builtTool.binaryPath } case .vendedTool(let name, let path): toolNamesToPaths[name] = path @@ -1218,7 +1215,8 @@ final class PluginDelegate: PluginInvocationDelegate { // Create a build operation. We have to disable the cache in order to get a build plan created. let outputStream = BufferedOutputByteStream() - let buildOperation = try self.swiftTool.createBuildOperation( + let buildSystem = try swiftTool.createBuildSystem( + explicitBuildSystem: .native, explicitProduct: explicitProduct, cacheBuildManifest: false, customBuildParameters: buildParameters, @@ -1227,15 +1225,10 @@ final class PluginDelegate: PluginInvocationDelegate { ) // Run the build. This doesn't return until the build is complete. - let success = buildOperation.buildIgnoringError(subset: buildSubset) - - // Get the build plan used - guard let buildPlan = buildOperation.buildPlan else { - throw InternalError("invalid state, buildPlan is undefined") - } + let success = buildSystem.buildIgnoringError(subset: buildSubset) // Create and return the build result record based on what the delegate collected and what's in the build plan. - let builtProducts = buildPlan.buildProducts.filter { + let builtProducts = try buildSystem.buildPlan.buildProducts.filter { switch subset { case .all(let includingTests): return includingTests ? true : $0.product.type != .test @@ -1248,9 +1241,9 @@ final class PluginDelegate: PluginInvocationDelegate { let builtArtifacts: [PluginInvocationBuildResult.BuiltArtifact] = builtProducts.compactMap { switch $0.product.type { case .library(let kind): - return .init(path: $0.binary.pathString, kind: (kind == .dynamic) ? .dynamicLibrary : .staticLibrary) + return .init(path: $0.binaryPath.pathString, kind: (kind == .dynamic) ? .dynamicLibrary : .staticLibrary) case .executable: - return .init(path: $0.binary.pathString, kind: .executable) + return .init(path: $0.binaryPath.pathString, kind: .executable) default: return nil } @@ -1403,17 +1396,17 @@ final class PluginDelegate: PluginInvocationDelegate { private func createSymbolGraphForPlugin(forTarget targetName: String, options: PluginInvocationSymbolGraphOptions) throws -> PluginInvocationSymbolGraphResult { // Current implementation uses `SymbolGraphExtract()` but in the future we should emit the symbol graph while building. - // Create a build operation for building the target., skipping the the cache because we need the build plan. - let buildOperation = try swiftTool.createBuildOperation(cacheBuildManifest: false) + // Create a build system for building the target., skipping the the cache because we need the build plan. + let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) // Find the target in the build operation's package graph; it's an error if we don't find it. - let packageGraph = try buildOperation.getPackageGraph() + let packageGraph = try buildSystem.getPackageGraph() guard let target = packageGraph.allTargets.first(where: { $0.name == targetName }) else { throw StringError("could not find a target named “\(targetName)”") } // Build the target, if needed. - try buildOperation.build(subset: .target(target.name)) + try buildSystem.build(subset: .target(target.name)) // Configure the symbol graph extractor. var symbolGraphExtractor = try SymbolGraphExtract( @@ -1437,19 +1430,16 @@ final class PluginDelegate: PluginInvocationDelegate { symbolGraphExtractor.includeSPISymbols = options.includeSPI // Determine the output directory, and remove any old version if it already exists. - guard let buildPlan = buildOperation.buildPlan else { - throw StringError("could not get the build plan from the build operation") - } guard let package = packageGraph.package(for: target) else { throw StringError("could not determine the package for target “\(target.name)”") } - let outputDir = buildPlan.buildParameters.dataPath.appending(components: "extracted-symbols", package.identity.description, target.name) + let outputDir = try buildSystem.buildPlan.buildParameters.dataPath.appending(components: "extracted-symbols", package.identity.description, target.name) try swiftTool.fileSystem.removeFileTree(outputDir) // Run the symbol graph extractor on the target. try symbolGraphExtractor.extractSymbolGraph( target: target, - buildPlan: buildPlan, + buildPlan: try buildSystem.buildPlan, outputRedirection: .collect, outputDirectory: outputDir, verboseOutput: self.swiftTool.logLevel <= .info @@ -1893,7 +1883,7 @@ private extension Basics.Diagnostic { } } -extension BuildOperation { +extension BuildSystem { fileprivate func buildIgnoringError(subset: BuildSubset) -> Bool { do { try self.build(subset: subset) diff --git a/Sources/Commands/SwiftRunTool.swift b/Sources/Commands/SwiftRunTool.swift index e125a75caef..8149759e55d 100644 --- a/Sources/Commands/SwiftRunTool.swift +++ b/Sources/Commands/SwiftRunTool.swift @@ -12,7 +12,6 @@ import ArgumentParser import Basics -import Build import PackageGraph import PackageModel import TSCBasic @@ -126,17 +125,18 @@ public struct SwiftRunTool: SwiftCommand { // Construct the build operation. // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the REPL. rdar://86112934 - let buildOp = try swiftTool.createBuildOperation( + let buildSystem = try swiftTool.createBuildSystem( + explicitBuildSystem: .native, cacheBuildManifest: false, customBuildParameters: buildParameters, customPackageGraphLoader: graphLoader ) // Perform build. - try buildOp.build() + try buildSystem.build() // Execute the REPL. - let arguments = try buildOp.buildPlan!.createREPLArguments() + let arguments = try buildSystem.buildPlan.createREPLArguments() print("Launching Swift REPL with arguments: \(arguments.joined(separator: " "))") try self.run( fileSystem: swiftTool.fileSystem, diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestTool.swift index de2f202a804..b7a43aa0dd2 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestTool.swift @@ -12,7 +12,6 @@ import ArgumentParser import Basics -import Build import Dispatch import class Foundation.NSLock import class Foundation.ProcessInfo diff --git a/Sources/Commands/SwiftTool.swift b/Sources/Commands/SwiftTool.swift index a82ddf79428..78e7b8cca57 100644 --- a/Sources/Commands/SwiftTool.swift +++ b/Sources/Commands/SwiftTool.swift @@ -12,8 +12,8 @@ import ArgumentParser import Basics -import Build import Dispatch +@_implementationOnly import DriverSupport import class Foundation.NSLock import class Foundation.ProcessInfo import OrderedCollections @@ -27,7 +27,6 @@ import class TSCUtility.MultiLineNinjaProgressAnimation import class TSCUtility.NinjaProgressAnimation import protocol TSCUtility.ProgressAnimationProtocol import Workspace -import XCBuildSupport #if canImport(WinSDK) import WinSDK @@ -44,6 +43,10 @@ import var TSCUtility.verbosity typealias Diagnostic = Basics.Diagnostic +// TODO: Refactor in upcoming PRs +import Build +import XCBuildSupport + private class ToolWorkspaceDelegate: WorkspaceDelegate { private struct DownloadProgress { let bytesDownloaded: Int64 @@ -216,12 +219,14 @@ protocol SwiftCommand: ParsableCommand { var globalOptions: GlobalOptions { get } var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { get } + func buildSystemProvider(_ swiftTool: SwiftTool) throws -> BuildSystemProvider func run(_ swiftTool: SwiftTool) throws } extension SwiftCommand { public func run() throws { let swiftTool = try SwiftTool(options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration) + swiftTool.buildSystemProvider = try buildSystemProvider(swiftTool) var toolError: Error? = .none do { try self.run(swiftTool) @@ -245,6 +250,37 @@ extension SwiftCommand { public var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { return .init() } + + public func buildSystemProvider(_ swiftTool: SwiftTool) throws -> BuildSystemProvider { + return .init(providers: [ + .native: { (explicitProduct: String?, cacheBuildManifest: Bool, customBuildParameters: BuildParameters?, customPackageGraphLoader: (() throws -> PackageGraph)?, customOutputStream: OutputByteStream?, customLogLevel: Diagnostic.Severity?, customObservabilityScope: ObservabilityScope?) throws -> BuildSystem in + let testEntryPointPath = customBuildParameters?.testProductStyle.explicitlySpecifiedEntryPointPath + let graphLoader = { try swiftTool.loadPackageGraph(explicitProduct: explicitProduct, testEntryPointPath: testEntryPointPath) } + return try BuildOperation( + buildParameters: customBuildParameters ?? swiftTool.buildParameters(), + cacheBuildManifest: cacheBuildManifest && swiftTool.canUseCachedBuildManifest(), + packageGraphLoader: customPackageGraphLoader ?? graphLoader, + additionalFileRules: FileRuleDescription.swiftpmFileTypes, + pluginScriptRunner: swiftTool.getPluginScriptRunner(), + pluginWorkDirectory: try swiftTool.getActiveWorkspace().location.pluginWorkingDirectory, + outputStream: customOutputStream ?? swiftTool.outputStream, + logLevel: customLogLevel ?? swiftTool.logLevel, + fileSystem: swiftTool.fileSystem, + observabilityScope: customObservabilityScope ?? swiftTool.observabilityScope) }, + .xcode: { (explicitProduct: String?, cacheBuildManifest: Bool, customBuildParameters: BuildParameters?, customPackageGraphLoader: (() throws -> PackageGraph)?, customOutputStream: OutputByteStream?, customLogLevel: Diagnostic.Severity?, customObservabilityScope: ObservabilityScope?) throws -> BuildSystem in + let graphLoader = { try swiftTool.loadPackageGraph(explicitProduct: explicitProduct) } + // FIXME: Implement the custom build command provider also. + return try XcodeBuildSystem( + buildParameters: customBuildParameters ?? swiftTool.buildParameters(), + packageGraphLoader: customPackageGraphLoader ?? graphLoader, + outputStream: customOutputStream ?? swiftTool.outputStream, + logLevel: customLogLevel ?? swiftTool.logLevel, + fileSystem: swiftTool.fileSystem, + observabilityScope: customObservabilityScope ?? swiftTool.observabilityScope + ) + } + ]) + } } /// A safe wrapper of TSCBasic.exec. @@ -333,6 +369,8 @@ public class SwiftTool { private let toolWorkspaceConfiguration: ToolWorkspaceConfiguration + fileprivate var buildSystemProvider: BuildSystemProvider? + /// Create an instance of this tool. /// /// - parameter options: The command line options to be passed to this tool. @@ -678,7 +716,7 @@ public class SwiftTool { return try _manifestLoader.get() } - private func canUseCachedBuildManifest() throws -> Bool { + fileprivate func canUseCachedBuildManifest() throws -> Bool { if !self.options.caching.cacheBuildManifest { return false } @@ -707,7 +745,8 @@ public class SwiftTool { // "customOutputStream" is designed to support build output redirection // but it is only expected to be used when invoking builds from "swift build" command. // in all other cases, the build output should go to the default which is stderr - func createBuildOperation( + func createBuildSystem( + explicitBuildSystem: BuildSystemProvider.Kind? = .none, explicitProduct: String? = .none, cacheBuildManifest: Bool = true, customBuildParameters: BuildParameters? = .none, @@ -715,66 +754,22 @@ public class SwiftTool { customOutputStream: OutputByteStream? = .none, customLogLevel: Diagnostic.Severity? = .none, customObservabilityScope: ObservabilityScope? = .none - ) throws -> BuildOperation { - let testEntryPointPath = customBuildParameters?.testProductStyle.explicitlySpecifiedEntryPointPath - let graphLoader = { try self.loadPackageGraph(explicitProduct: explicitProduct, testEntryPointPath: testEntryPointPath) } - - // Construct the build operation. - // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with dumping the symbol graph (the only case that currently goes through this path, as far as I can tell). rdar://86112934 - let buildOp = try BuildOperation( - buildParameters: customBuildParameters ?? self.buildParameters(), - cacheBuildManifest: cacheBuildManifest && self.canUseCachedBuildManifest(), - packageGraphLoader: customPackageGraphLoader ?? graphLoader, - additionalFileRules: FileRuleDescription.swiftpmFileTypes, - pluginScriptRunner: self.getPluginScriptRunner(), - pluginWorkDirectory: try self.getActiveWorkspace().location.pluginWorkingDirectory, - disableSandboxForPluginCommands: self.options.security.shouldDisableSandbox, - outputStream: customOutputStream ?? self.outputStream, - logLevel: customLogLevel ?? self.logLevel, - fileSystem: self.fileSystem, - observabilityScope: customObservabilityScope ?? self.observabilityScope - ) - - // register the build system with the cancellation handler - self.cancellator.register(name: "build system", handler: buildOp.cancel) - return buildOp - } - - // note: do not customize the OutputStream unless absolutely necessary - // "customOutputStream" is designed to support build output redirection - // but it is only expected to be used when invoking builds from "swift build" command. - // in all other cases, the build output should go to the default which is stderr - func createBuildSystem( - explicitProduct: String? = .none, - customBuildParameters: BuildParameters? = .none, - customPackageGraphLoader: (() throws -> PackageGraph)? = .none, - customOutputStream: OutputByteStream? = .none, - customObservabilityScope: ObservabilityScope? = .none ) throws -> BuildSystem { - let buildSystem: BuildSystem - switch options.build.buildSystem { - case .native: - buildSystem = try self.createBuildOperation( - explicitProduct: explicitProduct, - cacheBuildManifest: true, - customBuildParameters: customBuildParameters, - customPackageGraphLoader: customPackageGraphLoader, - customOutputStream: customOutputStream, - customObservabilityScope: customObservabilityScope - ) - case .xcode: - let graphLoader = { try self.loadPackageGraph(explicitProduct: explicitProduct) } - // FIXME: Implement the custom build command provider also. - buildSystem = try XcodeBuildSystem( - buildParameters: customBuildParameters ?? self.buildParameters(), - packageGraphLoader: customPackageGraphLoader ?? graphLoader, - outputStream: customOutputStream ?? self.outputStream, - logLevel: self.logLevel, - fileSystem: self.fileSystem, - observabilityScope: customObservabilityScope ?? self.observabilityScope - ) + guard let buildSystemProvider = buildSystemProvider else { + fatalError("build system provider not initialized") } + let buildSystem = try buildSystemProvider.createBuildSystem( + kind: explicitBuildSystem ?? options.build.buildSystem, + explicitProduct: explicitProduct, + cacheBuildManifest: cacheBuildManifest, + customBuildParameters: customBuildParameters, + customPackageGraphLoader: customPackageGraphLoader, + customOutputStream: customOutputStream, + customLogLevel: customLogLevel, + customObservabilityScope: customObservabilityScope + ) + // register the build system with the cancellation handler self.cancellator.register(name: "build system", handler: buildSystem.cancel) return buildSystem @@ -805,7 +800,7 @@ public class SwiftTool { xcbuildFlags: options.build.xcbuildFlags, jobs: options.build.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount), shouldLinkStaticSwiftStdlib: options.linker.shouldLinkStaticSwiftStdlib, - canRenameEntrypointFunctionName: SwiftTargetBuildDescription.checkSupportedFrontendFlags( + canRenameEntrypointFunctionName: DriverSupport.checkSupportedFrontendFlags( flags: ["entry-point-function-name"], fileSystem: self.fileSystem ), sanitizers: options.build.enabledSanitizers, @@ -889,11 +884,11 @@ public class SwiftTool { var extraManifestFlags = self.options.build.manifestFlags // Disable the implicit concurrency import if the compiler in use supports it to avoid warnings if we are building against an older SDK that does not contain a Concurrency module. - if SwiftTargetBuildDescription.checkSupportedFrontendFlags(flags: ["disable-implicit-concurrency-module-import"], fileSystem: self.fileSystem) { + if DriverSupport.checkSupportedFrontendFlags(flags: ["disable-implicit-concurrency-module-import"], fileSystem: self.fileSystem) { extraManifestFlags += ["-Xfrontend", "-disable-implicit-concurrency-module-import"] } // Disable the implicit string processing import if the compiler in use supports it to avoid warnings if we are building against an older SDK that does not contain a StringProcessing module. - if SwiftTargetBuildDescription.checkSupportedFrontendFlags(flags: ["disable-implicit-string-processing-module-import"], fileSystem: self.fileSystem) { + if DriverSupport.checkSupportedFrontendFlags(flags: ["disable-implicit-string-processing-module-import"], fileSystem: self.fileSystem) { extraManifestFlags += ["-Xfrontend", "-disable-implicit-string-processing-module-import"] } diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index 2e016abdbbb..4b4c136e2aa 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -16,7 +16,6 @@ import TSCBasic import SPMBuildCore import Basics -import Build import PackageGraph import PackageModel import SourceControl @@ -128,12 +127,13 @@ struct APIDigesterBaselineDumper { // Build the baseline module. // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the APIDigester. rdar://86112934 - let buildOp = try swiftTool.createBuildOperation( + let buildSystem = try swiftTool.createBuildSystem( + explicitBuildSystem: .native, cacheBuildManifest: false, customBuildParameters: buildParameters, customPackageGraphLoader: { graph } ) - try buildOp.build() + try buildSystem.build() // Dump the SDK JSON. try swiftTool.fileSystem.createDirectory(baselineDir, recursive: true) @@ -147,7 +147,7 @@ struct APIDigesterBaselineDumper { try apiDigesterTool.emitAPIBaseline( to: baselinePath(module), for: module, - buildPlan: buildOp.buildPlan! + buildPlan: buildSystem.buildPlan ) } catch { errors.append(error) @@ -185,7 +185,7 @@ public struct SwiftAPIDigester { public func emitAPIBaseline( to outputPath: AbsolutePath, for module: String, - buildPlan: BuildPlan + buildPlan: SPMBuildCore.BuildPlan ) throws { var args = ["-dump-sdk"] args += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: false) @@ -202,7 +202,7 @@ public struct SwiftAPIDigester { public func compareAPIToBaseline( at baselinePath: AbsolutePath, for module: String, - buildPlan: BuildPlan, + buildPlan: SPMBuildCore.BuildPlan, except breakageAllowlistPath: AbsolutePath? ) throws -> ComparisonResult? { var args = [ diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index 951b83f8937..5f04de12b33 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -12,7 +12,6 @@ import ArgumentParser import Basics -import Build import PackageGraph import PackageModel import SPMBuildCore diff --git a/Sources/DriverSupport/CMakeLists.txt b/Sources/DriverSupport/CMakeLists.txt new file mode 100644 index 00000000000..8df7b697089 --- /dev/null +++ b/Sources/DriverSupport/CMakeLists.txt @@ -0,0 +1,25 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2022 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 Swift project authors + +add_library(DriverSupport + Frontend.swift + SPMSwiftDriverExecutor.swift) +# NOTE(compnerd) workaround for CMake not setting up include flags yet +set_target_properties(DriverSupport PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) +target_link_libraries(DriverSupport PUBLIC + Basics + SwiftDriver) + +if(USE_CMAKE_INSTALL) +install(TARGETS DriverSupport + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +endif() +set_property(GLOBAL APPEND PROPERTY SwiftPM_EXPORTS DriverSupport) diff --git a/Sources/DriverSupport/Frontend.swift b/Sources/DriverSupport/Frontend.swift new file mode 100644 index 00000000000..992b8f87bc7 --- /dev/null +++ b/Sources/DriverSupport/Frontend.swift @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2022 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 SwiftDriver + +import protocol TSCBasic.FileSystem + +public struct DriverSupport { + public static func checkSupportedFrontendFlags(flags: Set, fileSystem: FileSystem) -> Bool { + do { + let executor = try SPMSwiftDriverExecutor(resolver: ArgsResolver(fileSystem: fileSystem), fileSystem: fileSystem, env: [:]) + let driver = try Driver(args: ["swiftc"], executor: executor) + return driver.supportedFrontendFlags.intersection(flags) == flags + } catch { + return false + } + } +} diff --git a/Sources/Build/SPMSwiftDriverExecutor.swift b/Sources/DriverSupport/SPMSwiftDriverExecutor.swift similarity index 83% rename from Sources/Build/SPMSwiftDriverExecutor.swift rename to Sources/DriverSupport/SPMSwiftDriverExecutor.swift index 0428c31ec6a..6e9f334d1aa 100644 --- a/Sources/Build/SPMSwiftDriverExecutor.swift +++ b/Sources/DriverSupport/SPMSwiftDriverExecutor.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2020-2022 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 @@ -11,11 +11,13 @@ //===----------------------------------------------------------------------===// import Basics -import TSCBasic -import Foundation -@_implementationOnly import SwiftDriver +import SwiftDriver -final class SPMSwiftDriverExecutor: DriverExecutor { +import protocol TSCBasic.FileSystem +import class TSCBasic.Process +import struct TSCBasic.ProcessResult + +public final class SPMSwiftDriverExecutor: DriverExecutor { private enum Error: Swift.Error, CustomStringConvertible { case inPlaceExecutionUnsupported @@ -28,11 +30,11 @@ final class SPMSwiftDriverExecutor: DriverExecutor { } } - let resolver: ArgsResolver + public let resolver: ArgsResolver let fileSystem: FileSystem let env: EnvironmentVariables - init(resolver: ArgsResolver, + public init(resolver: ArgsResolver, fileSystem: FileSystem, env: EnvironmentVariables) { self.resolver = resolver @@ -40,7 +42,7 @@ final class SPMSwiftDriverExecutor: DriverExecutor { self.env = env } - func execute(job: Job, + public func execute(job: Job, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : TimePoint]) throws -> ProcessResult { let arguments: [String] = try resolver.resolveArgumentList(for: job, @@ -60,18 +62,18 @@ final class SPMSwiftDriverExecutor: DriverExecutor { return try process.waitUntilExit() } - func execute(workload: DriverExecutorWorkload, + public func execute(workload: DriverExecutorWorkload, delegate: JobExecutionDelegate, numParallelJobs: Int, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : TimePoint]) throws { throw InternalError("Multi-job build plans should be lifted into the SPM build graph.") } - func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String { + public func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String { return try TSCBasic.Process.checkNonZeroExit(arguments: args, environment: environment) } - func description(of job: Job, forceResponseFiles: Bool) throws -> String { + public func description(of job: Job, forceResponseFiles: Bool) throws -> String { // FIXME: This is duplicated from SwiftDriver, maybe it shouldn't be a protocol requirement. let (args, usedResponseFile) = try resolver.resolveArgumentList(for: job, useResponseFiles: forceResponseFiles ? .forced : .heuristic) diff --git a/Sources/SPMBuildCore/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem.swift index 537d20edc12..9f96eeb0b7b 100644 --- a/Sources/SPMBuildCore/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem.swift @@ -13,6 +13,9 @@ import Basics import PackageGraph +import struct TSCBasic.AbsolutePath +import protocol TSCBasic.OutputByteStream + /// An enum representing what subset of the package to build. public enum BuildSubset { /// Represents the subset of all products and non-test targets. @@ -45,6 +48,8 @@ public protocol BuildSystem: Cancellable { /// - Parameters: /// - subset: The subset of the package graph to build. func build(subset: BuildSubset) throws + + var buildPlan: BuildPlan { get throws } } extension BuildSystem { @@ -54,3 +59,73 @@ extension BuildSystem { try build(subset: .allExcludingTests) } } + +public protocol ProductBuildDescription { + /// The reference to the product. + var package: ResolvedPackage { get } + + /// The reference to the product. + var product: ResolvedProduct { get } + + /// The build parameters. + var buildParameters: BuildParameters { get } +} + +extension ProductBuildDescription { + /// The path to the product binary produced. + public var binaryPath: AbsolutePath { + return buildParameters.binaryPath(for: product) + } +} + +public protocol BuildPlan { + var buildParameters: BuildParameters { get } + var buildProducts: AnySequence { get } + + func createAPIToolCommonArgs(includeLibrarySearchPaths: Bool) throws -> [String] + func createREPLArguments() throws -> [String] +} + +public struct BuildSystemProvider { + // TODO: In the future, we may want this to be about specific capabilities of a build system rather than choosing a concrete one. + public enum Kind: String, CaseIterable { + case native + case xcode + } + + public typealias Provider = ( + _ explicitProduct: String?, + _ cacheBuildManifest: Bool, + _ customBuildParameters: BuildParameters?, + _ customPackageGraphLoader: (() throws -> PackageGraph)?, + _ customOutputStream: OutputByteStream?, + _ customLogLevel: Diagnostic.Severity?, + _ customObservabilityScope: ObservabilityScope? + ) throws -> BuildSystem + + private let providers: [Kind:Provider] + + public init(providers: [Kind:Provider]) { + self.providers = providers + } + + public func createBuildSystem( + kind: Kind, + explicitProduct: String? = .none, + cacheBuildManifest: Bool = true, + customBuildParameters: BuildParameters? = .none, + customPackageGraphLoader: (() throws -> PackageGraph)? = .none, + customOutputStream: OutputByteStream? = .none, + customLogLevel: Diagnostic.Severity? = .none, + customObservabilityScope: ObservabilityScope? = .none + ) throws -> BuildSystem { + guard let provider = self.providers[kind] else { + throw Errors.buildSystemProviderNotRegistered(kind: kind) + } + return try provider(explicitProduct, cacheBuildManifest, customBuildParameters, customPackageGraphLoader, customOutputStream, customLogLevel, customObservabilityScope) + } +} + +private enum Errors: Swift.Error { + case buildSystemProviderNotRegistered(kind: BuildSystemProvider.Kind) +} diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index f750916cff8..6a3c53ba402 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -18,9 +18,10 @@ import Basics import PackageModel import PackageLoading import PackageGraph +import SPMBuildCore /// The parameters required by `PIFBuilder`. -public struct PIFBuilderParameters { +struct PIFBuilderParameters { /// Whether or not build for testability is enabled. public let enableTestability: Bool @@ -52,16 +53,16 @@ public final class PIFBuilder { public static let allIncludingTestsTargetName = "AllIncludingTests" /// The package graph to build from. - public let graph: PackageGraph + let graph: PackageGraph /// The parameters used to configure the PIF. - public let parameters: PIFBuilderParameters + let parameters: PIFBuilderParameters /// The ObservabilityScope to emit diagnostics to. - public let observabilityScope: ObservabilityScope + let observabilityScope: ObservabilityScope /// The file system to read from. - public let fileSystem: FileSystem + let fileSystem: FileSystem private var pif: PIF.TopLevelObject? @@ -71,7 +72,7 @@ public final class PIFBuilder { /// - parameters: The parameters used to configure the PIF. /// - fileSystem: The file system to read from. /// - observabilityScope: The ObservabilityScope to emit diagnostics to. - public init( + init( graph: PackageGraph, parameters: PIFBuilderParameters, fileSystem: FileSystem, @@ -87,7 +88,7 @@ public final class PIFBuilder { /// - Parameters: /// - prettyPrint: Whether to return a formatted JSON. /// - Returns: The package graph in the JSON PIF format. - public func generatePIF( + func generatePIF( prettyPrint: Bool = true, preservePIFModelStructure: Bool = false ) throws -> String { @@ -133,6 +134,18 @@ public final class PIFBuilder { return PIF.TopLevelObject(workspace: workspace) } } + + // Convenience method for generating PIF. + public static func generatePIF(buildParameters: BuildParameters, packageGraph: PackageGraph, fileSystem: FileSystem, observabilityScope: ObservabilityScope, preservePIFModelStructure: Bool) throws -> String { + let parameters = PIFBuilderParameters(buildParameters) + let builder = Self.init( + graph: packageGraph, + parameters: parameters, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + return try builder.generatePIF(preservePIFModelStructure: preservePIFModelStructure) + } } class PIFProjectBuilder { diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index fa6de25e2db..31fb5efe03c 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -60,6 +60,12 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { return builtProducts } + public var buildPlan: SPMBuildCore.BuildPlan { + get throws { + throw StringError("XCBuild does not provide a build plan") + } + } + public init( buildParameters: BuildParameters, packageGraphLoader: @escaping () throws -> PackageGraph, diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 7c367966fbf..3e281f38684 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -2855,7 +2855,7 @@ final class BuildPlanTests: XCTestCase { "-target", "x86_64-unknown-windows-msvc", ]) - let executablePathExtension = try result.buildProduct(for: "exe").binary.extension + let executablePathExtension = try result.buildProduct(for: "exe").binaryPath.extension XCTAssertMatch(executablePathExtension, "exe") } @@ -2937,7 +2937,7 @@ final class BuildPlanTests: XCTestCase { ] ) - let executablePathExtension = appBuildDescription.binary.extension + let executablePathExtension = appBuildDescription.binaryPath.extension XCTAssertEqual(executablePathExtension, "wasm") let testBuildDescription = try result.buildProduct(for: "PkgPackageTests") @@ -2954,7 +2954,7 @@ final class BuildPlanTests: XCTestCase { ] ) - let testPathExtension = testBuildDescription.binary.extension + let testPathExtension = testBuildDescription.binaryPath.extension XCTAssertEqual(testPathExtension, "wasm") } @@ -4136,10 +4136,10 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch(clibraryLinkArguments, [.anySequence, "-L", "\(buildPath)", .anySequence]) XCTAssertMatch(clibraryLinkArguments, ["-lStaticLibrary"]) - let executablePathExtension = try result.buildProduct(for: "exe").binary.extension ?? "" + let executablePathExtension = try result.buildProduct(for: "exe").binaryPath.extension ?? "" XCTAssertMatch(executablePathExtension, "") - let dynamicLibraryPathExtension = try result.buildProduct(for: "Library").binary.extension + let dynamicLibraryPathExtension = try result.buildProduct(for: "Library").binaryPath.extension XCTAssertMatch(dynamicLibraryPathExtension, "dylib") } diff --git a/Tests/BuildTests/MockBuildTestHelper.swift b/Tests/BuildTests/MockBuildTestHelper.swift index 07af9660c07..03793fd9aae 100644 --- a/Tests/BuildTests/MockBuildTestHelper.swift +++ b/Tests/BuildTests/MockBuildTestHelper.swift @@ -110,13 +110,13 @@ enum BuildError: Swift.Error { struct BuildPlanResult { - let plan: BuildPlan + let plan: Build.BuildPlan let targetMap: [String: TargetBuildDescription] - let productMap: [String: ProductBuildDescription] + let productMap: [String: Build.ProductBuildDescription] - init(plan: BuildPlan) throws { + init(plan: Build.BuildPlan) throws { self.plan = plan - self.productMap = try Dictionary(throwingUniqueKeysWithValues: plan.buildProducts.map{ ($0.product.name, $0) }) + self.productMap = try Dictionary(throwingUniqueKeysWithValues: plan.buildProducts.compactMap { $0 as? Build.ProductBuildDescription }.map{ ($0.product.name, $0) }) self.targetMap = try Dictionary(throwingUniqueKeysWithValues: plan.targetMap.map{ ($0.0.name, $0.1) }) } @@ -135,7 +135,7 @@ struct BuildPlanResult { return target } - func buildProduct(for name: String) throws -> ProductBuildDescription { + 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.") diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index c31f2ef3ba5..922d151df87 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -343,7 +343,7 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BarLogging" && $0.target.moduleAliases?["Logging"] == "BarLogging" }) #if os(macOS) let dylib = try result.buildProduct(for: "Logging") - XCTAssertTrue(dylib.binary.basename == "libLogging.dylib" && dylib.package.identity.description == "barpkg") + XCTAssertTrue(dylib.binaryPath.basename == "libLogging.dylib" && dylib.package.identity.description == "barpkg") #endif } @@ -410,7 +410,7 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BazLogging" && $0.target.moduleAliases?["Logging"] == "BazLogging" }) #if os(macOS) let staticlib = try result.buildProduct(for: "Logging") - XCTAssertTrue(staticlib.binary.basename == "libLogging.a" && staticlib.package.identity.description == "bazpkg") + XCTAssertTrue(staticlib.binaryPath.basename == "libLogging.a" && staticlib.package.identity.description == "bazpkg") #endif } @@ -513,7 +513,7 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "B" && $0.target.moduleAliases == nil }) #if os(macOS) let dylib = try result.buildProduct(for: "Logging") - XCTAssertTrue(dylib.binary.basename == "libLogging.dylib" && dylib.package.identity.description == "xpkg") + XCTAssertTrue(dylib.binaryPath.basename == "libLogging.dylib" && dylib.package.identity.description == "xpkg") #endif } diff --git a/Tests/CommandsTests/APIDiffTests.swift b/Tests/CommandsTests/APIDiffTests.swift index b2befbe3b42..da37e270b82 100644 --- a/Tests/CommandsTests/APIDiffTests.swift +++ b/Tests/CommandsTests/APIDiffTests.swift @@ -13,6 +13,7 @@ import Basics import Build import Commands +@_implementationOnly import DriverSupport import Foundation import PackageModel import SourceControl @@ -52,7 +53,7 @@ final class APIDiffTests: CommandsTestCase { // not all of which can be tested for easily. Fortunately, we can test for the // `-disable-fail-on-error` option, and any version which supports this flag // will meet the other requirements. - guard SwiftTargetBuildDescription.checkSupportedFrontendFlags(flags: ["disable-fail-on-error"], fileSystem: localFileSystem) else { + guard DriverSupport.checkSupportedFrontendFlags(flags: ["disable-fail-on-error"], fileSystem: localFileSystem) else { throw XCTSkip("swift-api-digester is too old") } } diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index 6f21efe469c..3daf14be122 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -18,7 +18,7 @@ import PackageLoading import SPMBuildCore import SPMTestSupport import TSCBasic -import XCBuildSupport +@testable import XCBuildSupport import XCTest class PIFBuilderTests: XCTestCase {