Skip to content

Commit c509c1f

Browse files
committed
New commands: wrap-java and resolve work from swift-java CLI tool
1 parent 1fcfd3a commit c509c1f

File tree

8 files changed

+91
-57
lines changed

8 files changed

+91
-57
lines changed

Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,10 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
8787
searchForConfigFiles(in: dependency)
8888
}
8989

90-
var arguments: [String] = [
91-
"wrap-java"
92-
]
90+
var arguments: [String] = []
9391
arguments += argumentsSwiftModule(sourceModule: sourceModule)
9492
arguments += argumentsOutputDirectory(context: context)
9593
arguments += argumentsDependedOnConfigs(dependentConfigFiles)
96-
arguments.append(configFile.path(percentEncoded: false))
9794

9895
let classes = config.classes ?? [:]
9996
print("[swift-java-plugin] Classes to wrap (\(classes.count)): \(classes.map(\.key))")
@@ -158,12 +155,7 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
158155
executable: executable,
159156
arguments: ["resolve"]
160157
+ argumentsOutputDirectory(context: context, generated: false)
161-
+ argumentsSwiftModule(sourceModule: sourceModule)
162-
// + [
163-
// // explicitly provide config path
164-
// configFile.path(percentEncoded: false)
165-
// ]
166-
,
158+
+ argumentsSwiftModule(sourceModule: sourceModule),
167159
environment: [:],
168160
inputFiles: [configFile],
169161
outputFiles: fetchDependenciesOutputFiles
@@ -174,13 +166,16 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
174166
}
175167

176168
if !outputSwiftFiles.isEmpty {
169+
arguments += [ configFile.path(percentEncoded: false) ]
170+
177171
let displayName = "Wrapping \(classes.count) Java classes in Swift target '\(sourceModule.name)'"
178172
log("Prepared: \(displayName)")
179173
commands += [
180174
.buildCommand(
181175
displayName: displayName,
182176
executable: executable,
183-
arguments: arguments,
177+
arguments: ["wrap-java"]
178+
+ arguments,
184179
inputFiles: compiledClassFiles + fetchDependenciesOutputFiles + [ configFile ],
185180
outputFiles: outputSwiftFiles
186181
)

Sources/JavaKitConfigurationShared/Configuration.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
5+
// Copyright (c) 2024-2025 Apple Inc. and the Swift.org project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -156,15 +156,15 @@ public func findSwiftJavaClasspaths(in basePath: String = FileManager.default.cu
156156
let baseURL = URL(fileURLWithPath: basePath)
157157
var classpathEntries: [String] = []
158158

159-
print("[debug][swift-java] Searching for *.swift-java.classpath files in: \(baseURL)")
159+
print("[debug][swift-java] Searching for *.swift-java.classpath files in: \(baseURL.absoluteString)")
160160
guard let enumerator = fileManager.enumerator(at: baseURL, includingPropertiesForKeys: []) else {
161161
print("[warning][swift-java] Failed to get enumerator for \(baseURL)")
162162
return []
163163
}
164164

165165
for case let fileURL as URL in enumerator {
166166
if fileURL.lastPathComponent.hasSuffix(".swift-java.classpath") {
167-
print("[debug][swift-java] Constructing classpath with entries from: \(fileURL.relativePath)")
167+
print("[debug][swift-java] Constructing classpath with entries from: \(fileURL.path)")
168168
if let contents = try? String(contentsOf: fileURL) {
169169
let entries = contents.split(separator: ":").map(String.init)
170170
for entry in entries {

Sources/SwiftJavaTool/Commands/ConfigureCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ extension SwiftJava {
6666

6767
extension SwiftJava.ConfigureCommand {
6868
mutating func runSwiftJavaCommand(config: inout Configuration) async throws {
69-
let classpathEntries =
70-
self.configureCommandJVMClasspath(effectiveSwiftModuleURL: self.effectiveSwiftModuleURL, config: config)
69+
let classpathEntries = self.configureCommandJVMClasspath(
70+
searchDirs: [self.effectiveSwiftModuleURL], config: config)
7171

7272
let jvm =
7373
try self.makeJVM(classpathEntries: classpathEntries)

Sources/SwiftJavaTool/Commands/SwiftJava+JExtract.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,32 @@ import JavaKitConfigurationShared
3131
/// ```
3232
extension SwiftJava {
3333

34+
struct JExtractCommand: SwiftJavaBaseAsyncParsableCommand, HasCommonOptions {
35+
static let configuration = CommandConfiguration(
36+
commandName: "jextract", // TODO: wrap-swift?
37+
abstract: "Resolve dependencies and write the resulting swift-java.classpath file")
38+
39+
@OptionGroup var commonOptions: SwiftJava.CommonOptions
40+
41+
@Option(help: "The mode of generation to use for the output files. Used with jextract mode.")
42+
var mode: GenerationMode = .ffm
43+
44+
@Option(help: "The name of the Swift module into which the resulting Swift types will be generated.")
45+
var swiftModule: String
46+
47+
var effectiveSwiftModule: String {
48+
swiftModule
49+
}
50+
}
51+
}
52+
53+
extension SwiftJava.JExtractCommand {
54+
func runSwiftJavaCommand(config: inout Configuration) async throws {
55+
fatalError("not implemented yet")
56+
}
57+
}
58+
59+
extension SwiftJava {
3460
mutating func jextractSwift(
3561
config: Configuration
3662
) throws {

Sources/SwiftJavaTool/Commands/WrapJavaCommand.swift

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
5+
// Copyright (c) 2024-2025 Apple Inc. and the Swift.org project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -52,6 +52,9 @@ extension SwiftJava {
5252
@Option(help: "The names of Java classes whose declared native methods will be implemented in Swift.")
5353
var swiftNativeImplementation: [String] = []
5454

55+
@Option(help: "Cache directory for intermediate results and other outputs between runs")
56+
var cacheDirectory: String?
57+
5558
@Argument(help: "Path to .jar file whose Java classes should be wrapped using Swift bindings")
5659
var input: String
5760
}
@@ -60,34 +63,46 @@ extension SwiftJava {
6063
extension SwiftJava.WrapJavaCommand {
6164

6265
mutating func runSwiftJavaCommand(config: inout Configuration) async throws {
66+
// Perform any config overrides by command options:
6367
if let javaPackage {
6468
config.javaPackage = javaPackage
6569
}
6670

67-
// Load all of the dependent configurations and associate them with Swift
68-
// modules.
69-
let dependentConfigs = try loadDependentConfigs()
7071

71-
// Configure our own classpath based on config
72-
var classpathEntries =
73-
self.configureCommandJVMClasspath(effectiveSwiftModuleURL: self.effectiveSwiftModuleURL, config: config)
72+
73+
// Get base classpath configuration for this target and configuration
74+
var classpathSearchDirs = [self.effectiveSwiftModuleURL]
75+
if let cacheDir = self.cacheDirectory {
76+
print("[trace][swift-java] Cache directory: \(cacheDir)")
77+
classpathSearchDirs += [URL(fileURLWithPath: cacheDir)]
78+
} else {
79+
print("[trace][swift-java] Cache directory: none")
80+
}
81+
print("[trace][swift-java] INPUT: \(input)")
82+
83+
var classpathEntries = self.configureCommandJVMClasspath(
84+
searchDirs: classpathSearchDirs, config: config)
85+
86+
// Load all of the dependent configurations and associate them with Swift modules.
87+
let dependentConfigs = try self.loadDependentConfigs()
88+
print("[debug][swift-java] Dependent configs: \(dependentConfigs.count)")
7489

7590
// Include classpath entries which libs we depend on require...
7691
for (fromModule, config) in dependentConfigs {
92+
print("[trace][swift-java] Add dependent config (\(fromModule)) classpath elements: \(config.classpathEntries.count)")
7793
// TODO: may need to resolve the dependent configs rather than just get their configs
7894
// TODO: We should cache the resolved classpaths as well so we don't do it many times
79-
config.classpath.map { entry in
95+
for entry in config.classpathEntries {
8096
print("[trace][swift-java] Add dependent config (\(fromModule)) classpath element: \(entry)")
8197
classpathEntries.append(entry)
8298
}
8399
}
84100

85-
let completeClasspath = classpathEntries.joined(separator: ":")
86101
let jvm = try self.makeJVM(classpathEntries: classpathEntries)
87102

88103
try self.generateWrappers(
89104
config: config,
90-
classpath: completeClasspath,
105+
// classpathEntries: classpathEntries,
91106
dependentConfigs: dependentConfigs,
92107
environment: jvm.environment()
93108
)
@@ -118,7 +133,7 @@ extension SwiftJava.WrapJavaCommand {
118133
extension SwiftJava.WrapJavaCommand {
119134
mutating func generateWrappers(
120135
config: Configuration,
121-
classpath: String,
136+
// classpathEntries: [String],
122137
dependentConfigs: [(String, Configuration)],
123138
environment: JNIEnvironment
124139
) throws {

Sources/SwiftJavaTool/CommonOptions.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,30 @@ extension HasCommonJVMOptions {
7373
}
7474

7575
extension HasCommonJVMOptions {
76-
func configureCommandJVMClasspath(effectiveSwiftModuleURL: Foundation.URL, config: Configuration) -> [String] {
76+
77+
/// Collect classpath information from various sources such as CLASSPATH, `-cp` option and
78+
/// swift-java.classpath files as configured.
79+
/// Parameters:
80+
/// - searchDirs: search directories where we can find swift.java.classpath files to include in the configuration
81+
func configureCommandJVMClasspath(searchDirs: [Foundation.URL], config: Configuration) -> [String] {
7782
// Form a class path from all of our input sources:
7883
// * Command-line option --classpath
7984
let classpathOptionEntries: [String] = self.classpathEntries
8085
let classpathFromEnv = ProcessInfo.processInfo.environment["CLASSPATH"]?.split(separator: ":").map(String.init) ?? []
86+
print("[debug][swift-java] Base classpath from CLASSPATH environment: \(classpathFromEnv)")
8187
let classpathFromConfig: [String] = config.classpath?.split(separator: ":").map(String.init) ?? []
8288
print("[debug][swift-java] Base classpath from config: \(classpathFromConfig)")
8389

8490
var classpathEntries: [String] = classpathFromConfig
8591

86-
let classPathFilesSearchDirectory = effectiveSwiftModuleURL.absoluteString
87-
print("[debug][swift-java] Search *.swift-java.classpath in: \(classPathFilesSearchDirectory)")
88-
let swiftJavaCachedModuleClasspath = findSwiftJavaClasspaths(in: classPathFilesSearchDirectory)
92+
for searchDir in searchDirs {
93+
let classPathFilesSearchDirectory = searchDir.path
94+
print("[debug][swift-java] Search *.swift-java.classpath in: \(classPathFilesSearchDirectory)")
95+
let foundSwiftJavaClasspath = findSwiftJavaClasspaths(in: classPathFilesSearchDirectory)
8996

90-
print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(swiftJavaCachedModuleClasspath)")
91-
classpathEntries += swiftJavaCachedModuleClasspath
97+
print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(foundSwiftJavaClasspath)")
98+
classpathEntries += foundSwiftJavaClasspath
99+
}
92100

93101
if !classpathOptionEntries.isEmpty {
94102
print("[debug][swift-java] Classpath from options: \(classpathOptionEntries)")

Sources/SwiftJavaTool/SwiftJava.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,19 @@ struct SwiftJava: SwiftJavaBaseAsyncParsableCommand, HasCommonJVMOptions { // FI
7777
@Option(help: "The mode of generation to use for the output files. Used with jextract mode.")
7878
var mode: GenerationMode = .ffm
7979

80-
@Option(name: .shortAndLong, help: "Directory where to write cached values (e.g. swift-java.classpath files)")
81-
var cacheDirectory: String? = nil
80+
// @Option(name: .shortAndLong, help: "Directory where to write cached values (e.g. swift-java.classpath files)")
81+
// var cacheDirectory: String? = nil
8282

8383
@OptionGroup var commonOptions: SwiftJava.CommonOptions
8484
@OptionGroup var commonJVMOptions: SwiftJava.CommonJVMOptions
8585

86-
var effectiveCacheDirectory: String? {
87-
if let cacheDirectory {
88-
return cacheDirectory
89-
} else {
90-
return nil
91-
}
92-
}
86+
// var effectiveCacheDirectory: String? {
87+
// if let cacheDirectory {
88+
// return cacheDirectory
89+
// } else {
90+
// return nil
91+
// }
92+
// }
9393

9494
// @Argument(
9595
// help: "The input file, which is either a Java2Swift configuration file or (if '-jar' was specified) a Jar file."
@@ -193,10 +193,10 @@ struct SwiftJava: SwiftJavaBaseAsyncParsableCommand, HasCommonJVMOptions { // FI
193193

194194
var classpathEntries: [String] = classpathFromConfig
195195

196-
let swiftJavaCachedModuleClasspath = findSwiftJavaClasspaths(
197-
in: self.effectiveCacheDirectory ?? FileManager.default.currentDirectoryPath)
198-
print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(swiftJavaCachedModuleClasspath)")
199-
classpathEntries += swiftJavaCachedModuleClasspath
196+
// let swiftJavaCachedModuleClasspath = findSwiftJavaClasspaths(
197+
// in: self.effectiveCacheDirectory ?? FileManager.default.currentDirectoryPath)
198+
// print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(swiftJavaCachedModuleClasspath)")
199+
// classpathEntries += swiftJavaCachedModuleClasspath
200200

201201
if !classpathOptionEntries.isEmpty {
202202
print("[debug][swift-java] Classpath from options: \(classpathOptionEntries)")

Sources/SwiftJavaTool/SwiftJavaBaseAsyncParsableCommand.swift

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ extension SwiftJavaBaseAsyncParsableCommand {
4646
extension SwiftJavaBaseAsyncParsableCommand {
4747
public mutating func run() async {
4848
print("[info][swift-java] Run \(Self.self): \(CommandLine.arguments.joined(separator: " "))")
49-
print("[info][swift-java] Current work directory: \(URL(fileURLWithPath: "."))")
49+
print("[info][swift-java] Current work directory: \(URL(fileURLWithPath: ".").path)")
5050

5151
do {
5252
var config = try readInitialConfiguration(command: self)
@@ -64,16 +64,6 @@ extension SwiftJavaBaseAsyncParsableCommand {
6464
}
6565

6666
extension SwiftJavaBaseAsyncParsableCommand {
67-
// mutating func writeContents(
68-
// _ contents: String,
69-
// to filename: String, description: String) throws {
70-
// try writeContents(
71-
// contents,
72-
// outputDirectoryOverride: self.actualOutputDirectory,
73-
// to: filename,
74-
// description: description)
75-
// }
76-
7767
mutating func writeContents(
7868
_ contents: String,
7969
outputDirectory: Foundation.URL?,

0 commit comments

Comments
 (0)