Skip to content

Commit a85f046

Browse files
committed
wip
1 parent a59263d commit a85f046

File tree

11 files changed

+238
-73
lines changed

11 files changed

+238
-73
lines changed

.github/workflows/pull_request.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ jobs:
127127
${{ runner.os }}-swiftpm-cache
128128
${{ runner.os }}-swiftpm-
129129
# run the actual build
130-
- name: Generate sources (make) (Temporary)
131-
# TODO: this should be triggered by the respective builds
132-
run: "make jextract-generate"
130+
# - name: Generate sources (make) (Temporary)
131+
# # TODO: this should be triggered by the respective builds
132+
# run: "make jextract-generate"
133133
- name: Test Swift
134134
run: "swift test"
135135
- name: Build (Swift) Sample Apps

Makefile

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ all:
5757
@echo "Welcome to swift-java! There are several makefile targets to choose from:"
5858
@echo " javakit-run: Run the JavaKit example program that uses Java libraries from Swift."
5959
@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."
60-
@echo " jextract-run: Run the Java example code that uses the wrapped Swift library. NOTE: this requires development toolchain described in the README."
61-
@echo " jextract-generate: Generate Java wrapper code for the example Swift library allowing Swift to be called from Java. NOTE: this requires development toolchain described in the README."
6260

6361
$(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/Java2Swift:
6462
swift build
@@ -105,42 +103,5 @@ format:
105103
### "SwiftKit" is the "call swift from java" ###
106104
#################################################
107105

108-
JEXTRACT_BUILD_DIR="$(BUILD_DIR)/jextract"
109-
110-
define make_swiftinterface
111-
$(eval $@_MODULE = $(1))
112-
$(eval $@_FILENAME = $(2))
113-
eval ${SWIFTC} \
114-
-emit-module-interface-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftinterface \
115-
-emit-module-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftmodule \
116-
-enable-library-evolution \
117-
-Xfrontend -abi-comments-in-module-interface \
118-
-module-name ${$@_MODULE} \
119-
-Xfrontend -abi-comments-in-module-interface \
120-
Sources/${$@_MODULE}/${$@_FILENAME}.swift
121-
echo "Generated: ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftinterface"
122-
endef
123-
124-
jextract-swift: generate-JExtract-interface-files
125-
swift build
126-
127-
generate-JExtract-interface-files: $(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX)
128-
@echo "Generate .swiftinterface files..."
129-
@$(call make_swiftinterface, "ExampleSwiftLibrary", "MySwiftLibrary")
130-
@$(call make_swiftinterface, "SwiftKitSwift", "SwiftKit")
131-
132-
jextract-generate: jextract-swift generate-JExtract-interface-files
133-
swift run jextract-swift \
134-
--package-name com.example.swift.generated \
135-
--swift-module ExampleSwiftLibrary \
136-
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/build/generated/sources/jextract/main \
137-
$(BUILD_DIR)/jextract/ExampleSwiftLibrary/MySwiftLibrary.swiftinterface; \
138-
swift run jextract-swift \
139-
--package-name org.swift.swiftkit.generated \
140-
--swift-module SwiftKitSwift \
141-
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/build/generated/sources/jextract/main \
142-
$(BUILD_DIR)/jextract/SwiftKitSwift/SwiftKit.swiftinterface
143-
144-
145106
jextract-run: jextract-generate
146107
./gradlew Samples:SwiftKitSampleApp:run

Samples/JExtractPluginSampleApp/build.gradle

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414

1515
import org.swift.swiftkit.gradle.BuildUtils
1616

17-
import java.nio.file.Files
18-
import java.nio.file.Paths
17+
import java.nio.file.*
1918

2019
plugins {
2120
id("build-logic.java-application-conventions")
@@ -89,6 +88,10 @@ sourceSets {
8988
}
9089
}
9190

91+
tasks.build {
92+
dependsOn("jextract")
93+
}
94+
9295
dependencies {
9396
implementation(project(':SwiftKit'))
9497

@@ -105,12 +108,6 @@ tasks.named('test', Test) {
105108
application {
106109
mainClass = "com.example.swift.JExtractPluginSampleMain"
107110

108-
// In order to silence:
109-
// WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called
110-
// WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.example.swift.JavaKitExample in an unnamed module
111-
// WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
112-
// WARNING: Restricted methods will be blocked in a future release unless native access is enabled
113-
// FIXME: Find out the proper solution to this
114111
applicationDefaultJvmArgs = [
115112
"--enable-native-access=ALL-UNNAMED",
116113

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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: "SwiftKitSampleApp",
45+
platforms: [
46+
.macOS(.v10_15)
47+
],
48+
products: [
49+
.library(
50+
name: "MySwiftLibrary",
51+
type: .dynamic,
52+
targets: ["MySwiftLibrary"]
53+
),
54+
55+
],
56+
dependencies: [
57+
.package(name: "swift-java", path: "../../"),
58+
],
59+
targets: [
60+
.target(
61+
name: "MySwiftLibrary",
62+
dependencies: [
63+
.product(name: "SwiftKitSwift", package: "swift-java"),
64+
],
65+
swiftSettings: [
66+
.swiftLanguageMode(.v5),
67+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
68+
],
69+
plugins: [
70+
.plugin(name: "JExtractSwiftPlugin", package: "swift-java"),
71+
]
72+
),
73+
]
74+
)
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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
// This is a "plain Swift" file containing various types of declarations,
16+
// that is exported to Java by using the `jextract-swift` tool.
17+
//
18+
// No annotations are necessary on the Swift side to perform the export.
19+
20+
#if os(Linux)
21+
import Glibc
22+
#else
23+
import Darwin.C
24+
#endif
25+
26+
public func helloWorld() {
27+
p("\(#function)")
28+
}
29+
30+
public func globalTakeInt(i: Int) {
31+
p("i:\(i)")
32+
}
33+
34+
public func globalTakeIntInt(i: Int, j: Int) {
35+
p("i:\(i), j:\(j)")
36+
}
37+
38+
public func globalCallMeRunnable(run: () -> ()) {
39+
run()
40+
}
41+
42+
public class MySwiftClass {
43+
44+
public var len: Int
45+
public var cap: Int
46+
47+
public init(len: Int, cap: Int) {
48+
self.len = len
49+
self.cap = cap
50+
51+
p("\(MySwiftClass.self).len = \(self.len)")
52+
p("\(MySwiftClass.self).cap = \(self.cap)")
53+
let addr = unsafeBitCast(self, to: UInt64.self)
54+
p("initializer done, self = 0x\(String(addr, radix: 16, uppercase: true))")
55+
}
56+
57+
deinit {
58+
let addr = unsafeBitCast(self, to: UInt64.self)
59+
p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
60+
}
61+
62+
public var counter: Int32 = 0
63+
64+
public func voidMethod() {
65+
p("")
66+
}
67+
68+
public func takeIntMethod(i: Int) {
69+
p("i:\(i)")
70+
}
71+
72+
public func echoIntMethod(i: Int) -> Int {
73+
p("i:\(i)")
74+
return i
75+
}
76+
77+
public func makeIntMethod() -> Int {
78+
p("make int -> 12")
79+
return 12
80+
}
81+
82+
public func makeRandomIntMethod() -> Int {
83+
return Int.random(in: 1..<256)
84+
}
85+
}
86+
87+
//@_silgen_name("swift_getTypeByMangledNameInEnvironment")
88+
//public func _getTypeByMangledNameInEnvironment(
89+
// _ name: UnsafePointer<UInt8>,
90+
// _ nameLength: UInt,
91+
// genericEnvironment: UnsafeRawPointer?,
92+
// genericArguments: UnsafeRawPointer?)
93+
// -> Any.Type?
94+
95+
// ==== Internal helpers
96+
97+
private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
98+
print("[swift][\(file):\(line)](\(function)) \(msg)")
99+
fflush(stdout)
100+
}

Samples/SwiftKitSampleApp/build.gradle

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import org.swift.swiftkit.gradle.BuildUtils
1616

17+
import java.nio.file.*
18+
1719
plugins {
1820
id("build-logic.java-application-conventions")
1921
id("me.champeau.jmh") version "0.7.2"
@@ -32,32 +34,61 @@ java {
3234
}
3335
}
3436

37+
// This is for development, when we edit the Swift swift-java project, the outputs of the generated sources may change.
38+
// Thus, we also need to watch and re-build the top level project.
39+
def buildJExtractPlugin = tasks.register("swiftBuildJExtractPlugin", Exec) {
40+
description = "Rebuild the swift-java root project"
41+
42+
inputs.file(new File(rootDir, "Package.swift"))
43+
inputs.dir(new File(rootDir, "Sources"))
44+
outputs.dir(new File(rootDir, ".build"))
45+
46+
workingDir = rootDir
47+
commandLine "swift"
48+
args "build"
49+
}
3550

36-
def jextract = tasks.register("jextract", Exec) {
37-
description = "Extracts Java accessor sources using jextract"
51+
def buildSwift = tasks.register("swiftBuildProject", Exec) {
52+
description = "Builds swift sources, including swift-java source generation"
53+
dependsOn buildJExtractPlugin
3854

39-
outputs.dir(layout.buildDirectory.dir("generated/sources/jextract/main"))
40-
inputs.dir("$rootDir/Sources/ExampleSwiftLibrary") // monitored library
55+
// only because we depend on "live developing" the plugin while using this project to test it
56+
inputs.file(new File(rootDir, "Package.swift"))
57+
inputs.dir(new File(rootDir, "Sources"))
4158

42-
// any changes in the source generator sources also mean the resulting output might change
43-
inputs.dir("$rootDir/Sources/JExtractSwift")
44-
inputs.dir("$rootDir/Sources/JExtractSwiftTool")
59+
inputs.file(layout.projectDirectory.file("Package.swift"))
60+
inputs.dir(layout.projectDirectory.dir("Sources"))
4561

46-
workingDir = rootDir
47-
commandLine "make"
48-
args "jextract-generate"
62+
// TODO: we can use package describe --type json to figure out which targets depend on JExtractSwiftPlugin and will produce outputs
63+
// Avoid adding this directory, but create the expected one specifically for all targets which WILL produce sources because they have the plugin
64+
outputs.dir(layout.buildDirectory.dir("../.build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}"))
65+
66+
File baseSwiftPluginOutputsDir = layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile
67+
if (!baseSwiftPluginOutputsDir.exists()) {
68+
baseSwiftPluginOutputsDir.mkdirs()
69+
}
70+
Files.walk(layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile.toPath()).each {
71+
if (it.endsWith("JExtractSwiftPlugin/src/generated/java")) {
72+
outputs.dir(it)
73+
}
74+
}
75+
76+
workingDir = layout.projectDirectory
77+
commandLine "swift"
78+
args "build"
4979
}
5080

81+
// Add the java-swift generated Java sources
5182
sourceSets {
5283
main {
5384
java {
54-
srcDir(jextract)
85+
srcDir(buildSwift)
5586
}
5687
}
5788
}
5889

5990
tasks.build {
60-
dependsOn("jextract")
91+
dependsOn(buildSwift)
6192
}
6293

6394
dependencies {
@@ -74,12 +105,6 @@ tasks.named('test', Test) {
74105
application {
75106
mainClass = "com.example.swift.HelloJava2Swift"
76107

77-
// In order to silence:
78-
// WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called
79-
// WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.example.swift.JavaKitExample in an unnamed module
80-
// WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
81-
// WARNING: Restricted methods will be blocked in a future release unless native access is enabled
82-
// FIXME: Find out the proper solution to this
83108
applicationDefaultJvmArgs = [
84109
"--enable-native-access=ALL-UNNAMED",
85110

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
package com.example.swift;
1616

1717
// Import swift-extract generated sources
18-
import com.example.swift.generated.ExampleSwiftLibrary;
19-
import com.example.swift.generated.MySwiftClass;
18+
import com.example.swift.ExampleSwiftLibrary;
19+
import com.example.swift.MySwiftClass;
2020

2121
// Import javakit/swiftkit support libraries
2222
import org.swift.swiftkit.SwiftArena;

Sources/JExtractSwift/Swift2JavaTranslator+MemoryLayouts.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ extension Swift2JavaTranslator {
3030
// decl.isInit ? nil : .wrapper
3131

3232
for param in decl.effectiveParameters(paramPassingStyle: paramPassingStyle) {
33+
if param.type.cCompatibleJavaMemoryLayout == CCompatibleJavaMemoryLayout.primitive(.void) {
34+
continue
35+
}
36+
3337
layouts.append(param.type.foreignValueLayout)
3438
}
3539

0 commit comments

Comments
 (0)