Skip to content

Commit cf8f4ae

Browse files
committed
introduce swift-java jextract subcommand, completing our migration
1 parent 25eac35 commit cf8f4ae

File tree

14 files changed

+114
-138
lines changed

14 files changed

+114
-138
lines changed

Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin
7171
let configuration = try readConfiguration(sourceDir: "\(sourceDir)")
7272

7373
var arguments: [String] = [
74+
/*subcommand=*/"jextract",
7475
"--input-swift", sourceDir,
7576
"--swift-module", sourceModule.name,
7677
"--output-java", context.outputJavaDirectory.path(percentEncoded: false),
@@ -80,7 +81,6 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin
8081
// as it depends on the contents of the input files. Therefore we have to implement this as a prebuild plugin.
8182
// We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc.
8283
]
83-
// arguments.append(sourceDir) // TODO: we could do this shape maybe? to have the dirs last?
8484
if let package = configuration?.javaPackage, !package.isEmpty {
8585
arguments += ["--java-package", package]
8686
}

Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
5454
let outputSwiftDirectory = context.outputSwiftDirectory
5555

5656
var arguments: [String] = [
57-
"--input-swift", sourceDir,
57+
/*subcommand=*/"jextract",
5858
"--swift-module", sourceModule.name,
59+
"--input-swift", sourceDir,
5960
"--output-java", outputJavaDirectory.path(percentEncoded: false),
6061
"--output-swift", outputSwiftDirectory.path(percentEncoded: false),
6162
// TODO: "--build-cache-directory", ...
6263
// Since plugins cannot depend on libraries we cannot detect what the output files will be,
6364
// as it depends on the contents of the input files. Therefore we have to implement this as a prebuild plugin.
6465
// We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc.
6566
]
66-
// arguments.append(sourceDir)
6767
if !javaPackage.isEmpty {
6868
arguments.append(contentsOf: ["--java-package", javaPackage])
6969
}

Samples/SwiftAndJavaJarSampleLib/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ let javaIncludePath = "\(javaHome)/include"
4343
let package = Package(
4444
name: "SwiftAndJavaJarSampleLib",
4545
platforms: [
46-
.macOS(.v10_15)
46+
.macOS(.v15)
4747
],
4848
products: [
4949
.library(

Samples/SwiftAndJavaJarSampleLib/build.gradle

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ dependencies {
5151

5252
// This is for development, when we edit the Swift swift-java project, the outputs of the generated sources may change.
5353
// Thus, we also need to watch and re-build the top level project.
54-
def compileSwiftJExtractPlugin = tasks.register("compileSwiftJExtractPlugin", Exec) {
55-
description = "Rebuild the swift-java root project"
54+
def compileSwiftJExtractPlugin = tasks.register("compileSwiftJava", Exec) {
55+
description = "Rebuild the swift-java root project (" + swiftBuildConfiguration() + ")"
5656

5757
inputs.file(new File(rootDir, "Package.swift"))
5858
inputs.dir(new File(rootDir, "Sources"))
@@ -62,9 +62,7 @@ def compileSwiftJExtractPlugin = tasks.register("compileSwiftJExtractPlugin", Ex
6262
commandLine "swift"
6363
args("build",
6464
"-c", swiftBuildConfiguration(),
65-
"--product", "SwiftKitSwift",
66-
"--product", "JExtractSwiftPlugin",
67-
"--product", "JExtractSwiftCommandPlugin")
65+
"--product", "swift-java")
6866
}
6967

7068
def jextract = tasks.register("jextract", Exec) {
@@ -96,7 +94,7 @@ def jextract = tasks.register("jextract", Exec) {
9694

9795
workingDir = layout.projectDirectory
9896
commandLine "swift"
99-
args("package", "jextract", "-v", "--log-level", "info") // TODO: pass log level from Gradle build
97+
args("run", "swift-java", "jextract", "-v", "--log-level", "info") // TODO: pass log level from Gradle build
10098
}
10199

102100

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extension FFMSwift2JavaGenerator {
3434
javaPackagePath: nil,
3535
filename: moduleFilename)
3636
{
37-
print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile)")
37+
print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile))")
3838
}
3939
} catch {
4040
log.warning("Failed to write to Swift thunks: \(moduleFilename)")
@@ -54,7 +54,7 @@ extension FFMSwift2JavaGenerator {
5454
javaPackagePath: nil,
5555
filename: filename)
5656
{
57-
print("[swift-java] Generated: \(fileNameBase.bold).swift (at \(outputFile)")
57+
print("[swift-java] Generated: \(fileNameBase.bold).swift (at \(outputFile))")
5858
}
5959
} catch {
6060
log.warning("Failed to write to Swift thunks: \(filename)")

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ extension JNISwift2JavaGenerator {
8787
javaPackagePath: javaPackagePath,
8888
filename: moduleFilename
8989
) {
90-
print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile)")
90+
print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile))")
9191
}
9292
} catch {
9393
logger.warning("Failed to write to Swift thunks: \(moduleFilename)")

Sources/JExtractSwiftLib/Swift2Java.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ public struct SwiftToJava {
3939
translator.log.warning("Configured java package is '', consider specifying concrete package for generated sources.")
4040
}
4141

42-
print("===== CONFIG ==== \(config)")
43-
4442
guard let inputSwift = config.inputSwiftDirectory else {
4543
fatalError("Missing '--swift-input' directory!")
4644
}
4745

46+
translator.log.info("Input swift = \(inputSwift)")
4847
let inputPaths = inputSwift.split(separator: ",").map { URL(string: String($0))! }
4948
translator.log.info("Input paths = \(inputPaths)")
5049

Sources/JavaKitConfigurationShared/Configuration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public struct Configuration: Codable {
3838

3939
public var outputJavaDirectory: String?
4040

41-
public var mode: GenerationMode?
41+
public var mode: JExtractGenerationMode?
4242

4343
// ==== java 2 swift ---------------------------------------------------------
4444

Sources/JavaKitConfigurationShared/GenerationMode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
public enum GenerationMode: String, Codable {
15+
public enum JExtractGenerationMode: String, Codable {
1616
/// Foreign Value and Memory API
1717
case ffm
1818

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
import ArgumentParser
17+
import SwiftJavaLib
18+
import JavaKit
19+
import JavaKitJar
20+
import SwiftJavaLib
21+
import JExtractSwiftLib
22+
import JavaKitConfigurationShared
23+
24+
/// Extract Java bindings from Swift sources or interface files.
25+
///
26+
/// Example usage:
27+
/// ```
28+
/// > swift-java jextract
29+
// --input-swift Sources/SwiftyBusiness \
30+
/// --output-swift .build/.../outputs/SwiftyBusiness \
31+
/// --output-Java .build/.../outputs/Java
32+
/// ```
33+
extension SwiftJava {
34+
35+
struct JExtractCommand: SwiftJavaBaseAsyncParsableCommand, HasCommonOptions {
36+
static let configuration = CommandConfiguration(
37+
commandName: "jextract", // TODO: wrap-swift?
38+
abstract: "Wrap Swift functions and types with Java bindings, making them available to be called from Java")
39+
40+
@OptionGroup var commonOptions: SwiftJava.CommonOptions
41+
42+
@Option(help: "The mode of generation to use for the output files. Used with jextract mode.")
43+
var mode: JExtractGenerationMode = .ffm
44+
45+
@Option(help: "The name of the Swift module into which the resulting Swift types will be generated.")
46+
var swiftModule: String
47+
48+
var effectiveSwiftModule: String {
49+
swiftModule
50+
}
51+
52+
@Option(help: "The Java package the generated Java code should be emitted into.")
53+
var javaPackage: String? = nil
54+
55+
@Option(help: "The directory where generated Swift files should be written. Generally used with jextract mode.")
56+
var outputSwift: String
57+
58+
@Option(help: "The directory where generated Java files should be written. Generally used with jextract mode.")
59+
var outputJava: String
60+
}
61+
}
62+
63+
extension SwiftJava.JExtractCommand {
64+
func runSwiftJavaCommand(config: inout Configuration) async throws {
65+
if let javaPackage {
66+
config.javaPackage = javaPackage
67+
}
68+
config.swiftModule = self.effectiveSwiftModule
69+
config.outputJavaDirectory = outputJava
70+
config.outputSwiftDirectory = outputSwift
71+
72+
if let inputSwift = commonOptions.inputSwift {
73+
config.inputSwiftDirectory = inputSwift
74+
} else if let swiftModule = config.swiftModule {
75+
// This is a "good guess" technically a target can be somewhere else, but then you can use --input-swift
76+
config.inputSwiftDirectory = "\(FileManager.default.currentDirectoryPath)/Sources/\(swiftModule)"
77+
}
78+
79+
print("[debug][swift-java] Running swift-java in mode: " + "\(self.mode)".bold)
80+
81+
try jextractSwift(config: config)
82+
}
83+
}
84+
85+
extension SwiftJava.JExtractCommand {
86+
func jextractSwift(
87+
config: Configuration
88+
) throws {
89+
try SwiftToJava(config: config).run()
90+
}
91+
92+
}
93+
94+
extension JExtractGenerationMode: ExpressibleByArgument {}

0 commit comments

Comments
 (0)