Skip to content

Commit 9b37612

Browse files
committed
WIP on JExtractSwiftPlugin
1 parent 70fb530 commit 9b37612

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

Package.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ let package = Package(
127127
targets: ["JExtractSwift"]
128128
),
129129

130+
// ==== Plugin for wrapping Java classes in Swift
131+
.plugin(
132+
name: "JExtractSwiftPlugin",
133+
targets: [
134+
"JExtractSwiftPlugin"
135+
]
136+
),
137+
130138
// ==== Examples
131139

132140
.library(
@@ -225,6 +233,7 @@ let package = Package(
225233
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
226234
]
227235
),
236+
228237
.plugin(
229238
name: "JavaCompilerPlugin",
230239
capability: .buildTool()
@@ -328,6 +337,14 @@ let package = Package(
328337
]
329338
),
330339

340+
.plugin(
341+
name: "JExtractSwiftPlugin",
342+
capability: .buildTool(),
343+
dependencies: [
344+
"JExtractSwiftTool"
345+
]
346+
),
347+
331348
.testTarget(
332349
name: "JavaKitTests",
333350
dependencies: ["JavaKit", "JavaKitNetwork"],
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
/// Configuration for the JExtractSwift translation tool, provided on a per-target
18+
/// basis.
19+
struct Configuration: Codable {
20+
var javaPackage: String
21+
}
22+
23+
func readConfiguration(sourceDir: String) throws -> Configuration {
24+
let configFile = URL(filePath: sourceDir).appending(path: "JExtractSwift.config")
25+
do {
26+
let configData = try Data(contentsOf: configFile)
27+
return try JSONDecoder().decode(Configuration.self, from: configData)
28+
} catch {
29+
throw ConfigurationError(message: "Failed to parse JExtractSwift configuration at '\(configFile)!'", error: error)
30+
}
31+
}
32+
33+
struct ConfigurationError: Error {
34+
let message: String
35+
let error: any Error
36+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 PackagePlugin
17+
18+
@main
19+
struct JExtractSwiftBuildToolPlugin: BuildToolPlugin {
20+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
21+
guard let sourceModule = target.sourceModule else { return [] }
22+
23+
// Note: Target doesn't have a directoryURL counterpart to directory,
24+
// so we cannot eliminate this deprecation warning.
25+
let sourceDir = target.directory.string
26+
27+
let configuration = try readConfiguration(sourceDir: "\(sourceDir)")
28+
29+
// We use the the usual maven-style structure of "src/[generated|main|test]/java/..."
30+
// that is common in JVM ecosystem
31+
let outputDirectory = context.pluginWorkDirectoryURL
32+
.appending(path: "src")
33+
.appending(path: "generated")
34+
.appending(path: "java")
35+
36+
var arguments: [String] = [
37+
"--swift-module", sourceModule.name,
38+
"--package-name", configuration.javaPackage,
39+
"--output-directory", outputDirectory.path(percentEncoded: false),
40+
// TODO: "--build-cache-directory", ...
41+
// Since plugins cannot depend on libraries we cannot detect what the output files will be,
42+
// as it depends on the contents of the input files. Therefore we have to implement this as a prebuild plugin.
43+
// We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc.
44+
]
45+
arguments.append(sourceDir)
46+
47+
return [
48+
.prebuildCommand(
49+
displayName: "Generate Java wrappers for Swift types",
50+
executable: try context.tool(named: "JExtractSwiftTool").url,
51+
arguments: arguments,
52+
// inputFiles: [ configFile ] + swiftFiles,
53+
// outputFiles: outputJavaFiles
54+
outputFilesDirectory: outputDirectory
55+
)
56+
]
57+
}
58+
}
59+
60+
// Note: the JAVA_HOME environment variable must be set to point to where
61+
// Java is installed, e.g.,
62+
// Library/Java/JavaVirtualMachines/openjdk-21.jdk/Contents/Home.
63+
func findJavaHome() -> String {
64+
if let home = ProcessInfo.processInfo.environment["JAVA_HOME"] {
65+
return home
66+
}
67+
68+
// This is a workaround for envs (some IDEs) which have trouble with
69+
// picking up env variables during the build process
70+
let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home"
71+
if let home = try? String(contentsOfFile: path, encoding: .utf8) {
72+
if let lastChar = home.last, lastChar.isNewline {
73+
return String(home.dropLast())
74+
}
75+
76+
return home
77+
}
78+
79+
fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.")
80+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
import class Foundation.FileManager
7+
import class Foundation.ProcessInfo
8+
9+
// Note: the JAVA_HOME environment variable must be set to point to where
10+
// Java is installed, e.g.,
11+
// Library/Java/JavaVirtualMachines/openjdk-21.jdk/Contents/Home.
12+
func findJavaHome() -> String {
13+
if let home = ProcessInfo.processInfo.environment["JAVA_HOME"] {
14+
return home
15+
}
16+
17+
// This is a workaround for envs (some IDEs) which have trouble with
18+
// picking up env variables during the build process
19+
let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home"
20+
if let home = try? String(contentsOfFile: path, encoding: .utf8) {
21+
if let lastChar = home.last, lastChar.isNewline {
22+
return String(home.dropLast())
23+
}
24+
25+
return home
26+
}
27+
28+
fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.")
29+
}
30+
let javaHome = findJavaHome()
31+
32+
let javaIncludePath = "\(javaHome)/include"
33+
#if os(Linux)
34+
let javaPlatformIncludePath = "\(javaIncludePath)/linux"
35+
#elseif os(macOS)
36+
let javaPlatformIncludePath = "\(javaIncludePath)/darwin"
37+
#else
38+
// TODO: Handle windows as well
39+
#error("Currently only macOS and Linux platforms are supported, this may change in the future.")
40+
#endif
41+
42+
let package = Package(
43+
name: "JExtractPluginSampleApp",
44+
platforms: [
45+
.macOS(.v10_15),
46+
],
47+
dependencies: [
48+
.package(name: "swift-java", path: "../../"),
49+
],
50+
targets: [
51+
.executableTarget(
52+
name: "JExtractPluginSample",
53+
dependencies: [
54+
],
55+
swiftSettings: [
56+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
57+
],
58+
plugins: [
59+
.plugin(name: "JExtractSwiftPlugin", package: "swift-java"),
60+
]
61+
),
62+
]
63+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"javaPackage": "com.example.swift"
3+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 JavaKit
16+
17+
let jvm = try JavaVirtualMachine.shared(classPath: ["QuadraticSieve-1.0.jar"])
18+
do {
19+
let sieveClass = try JavaClass<SieveOfEratosthenes>(environment: jvm.environment())
20+
for prime in sieveClass.findPrimes(100)! {
21+
print("Found prime: \(prime.intValue())")
22+
}
23+
} catch {
24+
print("Failure: \(error)")
25+
}

0 commit comments

Comments
 (0)