@@ -808,6 +808,177 @@ final class PluginInvocationTests: XCTestCase {
808
808
}
809
809
}
810
810
811
+ func testPrebuildPluginShouldUseBinaryTarget( ) async throws {
812
+ // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require).
813
+ try XCTSkipIf ( !UserToolchain. default. supportsSwiftConcurrency ( ) , " skipping because test environment doesn't support concurrency " )
814
+
815
+ try await testWithTemporaryDirectory { tmpPath in
816
+ // Create a sample package with a library target and a plugin.
817
+ let packageDir = tmpPath. appending ( components: " mypkg " )
818
+ try localFileSystem. createDirectory ( packageDir, recursive: true )
819
+ try localFileSystem. writeFileContents ( packageDir. appending ( " Package.swift " ) , string: """
820
+ // swift-tools-version:5.7
821
+
822
+ import PackageDescription
823
+
824
+ let package = Package(
825
+ name: " mypkg " ,
826
+ products: [
827
+ .library(
828
+ name: " MyLib " ,
829
+ targets: [ " MyLib " ])
830
+ ],
831
+ targets: [
832
+ .target(
833
+ name: " MyLib " ,
834
+ plugins: [
835
+ .plugin(name: " X " )
836
+ ]),
837
+ .plugin(
838
+ name: " X " ,
839
+ capability: .buildTool(),
840
+ dependencies: [ " Y " ]
841
+ ),
842
+ .binaryTarget(
843
+ name: " Y " ,
844
+ path: " Binaries/Y. \( artifactBundleExtension) "
845
+ ),
846
+ ]
847
+ )
848
+ """ )
849
+
850
+ let libTargetDir = packageDir. appending ( components: " Sources " , " MyLib " )
851
+ try localFileSystem. createDirectory ( libTargetDir, recursive: true )
852
+ try localFileSystem. writeFileContents ( libTargetDir. appending ( " file.swift " ) , string: """
853
+ public struct MyUtilLib {
854
+ public let strings: [String]
855
+ public init(args: [String]) {
856
+ self.strings = args
857
+ }
858
+ }
859
+ """ )
860
+
861
+ let depTargetDir = packageDir. appending ( components: " Sources " , " Y " )
862
+ try localFileSystem. createDirectory ( depTargetDir, recursive: true )
863
+ try localFileSystem. writeFileContents ( depTargetDir. appending ( " main.swift " ) , string: """
864
+ struct Y {
865
+ func run() {
866
+ print( " You passed us two arguments, argumentOne, and argumentTwo " )
867
+ }
868
+ }
869
+ Y.main()
870
+ """ )
871
+
872
+ let pluginTargetDir = packageDir. appending ( components: " Plugins " , " X " )
873
+ try localFileSystem. createDirectory ( pluginTargetDir, recursive: true )
874
+ try localFileSystem. writeFileContents ( pluginTargetDir. appending ( " plugin.swift " ) , string: """
875
+ import PackagePlugin
876
+ @main struct X: BuildToolPlugin {
877
+ func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
878
+ [
879
+ Command.prebuildCommand(
880
+ displayName: " X: Running Y before the build... " ,
881
+ executable: try context.tool(named: " Y " ).path,
882
+ arguments: [ " ARGUMENT_ONE " , " ARGUMENT_TWO " ],
883
+ outputFilesDirectory: context.pluginWorkDirectory.appending( " OUTPUT_FILES_DIRECTORY " )
884
+ )
885
+ ]
886
+ }
887
+ }
888
+ """ )
889
+
890
+ let artifactVariants = [ try UserToolchain . default. targetTriple] . map {
891
+ """
892
+ { " path " : " Y " , " supportedTriples " : [ " \( $0. tripleString) " ] }
893
+ """
894
+ }
895
+
896
+ let bundlePath = packageDir. appending ( components: " Binaries " , " Y. \( artifactBundleExtension) " )
897
+ let bundleMetadataPath = bundlePath. appending ( component: " info.json " )
898
+ try localFileSystem. createDirectory ( bundleMetadataPath. parentDirectory, recursive: true )
899
+ try localFileSystem. writeFileContents (
900
+ bundleMetadataPath,
901
+ string: """
902
+ { " schemaVersion " : " 1.0 " ,
903
+ " artifacts " : {
904
+ " Y " : {
905
+ " type " : " executable " ,
906
+ " version " : " 1.2.3 " ,
907
+ " variants " : [
908
+ \( artifactVariants. joined ( separator: " , " ) )
909
+ ]
910
+ }
911
+ }
912
+ }
913
+ """
914
+ )
915
+ let binaryPath = bundlePath. appending ( component: " Y " )
916
+ try localFileSystem. writeFileContents ( binaryPath, string: " " )
917
+
918
+ // Load a workspace from the package.
919
+ let observability = ObservabilitySystem . makeForTesting ( )
920
+ let workspace = try Workspace (
921
+ fileSystem: localFileSystem,
922
+ forRootPackage: packageDir,
923
+ customManifestLoader: ManifestLoader ( toolchain: UserToolchain . default) ,
924
+ delegate: MockWorkspaceDelegate ( )
925
+ )
926
+
927
+ // Load the root manifest.
928
+ let rootInput = PackageGraphRootInput ( packages: [ packageDir] , dependencies: [ ] )
929
+ let rootManifests = try await workspace. loadRootManifests (
930
+ packages: rootInput. packages,
931
+ observabilityScope: observability. topScope
932
+ )
933
+ XCTAssert ( rootManifests. count == 1 , " \( rootManifests) " )
934
+
935
+ // Load the package graph.
936
+ let packageGraph = try await workspace. loadPackageGraph (
937
+ rootInput: rootInput,
938
+ observabilityScope: observability. topScope
939
+ )
940
+ XCTAssertNoDiagnostics ( observability. diagnostics)
941
+ XCTAssert ( packageGraph. packages. count == 1 , " \( packageGraph. packages) " )
942
+
943
+ // Find the build tool plugin.
944
+ let buildToolPlugin = try XCTUnwrap ( packageGraph. packages. first? . modules. map ( \. underlying) . filter { $0. name == " X " } . first as? PluginModule )
945
+ XCTAssertEqual ( buildToolPlugin. name, " X " )
946
+ XCTAssertEqual ( buildToolPlugin. capability, . buildTool)
947
+
948
+ // Create a plugin script runner for the duration of the test.
949
+ let pluginCacheDir = tmpPath. appending ( " plugin-cache " )
950
+ let pluginScriptRunner = DefaultPluginScriptRunner (
951
+ fileSystem: localFileSystem,
952
+ cacheDir: pluginCacheDir,
953
+ toolchain: try UserToolchain . default
954
+ )
955
+
956
+ // Invoke build tool plugin
957
+ do {
958
+ let outputDir = packageDir. appending ( " .build " )
959
+ let buildParameters = mockBuildParameters (
960
+ destination: . host,
961
+ environment: BuildEnvironment ( platform: . macOS, configuration: . debug)
962
+ )
963
+
964
+ let result = try await invokeBuildToolPlugins (
965
+ graph: packageGraph,
966
+ buildParameters: buildParameters,
967
+ fileSystem: localFileSystem,
968
+ outputDir: outputDir,
969
+ pluginScriptRunner: pluginScriptRunner,
970
+ observabilityScope: observability. topScope
971
+ )
972
+
973
+ let diags = result. flatMap ( \. value. results) . flatMap ( \. diagnostics)
974
+ testDiagnostics ( diags) { result in
975
+ result. checkIsEmpty ( )
976
+ }
977
+ }
978
+ }
979
+ }
980
+
981
+
811
982
func testPrebuildPluginShouldNotUseExecTarget( ) async throws {
812
983
// Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require).
813
984
try XCTSkipIf ( !UserToolchain. default. supportsSwiftConcurrency ( ) , " skipping because test environment doesn't support concurrency " )
@@ -1201,7 +1372,7 @@ final class PluginInvocationTests: XCTestCase {
1201
1372
try localFileSystem. writeFileContents ( myPluginTargetDir. appending ( " plugin.swift " ) , string: content)
1202
1373
let artifactVariants = artifactSupportedTriples. map {
1203
1374
"""
1204
- { " path " : " LocalBinaryTool \( $0. tripleString) .sh " , " supportedTriples " : [ " \( $0. tripleString) " ] }
1375
+ { " path " : " \( $0. tripleString) /LocalBinaryTool " , " supportedTriples " : [ " \( $0. tripleString) " ] }
1205
1376
"""
1206
1377
}
1207
1378
@@ -1337,7 +1508,7 @@ final class PluginInvocationTests: XCTestCase {
1337
1508
$0. value. forEach {
1338
1509
XCTAssertTrue ( $0. succeeded, " plugin unexpectedly failed " )
1339
1510
XCTAssertEqual ( $0. diagnostics. map { $0. message } , [ ] , " plugin produced unexpected diagnostics " )
1340
- XCTAssertEqual ( $0. buildCommands. first? . configuration. executable. basename, " LocalBinaryTool \( hostTriple . tripleString ) .sh " )
1511
+ XCTAssertEqual ( $0. buildCommands. first? . configuration. executable. basename, " LocalBinaryTool " )
1341
1512
}
1342
1513
}
1343
1514
}
0 commit comments