diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/.gitignore b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/.gitignore new file mode 100644 index 00000000000..3b29812086f --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/info.json b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/info.json new file mode 100644 index 00000000000..322cdaa8f4a --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/info.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": "1.0", + "artifacts": { + "mytool": { + "type": "executable", + "version": "1.2.3", + "variants": [ + { + "path": "mytool-macos/mytool", + "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"] + }, + { + "path": "mytool-linux/mytool", + "supportedTriples": ["x86_64-unknown-linux-gnu"] + } + ] + } + } +} diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-linux/mytool b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-linux/mytool new file mode 100755 index 00000000000..bc45801290f --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-linux/mytool @@ -0,0 +1,29 @@ +#!/bin/bash + +print_usage() { + echo "usage: ${0##*/} [--verbose] " +} + +# Parse arguments until we find '--' or an argument that isn't an option. +until [ $# -eq 0 ] +do + case "$1" in + --verbose) verbose=1; shift;; + --) shift; break;; + -*) echo "unknown option: ${1}"; print_usage; exit 1; shift;; + *) break;; + esac +done + +# Print usage and leave if we don't have exactly two arguments. +if [ $# -ne 2 ]; then + print_usage + exit 1 +fi + +# For our sample tool we just copy from one to the other. +if [ $verbose != 0 ]; then + echo "[${0##*/}-linux] '$1' '$2'" +fi + +cp "$1" "$2" diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-macos/mytool b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-macos/mytool new file mode 100755 index 00000000000..38d637c5f8e --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Binaries/MyVendedSourceGenBuildTool.artifactbundle/mytool-macos/mytool @@ -0,0 +1,29 @@ +#!/bin/bash + +print_usage() { + echo "usage: ${0##*/} [--verbose] " +} + +# Parse arguments until we find '--' or an argument that isn't an option. +until [ $# -eq 0 ] +do + case "$1" in + --verbose) verbose=1; shift;; + --) shift; break;; + -*) echo "unknown option: ${1}"; print_usage; exit 1; shift;; + *) break;; + esac +done + +# Print usage and leave if we don't have exactly two arguments. +if [ $# -ne 2 ]; then + print_usage + exit 1 +fi + +# For our sample tool we just copy from one to the other. +if [ $verbose != 0 ]; then + echo "[${0##*/}-macosx] '$1' '$2'" +fi + +cp "$1" "$2" diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Package.swift b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Package.swift new file mode 100644 index 00000000000..23a31c61d40 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Dependency/Package.swift @@ -0,0 +1,18 @@ +// swift-tools-version: 5.6 +import PackageDescription + +let package = Package( + name: "MyBinaryProduct", + products: [ + .executable( + name: "MyVendedSourceGenBuildTool", + targets: ["MyVendedSourceGenBuildTool"] + ), + ], + targets: [ + .binaryTarget( + name: "MyVendedSourceGenBuildTool", + path: "Binaries/MyVendedSourceGenBuildTool.artifactbundle" + ), + ] +) diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Package.swift b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Package.swift new file mode 100644 index 00000000000..7b6a49f2f50 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 5.6 +import PackageDescription + +let package = Package( + name: "MyBinaryToolPlugin", + dependencies: [ + .package(path: "Dependency"), + ], + targets: [ + // A local tool that uses a build tool plugin. + .executableTarget( + name: "MyLocalTool", + plugins: [ + "MySourceGenBuildToolPlugin", + ] + ), + // The plugin that generates build tool commands to invoke MySourceGenBuildTool. + .plugin( + name: "MySourceGenBuildToolPlugin", + capability: .buildTool(), + dependencies: [ + .product(name: "MyVendedSourceGenBuildTool", package: "Dependency"), + ] + ), + ] +) diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift new file mode 100644 index 00000000000..0d9066674c5 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift @@ -0,0 +1,34 @@ +import PackagePlugin + +@main +struct MyPlugin: BuildToolPlugin { + + func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { + print("Hello from the Build Tool Plugin!") + guard let target = target as? SourceModuleTarget else { return [] } + let inputFiles = target.sourceFiles.filter({ $0.path.extension == "dat" }) + return try inputFiles.map { + let inputFile = $0 + let inputPath = inputFile.path + let outputName = inputPath.stem + ".swift" + let outputPath = context.pluginWorkDirectory.appending(outputName) + return .buildCommand( + displayName: + "Generating \(outputName) from \(inputPath.lastComponent)", + executable: + try context.tool(named: "mytool").path, + arguments: [ + "--verbose", + "\(inputPath)", + "\(outputPath)" + ], + inputFiles: [ + inputPath, + ], + outputFiles: [ + outputPath + ] + ) + } + } +} diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/foo.dat b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/foo.dat new file mode 100644 index 00000000000..b93a98c857c --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/foo.dat @@ -0,0 +1 @@ +let foo = "I am Foo!" diff --git a/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/main.swift b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/main.swift new file mode 100644 index 00000000000..1bbab0f2fc3 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryToolProductPlugin/Sources/MyLocalTool/main.swift @@ -0,0 +1 @@ +print("Generated string Foo: '\(foo)'") diff --git a/Sources/Build/BuildPlan.swift b/Sources/Build/BuildPlan.swift index b39bc9b919f..21751e8099f 100644 --- a/Sources/Build/BuildPlan.swift +++ b/Sources/Build/BuildPlan.swift @@ -1469,7 +1469,7 @@ public final class ProductBuildDescription { // we will instead have generated a source file containing the redirect. // Support for linking tests against executables is conditional on the tools // version of the package that defines the executable product. - let executableTarget = try product.executableTarget() + let executableTarget = try product.executableTarget if executableTarget.underlyingTarget is SwiftTarget, toolsVersion >= .v5_5, buildParameters.canRenameEntrypointFunctionName { if let flags = buildParameters.linkerFlagsForRenamingMainFunction(of: executableTarget) { @@ -1989,8 +1989,8 @@ public class BuildPlan { var productMap: [ResolvedProduct: ProductBuildDescription] = [:] // Create product description for each product we have in the package graph except - // for automatic libraries and plugins, because they don't produce any output. - for product in graph.allProducts where product.type != .library(.automatic) && product.type != .plugin { + // for automatic libraries, plugins or products which consist solely of binary targets, because they don't produce any output. + for product in graph.allProducts where product.type != .library(.automatic) && product.type != .plugin && !product.targets.filter({ !($0.underlyingTarget is BinaryTarget) }).isEmpty { guard let package = graph.package(for: product) else { throw InternalError("unknown package for \(product)") } diff --git a/Sources/Build/LLBuildManifestBuilder.swift b/Sources/Build/LLBuildManifestBuilder.swift index 66c6f45ad37..31d9b8e2af6 100644 --- a/Sources/Build/LLBuildManifestBuilder.swift +++ b/Sources/Build/LLBuildManifestBuilder.swift @@ -553,7 +553,7 @@ extension LLBuildManifestBuilder { if target.type == .executable { // FIXME: Optimize. let _product = try plan.graph.allProducts.first { - try $0.type == .executable && $0.executableTarget() == target + try $0.type == .executable && $0.executableTarget == target } if let product = _product { guard let planProduct = plan.productMap[product] else { diff --git a/Sources/Commands/SwiftPackageTool.swift b/Sources/Commands/SwiftPackageTool.swift index f703aec2a30..f05cc5c9761 100644 --- a/Sources/Commands/SwiftPackageTool.swift +++ b/Sources/Commands/SwiftPackageTool.swift @@ -1010,6 +1010,7 @@ extension SwiftPackageTool { try PluginCommand.run( plugin: matchingPlugins[0], package: packageGraph.rootPackages[0], + packageGraph: packageGraph, options: pluginOptions, arguments: arguments, swiftTool: swiftTool) @@ -1018,6 +1019,7 @@ extension SwiftPackageTool { static func run( plugin: PluginTarget, package: ResolvedPackage, + packageGraph: PackageGraph, options: PluginOptions, arguments: [String], swiftTool: SwiftTool @@ -1080,29 +1082,17 @@ 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 plugin.dependencies(satisfying: try swiftTool.buildParameters().buildEnvironment) { + 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) switch dep { - case .product(let productRef, _): - // Build the product referenced by the tool, and add the executable to the tool map. - try buildOperation.build(subset: .product(productRef.name)) - if let builtTool = buildOperation.buildPlan?.buildProducts.first(where: { $0.product.name == productRef.name}) { - toolNamesToPaths[productRef.name] = builtTool.binary - } - case .target(let target, _): - if let target = target as? BinaryTarget { - // Add the executables vended by the binary target to the tool map. - for exec in try target.parseArtifactArchives(for: pluginScriptRunner.hostTriple, fileSystem: swiftTool.fileSystem) { - toolNamesToPaths[exec.name] = exec.executablePath - } - } - else { - // Build the product referenced by the tool, and add the executable to the tool map. Product dependencies are not supported within a package, so 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(target.name)) - if let builtTool = buildOperation.buildPlan?.buildProducts.first(where: { $0.product.name == target.name}) { - toolNamesToPaths[target.name] = builtTool.binary - } + 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 } + case .vendedTool(let name, let path): + toolNamesToPaths[name] = path } } @@ -1536,6 +1526,7 @@ extension SwiftPackageTool { try PluginCommand.run( plugin: matchingPlugins[0], package: packageGraph.rootPackages[0], + packageGraph: packageGraph, options: pluginOptions, arguments: Array( remaining.dropFirst()), swiftTool: swiftTool) diff --git a/Sources/PackageGraph/ResolvedProduct.swift b/Sources/PackageGraph/ResolvedProduct.swift index b1e096fdcb4..6983bce0dfb 100644 --- a/Sources/PackageGraph/ResolvedProduct.swift +++ b/Sources/PackageGraph/ResolvedProduct.swift @@ -43,11 +43,16 @@ public final class ResolvedProduct { /// The main executable target of product. /// /// Note: This property is only valid for executable products. - public func executableTarget() throws -> ResolvedTarget { - guard type == .executable || type == .snippet else { - throw InternalError("firstExecutableModule should only be called for executable targets") + public var executableTarget: ResolvedTarget { + get throws { + guard type == .executable || type == .snippet else { + throw InternalError("`executableTarget` should only be called for executable targets") + } + guard let underlyingExecutableTarget = targets.map({ $0.underlyingTarget }).executables.first, let executableTarget = targets.first(where: { $0.underlyingTarget == underlyingExecutableTarget }) else { + throw InternalError("could not determine executable target") + } + return executableTarget } - return targets.first(where: { $0.type == .executable || $0.type == .snippet })! } public init(product: Product, targets: [ResolvedTarget]) { diff --git a/Sources/PackageLoading/ManifestLoader+Validation.swift b/Sources/PackageLoading/ManifestLoader+Validation.swift index 7ec22620ff7..9509e27b154 100644 --- a/Sources/PackageLoading/ManifestLoader+Validation.swift +++ b/Sources/PackageLoading/ManifestLoader+Validation.swift @@ -78,10 +78,14 @@ public struct ManifestValidator { } } - // Check that products that reference only binary targets don't define a type. - let areTargetsBinary = product.targets.allSatisfy { self.manifest.targetMap[$0]?.type == .binary } - if areTargetsBinary && product.type != .library(.automatic) { - diagnostics.append(.invalidBinaryProductType(productName: product.name)) + // Check that products that reference only binary targets don't define an explicit library type. + if product.targets.allSatisfy({ self.manifest.targetMap[$0]?.type == .binary }) { + switch product.type { + case .library(.automatic), .executable: + break + default: + diagnostics.append(.invalidBinaryProductType(productName: product.name)) + } } } @@ -262,7 +266,7 @@ extension Basics.Diagnostic { } static func invalidBinaryProductType(productName: String) -> Self { - .error("invalid type for binary product '\(productName)'; products referencing only binary targets must have a type of 'library'") + .error("invalid type for binary product '\(productName)'; products referencing only binary targets must be executable or automatic library products") } /*static func duplicateDependency(dependencyIdentity: PackageIdentity) -> Self { diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 7424ded6401..879d43676e8 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -1290,7 +1290,7 @@ public final class PackageBuilder { } private func validateExecutableProduct(_ product: ProductDescription, with targets: [Target]) -> Bool { - let executableTargetCount = targets.filter { $0.type == .executable }.count + let executableTargetCount = targets.executables.count guard executableTargetCount == 1 else { if executableTargetCount == 0 { if let target = targets.spm_only { diff --git a/Sources/PackageModel/Product.swift b/Sources/PackageModel/Product.swift index 093916b4559..e6fc8ba0233 100644 --- a/Sources/PackageModel/Product.swift +++ b/Sources/PackageModel/Product.swift @@ -43,7 +43,7 @@ public class Product: Codable { throw InternalError("Targets cannot be empty") } if type == .executable { - guard targets.filter({ $0.type == .executable }).count == 1 else { + guard targets.executables.count == 1 else { throw InternalError("Executable products should have exactly one executable target.") } } diff --git a/Sources/PackageModel/Target.swift b/Sources/PackageModel/Target.swift index 1b03e9e6241..8fd4b500332 100644 --- a/Sources/PackageModel/Target.swift +++ b/Sources/PackageModel/Target.swift @@ -638,6 +638,11 @@ public final class BinaryTarget: Target { } } + public var containsExecutable: Bool { + // FIXME: needs to be revisited once libraries are supported in artifact bundles + return self.kind == .artifactsArchive + } + public enum Origin: Equatable, Codable { /// Represents an artifact that was downloaded from a remote URL. @@ -799,3 +804,18 @@ public enum PluginPermission: Hashable, Codable { } } } + +public extension Sequence where Iterator.Element == Target { + var executables: [Target] { + return filter { + switch $0.type { + case .binary: + return ($0 as? BinaryTarget)?.containsExecutable == true + case .executable: + return true + default: + return false + } + } + } +} diff --git a/Sources/SPMBuildCore/PluginContextSerializer.swift b/Sources/SPMBuildCore/PluginContextSerializer.swift index 2b5ca25f1ce..a121fd9ad63 100644 --- a/Sources/SPMBuildCore/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/PluginContextSerializer.swift @@ -184,9 +184,7 @@ internal struct PluginContextSerializer { switch product.type { case .executable: - guard let mainExecTarget = product.targets.first(where: { $0.type == .executable }) else { - throw InternalError("could not determine main executable target for product \(product)") - } + let mainExecTarget = try product.executableTarget guard let mainExecTargetId = try serialize(target: mainExecTarget) else { throw InternalError("unable to serialize main executable target \(mainExecTarget) for product \(product)") } diff --git a/Sources/SPMBuildCore/PluginInvocation.swift b/Sources/SPMBuildCore/PluginInvocation.swift index 6ef5f3d21c6..e5fd82a78cb 100644 --- a/Sources/SPMBuildCore/PluginInvocation.swift +++ b/Sources/SPMBuildCore/PluginInvocation.swift @@ -355,7 +355,7 @@ extension PackageGraph { for pluginTarget in pluginTargets { // Determine the tools to which this plugin has access, and create a name-to-path mapping from tool // names to the corresponding paths. Built tools are assumed to be in the build tools directory. - let accessibleTools = pluginTarget.accessibleTools(fileSystem: fileSystem, environment: buildEnvironment, for: try pluginScriptRunner.hostTriple) + let accessibleTools = try pluginTarget.accessibleTools(packageGraph: self, fileSystem: fileSystem, environment: buildEnvironment, for: try pluginScriptRunner.hostTriple) let toolNamesToPaths = accessibleTools.reduce(into: [String: AbsolutePath](), { dict, tool in switch tool { case .builtTool(let name, let path): @@ -502,29 +502,31 @@ public extension PluginTarget { } /// The set of tools that are accessible to this plugin. - func accessibleTools(fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple) -> Set { - return Set(self.dependencies(satisfying: environment).flatMap { dependency -> [PluginAccessibleTool] in + func accessibleTools(packageGraph: PackageGraph, fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple) throws -> Set { + return try Set(self.dependencies(satisfying: environment).flatMap { dependency -> [PluginAccessibleTool] in + let executableOrBinaryTarget: Target switch dependency { case .target(let target, _): - // For a binary target we create a `vendedTool`. - if let target = target as? BinaryTarget { - // TODO: Memoize this result for the host triple - guard let execInfos = try? target.parseArtifactArchives(for: hostTriple, fileSystem: fileSystem) else { - // TODO: Deal better with errors in parsing the artifacts - return [] - } - return execInfos.map{ .vendedTool(name: $0.name, path: $0.executablePath) } - } - // For an executable target we create a `builtTool`. - else if target.type == .executable { - // TODO: How do we determine what the executable name will be for the host platform? - return [.builtTool(name: target.name, path: RelativePath(target.name))] + executableOrBinaryTarget = target + case .product(let productRef, _): + guard let product = packageGraph.allProducts.first(where: { $0.name == productRef.name }), let executableTarget = product.targets.map({ $0.underlyingTarget }).executables.spm_only else { + throw StringError("no product named \(productRef.name)") } - else { - return [] - } - case .product(let product, _): - return [.builtTool(name: product.name, path: RelativePath(product.name))] + executableOrBinaryTarget = executableTarget + } + + // For a binary target we create a `vendedTool`. + if let target = executableOrBinaryTarget as? BinaryTarget { + // TODO: Memoize this result for the host triple + let execInfos = try target.parseArtifactArchives(for: hostTriple, fileSystem: fileSystem) + return execInfos.map{ .vendedTool(name: $0.name, path: $0.executablePath) } + } + // For an executable target we create a `builtTool`. + else if executableOrBinaryTarget.type == .executable { + return [.builtTool(name: executableOrBinaryTarget.name, path: RelativePath(executableOrBinaryTarget.name))] + } + else { + return [] } }) } diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index ad24c5801d5..14231211ec2 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -155,6 +155,19 @@ class PluginTests: XCTestCase { XCTAssert(stdout.contains("Build complete!"), "stdout:\n\(stdout)") } } + + func testUseOfBinaryToolVendedAsProduct() throws { + #if !os(macOS) + try XCTSkipIf(true, "test is only supported on macOS") + #endif + // 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") + try fixture(name: "Miscellaneous/Plugins") { fixturePath in + let (stdout, _) = try executeSwiftBuild(fixturePath.appending(component: "BinaryToolProductPlugin"), configuration: .Debug, extraArgs: ["--product", "MyLocalTool"]) + XCTAssert(stdout.contains("Linking MyLocalTool"), "stdout:\n\(stdout)") + XCTAssert(stdout.contains("Build complete!"), "stdout:\n\(stdout)") + } + } func testCommandPluginInvocation() throws { // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). diff --git a/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift index 72d45a45dc5..1979534786b 100644 --- a/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift @@ -177,29 +177,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in - result.check(diagnostic: "invalid type for binary product 'FooLibrary'; products referencing only binary targets must have a type of 'library'", severity: .error) - } - } - - do { - let content = """ - import PackageDescription - let package = Package( - name: "Foo", - products: [ - .executable(name: "FooLibrary", targets: ["Foo"]), - ], - targets: [ - .binaryTarget(name: "Foo", path: "Foo.xcframework"), - ] - ) - """ - - let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) - XCTAssertNoDiagnostics(observability.diagnostics) - testDiagnostics(validationDiagnostics) { result in - result.check(diagnostic: "invalid type for binary product 'FooLibrary'; products referencing only binary targets must have a type of 'library'", severity: .error) + result.check(diagnostic: "invalid type for binary product 'FooLibrary'; products referencing only binary targets must be executable or automatic library products", severity: .error) } }