Skip to content

Commit be895f4

Browse files
committed
Introduce resolve subcommand and further dis-entangle commands
1 parent 2fb33d8 commit be895f4

File tree

16 files changed

+553
-280
lines changed

16 files changed

+553
-280
lines changed

Package.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ let javaIncludePath = "\(javaHome)/include"
8585
let package = Package(
8686
name: "SwiftJava",
8787
platforms: [
88-
.macOS(.v10_15)
88+
.macOS(.v13)
8989
],
9090
products: [
9191
// ==== JavaKit (i.e. calling Java directly Swift utilities)
@@ -467,6 +467,15 @@ let package = Package(
467467
]
468468
),
469469

470+
.testTarget(
471+
name: "JavaKitConfigurationSharedTests",
472+
dependencies: ["JavaKitConfigurationShared"],
473+
swiftSettings: [
474+
.swiftLanguageMode(.v5),
475+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
476+
]
477+
),
478+
470479
.testTarget(
471480
name: "JExtractSwiftTests",
472481
dependencies: [

Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin
8282
]
8383
// arguments.append(sourceDir) // TODO: we could do this shape maybe? to have the dirs last?
8484
if let package = configuration?.javaPackage, !package.isEmpty {
85-
["--java-package", package]
85+
arguments += ["--java-package", package]
8686
}
8787

8888
return arguments

Plugins/PluginsShared/SwiftJavaPluginProtocol.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ protocol SwiftJavaPluginProtocol {
2121

2222
extension SwiftJavaPluginProtocol {
2323
func log(_ message: @autoclosure () -> String, terminator: String = "\n") {
24-
// if self.verbose {
25-
print("[\(pluginName)] \(message())", terminator: terminator)
26-
// }
24+
print("[\(pluginName)] \(message())", terminator: terminator)
2725
}
2826
}

Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,13 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
8888
}
8989

9090
var arguments: [String] = []
91-
arguments += argumentsModuleName(sourceModule: sourceModule)
91+
arguments += argumentsSwiftModuleDeprecated(sourceModule: sourceModule)
9292
arguments += argumentsOutputDirectory(context: context)
93-
94-
arguments += dependentConfigFiles.flatMap { moduleAndConfigFile in
95-
let (moduleName, configFile) = moduleAndConfigFile
96-
return [
97-
"--depends-on",
98-
"\(moduleName)=\(configFile.path(percentEncoded: false))"
99-
]
100-
}
93+
arguments += argumentsDependedOnConfigs(dependentConfigFiles)
10194
arguments.append(configFile.path(percentEncoded: false))
10295

103-
// guard let classes = config.classes else {
104-
// log("Config at \(configFile) did not have 'classes' configured, skipping java2swift step.")
105-
// return []
106-
// }
10796
let classes = config.classes ?? [:]
108-
print("Classes to wrap: \(classes.map(\.key))")
97+
print("[swift-java-plugin] Classes to wrap (\(classes.count)): \(classes.map(\.key))")
10998

11099
/// Determine the set of Swift files that will be emitted by the Java2Swift tool.
111100
// TODO: this is not precise and won't work with more advanced Java files, e.g. lambdas etc.
@@ -165,12 +154,14 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
165154
.buildCommand(
166155
displayName: displayName,
167156
executable: executable,
168-
arguments: [
169-
// FIXME: change to 'resolve' subcommand
170-
"--fetch", configFile.path(percentEncoded: false),
171-
"--swift-module", sourceModule.name,
172-
"--output-directory", outputDirectory(context: context, generated: false).path(percentEncoded: false)
173-
],
157+
arguments: ["resolve"]
158+
+ argumentsOutputDirectory(context: context, generated: false)
159+
+ argumentsSwiftModule(sourceModule: sourceModule)
160+
// + [
161+
// // explicitly provide config path
162+
// configFile.path(percentEncoded: false)
163+
// ]
164+
,
174165
environment: [:],
175166
inputFiles: [configFile],
176167
outputFiles: fetchDependenciesOutputFiles
@@ -192,28 +183,49 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
192183
outputFiles: outputSwiftFiles
193184
)
194185
]
195-
} else {
196-
log("No Swift output files, skip wrapping")
186+
}
187+
188+
if commands.isEmpty {
189+
log("No swift-java commands for module '\(sourceModule.name)'")
197190
}
198191

199192
return commands
200193
}
201194
}
202195

203196
extension SwiftJavaBuildToolPlugin {
204-
func argumentsModuleName(sourceModule: Target) -> [String] {
197+
func argumentsSwiftModule(sourceModule: Target) -> [String] {
205198
return [
206199
"--swift-module", sourceModule.name
207200
]
208201
}
209-
202+
203+
// FIXME: remove this and the deprecated property inside SwiftJava, this is a workaround
204+
// since we cannot have the same option in common options and in the top level
205+
// command from which we get into sub commands. The top command will NOT have this option.
206+
func argumentsSwiftModuleDeprecated(sourceModule: Target) -> [String] {
207+
return [
208+
"--swift-module-deprecated", sourceModule.name
209+
]
210+
}
211+
210212
func argumentsOutputDirectory(context: PluginContext, generated: Bool = true) -> [String] {
211213
return [
212214
"--output-directory",
213215
outputDirectory(context: context, generated: generated).path(percentEncoded: false)
214216
]
215217
}
216-
218+
219+
func argumentsDependedOnConfigs(_ dependentConfigFiles: [(String, URL)]) -> [String] {
220+
dependentConfigFiles.flatMap { moduleAndConfigFile in
221+
let (moduleName, configFile) = moduleAndConfigFile
222+
return [
223+
"--depends-on",
224+
"\(moduleName)=\(configFile.path(percentEncoded: false))"
225+
]
226+
}
227+
}
228+
217229
func outputDirectory(context: PluginContext, generated: Bool = true) -> URL {
218230
let dir = context.pluginWorkDirectoryURL
219231
if generated {

Samples/JavaDependencySampleApp/%

Whitespace-only changes.

Samples/JavaDependencySampleApp/ci-validate.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
set -e
44
set -x
55

6+
# invoke resolve as part of a build run
67
swift run --disable-sandbox
8+
9+
# explicitly invoke resolve without explicit path or dependency
10+
# the dependencies should be uses from the --swift-module
11+
.build/plugins/tools/debug/SwiftJavaTool-tool resolve \
12+
Sources/JavaCommonsCSV/swift-java.config \
13+
--swift-module JavaCommonsCSV \
14+
--output-directory .build/plugins/outputs/javadependencysampleapp/JavaCommonsCSV/destination/SwiftJavaPlugin/

Sources/JavaKitConfigurationShared/Configuration.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ public struct Configuration: Codable {
4646
public var classpath: String? = nil
4747

4848
public var classpathEntries: [String] {
49-
guard let classpath else {
50-
return []
51-
}
52-
53-
return classpath.split(separator: ":").map(String.init)
49+
return classpath?.split(separator: ":").map(String.init) ?? []
5450
}
5551

5652
/// The Java classes that should be translated to Swift. The keys are
@@ -80,6 +76,12 @@ public struct JavaDependencyDescriptor: Hashable, Codable {
8076
public var artifactID: String
8177
public var version: String
8278

79+
public init(groupID: String, artifactID: String, version: String) {
80+
self.groupID = groupID
81+
self.artifactID = artifactID
82+
self.version = version
83+
}
84+
8385
public init(from decoder: any Decoder) throws {
8486
let container = try decoder.singleValueContainer()
8587
let string = try container.decode(String.self)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
17+
// Regex is not sendable yet so we can't cache it in a let
18+
fileprivate var GradleDependencyDescriptorRegex: Regex<(Substring, Substring, Substring, Substring)> {
19+
try! Regex(#"^([^:]+):([^:]+):(\d[^:]+)$"#) // TODO: improve the regex to be more precise
20+
}
21+
22+
// note: can't use `package` access level since it would break in usage in plugins in `_PluginsShared`.
23+
public func parseDependencyDescriptor(_ descriptor: String) -> JavaDependencyDescriptor? {
24+
guard let match = try? GradleDependencyDescriptorRegex.firstMatch(in: descriptor) else {
25+
return nil
26+
}
27+
28+
let groupID = String(match.1)
29+
let artifactID = String(match.2)
30+
let version = String(match.3)
31+
32+
return JavaDependencyDescriptor(groupID: groupID, artifactID: artifactID, version: version)
33+
}
34+
35+
// note: can't use `package` access level since it would break in usage in plugins in `_PluginsShared`.
36+
public func parseDependencyDescriptors(_ string: String) -> [JavaDependencyDescriptor] {
37+
let descriptors = string.components(separatedBy: ",")
38+
var parsedDependencies: [JavaDependencyDescriptor] = []
39+
parsedDependencies.reserveCapacity(descriptors.count)
40+
41+
for descriptor in descriptors {
42+
if let dependency = parseDependencyDescriptor(descriptor) {
43+
parsedDependencies.append(dependency)
44+
}
45+
}
46+
47+
return parsedDependencies
48+
}

Sources/SwiftJavaLib/JavaTranslator+Configuration.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ import Foundation
1616
import JavaKitConfigurationShared
1717

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

2525
/// Load the configuration file with the given name to populate the known set of
2626
/// translated Java classes.

Sources/SwiftJavaTool/Commands/ConfigureCommand.swift

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -66,42 +66,11 @@ extension SwiftJava {
6666

6767
extension SwiftJava.ConfigureCommand {
6868
mutating func runSwiftJavaCommand(config: inout Configuration) async throws {
69-
// Form a class path from all of our input sources:
70-
// * Command-line option --classpath
71-
let classpathOptionEntries: [String] = self.commonJVMOptions.classpath.flatMap { $0.split(separator: ":").map(String.init) }
72-
let classpathFromEnv = ProcessInfo.processInfo.environment["CLASSPATH"]?.split(separator: ":").map(String.init) ?? []
73-
let classpathFromConfig: [String] = config.classpath?.split(separator: ":").map(String.init) ?? []
74-
print("[debug][swift-java] Base classpath from config: \(classpathFromConfig)")
75-
76-
var classpathEntries: [String] = classpathFromConfig
77-
78-
let swiftJavaCachedModuleClasspath = findSwiftJavaClasspaths(in:
79-
// self.effectiveCacheDirectory ??
80-
FileManager.default.currentDirectoryPath)
81-
print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(swiftJavaCachedModuleClasspath)")
82-
classpathEntries += swiftJavaCachedModuleClasspath
83-
84-
if !classpathOptionEntries.isEmpty {
85-
print("[debug][swift-java] Classpath from options: \(classpathOptionEntries)")
86-
classpathEntries += classpathOptionEntries
87-
} else {
88-
// * Base classpath from CLASSPATH env variable
89-
print("[debug][swift-java] Classpath from environment: \(classpathFromEnv)")
90-
classpathEntries += classpathFromEnv
91-
}
92-
93-
let extraClasspath = input ?? "" // FIXME: just use the -cp as usual
94-
let extraClasspathEntries = extraClasspath.split(separator: ":").map(String.init)
95-
print("[debug][swift-java] Extra classpath: \(extraClasspathEntries)")
96-
classpathEntries += extraClasspathEntries
69+
let classpathEntries =
70+
self.configureCommandJVMClasspath(effectiveSwiftModuleURL: self.effectiveSwiftModuleURL, config: config)
9771

98-
// Bring up the Java VM when necessary
99-
100-
if logLevel >= .debug {
101-
let classpathString = classpathEntries.joined(separator: ":")
102-
print("[debug][swift-java] Initialize JVM with classpath: \(classpathString)")
103-
}
104-
let jvm = try JavaVirtualMachine.shared(classpath: classpathEntries)
72+
let jvm =
73+
try self.makeJVM(classpathEntries: classpathEntries)
10574

10675
try emitConfiguration(classpath: self.commonJVMOptions.classpath, environment: jvm.environment())
10776
}
@@ -178,6 +147,7 @@ extension SwiftJava.ConfigureCommand {
178147
// Write the file.
179148
try writeContents(
180149
contents,
150+
outputDirectory: self.actualOutputDirectory,
181151
to: "swift-java.config",
182152
description: "swift-java configuration file"
183153
)

0 commit comments

Comments
 (0)