Skip to content

Commit fe7ee50

Browse files
committed
add a sample
1 parent c434bcc commit fe7ee50

File tree

6 files changed

+198
-4
lines changed

6 files changed

+198
-4
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 CompilerPluginSupport
5+
import PackageDescription
6+
7+
import class Foundation.FileManager
8+
import class Foundation.ProcessInfo
9+
10+
// Note: the JAVA_HOME environment variable must be set to point to where
11+
// Java is installed, e.g.,
12+
// Library/Java/JavaVirtualMachines/openjdk-21.jdk/Contents/Home.
13+
func findJavaHome() -> String {
14+
if let home = ProcessInfo.processInfo.environment["JAVA_HOME"] {
15+
return home
16+
}
17+
18+
// This is a workaround for envs (some IDEs) which have trouble with
19+
// picking up env variables during the build process
20+
let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home"
21+
if let home = try? String(contentsOfFile: path, encoding: .utf8) {
22+
if let lastChar = home.last, lastChar.isNewline {
23+
return String(home.dropLast())
24+
}
25+
26+
return home
27+
}
28+
29+
fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.")
30+
}
31+
let javaHome = findJavaHome()
32+
33+
let javaIncludePath = "\(javaHome)/include"
34+
#if os(Linux)
35+
let javaPlatformIncludePath = "\(javaIncludePath)/linux"
36+
#elseif os(macOS)
37+
let javaPlatformIncludePath = "\(javaIncludePath)/darwin"
38+
#else
39+
// TODO: Handle windows as well
40+
#error("Currently only macOS and Linux platforms are supported, this may change in the future.")
41+
#endif
42+
43+
let package = Package(
44+
name: "JavaKitSampleApp",
45+
platforms: [
46+
.macOS(.v13),
47+
.iOS(.v13),
48+
.tvOS(.v13),
49+
.watchOS(.v6),
50+
.macCatalyst(.v13),
51+
],
52+
53+
products: [
54+
.library(
55+
name: "JavaKitExample",
56+
type: .dynamic,
57+
targets: ["JavaKitExample"]
58+
),
59+
],
60+
61+
dependencies: [
62+
.package(name: "swift-java", path: "../../")
63+
],
64+
65+
targets: [
66+
.target(
67+
name: "JavaKitExample",
68+
dependencies: [
69+
.product(name: "JavaKit", package: "swift-java"),
70+
.product(name: "JavaKitFunction", package: "swift-java"),
71+
],
72+
swiftSettings: [
73+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
74+
],
75+
plugins: [
76+
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
77+
]
78+
),
79+
]
80+
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
import JavaKitFunction
17+
18+
enum SwiftWrappedError: Error {
19+
case message(String)
20+
}
21+
22+
@JavaImplementation("com.example.swift.HelloSwift")
23+
extension HelloSwift: HelloSwiftNativeMethods {
24+
@JavaMethod
25+
func sayHello(_ i: Int32, _ j: Int32) -> Int32 {
26+
print("Hello from Swift!")
27+
let answer = self.sayHelloBack(i + j)
28+
print("Swift got back \(answer) from Java")
29+
30+
print("We expect the above value to be the initial value, \(self.javaClass.initialValue)")
31+
32+
print("Updating Java field value to something different")
33+
self.value = 2.71828
34+
35+
let newAnswer = self.sayHelloBack(17)
36+
print("Swift got back updated \(newAnswer) from Java")
37+
38+
let newHello = HelloSwift(environment: javaEnvironment)
39+
print("Swift created a new Java instance with the value \(newHello.value)")
40+
41+
let name = newHello.name
42+
print("Hello to \(name)")
43+
newHello.greet("Swift 👋🏽 How's it going")
44+
45+
self.name = "a 🗑️-collected language"
46+
_ = self.sayHelloBack(42)
47+
48+
let predicate: JavaPredicate<JavaInteger> = self.lessThanTen()!
49+
let value = predicate.test(JavaInteger(3).as(JavaObject.self))
50+
print("Running a JavaPredicate from swift 3 < 10 = \(value)")
51+
52+
let strings = doublesToStrings([3.14159, 2.71828])
53+
print("Converting doubles to strings: \(strings)")
54+
55+
// Try downcasting
56+
if let helloSub = self.as(HelloSubclass.self) {
57+
print("Hello from the subclass!")
58+
helloSub.greetMe()
59+
60+
assert(helloSub.value == 2.71828)
61+
} else {
62+
fatalError("Expected subclass here")
63+
}
64+
65+
// Check "is" behavior
66+
assert(newHello.is(HelloSwift.self))
67+
assert(!newHello.is(HelloSubclass.self))
68+
69+
// Create a new instance.
70+
let helloSubFromSwift = HelloSubclass("Hello from Swift", environment: javaEnvironment)
71+
helloSubFromSwift.greetMe()
72+
73+
do {
74+
try throwMessage("I am an error")
75+
} catch {
76+
print("Caught Java error: \(error)")
77+
}
78+
79+
// Make sure that the thread safe class is sendable
80+
let threadSafe: Sendable = ThreadSafeHelperClass(environment: javaEnvironment)
81+
82+
return i * j
83+
}
84+
85+
@JavaMethod
86+
func throwMessageFromSwift(_ message: String) throws -> String {
87+
throw SwiftWrappedError.message(message)
88+
}
89+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"dependencies": [
3+
"org.reactivestreams:reactive-streams:1.0.4"
4+
],
5+
"__FIXME": "we should not need to express this classpath!",
6+
"classPath": "JavaKit/build/classes/java/main",
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/sh
2+
3+
swift build
4+
"$JAVA_HOME/bin/java" \
5+
-cp .build/plugins/outputs/javakitsampleapp/JavaKitExample/destination/JavaCompilerPlugin/Java \
6+
-Djava.library.path=.build/debug \
7+
"com.example.swift.JavaKitSampleMain"

Sources/Java2Swift/JavaToSwift+FetchDependencies.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import JavaKitDependencyResolver
2222
import JavaKitConfigurationShared
2323

2424
extension JavaToSwift {
25-
func fetchDependencies(projectName: String,
25+
func fetchDependencies(moduleName: String,
2626
dependencies: [JavaDependencyDescriptor],
2727
baseClasspath: [String]) throws -> JavaClasspath {
2828
let deps = dependencies.map { $0.descriptionGradleStyle }
@@ -36,9 +36,16 @@ extension JavaToSwift {
3636
projectBaseDirectory: URL(fileURLWithPath: ".").path,
3737
dependencies: deps)
3838

39-
let entryCount = classpath.split(separator: ":").count
40-
print("[debug][swift-java] Resolved classpath for \(deps.count) dependencies: classpath entries: \(entryCount)... ", terminator: "")
39+
let entries = classpath.split(separator: ":")
40+
let entryCount = entries.count
41+
42+
print("[info][swift-java] Resolved classpath for \(deps.count) dependencies of '\(moduleName)': classpath entries: \(entryCount)... ", terminator: "")
4143
print("done.".green)
44+
45+
for entry in entries {
46+
print("[debug][swift-java] Classpath entry: \(entry)")
47+
}
48+
4249
return .init(classpath)
4350
}
4451
}

Sources/Java2Swift/JavaToSwift.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ struct JavaToSwift: ParsableCommand {
137137
toolMode = .classWrappers(config)
138138
}
139139

140+
let moduleName = self.moduleName ??
141+
input.split(separator: "/").dropLast().last.map(String.init) ??
142+
"__UnknownModule"
143+
140144
// Load all of the dependent configurations and associate them with Swift
141145
// modules.
142146
let dependentConfigs = try dependsOn.map { dependentConfig in
@@ -206,7 +210,7 @@ struct JavaToSwift: ParsableCommand {
206210

207211
case .fetchDependencies(let config):
208212
let dependencies = config.dependencies! // TODO: cleanup how we do config
209-
try fetchDependencies(dependencies: dependencies, baseClasspath: classPathPieces)
213+
try fetchDependencies(moduleName: moduleName, dependencies: dependencies, baseClasspath: classPathPieces)
210214
}
211215
}
212216

0 commit comments

Comments
 (0)