Skip to content

Refactor to converge many command line tools into a single "swift-java" #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -11,8 +11,8 @@ DerivedData/
*.class
bin/
BuildLogic/out/
.index-build
.build-vscode
**/.index-build/
**/.build-vscode/

# Ignore gradle build artifacts
.gradle
20 changes: 10 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -56,29 +56,29 @@ SAMPLES_DIR := "Samples"
all:
@echo "Welcome to swift-java! There are several makefile targets to choose from:"
@echo " javakit-run: Run the JavaKit example program that uses Java libraries from Swift."
@echo " javakit-generate: Regenerate the Swift wrapper code for the various JavaKit libraries from Java. This only has to be done when changing the Java2Swift tool."
@echo " javakit-generate: Regenerate the Swift wrapper code for the various JavaKit libraries from Java. This only has to be done when changing the SwiftJava tool."

$(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/Java2Swift:
$(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/SwiftJava:
swift build

javakit-run:
cd Samples/JavaKitSampleApp && swift build && java -cp .build/plugins/outputs/javakitsampleapp/JavaKitExample/destination/JavaCompilerPlugin/Java -Djava.library.path=.build/debug com.example.swift.JavaKitSampleMain

Java2Swift: $(BUILD_DIR)/debug/Java2Swift
SwiftJava: $(BUILD_DIR)/debug/SwiftJava

generate-JavaKit: Java2Swift
generate-JavaKit: SwiftJava
mkdir -p Sources/JavaKit/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKit -o Sources/JavaKit/generated Sources/JavaKit/swift-java.config
$(BUILD_DIR)/debug/SwiftJava --module-name JavaKit -o Sources/JavaKit/generated Sources/JavaKit/swift-java.config

generate-JavaKitCollection: Java2Swift
generate-JavaKitCollection: SwiftJava
mkdir -p Sources/JavaKitCollection/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitCollection --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitCollection/generated Sources/JavaKitCollection/swift-java.config
$(BUILD_DIR)/debug/SwiftJava --module-name JavaKitCollection --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitCollection/generated Sources/JavaKitCollection/swift-java.config

generate-JavaKitFunction: Java2Swift
generate-JavaKitFunction: SwiftJava
mkdir -p Sources/JavaKitFunction/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitFunction --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitFunction/generated Sources/JavaKitFunction/swift-java.config
$(BUILD_DIR)/debug/SwiftJava --module-name JavaKitFunction --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitFunction/generated Sources/JavaKitFunction/swift-java.config

generate-JavaKitReflection: Java2Swift generate-JavaKit generate-JavaKitCollection
generate-JavaKitReflection: SwiftJava generate-JavaKit generate-JavaKitCollection
mkdir -p Sources/JavaKitReflection/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitReflection --depends-on JavaKit=Sources/JavaKit/swift-java.config --depends-on JavaKitCollection=Sources/JavaKitCollection/swift-java.config -o Sources/JavaKitReflection/generated Sources/JavaKitReflection/swift-java.config

35 changes: 14 additions & 21 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -87,8 +87,8 @@ let package = Package(
),

.executable(
name: "Java2Swift",
targets: ["Java2Swift"]
name: "SwiftJavaTool",
targets: ["SwiftJavaTool"]
),

// ==== Plugin for building Java code
@@ -101,17 +101,17 @@ let package = Package(

// ==== Plugin for wrapping Java classes in Swift
.plugin(
name: "Java2SwiftPlugin",
name: "SwiftJavaPlugin",
targets: [
"Java2SwiftPlugin"
"SwiftJavaPlugin"
]
),

// ==== jextract-swift (extract Java accessors from Swift interface files)

.executable(
name: "jextract-swift",
targets: ["JExtractSwiftTool"]
targets: ["SwiftJavaTool"]
),

// Support library written in Swift for SwiftKit "Java"
@@ -257,10 +257,10 @@ let package = Package(
),

.plugin(
name: "Java2SwiftPlugin",
name: "SwiftJavaPlugin",
capability: .buildTool(),
dependencies: [
"Java2Swift"
"SwiftJavaTool"
]
),

@@ -320,7 +320,7 @@ let package = Package(
),

.executableTarget(
name: "Java2Swift",
name: "SwiftJavaTool",
dependencies: [
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
@@ -330,6 +330,7 @@ let package = Package(
"JavaKitJar",
"JavaKitNetwork",
"Java2SwiftLib",
"JExtractSwift", // TODO: Swift2JavaLib
"JavaKitShared",
],

@@ -356,21 +357,11 @@ let package = Package(
]
),

.executableTarget(
name: "JExtractSwiftTool",
dependencies: [
"JExtractSwift",
],
swiftSettings: [
.swiftLanguageMode(.v5)
]
),

.plugin(
name: "JExtractSwiftPlugin",
capability: .buildTool(),
dependencies: [
"JExtractSwiftTool"
"SwiftJavaTool"
]
),
.plugin(
@@ -380,7 +371,7 @@ let package = Package(
permissions: [
]),
dependencies: [
"JExtractSwiftTool"
"SwiftJavaTool"
]
),

@@ -414,7 +405,9 @@ let package = Package(

.testTarget(
name: "Java2SwiftTests",
dependencies: ["Java2SwiftLib"],
dependencies: [
"Java2SwiftLib"
],
swiftSettings: [
.swiftLanguageMode(.v5),
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
Original file line number Diff line number Diff line change
@@ -79,6 +79,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin
}

var arguments: [String] = [
"jextract",
"--swift-module", sourceModule.name,
"--package-name", javaPackage,
"--output-directory-java", context.outputDirectoryJava.path(percentEncoded: false),
@@ -135,7 +136,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin

func runExtract(context: PluginContext, target: Target, arguments: [String]) throws {
let process = Process()
process.executableURL = try context.tool(named: "JExtractSwiftTool").url
process.executableURL = try context.tool(named: "SwiftJavaTool").url
process.arguments = arguments

do {
3 changes: 2 additions & 1 deletion Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
var verbose: Bool = getEnvironmentBool("SWIFT_JAVA_VERBOSE")

func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
let toolURL = try context.tool(named: "JExtractSwiftTool").url
let toolURL = try context.tool(named: "SwiftJavaTool").url

guard let sourceModule = target.sourceModule else { return [] }

@@ -54,6 +54,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
let outputDirectorySwift = context.outputDirectorySwift

var arguments: [String] = [
"jextract",
"--swift-module", sourceModule.name,
"--package-name", javaPackage,
"--output-directory-java", outputDirectoryJava.path(percentEncoded: false),
2 changes: 1 addition & 1 deletion Plugins/JavaCompilerPlugin/JavaCompilerPlugin.swift
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ struct JavaCompilerBuildToolPlugin: BuildToolPlugin {

// The name of the configuration file JavaKit.config from the target for
// which we are generating Swift wrappers for Java classes.
let configFile = URL(filePath: sourceDir).appending(path: "Java2Swift.config")
let configFile = URL(filePath: sourceDir).appending(path: "swift-java.config")
let config: Configuration?

if let configData = try? Data(contentsOf: configFile) {
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ import PackagePlugin
fileprivate let SwiftJavaConfigFileName = "swift-java.config"

@main
struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {

var pluginName: String = "swift-java"
var verbose: Bool = getEnvironmentBool("SWIFT_JAVA_VERBOSE")
@@ -27,7 +27,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
log("Create build commands for target '\(target.name)'")
guard let sourceModule = target.sourceModule else { return [] }

let executable = try context.tool(named: "Java2Swift").url
let executable = try context.tool(named: "SwiftJavaTool").url
var commands: [Command] = []

// Note: Target doesn't have a directoryURL counterpart to directory,
@@ -44,7 +44,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
log("Config was: \(config)")
var javaDependencies = config.dependencies ?? []

/// Find the manifest files from other Java2Swift executions in any targets
/// Find the manifest files from other SwiftJava executions in any targets
/// this target depends on.
var dependentConfigFiles: [(String, URL)] = []
func searchForConfigFiles(in target: any Target) {
@@ -107,7 +107,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
let classes = config.classes ?? [:]
print("Classes to wrap: \(classes.map(\.key))")

/// Determine the set of Swift files that will be emitted by the Java2Swift tool.
/// Determine the set of Swift files that will be emitted by the SwiftJava tool.
// TODO: this is not precise and won't work with more advanced Java files, e.g. lambdas etc.
let outputDirectoryGenerated = self.outputDirectory(context: context, generated: true)
let outputSwiftFiles = classes.map { (javaClassName, swiftName) in
@@ -199,7 +199,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
}
}

extension Java2SwiftBuildToolPlugin {
extension SwiftJavaBuildToolPlugin {
func argumentsModuleName(sourceModule: Target) -> [String] {
return [
"--module-name", sourceModule.name
File renamed without changes.
4 changes: 2 additions & 2 deletions Samples/JavaDependencySampleApp/Package.swift
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ let package = Package(
.swiftLanguageMode(.v5),
],
plugins: [
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),

@@ -94,7 +94,7 @@ let package = Package(
],
plugins: [
// .plugin(name: "SwiftJavaBootstrapJavaPlugin", package: "swift-java"),
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),

2 changes: 1 addition & 1 deletion Samples/JavaKitSampleApp/Package.swift
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ let package = Package(
plugins: [
.plugin(name: "JavaCompilerPlugin", package: "swift-java"),
.plugin(name: "JExtractSwiftPlugin", package: "swift-java"),
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),
]
2 changes: 1 addition & 1 deletion Samples/JavaProbablyPrime/Package.swift
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ let package = Package(
.swiftLanguageMode(.v5)
],
plugins: [
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),
]
2 changes: 1 addition & 1 deletion Samples/JavaProbablyPrime/README.md
Original file line number Diff line number Diff line change
@@ -8,5 +8,5 @@ swift run JavaProbablyPrime <very big number>

The package itself demonstrates how to:

* Use the Java2Swift build tool plugin to wrap the `java.math.BigInteger` type in Swift.
* Use the SwiftJava build tool plugin to wrap the `java.math.BigInteger` type in Swift.
* Create an instance of `BigInteger` in Swift and use its `isProbablyPrime`.
4 changes: 2 additions & 2 deletions Samples/JavaSieve/Package.swift
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ let package = Package(
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
],
plugins: [
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),

@@ -75,7 +75,7 @@ let package = Package(
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
],
plugins: [
.plugin(name: "Java2SwiftPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),
]
4 changes: 2 additions & 2 deletions Samples/JavaSieve/README.md
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@

This package contains an example program that demonstrates importing a Java library distributed as a Jar file into Swift and using some APIs from that library. It demonstrates how to:

* Use the Java2Swift tool to discover the classes in a Jar file and make them available in Swift
* Layer Swift wrappers for Java classes as separate Swift modules using Java2Swift
* Use the SwiftJava tool to discover the classes in a Jar file and make them available in Swift
* Layer Swift wrappers for Java classes as separate Swift modules using SwiftJava
* Access static methods of Java classes from Swift

This example wraps an [open-source Java library](https://github.com/gazman-sdk/quadratic-sieve-Java) implementing the [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) algorithm for finding prime numbers, among other algorithms. To get started, clone that repository and build a Jar file containing the library:
24 changes: 17 additions & 7 deletions Sources/JExtractSwift/Swift2Java.swift
Original file line number Diff line number Diff line change
@@ -20,11 +20,14 @@ import JavaKitShared

/// Command-line utility, similar to `jextract` to export Swift types to Java.
public struct SwiftToJava: ParsableCommand {
public init() {}
public static var _commandName: String { "jextract" }

public static var configuration = CommandConfiguration(
commandName: Self._commandName,
abstract: "Generate Java wrappers for Swift code.")

public static var _commandName: String {
"jextract-swift"
}

public init() {}

@Option(help: "The package the generated Java code should be emitted into.")
var packageName: String
@@ -49,18 +52,23 @@ public struct SwiftToJava: ParsableCommand {
var input: [String]

public func run() throws {
let inputPaths = self.input.dropFirst().map { URL(string: $0)! }
let inputPaths = self.input.map { URL(string: $0)! }

let translator = Swift2JavaTranslator(
javaPackage: packageName,
swiftModuleName: swiftModule
)
translator.log.logLevel = logLevel

var allFiles: [URL] = []
let fileManager = FileManager.default
let log = translator.log


guard !inputPaths.isEmpty else {
log.warning("Input paths are empty!")
return
}

for path in inputPaths {
log.debug("Input path: \(path)")
if isDirectory(url: path) {
@@ -74,6 +82,8 @@ public struct SwiftToJava: ParsableCommand {
}
}

log.trace("Input file: \(allFiles)")

for file in allFiles where canExtract(from: file) {
translator.log.debug("Importing module '\(swiftModule)', file: \(file)")

4 changes: 2 additions & 2 deletions Sources/JExtractSwift/Swift2JavaVisitor.swift
Original file line number Diff line number Diff line change
@@ -263,12 +263,12 @@ extension InitializerDeclSyntax {
let isFailable = self.optionalMark != nil

if isFailable {
log.warning("Skip importing failable initializer: \(self)")
log.debug("Skip importing failable initializer: \(self)")
return false
}

// Ok, import it
log.warning("Import initializer: \(self)")
log.debug("Import initializer: \(self)")
return true
}
}
Loading
Loading