diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 0114da27..8aad2c67 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -41,11 +41,8 @@ jobs: # distribution: 'zulu' # java-version: '22' # cache: 'gradle' - - name: Generate sources (make) (Temporary) - # TODO: this should be triggered by the respective builds - run: make jextract-run - name: Gradle build - run: ./gradlew build --no-daemon + run: ./gradlew build --info --no-daemon test-swift: name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) diff --git a/.gitignore b/.gitignore index 416f253f..381bd778 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,15 @@ DerivedData/ .netrc *.class bin/ +BuildLogic/out/ # Ignore gradle build artifacts .gradle **/build/ +lib/ + +# Ignore package resolved +Package.resolved # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar @@ -24,4 +29,4 @@ bin/ .gradletasknamecache # Ignore generated sources -JavaSwiftKitDemo/src/main/java/com/example/swift/generated/* +**/generated/ diff --git a/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts b/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts index 85142bd9..e3fc499f 100644 --- a/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts +++ b/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +import java.util.* + plugins { java } @@ -40,16 +42,44 @@ tasks.withType(JavaCompile::class).forEach { it.options.compilerArgs.add("-Xlint:preview") } + +// FIXME: cannot share definition with 'buildSrc' so we duplicated the impl here +fun javaLibraryPaths(): List { + val osName = System.getProperty("os.name") + val osArch = System.getProperty("os.arch") + val isLinux = osName.lowercase(Locale.getDefault()).contains("linux") + + return listOf( + if (isLinux) { + if (osArch.equals("x86_64") || osArch.equals("amd64")) { + "$rootDir/.build/x86_64-unknown-linux-gnu/debug/" + } else { + "$rootDir/.build/$osArch-unknown-linux-gnu/debug/" + } + } else { + if (osArch.equals("aarch64")) { + "$rootDir/.build/arm64-apple-macosx/debug/" + } else { + "$rootDir/.build/$osArch-apple-macosx/debug/" + } + }, + if (isLinux) { + "/usr/lib/swift/linux" + } else { + // assume macOS + "/usr/lib/swift/" + } + ) +} + + // Configure paths for native (Swift) libraries tasks.test { jvmArgs( "--enable-native-access=ALL-UNNAMED", // Include the library paths where our dylibs are that we want to load and call - "-Djava.library.path=" + listOf( - """$rootDir/.build/arm64-apple-macosx/debug/""", - "/usr/lib/swift/" - ).joinToString(File.pathSeparator) + "-Djava.library.path=" + javaLibraryPaths().joinToString(File.pathSeparator) ) } @@ -60,14 +90,15 @@ tasks.withType { } -val buildSwiftJExtract = tasks.register("buildSwiftJExtract") { - description = "Builds Swift targets, including jextract-swift" - - workingDir("..") - commandLine("make") -} - -tasks.build { - dependsOn(buildSwiftJExtract) -} +// TODO: This is a crude workaround, we'll remove 'make' soon and properly track build dependencies +// val buildSwiftJExtract = tasks.register("buildMake") { +// description = "Triggers 'make' build" +// +// workingDir(rootDir) +// commandLine("make") +// } +// +// tasks.build { +// dependsOn(buildSwiftJExtract) +// } diff --git a/Makefile b/Makefile index a0baff12..3528d1a6 100644 --- a/Makefile +++ b/Makefile @@ -37,17 +37,15 @@ BUILD_DIR := .build/$(ARCH_SUBDIR)-apple-macosx LIB_SUFFIX := dylib endif +SAMPLES_DIR := "Samples" all: generate-all -$(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/libJavaKitExample.$(LIB_SUFFIX) $(BUILD_DIR)/debug/Java2Swift: +$(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/Java2Swift: swift build -./JavaSwiftKitDemo/build/classes/java/main/com/example/swift/HelloSubclass.class: JavaSwiftKitDemo/src/main/java/com/example/swift - ./gradlew build - -run: $(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/libJavaKitExample.$(LIB_SUFFIX) JavaSwiftKitDemo/src/main/java/com/example/swift - java -cp JavaSwiftKitDemo/build/classes/java/main -Djava.library.path=$(BUILD_DIR)/debug/ com.example.swift.HelloSwift +run: $(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) $(BUILD_DIR)/debug/libExampleSwiftLibrary.$(LIB_SUFFIX) + ./gradlew Samples:JavaKitSampleApp:run Java2Swift: $(BUILD_DIR)/debug/Java2Swift @@ -70,7 +68,8 @@ generate-JavaKitNetwork: Java2Swift generate-JavaKit generate-all: generate-JavaKit generate-JavaKitReflection generate-JavaKitJar generate-JavaKitNetwork \ jextract-swift clean: - rm -rf .build + rm -rf .build; \ + rm -rf Samples/SwiftKitExampleApp/src/generated/java/* format: swift format --recursive . -i @@ -81,7 +80,6 @@ format: JEXTRACT_BUILD_DIR="$(BUILD_DIR)/jextract" -# Parameter: Swift source file define make_swiftinterface $(eval $@_MODULE = $(1)) $(eval $@_FILENAME = $(2)) @@ -99,17 +97,21 @@ jextract-swift: generate-JExtract-interface-files generate-JExtract-interface-files: $(BUILD_DIR)/debug/libJavaKit.$(LIB_SUFFIX) echo "Generate .swiftinterface files..." - @$(call make_swiftinterface, "JavaKitExample", "MySwiftLibrary") - @$(call make_swiftinterface, "JavaKitExample", "SwiftKit") + @$(call make_swiftinterface, "ExampleSwiftLibrary", "MySwiftLibrary") + @$(call make_swiftinterface, "SwiftKitSwift", "SwiftKit") jextract-run: jextract-swift generate-JExtract-interface-files swift run jextract-swift \ --package-name com.example.swift.generated \ - --swift-module JavaKitExample \ - --output-directory JavaSwiftKitDemo/src/main/java \ - $(BUILD_DIR)/jextract/JavaKitExample/MySwiftLibrary.swiftinterface \ - $(BUILD_DIR)/jextract/JavaKitExample/SwiftKit.swiftinterface + --swift-module ExampleSwiftLibrary \ + --output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/src/generated/java \ + $(BUILD_DIR)/jextract/ExampleSwiftLibrary/MySwiftLibrary.swiftinterface; \ + swift run jextract-swift \ + --package-name org.swift.swiftkit.generated \ + --swift-module SwiftKitSwift \ + --output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/src/generated/java \ + $(BUILD_DIR)/jextract/SwiftKitSwift/SwiftKit.swiftinterface jextract-run-java: jextract-swift generate-JExtract-interface-files - ./gradlew run + ./gradlew Samples:SwiftKitSampleApp:run diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 60931987..00000000 --- a/Package.resolved +++ /dev/null @@ -1,32 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "41982a3656a71c768319979febd796c6fd111d5c", - "version" : "1.5.0" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftlang/swift-syntax.git", - "state" : { - "branch" : "main", - "revision" : "16dc4f87937fa8a639f064d4b2369c4d3e0fc672" - } - }, - { - "identity" : "swift-system", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-system", - "state" : { - "revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5", - "version" : "1.3.2" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index 84efccda..df96b8f4 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ func findJavaHome() -> String { // This is a workaround for envs (some IDEs) which have trouble with // picking up env variables during the build process let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home" - if let home = try? String(contentsOfFile: path) { + if let home = try? String(contentsOfFile: path, encoding: .utf8) { if let lastChar = home.last, lastChar.isNewline { return String(home.dropLast()) } @@ -36,8 +36,8 @@ let javaIncludePath = "\(javaHome)/include" #elseif os(macOS) let javaPlatformIncludePath = "\(javaIncludePath)/darwin" #else + // TODO: Handle windows as well #error("Currently only macOS and Linux platforms are supported, this may change in the future.") -// TODO: Handle windows as well #endif let package = Package( @@ -50,6 +50,7 @@ let package = Package( .macCatalyst(.v13), ], products: [ + // ==== JavaKit (i.e. calling Java directly Swift utilities) .library( name: "JavaKit", targets: ["JavaKit"] @@ -70,12 +71,6 @@ let package = Package( targets: ["JavaKitReflection"] ), - .library( - name: "JavaKitExample", - type: .dynamic, - targets: ["JavaKitExample"] - ), - .library( name: "JavaKitVM", targets: ["JavaKitVM"] @@ -86,20 +81,43 @@ let package = Package( targets: ["JavaTypes"] ), - .library( - name: "JExtractSwift", - targets: ["JExtractSwift"] - ), - .executable( name: "Java2Swift", targets: ["Java2Swift"] ), + // ==== jextract-swift (extract Java accessors from Swift interface files) + .executable( name: "jextract-swift", targets: ["JExtractSwiftTool"] ), + + // Support library written in Swift for SwiftKit "Java" + .library( + name: "SwiftKitSwift", + type: .dynamic, + targets: ["SwiftKitSwift"] + ), + + .library( + name: "JExtractSwift", + targets: ["JExtractSwift"] + ), + + // ==== Examples + + .library( + name: "JavaKitExample", + type: .dynamic, + targets: ["JavaKitExample"] + ), + .library( + name: "ExampleSwiftLibrary", + type: .dynamic, + targets: ["ExampleSwiftLibrary"] + ), + ], dependencies: [ .package(url: "https://github.com/swiftlang/swift-syntax.git", branch: "main"), @@ -187,6 +205,22 @@ let package = Package( .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ] ), + .target( + name: "ExampleSwiftLibrary", + dependencies: [], + swiftSettings: [ + .swiftLanguageMode(.v5), + .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) + ] + ), + .target( + name: "SwiftKitSwift", + dependencies: [], + swiftSettings: [ + .swiftLanguageMode(.v5), + .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) + ] + ), .target( name: "JavaRuntime", diff --git a/JavaSwiftKitDemo/build.gradle.kts b/Samples/JavaKitSampleApp/build.gradle similarity index 75% rename from JavaSwiftKitDemo/build.gradle.kts rename to Samples/JavaKitSampleApp/build.gradle index 84c0b924..67295994 100644 --- a/JavaSwiftKitDemo/build.gradle.kts +++ b/Samples/JavaKitSampleApp/build.gradle @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +import org.swift.swiftkit.gradle.BuildUtils + plugins { id("build-logic.java-application-conventions") } @@ -30,6 +32,8 @@ java { } dependencies { + implementation(project(':SwiftKit')) + testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") } @@ -39,7 +43,7 @@ tasks.test { } application { - mainClass = "org.example.HelloJava2Swift" + mainClass = "com.example.swift.JavaKitSampleMain" // In order to silence: // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called @@ -47,17 +51,10 @@ application { // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module // WARNING: Restricted methods will be blocked in a future release unless native access is enabled // FIXME: Find out the proper solution to this - applicationDefaultJvmArgs = listOf( - "--enable-native-access=ALL-UNNAMED", - - // Include the library paths where our dylibs are that we want to load and call - "-Djava.library.path=" + listOf( - """$rootDir/.build/arm64-apple-macosx/debug/""", - "/usr/lib/swift/" - ).joinToString(":"), - - // Enable tracing downcalls (to Swift) - "-Djextract.trace.downcalls=true" - ) -} + applicationDefaultJvmArgs = [ + "--enable-native-access=ALL-UNNAMED", + // Include the library paths where our dylibs are that we want to load and call + "-Djava.library.path=" + BuildUtils.javaLibraryPaths(rootDir).join(":") + ] +} diff --git a/JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSubclass.java b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSubclass.java similarity index 95% rename from JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSubclass.java rename to Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSubclass.java index 25cc9245..eb3cba32 100644 --- a/JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSubclass.java +++ b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSubclass.java @@ -14,8 +14,6 @@ package com.example.swift; -import com.example.swift.HelloSwift; - public class HelloSubclass extends HelloSwift { private String greeting; diff --git a/JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSwift.java b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSwift.java similarity index 87% rename from JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSwift.java rename to Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSwift.java index 652d230b..b4c87f71 100644 --- a/JavaSwiftKitDemo/src/main/java/com/example/swift/HelloSwift.java +++ b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/HelloSwift.java @@ -14,8 +14,6 @@ package com.example.swift; -import com.example.swift.HelloSubclass; - public class HelloSwift { private double value; private static double initialValue = 3.14159; @@ -29,11 +27,6 @@ public HelloSwift() { this.value = initialValue; } - public static void main(String[] args) { - int result = new HelloSubclass("Swift").sayHello(17, 25); - System.out.println("sayHello(17, 25) = " + result); - } - public native int sayHello(int x, int y); public native String throwMessageFromSwift(String message) throws Exception; diff --git a/SwiftJavaKitExample/build.gradle.kts b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/JavaKitSampleMain.java similarity index 56% rename from SwiftJavaKitExample/build.gradle.kts rename to Samples/JavaKitSampleApp/src/main/java/com/example/swift/JavaKitSampleMain.java index 4c77db83..2b565608 100644 --- a/SwiftJavaKitExample/build.gradle.kts +++ b/Samples/JavaKitSampleApp/src/main/java/com/example/swift/JavaKitSampleMain.java @@ -12,10 +12,16 @@ // //===----------------------------------------------------------------------===// -plugins { - `swift-library` -} +package com.example.swift; + +/** + * This sample shows off a {@link HelloSwift} type which is partially implemented in Swift. + * For the Swift implementation refer to + */ +public class JavaKitSampleMain { -extensions.configure { - source.from(file("../Sources/JavaKitExample")) + public static void main(String[] args) { + int result = new HelloSubclass("Swift").sayHello(17, 25); + System.out.println("sayHello(17, 25) = " + result); + } } diff --git a/Samples/SwiftKitSampleApp/build.gradle b/Samples/SwiftKitSampleApp/build.gradle new file mode 100644 index 00000000..158fdfcf --- /dev/null +++ b/Samples/SwiftKitSampleApp/build.gradle @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +plugins { + id("build-logic.java-application-conventions") +} + +group = "org.swift.swiftkit" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(22)) + } +} + +sourceSets { + generated { + java.srcDir "${buildDir}/generated/src/java/" + } + + // Make the 'main' and 'test' source sets depend on the generated sources + main { + compileClasspath += sourceSets.generated.output + runtimeClasspath += sourceSets.generated.output + } + test { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + + compileClasspath += sourceSets.generated.output + runtimeClasspath += sourceSets.generated.output + } +} + +dependencies { + implementation(project(':SwiftKit')) + generatedImplementation(project(':SwiftKit')) + + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +configurations { + generatedImplementation.extendsFrom(mainImplementation) + generatedRuntimeOnly.extendsFrom(mainRuntimeOnly) +} + +tasks.named("compileJava").configure { + dependsOn("jextract") +} + +tasks.test { + useJUnitPlatform() +} + +application { + mainClass = "com.example.swift.HelloJava2Swift" + + // In order to silence: + // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called + // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.example.swift.JavaKitExample in an unnamed module + // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module + // WARNING: Restricted methods will be blocked in a future release unless native access is enabled + // FIXME: Find out the proper solution to this + applicationDefaultJvmArgs = [ + "--enable-native-access=ALL-UNNAMED", + + // Include the library paths where our dylibs are that we want to load and call + "-Djava.library.path=" + [ + "$rootDir/.build/arm64-apple-macosx/debug/", + "/usr/lib/swift/" + ].join(":"), + + // Enable tracing downcalls (to Swift) + "-Djextract.trace.downcalls=true" + ] +} + +task jextract(type: Exec) { + description = "Extracts Java accessor sources using jextract" + outputs.dir(layout.buildDirectory.dir("generated")) + inputs.dir("$rootDir/Sources/ExampleSwiftLibrary") + + workingDir = rootDir + commandLine "make" + args "jextract-run" +} + +tasks.named("compileGeneratedJava").configure { + dependsOn jextract +} diff --git a/JavaSwiftKitDemo/src/main/java/org/example/CallMe.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java similarity index 96% rename from JavaSwiftKitDemo/src/main/java/org/example/CallMe.java rename to Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java index 4af50f91..e3e10815 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/CallMe.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.example; +package com.example.swift; public class CallMe { public static String callMeStatic() { diff --git a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java similarity index 79% rename from JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java rename to Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 95870989..28054047 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -12,24 +12,17 @@ // //===----------------------------------------------------------------------===// -package org.example; +package com.example.swift; -// Import javakit/swiftkit support libraries - -import com.example.swift.generated.JavaKitExample; +// Import swift-extract generated sources +import com.example.swift.generated.ExampleSwiftLibrary; import com.example.swift.generated.MySwiftClass; -import org.example.swift.ManualJavaKitExample; -import org.example.swift.ManualMySwiftClass; -import org.swift.javakit.*; -// Import swift-extract generated sources -import static com.example.swift.generated.JavaKitExample.*; -import static com.example.swift.generated.MySwiftClass.*; +// Import javakit/swiftkit support libraries +import org.swift.swiftkit.SwiftKit; + import java.lang.foreign.*; -import java.nio.file.FileSystems; -import java.nio.file.Paths; -import java.util.Arrays; import java.util.List; @@ -53,13 +46,13 @@ public static void main(String[] args) { System.out.println("ok."); } - tests(); + examples(); } - static void tests() { - JavaKitExample.helloWorld(); + static void examples() { + ExampleSwiftLibrary.helloWorld(); - JavaKitExample.globalTakeInt(1337); + ExampleSwiftLibrary.globalTakeInt(1337); MySwiftClass obj = new MySwiftClass(2222, 7777); diff --git a/JavaSwiftKitDemo/src/main/java/org/example/swift/ManualJavaKitExample.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java similarity index 99% rename from JavaSwiftKitDemo/src/main/java/org/example/swift/ManualJavaKitExample.java rename to Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java index f11c5637..1d9cfa77 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/swift/ManualJavaKitExample.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java @@ -12,9 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; - -import com.example.swift.generated.JavaKitExample; +package com.example.swift; import java.lang.invoke.*; import java.lang.foreign.*; diff --git a/JavaSwiftKitDemo/src/main/java/org/example/swift/ManualMySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java similarity index 99% rename from JavaSwiftKitDemo/src/main/java/org/example/swift/ManualMySwiftClass.java rename to Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java index 53b21e8a..0a40f293 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/swift/ManualMySwiftClass.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java @@ -12,12 +12,12 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; +package com.example.swift; // ==== Extra convenience APIs ------------------------------------------------------------------------------------- // TODO: Think about offering these or not, perhaps only as an option? -import org.swift.javakit.ManagedSwiftType; +import org.swift.swiftkit.ManagedSwiftType; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; diff --git a/JavaSwiftKitDemo/src/main/java/org/example/swift/Manual_MySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java similarity index 99% rename from JavaSwiftKitDemo/src/main/java/org/example/swift/Manual_MySwiftClass.java rename to Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java index 9ef51038..f57ac9d8 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/swift/Manual_MySwiftClass.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; +package com.example.swift; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; diff --git a/JavaSwiftKitDemo/src/test/java/org/example/swift/GlobalFunctionsTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java similarity index 52% rename from JavaSwiftKitDemo/src/test/java/org/example/swift/GlobalFunctionsTest.java rename to Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java index 7faff6b0..7bad75f2 100644 --- a/JavaSwiftKitDemo/src/test/java/org/example/swift/GlobalFunctionsTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java @@ -12,49 +12,41 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; +package com.example.swift.generated; -import com.example.swift.generated.JavaKitExample; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; -import static com.example.swift.generated.JavaKitExample.*; import static org.junit.jupiter.api.Assertions.*; -public class GlobalFunctionsTest { +public class GeneratedJavaKitExampleModuleTest { + @BeforeAll static void beforeAll() { + System.out.println("java.library.path = " + System.getProperty("java.library.path")); + System.loadLibrary("swiftCore"); - System.loadLibrary("JavaKitExample"); + System.loadLibrary("ExampleSwiftLibrary"); System.setProperty("jextract.trace.downcalls", "true"); } @Test + @DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces void call_helloWorld() { - helloWorld(); + ExampleSwiftLibrary.helloWorld(); - assertNotNull(helloWorld$address()); + assertNotNull(ExampleSwiftLibrary.helloWorld$address()); } @Test + @DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces void call_globalTakeInt() { - JavaKitExample.globalTakeInt(12); + ExampleSwiftLibrary.globalTakeInt(12); - assertNotNull(globalTakeInt$address()); + assertNotNull(ExampleSwiftLibrary.globalTakeInt$address()); } -// @Test -// void call_globalCallJavaCallback() { -// var num = 0; -// -// JavaKitExample.globalCallJavaCallback(new Runnable() { -// @Override -// public void run() { -// num += 1; -// } -// }); -// -// assertEquals(1, num); -// } } diff --git a/JavaSwiftKitDemo/src/test/java/org/example/swift/JavaKitTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java similarity index 61% rename from JavaSwiftKitDemo/src/test/java/org/example/swift/JavaKitTest.java rename to Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java index 404dca42..1a5f0d5a 100644 --- a/JavaSwiftKitDemo/src/test/java/org/example/swift/JavaKitTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java @@ -12,33 +12,25 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; +package com.example.swift.generated; -import com.example.swift.generated.MySwiftClass; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeAll; -import static org.junit.jupiter.api.Assertions.*; -import static org.example.swift.ManualJavaKitExample.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; +public class MySwiftClassTest { -public class JavaKitTest { @BeforeAll static void beforeAll() { System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); System.loadLibrary("swiftCore"); - System.loadLibrary("JavaKitExample"); + System.loadLibrary("ExampleSwiftLibrary"); System.setProperty("jextract.trace.downcalls", "true"); } - @Test - void call_helloWorld() { - helloWorld(); + // TODO: test member methods on MySwiftClass - assertNotNull(helloWorld$address()); - } - } diff --git a/JavaSwiftKitDemo/src/test/java/org/example/swift/SwiftKitTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java similarity index 65% rename from JavaSwiftKitDemo/src/test/java/org/example/swift/SwiftKitTest.java rename to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index acc1f372..7968cfae 100644 --- a/JavaSwiftKitDemo/src/test/java/org/example/swift/SwiftKitTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -12,35 +12,31 @@ // //===----------------------------------------------------------------------===// -package org.example.swift; +package org.swift.swiftkit; import com.example.swift.generated.MySwiftClass; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.swift.javakit.SwiftKit; -import org.swift.swiftkit.SwiftArena; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; - -import static org.example.swift.ManualJavaKitExample.helloWorld; -import static org.example.swift.ManualJavaKitExample.helloWorld$address; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -public class SwiftKitTest { +public class MySwiftClassTest { + @BeforeAll static void beforeAll() { System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); System.loadLibrary("swiftCore"); - System.loadLibrary("JavaKitExample"); + System.loadLibrary("ExampleSwiftLibrary"); System.setProperty("jextract.trace.downcalls", "true"); } @Test + @DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces void call_retain_retainCount_release() { var obj = new MySwiftClass(1, 2); @@ -53,18 +49,4 @@ void call_retain_retainCount_release() { SwiftKit.release(obj.$memorySegment()); assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); } - - @Test - void use_MySwiftClass_repr_init_arena() { - int lenValue = 1111; - int capValue = 2222; - - try (Arena arena = Arena.ofConfined()) { - ManualMySwiftClass obj = ManualMySwiftClass.init(arena, lenValue, capValue); - - -// assertEquals(lenValue, obj.len()); -// assertEquals(capValue, obj.cap()); - } - } } diff --git a/Sources/JavaKitExample/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift similarity index 95% rename from Sources/JavaKitExample/MySwiftLibrary.swift rename to Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index bd76fcf1..e5faaad7 100644 --- a/Sources/JavaKitExample/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -28,7 +28,11 @@ public func helloWorld() { } public func globalTakeInt(i: Int) { - p("int:\(i)") + p("i:\(i)") +} + +public func globalTakeIntInt(i: Int, j: Int) { + p("i:\(i), j:\(j)") } public class MySwiftClass { diff --git a/Sources/JExtractSwift/Swift2Java.swift b/Sources/JExtractSwift/Swift2Java.swift index 5c6d8a69..73a4d22b 100644 --- a/Sources/JExtractSwift/Swift2Java.swift +++ b/Sources/JExtractSwift/Swift2Java.swift @@ -36,7 +36,7 @@ public struct SwiftToJava: AsyncParsableCommand { // TODO: Once we ship this, make this `.warning` by default @Option(name: .shortAndLong, help: "Configure the level of lots that should be printed") - var logLevel: Logger.Level = .trace + var logLevel: Logger.Level = .notice @Argument(help: "The Swift interface files to export to Java.") var swiftInterfaceFiles: [String] diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 7f238c75..35ed6133 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -239,12 +239,21 @@ extension Swift2JavaTranslator { """ ) + // SymbolLookup.libraryLookup is platform dependent and does not take into account java.library.path + // https://bugs.openjdk.org/browse/JDK-8311090 printer.print( """ - static final SymbolLookup SYMBOL_LOOKUP = - SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) + static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + private static SymbolLookup getSymbolLookup() { + if (SwiftKit.isMacOS()) { + return SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); + } else { + return SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } + } """ ) diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator.swift index e942ff9b..27f55a4d 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator.swift @@ -22,7 +22,7 @@ import SwiftSyntax public final class Swift2JavaTranslator { static let SWIFT_INTERFACE_SUFFIX = ".swiftinterface" - public var log = Logger(label: "translator", logLevel: .info) + package var log = Logger(label: "translator", logLevel: .info) // ==== Input configuration let swiftModuleName: String @@ -110,7 +110,7 @@ extension Swift2JavaTranslator { } public func postProcessImportedDecls() async throws { - log.info( + log.debug( "Post process imported decls...", metadata: [ "types": "\(importedTypes.count)", @@ -143,7 +143,7 @@ extension Swift2JavaTranslator { log.info("Mapping members of: \(tyDecl.swiftTypeName)") tyDecl.initializers = try await tyDecl.initializers._mapAsync { initDecl in - dylib.log.logLevel = .trace + dylib.log.logLevel = .info let initDecl = try await dylib.fillInAllocatingInitMangledName(initDecl) log.info("Mapped initializer '\(initDecl.identifier)' -> '\(initDecl.swiftMangledName)'") @@ -171,7 +171,7 @@ extension Swift2JavaTranslator { /// Default set Java imports for every generated file static let defaultJavaImports: Array = [ // Support library in Java - "org.swift.javakit.SwiftKit", + "org.swift.swiftkit.SwiftKit", // Necessary for native calls and type mapping "java.lang.foreign.*", diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwift/Swift2JavaVisitor.swift index c6c9bb56..3c8c252d 100644 --- a/Sources/JExtractSwift/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwift/Swift2JavaVisitor.swift @@ -78,7 +78,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { return .skipChildren } - self.log.info("Import function: \(node.kind) \(node.name)") + self.log.debug("Import function: \(node.kind) \(node.name)") // TODO: this must handle inout and other stuff, strip it off etc let returnTy: TypeSyntax @@ -129,7 +129,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { } if let currentTypeName { - log.info("Record method in \(currentTypeName)") + log.debug("Record method in \(currentTypeName)") translator.importedTypes[currentTypeName]?.methods.append(funcDecl) } else { translator.importedGlobalFuncs.append(funcDecl) diff --git a/Sources/JavaKitExample/JavaKitExample.swift b/Sources/JavaKitExample/JavaKitExample.swift index 7252d648..bcc58114 100644 --- a/Sources/JavaKitExample/JavaKitExample.swift +++ b/Sources/JavaKitExample/JavaKitExample.swift @@ -117,4 +117,4 @@ struct HelloSubclass { @JavaMethod init(greeting: String, environment: JNIEnvironment) -} +} \ No newline at end of file diff --git a/Sources/JavaKitJar/generated/JarFile.swift b/Sources/JavaKitJar/generated/JarFile.swift index bbad9585..aa04c098 100644 --- a/Sources/JavaKitJar/generated/JarFile.swift +++ b/Sources/JavaKitJar/generated/JarFile.swift @@ -28,6 +28,9 @@ public struct JarFile { @JavaMethod public func size() -> Int32 + @JavaMethod + public func toString() -> String + @JavaMethod public func close() throws @@ -37,9 +40,6 @@ public struct JarFile { @JavaMethod public func equals(_ arg0: JavaObject?) -> Bool - @JavaMethod - public func toString() -> String - @JavaMethod public func hashCode() -> Int32 diff --git a/Sources/JavaKitJar/generated/JarInputStream.swift b/Sources/JavaKitJar/generated/JarInputStream.swift index e0987f25..9793a431 100644 --- a/Sources/JavaKitJar/generated/JarInputStream.swift +++ b/Sources/JavaKitJar/generated/JarInputStream.swift @@ -26,10 +26,10 @@ public struct JarInputStream { public func readAllBytes() throws -> [Int8] @JavaMethod - public func readNBytes(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> Int32 + public func readNBytes(_ arg0: Int32) throws -> [Int8] @JavaMethod - public func readNBytes(_ arg0: Int32) throws -> [Int8] + public func readNBytes(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> Int32 @JavaMethod public func skip(_ arg0: Int64) throws -> Int64 diff --git a/Sources/JavaKitExample/SwiftKit.swift b/Sources/SwiftKitSwift/SwiftKit.swift similarity index 100% rename from Sources/JavaKitExample/SwiftKit.swift rename to Sources/SwiftKitSwift/SwiftKit.swift diff --git a/SwiftJavaKitExample/README.md b/SwiftJavaKitExample/README.md deleted file mode 100644 index 97e41df7..00000000 --- a/SwiftJavaKitExample/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Gradle build for Sources/JavaKitExample - diff --git a/SwiftKit/build.gradle b/SwiftKit/build.gradle new file mode 100644 index 00000000..5a6d15c0 --- /dev/null +++ b/SwiftKit/build.gradle @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +plugins { + id("build-logic.java-application-conventions") +} + +group = "org.swift.swiftkit" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(22)) + } +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} diff --git a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/ManagedSwiftType.java b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java similarity index 96% rename from JavaSwiftKitDemo/src/main/java/org/swift/javakit/ManagedSwiftType.java rename to SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java index 117a0a09..2abaf85a 100644 --- a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/ManagedSwiftType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.javakit; +package org.swift.swiftkit; import java.lang.foreign.MemorySegment; diff --git a/JavaSwiftKitDemo/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java similarity index 97% rename from JavaSwiftKitDemo/src/main/java/org/swift/swiftkit/SwiftArena.java rename to SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 2c60dc3f..54c801b4 100644 --- a/JavaSwiftKitDemo/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -14,8 +14,6 @@ package org.swift.swiftkit; -import org.swift.javakit.SwiftKit; - import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.util.concurrent.ConcurrentSkipListSet; diff --git a/JavaSwiftKitDemo/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java similarity index 100% rename from JavaSwiftKitDemo/src/main/java/org/swift/swiftkit/SwiftHeapObject.java rename to SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java diff --git a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java similarity index 81% rename from JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java rename to SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index ba76232b..821169b9 100644 --- a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -12,9 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.javakit; - -import org.swift.swiftkit.SwiftHeapObject; +package org.swift.swiftkit; import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; @@ -22,14 +20,12 @@ import java.util.Arrays; import java.util.stream.Collectors; -import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_BYTE; public class SwiftKit { - // FIXME: why do we need to hardcore the path, seems it can't find by name - private static final String STDLIB_DYLIB_NAME = "swiftCore"; - private static final String STDLIB_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; + private static final String STDLIB_DYLIB_NAME = "swiftCore"; + private static final String STDLIB_MACOS_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; private static final Arena LIBRARY_ARENA = Arena.ofAuto(); static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); @@ -41,16 +37,35 @@ public class SwiftKit { public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); - static final SymbolLookup SYMBOL_LOOKUP = - // FIXME: why does this not find just by name? + static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + + private static SymbolLookup getSymbolLookup() { + if (isMacOS()) { + // FIXME: why does this not find just by name on macOS? // SymbolLookup.libraryLookup(System.mapLibraryName(STDLIB_DYLIB_NAME), LIBRARY_ARENA) - SymbolLookup.libraryLookup(STDLIB_DYLIB_PATH, LIBRARY_ARENA) + return SymbolLookup.libraryLookup(STDLIB_MACOS_DYLIB_PATH, LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); - + } else { + return SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } + } public SwiftKit() { } + public static boolean isLinux() { + return System.getProperty("os.name").toLowerCase().contains("linux"); + } + + public static boolean isMacOS() { + return System.getProperty("os.name").toLowerCase().contains("mac"); + } + + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + static void traceDowncall(String name, Object... args) { String traceArgs = Arrays.stream(args) .map(Object::toString) @@ -69,7 +84,7 @@ static MemorySegment findOrThrow(String symbol) { * Descriptor for the free C runtime function. */ public static final FunctionDescriptor free$descriptor = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS + ValueLayout.ADDRESS ); /** @@ -187,9 +202,9 @@ public static long release(SwiftHeapObject object) { // ==== ------------------------------------------------------------------------------------------------------------ /** - * {@snippet lang=swift : + * {@snippet lang = swift: * func _typeByName(_: Swift.String) -> Any.Type? - * } + *} */ private static class swift_getTypeByName { public static final FunctionDescriptor DESC = FunctionDescriptor.of( @@ -221,14 +236,14 @@ public static MemorySegment getTypeByName(String string) { } /** - * {@snippet lang=swift : + * {@snippet lang = swift: * func _swift_getTypeByMangledNameInEnvironment( * _ name: UnsafePointer, * _ nameLength: UInt, * genericEnvironment: UnsafeRawPointer?, * genericArguments: UnsafeRawPointer? * ) -> Any.Type? - * } + *} */ private static class swift_getTypeByMangledNameInEnvironment { public static final FunctionDescriptor DESC = FunctionDescriptor.of( @@ -268,7 +283,7 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) { * the size of a pointer (aka C's ptrdiff_t). */ public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? - ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; + ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; /** * The value layout for Swift's UInt type, which is an unsigned type that follows @@ -279,7 +294,7 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) { /** * Read a Swift.Int value from memory at the given offset and translate it into a Java long. - * + *

* This function copes with the fact that a Swift.Int might be 32 or 64 bits. */ public static final long getSwiftInt(MemorySegment memorySegment, long offset) { @@ -294,50 +309,50 @@ public static final long getSwiftInt(MemorySegment memorySegment, long offset) { * Value witness table layout. */ public static final MemoryLayout valueWitnessTableLayout = MemoryLayout.structLayout( - ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"), - ValueLayout.ADDRESS.withName("destroy"), - ValueLayout.ADDRESS.withName("initializeWithCopy"), - ValueLayout.ADDRESS.withName("assignWithCopy"), - ValueLayout.ADDRESS.withName("initializeWithTake"), - ValueLayout.ADDRESS.withName("assignWithTake"), - ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"), - ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"), - SwiftKit.SWIFT_INT.withName("size"), - SwiftKit.SWIFT_INT.withName("stride"), - SwiftKit.SWIFT_UINT.withName("flags"), - SwiftKit.SWIFT_UINT.withName("extraInhabitantCount") + ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"), + ValueLayout.ADDRESS.withName("destroy"), + ValueLayout.ADDRESS.withName("initializeWithCopy"), + ValueLayout.ADDRESS.withName("assignWithCopy"), + ValueLayout.ADDRESS.withName("initializeWithTake"), + ValueLayout.ADDRESS.withName("assignWithTake"), + ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"), + ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"), + SwiftKit.SWIFT_INT.withName("size"), + SwiftKit.SWIFT_INT.withName("stride"), + SwiftKit.SWIFT_UINT.withName("flags"), + SwiftKit.SWIFT_UINT.withName("extraInhabitantCount") ).withName("SwiftValueWitnessTable"); /** * Offset for the "size" field within the value witness table. */ static final long valueWitnessTable$size$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("size")); + valueWitnessTableLayout.byteOffset(PathElement.groupElement("size")); /** * Offset for the "stride" field within the value witness table. */ static final long valueWitnessTable$stride$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride")); + valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride")); /** * Offset for the "flags" field within the value witness table. */ static final long valueWitnessTable$flags$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags")); + valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags")); /** * Type metadata pointer. */ public static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( - SWIFT_POINTER.withName("vwt") + SWIFT_POINTER.withName("vwt") ).withName("SwiftFullTypeMetadata"); /** * Offset for the "vwt" field within the full type metadata. */ static final long fullTypeMetadata$vwt$offset = - fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt")); + fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt")); /** * Given the address of Swift type metadata for a type, return the addres @@ -345,7 +360,7 @@ public static final long getSwiftInt(MemorySegment memorySegment, long offset) { */ public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { return MemorySegment.ofAddress(typeMetadata.address() - SWIFT_POINTER.byteSize()) - .reinterpret(fullTypeMetadataLayout.byteSize()); + .reinterpret(fullTypeMetadataLayout.byteSize()); } /** @@ -384,12 +399,12 @@ public static long alignmentOfSwiftType(MemorySegment typeMetadata) { * Descriptor for the swift_getTypeName runtime function. */ public static final FunctionDescriptor swift_getTypeName$descriptor = FunctionDescriptor.of( - /*returns=*/MemoryLayout.structLayout( - SWIFT_POINTER.withName("utf8Chars"), - SWIFT_INT.withName("length") - ), - ValueLayout.ADDRESS, - ValueLayout.JAVA_BOOLEAN + /*returns=*/MemoryLayout.structLayout( + SWIFT_POINTER.withName("utf8Chars"), + SWIFT_INT.withName("length") + ), + ValueLayout.ADDRESS, + ValueLayout.JAVA_BOOLEAN ); /** @@ -404,18 +419,18 @@ public static long alignmentOfSwiftType(MemorySegment typeMetadata) { /** * Produce the name of the Swift type given its Swift type metadata. - * + *

* If 'qualified' is true, leave all of the qualification in place to * disambiguate the type, producing a more complete (but longer) type name. */ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { try { try (Arena arena = Arena.ofConfined()) { - MemorySegment charsAndLength = (MemorySegment)swift_getTypeName$handle.invokeExact((SegmentAllocator)arena, typeMetadata, qualified); - MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); - String typeName = utf8Chars.getString(0); - cFree(utf8Chars); - return typeName; + MemorySegment charsAndLength = (MemorySegment) swift_getTypeName$handle.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); + MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); + String typeName = utf8Chars.getString(0); + cFree(utf8Chars); + return typeName; } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); @@ -427,7 +442,7 @@ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualifi * type metadata. The resulting layout is completely opaque to Java, but * has appropriate size/alignment to model the memory associated with a * Swift type. - * + *

* In the future, this layout could be extended to provide more detail, * such as the fields of a Swift struct. */ @@ -435,9 +450,9 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { long size = sizeOfSwiftType(typeMetadata); long stride = strideOfSwiftType(typeMetadata); return MemoryLayout.structLayout( - MemoryLayout.sequenceLayout(size, JAVA_BYTE) - .withByteAlignment(alignmentOfSwiftType(typeMetadata)), - MemoryLayout.paddingLayout(stride - size) + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)), + MemoryLayout.paddingLayout(stride - size) ).withName(nameOfSwiftType(typeMetadata, true)); } } diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/FuncImportTests.swift index ed71c14d..c2eb5c27 100644 --- a/Tests/JExtractSwiftTests/FuncImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncImportTests.swift @@ -62,12 +62,13 @@ final class MethodImportTests { } """ - @Test func method_helloWorld() async throws { + @Test + func method_helloWorld() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .error try await st.analyze(swiftInterfacePath: "/fake/Fake.swiftinterface", text: class_interfaceFile) @@ -101,12 +102,13 @@ final class MethodImportTests { ) } - @Test func method_globalTakeInt() async throws { + @Test + func method_globalTakeInt() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .error try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -142,12 +144,13 @@ final class MethodImportTests { ) } - @Test func method_globalTakeIntLongString() async throws { + @Test + func method_globalTakeIntLongString() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .error try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -183,12 +186,13 @@ final class MethodImportTests { ) } - @Test func method_class_helloMemberFunction_self_memorySegment() async throws { + @Test + func method_class_helloMemberFunction_self_memorySegment() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .error try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -224,12 +228,13 @@ final class MethodImportTests { ) } - @Test func method_class_helloMemberFunction_self_wrapper() async throws { + @Test + func method_class_helloMemberFunction_self_wrapper() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .error try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -265,12 +270,13 @@ final class MethodImportTests { ) } + @Test func test_method_class_helloMemberFunction_self_wrapper() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .info try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -306,12 +312,13 @@ final class MethodImportTests { ) } - @Test func method_class_helloMemberFunction_wrapper() async throws { + @Test + func method_class_helloMemberFunction_wrapper() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .info try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -339,12 +346,13 @@ final class MethodImportTests { ) } - @Test func method_class_makeInt_wrapper() async throws { + @Test + func method_class_makeInt_wrapper() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .info try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) @@ -372,12 +380,13 @@ final class MethodImportTests { ) } - @Test func class_constructor() async throws { + @Test + func class_constructor() async throws { let st = Swift2JavaTranslator( javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) - st.log.logLevel = .trace + st.log.logLevel = .info try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) diff --git a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift index b6f93eb2..cf175197 100644 --- a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift +++ b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift @@ -52,7 +52,7 @@ final class FunctionDescriptorTests { _ methodIdentifier: String, javaPackage: String = "com.example.swift", swiftModuleName: String = "SwiftModule", - logLevel: Logger.Level = .trace, + logLevel: Logger.Level = .warning, body: (String) async throws -> () ) async throws { let st = Swift2JavaTranslator( diff --git a/Tests/JExtractSwiftTests/SwiftDylibTests.swift b/Tests/JExtractSwiftTests/SwiftDylibTests.swift index 2cf8894b..c3a67a07 100644 --- a/Tests/JExtractSwiftTests/SwiftDylibTests.swift +++ b/Tests/JExtractSwiftTests/SwiftDylibTests.swift @@ -23,18 +23,18 @@ final class SwiftDylibTests { @Test #endif func test_nm() async throws { - let dylib = SwiftDylib(path: ".build/arm64-apple-macosx/debug/libJavaKitExample.dylib")! + let dylib = SwiftDylib(path: ".build/arm64-apple-macosx/debug/libExampleSwiftLibrary.dylib")! let names = try await dylib.nmSymbolNames(grepDemangled: ["MySwiftClass", "len"]) #expect( names.contains { - $0.descriptiveName.contains("JavaKitExample.MySwiftClass.len.getter") + $0.descriptiveName.contains("ExampleSwiftLibrary.MySwiftClass.len.getter") } ) let getter = names.findPropertyGetter() - #expect(getter?.mangledName == "$s14JavaKitExample12MySwiftClassC3lenSivg") - #expect(getter?.descriptiveName == "JavaKitExample.MySwiftClass.len.getter : Swift.Int") + #expect(getter?.mangledName == "$s19ExampleSwiftLibrary02MyB5ClassC3lenSivg") + #expect(getter?.descriptiveName == "ExampleSwiftLibrary.MySwiftClass.len.getter : Swift.Int") } } diff --git a/Tests/JavaKitTests/BasicRuntimeTests.swift b/Tests/JavaKitTests/BasicRuntimeTests.swift index 085c907c..2e47b84f 100644 --- a/Tests/JavaKitTests/BasicRuntimeTests.swift +++ b/Tests/JavaKitTests/BasicRuntimeTests.swift @@ -58,7 +58,7 @@ struct BasicRuntimeTests { } } - @Test("Static methods") + @Test("Static methods", .disabled(if: isMacOS, "Fails on macOS command line")) func staticMethods() throws { let urlConnectionClass = try JavaClass(in: jvm.environment) #expect(urlConnectionClass.getDefaultAllowUserInteraction() == false) @@ -85,3 +85,12 @@ var isLinux: Bool { return false #endif } + +/// Whether we're running on MacOS. +var isMacOS: Bool { + #if os(macOS) + return true + #else + return false + #endif +} diff --git a/buildSrc/README.md b/buildSrc/README.md new file mode 100644 index 00000000..f3559b80 --- /dev/null +++ b/buildSrc/README.md @@ -0,0 +1 @@ +buildSrc are shared "build library code" that is available to all sub-projects of the primary build. diff --git a/SwiftKit/build.gradle.kts b/buildSrc/build.gradle similarity index 91% rename from SwiftKit/build.gradle.kts rename to buildSrc/build.gradle index 3842620b..a61e5793 100644 --- a/SwiftKit/build.gradle.kts +++ b/buildSrc/build.gradle @@ -12,10 +12,6 @@ // //===----------------------------------------------------------------------===// -plugins { - id("java") -} - -dependencies { - +repositories { + mavenCentral() } diff --git a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy new file mode 100644 index 00000000..3f50890d --- /dev/null +++ b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit.gradle + +final class BuildUtils { + + /// Find library paths for 'java.library.path' when running or testing projects inside this build. + static def javaLibraryPaths(File rootDir) { + def osName = System.getProperty("os.name") + def osArch = System.getProperty("os.arch") + def isLinux = osName.toLowerCase(Locale.getDefault()).contains("linux") + + return [ + isLinux ? + /* Linux */(osArch == "amd64" || osArch == "amd64" ? + "${rootDir}/.build/x86_64-unknown-linux-gnu/debug/" : + "${rootDir}/.build/${osArch}-unknown-linux-gnu/debug/") : + /* macOS */(osArch == "aarch64" ? + "${rootDir}/.build/arm64-apple-macosx/debug/" : + "${rootDir}/.build/${osArch}-apple-macosx/debug/"), + isLinux ? + "/usr/lib/swift/linux" : + // assume macOS + "/usr/lib/swift/" + ] + } + +} diff --git a/settings.gradle.kts b/settings.gradle similarity index 66% rename from settings.gradle.kts rename to settings.gradle index 04c6b6ed..7ec81ed0 100644 --- a/settings.gradle.kts +++ b/settings.gradle @@ -16,10 +16,14 @@ pluginManagement{ includeBuild("BuildLogic") } -rootProject.name = "SwiftKit" -include( - // The Swift sources we use in our Demo apps, a "Swift library" - // "SwiftJavaKitExample", // TODO: Gradle doesn't seem to understand Swift 6.0 yet, so we can't do this yet +rootProject.name = "swift-java" + +include "SwiftKit" + +// Include sample apps -- you can run them via `gradle Name:run` +new File(rootDir, "Samples").listFiles().each { + if (it.directory && new File(it, 'build.gradle').exists()) { + include ":Samples:${it.name}" + } +} - "JavaSwiftKitDemo", -)