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