@@ -17,41 +17,45 @@ import PackagePlugin
17
17
18
18
@main
19
19
final class JExtractSwiftCommandPlugin : BuildToolPlugin , CommandPlugin {
20
-
20
+
21
21
var verbose : Bool = false
22
-
22
+
23
23
/// Build the target before attempting to extract from it.
24
24
/// This avoids trying to extract from broken sources.
25
25
///
26
26
/// 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.
27
27
var buildInputs : Bool = true
28
-
28
+
29
29
/// Build the target once swift-java sources have been generated.
30
30
/// This helps verify that the generated output is correct, and won't miscompile on the next build.
31
31
var buildOutputs : Bool = true
32
-
32
+
33
33
func createBuildCommands( context: PackagePlugin . PluginContext , target: any PackagePlugin . Target ) async throws -> [ PackagePlugin . Command ] {
34
34
// FIXME: This is not a build plugin but SwiftPM forces us to impleme the protocol anyway? rdar://139556637
35
35
return [ ]
36
36
}
37
-
37
+
38
38
func performCommand( context: PluginContext , arguments: [ String ] ) throws {
39
+ // Plugin can't have dependencies, so we have some naive argument parsing instead:
39
40
self . verbose = arguments. contains ( " -v " ) || arguments. contains ( " --verbose " )
40
-
41
+ if !self . verbose {
42
+ fatalError ( " Plugin should be verbose " )
43
+ }
44
+
41
45
let selectedTargets : [ String ] =
42
46
if let last = arguments. lastIndex ( where: { $0. starts ( with: " - " ) } ) ,
43
47
last < arguments. endIndex {
44
48
Array ( arguments [ ..< last] )
45
49
} else {
46
50
[ ]
47
51
}
48
-
52
+
49
53
for target in context. package . targets {
50
54
guard let configPath = getSwiftJavaConfig ( target: target) else {
51
55
log ( " Skipping target ' \( target. name) , has no 'swift-java.config' file " )
52
56
continue
53
57
}
54
-
58
+
55
59
do {
56
60
print ( " [swift-java] Extracting Java wrappers from target: ' \( target. name) '... " )
57
61
try performCommand ( context: context, target: target, arguments: arguments)
@@ -60,24 +64,19 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
60
64
}
61
65
}
62
66
}
63
-
67
+
64
68
/// Perform the command on a specific target.
65
69
func performCommand( context: PluginContext , target: Target , arguments: [ String ] ) throws {
66
70
// Make sure the target can builds properly
67
71
try self . packageManager. build ( . target( target. name) , parameters: . init( ) )
68
-
72
+
69
73
guard let sourceModule = target. sourceModule else { return }
70
74
71
75
if self . buildInputs {
72
76
log ( " Pre-building target ' \( target. name) ' before extracting sources... " )
73
77
try self . packageManager. build ( . target( target. name) , parameters: . init( ) )
74
78
}
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
+
81
80
// Note: Target doesn't have a directoryURL counterpart to directory,
82
81
// so we cannot eliminate this deprecation warning.
83
82
let sourceDir = target. directory. string
@@ -91,8 +90,6 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
91
90
. appending ( path: " generated " )
92
91
. appending ( path: " java " )
93
92
let outputDirectorySwift = context. pluginWorkDirectoryURL
94
- . appending ( path: " src " )
95
- . appending ( path: " generated " )
96
93
. appending ( path: " Sources " )
97
94
98
95
var arguments : [ String ] = [
@@ -108,28 +105,42 @@ final class JExtractSwiftCommandPlugin: BuildToolPlugin, CommandPlugin {
108
105
arguments. append ( sourceDir)
109
106
110
107
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
+ }
111
122
}
112
-
123
+
113
124
func runExtract( context: PluginContext , target: Target , arguments: [ String ] ) throws {
114
125
let process = Process ( )
115
126
process. executableURL = try context. tool ( named: " JExtractSwiftTool " ) . url
116
127
process. arguments = arguments
117
-
128
+
118
129
do {
119
- log ( " Execute: \( process. executableURL) \( arguments) " )
120
-
130
+ log ( " Execute: \( process. executableURL! . absoluteURL . relativePath ) \( arguments. joined ( separator : " " ) ) " )
131
+
121
132
try process. run ( )
122
133
process. waitUntilExit ( )
123
-
134
+
124
135
assert ( process. terminationStatus == 0 , " Process failed with exit code: \( process. terminationStatus) " )
125
136
} 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) " )
127
138
}
128
139
}
129
-
130
- func log( _ message: @autoclosure ( ) -> String ) {
140
+
141
+ func log( _ message: @autoclosure ( ) -> String , terminator : String = " \n " ) {
131
142
if self . verbose {
132
- print ( " [swift-java] \( message ( ) ) " )
143
+ print ( " [swift-java-command ] \( message ( ) ) " , terminator : terminator )
133
144
}
134
145
}
135
146
}
0 commit comments