Skip to content

Commit e453505

Browse files
committed
Java2Swift: Handle class paths provided in config files
Read the `classPath` field from each config file and feed the concatenated class path into the Java virtual machine instance we create. As part of this, clean up the code path that emits a configuration file with all of the classes within a Jar file.
1 parent f01800e commit e453505

File tree

2 files changed

+115
-53
lines changed

2 files changed

+115
-53
lines changed

Sources/Java2Swift/JavaToSwift.swift

Lines changed: 113 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -58,55 +58,118 @@ struct JavaToSwift: ParsableCommand {
5858
)
5959
var input: String
6060

61+
/// Describes what kind of generation action is being performed by
62+
/// Java2Swift.
63+
enum GenerationMode {
64+
/// Generate a configuration file given a Jar file.
65+
case configuration(jarFile: String)
66+
67+
/// Generate Swift wrappers for Java classes based on the given
68+
/// configuration.
69+
case classWrappers(Configuration)
70+
}
71+
6172
mutating func run() throws {
62-
var vmOptions: [String] = []
63-
let classpath = classPathWithJarFile
64-
if !classpath.isEmpty {
65-
vmOptions.append("-cp")
66-
vmOptions.append(contentsOf: classpath)
73+
// Determine the mode in which we'll execute.
74+
let generationMode: GenerationMode
75+
if jarFile {
76+
generationMode = .configuration(jarFile: input)
77+
} else {
78+
let config = try JavaTranslator.readConfiguration(from: URL(filePath: input))
79+
generationMode = .classWrappers(config)
80+
}
81+
82+
// Load all of the dependent configurations and associate them with Swift
83+
// modules.
84+
let dependentConfigs = try dependsOn.map { dependentConfig in
85+
guard let equalLoc = dependentConfig.firstIndex(of: "=") else {
86+
throw JavaToSwiftError.badConfigOption(dependentConfig)
87+
}
88+
89+
let afterEqual = dependentConfig.index(after: equalLoc)
90+
let swiftModuleName = String(dependentConfig[..<equalLoc])
91+
let configFileName = String(dependentConfig[afterEqual...])
92+
93+
let config = try JavaTranslator.readConfiguration(from: URL(filePath: configFileName))
94+
95+
return (swiftModuleName, config)
6796
}
6897

69-
let jvm = try JavaVirtualMachine.shared(vmOptions: vmOptions)
70-
try run(environment: jvm.environment())
98+
// Form a class path from all of our input sources:
99+
// * Command-line option --classpath
100+
var classPathPieces: [String] = classpath
101+
switch generationMode {
102+
case .configuration(jarFile: let jarFile):
103+
// * Jar file (in `-jar-file` mode)
104+
classPathPieces.append(jarFile)
105+
case .classWrappers(let config):
106+
// * Class path specified in the configuration file (if any)
107+
config.classPath.map { classPathPieces.append($0) }
108+
}
109+
110+
// * Classes paths from all dependent configuration files
111+
for (_, config) in dependentConfigs {
112+
config.classPath.map { classPathPieces.append($0) }
113+
}
114+
115+
// Bring up the Java VM.
116+
let jvm = try JavaVirtualMachine.shared(classPath: classPathPieces)
117+
118+
// Run the generation step.
119+
let classPath = classPathPieces.joined(separator: ":")
120+
switch generationMode {
121+
case .configuration(jarFile: let jarFile):
122+
try emitConfiguration(
123+
forJarFile: jarFile,
124+
classPath: classPath,
125+
environment: jvm.environment()
126+
)
127+
128+
case .classWrappers(let config):
129+
try generateWrappers(
130+
config: config,
131+
classPath: classPath,
132+
dependentConfigs: dependentConfigs,
133+
environment: jvm.environment()
134+
)
135+
}
71136
}
72137

73-
mutating func run(environment: JNIEnvironment) throws {
138+
/// Generate wrapper
139+
mutating func generateWrappers(
140+
config: Configuration,
141+
classPath: String,
142+
dependentConfigs: [(String, Configuration)],
143+
environment: JNIEnvironment
144+
) throws {
74145
let translator = JavaTranslator(
75146
swiftModuleName: moduleName,
76147
environment: environment
77148
)
78149

79-
// Load all of the configurations this depends on.
80-
for config in dependsOn {
81-
guard let equalLoc = config.firstIndex(of: "=") else {
82-
throw JavaToSwiftError.badConfigOption(config)
83-
}
84-
85-
let afterEqual = config.index(after: equalLoc)
86-
let swiftModuleName = String(config[..<equalLoc])
87-
let configFileName = String(config[afterEqual...])
88-
89-
try translator.loadDependentConfiguration(
90-
forSwiftModule: swiftModuleName,
91-
from: URL(filePath: configFileName)
150+
// Note all of the dependent configurations.
151+
for (swiftModuleName, dependentConfig) in dependentConfigs {
152+
translator.addConfiguration(
153+
dependentConfig,
154+
forSwiftModule: swiftModuleName
92155
)
93156
}
94157

95-
// Jar file mode: read a Jar file and output a configuration.
96-
if jarFile {
97-
return try emitConfiguration(forJarFile: input, environment: environment)
98-
}
99-
100-
// Load the configuration file.
101-
let config = try translator.readConfiguration(from: URL(filePath: input))
158+
// Add the configuration for this module.
159+
translator.addConfiguration(config, forSwiftModule: moduleName)
102160

103161
// Load all of the requested classes.
162+
#if false
104163
let classLoader = URLClassLoader(
105-
try classPathWithJarFile.map {
106-
try URL("file://\($0)", environment: environment)
107-
},
164+
[
165+
try URL("file://\(classPath)", environment: environment)
166+
],
108167
environment: environment
109168
)
169+
#else
170+
let classLoader = try JavaClass<ClassLoader>(in: environment)
171+
.getSystemClassLoader()!
172+
#endif
110173
var javaClasses: [JavaClass<JavaObject>] = []
111174
for (javaClassName, swiftName) in config.classes {
112175
guard let javaClass = try classLoader.loadClass(javaClassName) else {
@@ -146,15 +209,6 @@ struct JavaToSwift: ParsableCommand {
146209
}
147210
}
148211

149-
/// Return the class path augmented with the Jar file, if there is one.
150-
var classPathWithJarFile: [String] {
151-
if jarFile {
152-
return [input] + classpath
153-
}
154-
155-
return classpath
156-
}
157-
158212
func writeContents(_ contents: String, to filename: String, description: String) throws {
159213
if outputDirectory == "-" {
160214
print("// \(filename) - \(description)")
@@ -171,10 +225,13 @@ struct JavaToSwift: ParsableCommand {
171225
print(" done.")
172226
}
173227

174-
func emitConfiguration(forJarFile jarFileName: String, environment: JNIEnvironment) throws {
175-
var configuration = Configuration(
176-
classPath: classPathWithJarFile.joined(separator: ":")
177-
)
228+
func emitConfiguration(
229+
forJarFile jarFileName: String,
230+
classPath: String,
231+
environment: JNIEnvironment
232+
) throws {
233+
var configuration = Configuration(classPath: classPath)
234+
178235
let jarFile = try JarFile(jarFileName, false, environment: environment)
179236
for entry in jarFile.entries()! {
180237
// We only look at class files in the Jar file.
@@ -205,9 +262,7 @@ struct JavaToSwift: ParsableCommand {
205262
// Write the file.
206263
try writeContents(
207264
contents,
208-
to: URL(filePath: outputDirectory)
209-
.appending(path: "Java2Swift.config")
210-
.path(percentEncoded: false),
265+
to: "Java2Swift.config",
211266
description: "Java2Swift configuration file"
212267
)
213268
}
@@ -238,3 +293,14 @@ extension String {
238293
return self
239294
}
240295
}
296+
297+
@JavaClass("java.lang.ClassLoader")
298+
public struct ClassLoader {
299+
@JavaMethod
300+
public func loadClass(_ arg0: String) throws -> JavaClass<JavaObject>?
301+
}
302+
303+
extension JavaClass<ClassLoader> {
304+
@JavaStaticMethod
305+
public func getSystemClassLoader() -> ClassLoader?
306+
}

Sources/Java2SwiftLib/JavaTranslator+Configuration.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ import Foundation
1616

1717
extension JavaTranslator {
1818
/// Read a configuration file from the given URL.
19-
package func readConfiguration(from url: URL) throws -> Configuration {
19+
package static func readConfiguration(from url: URL) throws -> Configuration {
2020
let contents = try Data(contentsOf: url)
2121
return try JSONDecoder().decode(Configuration.self, from: contents)
2222
}
2323

2424
/// Load the configuration file with the given name to populate the known set of
2525
/// translated Java classes.
26-
package func loadDependentConfiguration(forSwiftModule swiftModule: String, from url: URL) throws {
27-
let config = try readConfiguration(from: url)
28-
29-
// TODO: Should we merge the class path from our dependencies?
30-
26+
package func addConfiguration(_ config: Configuration, forSwiftModule swiftModule: String) {
3127
for (javaClassName, swiftName) in config.classes {
3228
translatedClasses[javaClassName] = (
3329
swiftType: swiftName,

0 commit comments

Comments
 (0)