Skip to content

Commit c58bbed

Browse files
madsodgaardktoso
andauthored
[jextract] Propagate build plugin config to command (#415)
Co-authored-by: Konrad `ktoso` Malawski <konrad.malawski@project13.pl>
1 parent 1de7279 commit c58bbed

File tree

9 files changed

+45
-34
lines changed

9 files changed

+45
-34
lines changed

Plugins/PluginsShared/PluginUtils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ extension PluginContext {
6666
.appending(path: "generated")
6767
.appending(path: "java")
6868
}
69-
69+
7070
var outputSwiftDirectory: URL {
7171
self.pluginWorkDirectoryURL
7272
.appending(path: "Sources")
7373
}
74-
74+
7575
func cachedClasspathFile(swiftModule: String) -> URL {
7676
self.pluginWorkDirectoryURL
7777
.appending(path: "\(swiftModule)", directoryHint: .notDirectory)

Samples/SwiftJavaExtractJNISampleApp/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ def jextract = tasks.register("jextract", Exec) {
102102

103103
workingDir = layout.projectDirectory
104104
commandLine "swift"
105-
// TODO: -v for debugging build issues...
106-
args("build", "-v") // since Swift targets which need to be jextract-ed have the jextract build plugin, we just need to build
105+
args("build") // since Swift targets which need to be jextract-ed have the jextract build plugin, we just need to build
107106
// If we wanted to execute a specific subcommand, we can like this:
108107
// args("run",/*
109108
// "swift-java", "jextract",

Sources/JExtractSwiftLib/Swift2Java.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ public struct SwiftToJava {
9595

9696
try translator.analyze()
9797

98-
switch config.mode {
99-
case .some(.ffm), .none:
98+
switch config.effectiveMode {
99+
case .ffm:
100100
let generator = FFMSwift2JavaGenerator(
101101
config: self.config,
102102
translator: translator,

Sources/SwiftJavaConfigurationShared/Configuration.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public struct Configuration: Codable {
3939
public var outputJavaDirectory: String?
4040

4141
public var mode: JExtractGenerationMode?
42+
public var effectiveMode: JExtractGenerationMode {
43+
mode ?? .default
44+
}
4245

4346
public var writeEmptyFiles: Bool? // FIXME: default it to false, but that plays not nice with Codable
4447

Sources/SwiftJavaConfigurationShared/JExtract/JExtractGenerationMode.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ public enum JExtractGenerationMode: String, Codable {
1919

2020
/// Java Native Interface
2121
case jni
22+
23+
public static var `default`: JExtractGenerationMode {
24+
.ffm
25+
}
2226
}

Sources/SwiftJavaConfigurationShared/JExtract/JExtractUnsignedIntegerMode.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14-
1514
/// Configures how Swift unsigned integers should be extracted by jextract.
1615
public enum JExtractUnsignedIntegerMode: String, Codable {
1716
/// Treat unsigned Swift integers as their signed equivalents in Java signatures,

Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ on the Java side.
135135
| `Float` | `float` |
136136
| `Double` | `double` |
137137

138-
#### Unsigned numbers mode: wrap-guava
138+
#### Unsigned numbers mode: wrapGuava
139139

140140
You can configure `jextract` (in FFM mode) to instead import unsigned values as their unsigned type-safe representations
141-
as offered by the Guava library: `UnsignedLong` or `UnsignedInt`. To enable this mode pass the `--unsigned-numbers wrap-guava`
141+
as offered by the Guava library: `UnsignedLong` or `UnsignedInt`. To enable this mode pass the `--unsigned-numbers-mode wrapGuava`
142142
command line option, or set the corresponding configuration value in `swift-java.config` (TODO).
143143

144144
This approach is type-safe, however it incurs a performance penalty for allocating a wrapper class for every
@@ -162,7 +162,7 @@ you are expected to add a Guava dependency to your Java project.
162162
| `Float` | `float` |
163163
| `Double` | `double` |
164164

165-
> Note: The `wrap-guava` mode is currently only available in FFM mode of jextract.
165+
> Note: The `wrapGuava` mode is currently only available in FFM mode of jextract.
166166
167167
### Enums
168168

Sources/SwiftJavaTool/Commands/JExtractCommand.swift

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ extension SwiftJava {
6161
@Flag(help: "Some build systems require an output to be present when it was 'expected', even if empty. This is used by the JExtractSwiftPlugin build plugin, but otherwise should not be necessary.")
6262
var writeEmptyFiles: Bool = false
6363

64-
@Option(help: "The mode of generation to use for the output files. Used with jextract mode. By default, unsigned Swift types are imported as their bit-width compatible signed Java counterparts, and annotated using the '@Unsigned' annotation. You may choose the 'wrap-guava' mode in order to import types as class wrapper types (`UnsignedInteger` et al) defined by the Google Guava library's `com.google.common.primitives' package. that ensure complete type-safety with regards to unsigned values, however they incur an allocation and performance overhead.")
65-
var unsignedNumbers: JExtractUnsignedIntegerMode = .default
64+
@Option(help: "The mode of generation to use for the output files. Used with jextract mode. By default, unsigned Swift types are imported as their bit-width compatible signed Java counterparts, and annotated using the '@Unsigned' annotation. You may choose the 'wrapGuava' mode in order to import types as class wrapper types (`UnsignedInteger` et al) defined by the Google Guava library's `com.google.common.primitives' package. that ensure complete type-safety with regards to unsigned values, however they incur an allocation and performance overhead.")
65+
var unsignedNumbersMode: JExtractUnsignedIntegerMode?
6666

6767
@Option(help: "The lowest access level of Swift declarations that should be extracted, defaults to 'public'.")
68-
var minimumInputAccessLevel: JExtractMinimumAccessLevelMode = .default
68+
var minimumInputAccessLevelMode: JExtractMinimumAccessLevelMode?
6969

70-
@Option(help: "The memory management mode to use for the generated code. By default, the user must explicitly provide `SwiftArena` to all calls that require it. By choosing `allow-automatic`, user can omit this parameter and a global GC-based arena will be used. `force-automatic` removes all explicit memory management.")
71-
var memoryManagementMode: JExtractMemoryManagementMode = .default
70+
@Option(help: "The memory management mode to use for the generated code. By default, the user must explicitly provide `SwiftArena` to all calls that require it. By choosing `allowGlobalAutomatic`, user can omit this parameter and a global GC-based arena will be used.")
71+
var memoryManagementMode: JExtractMemoryManagementMode?
7272

7373
@Option(
7474
help: """
@@ -80,7 +80,7 @@ extension SwiftJava {
8080
var dependsOn: [String] = []
8181

8282
@Option(help: "The mode to use for extracting asynchronous Swift functions. By default async methods are extracted as Java functions returning CompletableFuture.")
83-
var asyncFuncMode: JExtractAsyncFuncMode = .default
83+
var asyncFuncMode: JExtractAsyncFuncMode?
8484
}
8585
}
8686

@@ -89,21 +89,21 @@ extension SwiftJava.JExtractCommand {
8989
if let javaPackage {
9090
config.javaPackage = javaPackage
9191
}
92-
if let mode {
93-
config.mode = mode
94-
} else if config.mode == nil {
95-
config.mode = .ffm
96-
}
92+
configure(&config.mode, overrideWith: self.mode)
9793
config.swiftModule = self.effectiveSwiftModule
9894
config.outputJavaDirectory = outputJava
9995
config.outputSwiftDirectory = outputSwift
100-
config.writeEmptyFiles = writeEmptyFiles
101-
config.unsignedNumbersMode = unsignedNumbers
102-
config.minimumInputAccessLevelMode = minimumInputAccessLevel
103-
config.memoryManagementMode = memoryManagementMode
104-
config.asyncFuncMode = asyncFuncMode
10596

106-
try checkModeCompatibility()
97+
// @Flag does not support optional, so we check ourself if it is passed
98+
let writeEmptyFiles = CommandLine.arguments.contains("--write-empty-files") ? true : nil
99+
configure(&config.writeEmptyFiles, overrideWith: writeEmptyFiles)
100+
101+
configure(&config.unsignedNumbersMode, overrideWith: self.unsignedNumbersMode)
102+
configure(&config.minimumInputAccessLevelMode, overrideWith: self.minimumInputAccessLevelMode)
103+
configure(&config.memoryManagementMode, overrideWith: self.memoryManagementMode)
104+
configure(&config.asyncFuncMode, overrideWith: self.asyncFuncMode)
105+
106+
try checkModeCompatibility(config: config)
107107

108108
if let inputSwift = commonOptions.inputSwift {
109109
config.inputSwiftDirectory = inputSwift
@@ -112,7 +112,7 @@ extension SwiftJava.JExtractCommand {
112112
config.inputSwiftDirectory = "\(FileManager.default.currentDirectoryPath)/Sources/\(swiftModule)"
113113
}
114114

115-
print("[debug][swift-java] Running 'swift-java jextract' in mode: " + "\(config.mode ?? .ffm)".bold)
115+
print("[debug][swift-java] Running 'swift-java jextract' in mode: " + "\(config.effectiveMode)".bold)
116116

117117
// Load all of the dependent configurations and associate them with Swift modules.
118118
let dependentConfigs = try loadDependentConfigs(dependsOn: self.dependsOn)
@@ -122,20 +122,26 @@ extension SwiftJava.JExtractCommand {
122122
}
123123

124124
/// Check if the configured modes are compatible, and fail if not
125-
func checkModeCompatibility() throws {
126-
if self.mode == .jni {
127-
switch self.unsignedNumbers {
125+
func checkModeCompatibility(config: Configuration) throws {
126+
if config.effectiveMode == .jni {
127+
switch config.effectiveUnsignedNumbersMode {
128128
case .annotate:
129129
() // OK
130130
case .wrapGuava:
131131
throw IllegalModeCombinationError("JNI mode does not support '\(JExtractUnsignedIntegerMode.wrapGuava)' Unsigned integer mode! \(Self.helpMessage)")
132132
}
133-
} else if self.mode == .ffm {
134-
guard self.memoryManagementMode == .explicit else {
133+
} else if config.effectiveMode == .ffm {
134+
guard config.effectiveMemoryManagementMode == .explicit else {
135135
throw IllegalModeCombinationError("FFM mode does not support '\(self.memoryManagementMode)' memory management mode! \(Self.helpMessage)")
136136
}
137137
}
138138
}
139+
140+
func configure<T>(_ setting: inout T?, overrideWith value: T?) {
141+
if let value {
142+
setting = value
143+
}
144+
}
139145
}
140146

141147
struct IncompatibleModeError: Error {

Sources/SwiftJavaTool/SwiftJavaBaseAsyncParsableCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,4 @@ extension SwiftJavaBaseAsyncParsableCommand {
167167
config.logLevel = command.logLevel
168168
return config
169169
}
170-
}
170+
}

0 commit comments

Comments
 (0)