@@ -17,41 +17,45 @@ import PackagePlugin
1717
1818@main
1919final class JExtractSwiftCommandPlugin : BuildToolPlugin , CommandPlugin {
20-
20+
2121 var verbose : Bool = false
22-
22+
2323 /// Build the target before attempting to extract from it.
2424 /// This avoids trying to extract from broken sources.
2525 ///
2626 /// You may disable this if confident that input targets sources are correct and there's no need to kick off a pre-build for some reason.
2727 var buildInputs : Bool = true
28-
28+
2929 /// Build the target once swift-java sources have been generated.
3030 /// This helps verify that the generated output is correct, and won't miscompile on the next build.
3131 var buildOutputs : Bool = true
32-
32+
3333 func createBuildCommands( context: PackagePlugin . PluginContext , target: any PackagePlugin . Target ) async throws -> [ PackagePlugin . Command ] {
3434 // FIXME: This is not a build plugin but SwiftPM forces us to impleme the protocol anyway? rdar://139556637
3535 return [ ]
3636 }
37-
37+
3838 func performCommand( context: PluginContext , arguments: [ String ] ) throws {
39+ // Plugin can't have dependencies, so we have some naive argument parsing instead:
3940 self . verbose = arguments. contains ( " -v " ) || arguments. contains ( " --verbose " )
40-
41+ if !self . verbose {
42+ fatalError ( " Plugin should be verbose " )
43+ }
44+
4145 let selectedTargets : [ String ] =
4246 if let last = arguments. lastIndex ( where: { $0. starts ( with: " - " ) } ) ,
4347 last < arguments. endIndex {
4448 Array ( arguments [ ..< last] )
4549 } else {
4650 [ ]
4751 }
48-
52+
4953 for target in context. package . targets {
5054 guard let configPath = getSwiftJavaConfig ( target: target) else {
5155 log ( " Skipping target ' \( target. name) , has no 'swift-java.config' file " )
5256 continue
5357 }
54-
58+
5559 do {
5660 print ( " [swift-java] Extracting Java wrappers from target: ' \( target. name) '... " )
5761 try performCommand ( context: context, target: target, arguments: arguments)
@@ -60,24 +64,19 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
6064 }
6165 }
6266 }
63-
67+
6468 /// Perform the command on a specific target.
6569 func performCommand( context: PluginContext , target: Target , arguments: [ String ] ) throws {
6670 // Make sure the target can builds properly
6771 try self . packageManager. build ( . target( target. name) , parameters: . init( ) )
68-
72+
6973 guard let sourceModule = target. sourceModule else { return }
7074
7175 if self . buildInputs {
7276 log ( " Pre-building target ' \( target. name) ' before extracting sources... " )
7377 try self . packageManager. build ( . target( target. name) , parameters: . init( ) )
7478 }
75-
76- if self . buildOutputs {
77- log ( " Post-building target ' \( target. name) ' to verify generated sources... " )
78- try self . packageManager. build ( . target( target. name) , parameters: . init( ) )
79- }
80-
79+
8180 // Note: Target doesn't have a directoryURL counterpart to directory,
8281 // so we cannot eliminate this deprecation warning.
8382 let sourceDir = target. directory. string
@@ -91,8 +90,6 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
9190 . appending ( path: " generated " )
9291 . appending ( path: " java " )
9392 let outputDirectorySwift = context. pluginWorkDirectoryURL
94- . appending ( path: " src " )
95- . appending ( path: " generated " )
9693 . appending ( path: " Sources " )
9794
9895 var arguments : [ String ] = [
@@ -108,28 +105,42 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
108105 arguments. append ( sourceDir)
109106
110107 try runExtract ( context: context, target: target, arguments: arguments)
108+
109+ if self . buildOutputs {
110+ // Building the *products* since we need to build the dylib that contains our newly generated sources,
111+ // so just building the target again would not be enough. We build all products which we affected using
112+ // our source generation, which usually would be just a product dylib with our library.
113+ //
114+ // In practice, we'll always want to build after generating; either here,
115+ // or via some other task before we run any Java code, calling into Swift.
116+ log ( " Post-extract building products with target ' \( target. name) '... " )
117+ for product in context. package . products where product. targets. contains ( where: { $0. id == target. id } ) {
118+ log ( " Post-extract building product ' \( product. name) '... " )
119+ try self . packageManager. build ( . product( product. name) , parameters: . init( ) )
120+ }
121+ }
111122 }
112-
123+
113124 func runExtract( context: PluginContext , target: Target , arguments: [ String ] ) throws {
114125 let process = Process ( )
115126 process. executableURL = try context. tool ( named: " JExtractSwiftTool " ) . url
116127 process. arguments = arguments
117-
128+
118129 do {
119- log ( " Execute: \( process. executableURL) \( arguments) " )
120-
130+ log ( " Execute: \( process. executableURL! . absoluteURL . relativePath ) \( arguments. joined ( separator : " " ) ) " )
131+
121132 try process. run ( )
122133 process. waitUntilExit ( )
123-
134+
124135 assert ( process. terminationStatus == 0 , " Process failed with exit code: \( process. terminationStatus) " )
125136 } catch {
126- print ( " [swift-java][ command] Failed to extract Java sources for target: ' \( target. name) ; Error: \( error) " )
137+ print ( " [swift-java- command] Failed to extract Java sources for target: ' \( target. name) ; Error: \( error) " )
127138 }
128139 }
129-
130- func log( _ message: @autoclosure ( ) -> String ) {
140+
141+ func log( _ message: @autoclosure ( ) -> String , terminator : String = " \n " ) {
131142 if self . verbose {
132- print ( " [swift-java] \( message ( ) ) " )
143+ print ( " [swift-java-command ] \( message ( ) ) " , terminator : terminator )
133144 }
134145 }
135146}
0 commit comments