From a59515962b48f33c7b72c67ec8154da8aaad0724 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 16 Jan 2026 15:20:07 +0900 Subject: [PATCH 1/2] Correct how we find JavaStdlib/ in swiftpm plugin for wrap-java --- .../SwiftJavaPluginProtocol.swift | 4 ++ Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift | 52 ++++++++++--------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Plugins/PluginsShared/SwiftJavaPluginProtocol.swift b/Plugins/PluginsShared/SwiftJavaPluginProtocol.swift index 57e38ef7a..89aa483e9 100644 --- a/Plugins/PluginsShared/SwiftJavaPluginProtocol.swift +++ b/Plugins/PluginsShared/SwiftJavaPluginProtocol.swift @@ -23,4 +23,8 @@ extension SwiftJavaPluginProtocol { func log(_ message: @autoclosure () -> String, terminator: String = "\n") { print("[\(pluginName)] \(message())", terminator: terminator) } + + func warn(_ message: @autoclosure () -> String, terminator: String = "\n") { + print("[\(pluginName)][warning] \(message())", terminator: terminator) + } } diff --git a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift index d7c773aa3..fb00888c5 100644 --- a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -242,30 +242,34 @@ extension SwiftJavaBuildToolPlugin { func outputFilePath(context: PluginContext, generated: Bool, filename: String) -> URL { outputDirectory(context: context, generated: generated).appending(path: filename) } -} - -func getExtractedJavaStdlibModules() -> [String] { - let fileManager = FileManager.default - let sourcesPath = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appendingPathComponent("Sources") - .appendingPathComponent("JavaStdlib") - - guard let stdlibDirContents = try? fileManager.contentsOfDirectory( - at: sourcesPath, - includingPropertiesForKeys: [.isDirectoryKey], - options: [.skipsHiddenFiles] - ) else { - return [] - } - return stdlibDirContents.compactMap { url in - guard let resourceValues = try? url.resourceValues(forKeys: [.isDirectoryKey]), - let isDirectory = resourceValues.isDirectory, - isDirectory else { - return nil + func getExtractedJavaStdlibModules() -> [String] { + let fileManager = FileManager.default + log("Search for JavaStdlib inside: \(#filePath)") + let sourcesPath = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("Sources") + .appendingPathComponent("JavaStdlib") + + guard let stdlibDirContents = try? fileManager.contentsOfDirectory( + at: sourcesPath, + includingPropertiesForKeys: [.isDirectoryKey], + options: [.skipsHiddenFiles] + ) else { + warn("Unable to find Java standard library Swift wrappers! Expected \(sourcesPath) to exist." + + "May be unable to wrap-java types involving Java standard library types.") + return [] } - return url.lastPathComponent - }.sorted() + + return stdlibDirContents.compactMap { url in + guard let resourceValues = try? url.resourceValues(forKeys: [.isDirectoryKey]), + let isDirectory = resourceValues.isDirectory, + isDirectory else { + return nil + } + return url.lastPathComponent + }.sorted() + } } From a7e706328a43ba771ef3b2c42eb92d4567600f37 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 16 Jan 2026 15:22:06 +0900 Subject: [PATCH 2/2] add a test using wrap-java and a java stdlib type --- Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift | 6 +++ .../Commands/WrapJavaCommand.swift | 2 +- Sources/SwiftJavaTool/SwiftJava.swift | 6 +-- .../WrapJavaTests/BasicWrapJavaTests.swift | 42 +++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift index fb00888c5..33f8f4694 100644 --- a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -264,6 +264,12 @@ extension SwiftJavaBuildToolPlugin { } return stdlibDirContents.compactMap { url in + let swiftJavaConfigURL = url.appending(path: "swift-java.config") + guard fileManager.fileExists(atPath: swiftJavaConfigURL.absoluteString) else { + warn("Expected JavaStdlib directory \(url) to contain swift-java.config but it was missing!") + return nil + } + guard let resourceValues = try? url.resourceValues(forKeys: [.isDirectoryKey]), let isDirectory = resourceValues.isDirectory, isDirectory else { diff --git a/Sources/SwiftJavaTool/Commands/WrapJavaCommand.swift b/Sources/SwiftJavaTool/Commands/WrapJavaCommand.swift index 3e4e482b3..78b095acd 100644 --- a/Sources/SwiftJavaTool/Commands/WrapJavaCommand.swift +++ b/Sources/SwiftJavaTool/Commands/WrapJavaCommand.swift @@ -86,7 +86,7 @@ extension SwiftJava.WrapJavaCommand { // Load all of the dependent configurations and associate them with Swift modules. let dependentConfigs = try loadDependentConfigs(dependsOn: self.dependsOn).map { moduleName, config in guard let moduleName else { - throw JavaToSwiftError.badConfigOption + throw JavaToSwiftError.badConfigOption(self.dependsOn.joined(separator: " ")) } return (moduleName, config) } diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index ac5182a07..fab4f20b4 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -88,14 +88,14 @@ struct SwiftJava: AsyncParsableCommand { } enum JavaToSwiftError: Error { - case badConfigOption + case badConfigOption(String) } extension JavaToSwiftError: CustomStringConvertible { var description: String { switch self { - case .badConfigOption: - "configuration option must be of the form '=" + case .badConfigOption(let string): + "configuration option must be of the form '=. Was: \(string)" } } } diff --git a/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift b/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift index 9e167cf44..faf8fa239 100644 --- a/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift +++ b/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift @@ -198,4 +198,46 @@ final class BasicWrapJavaTests: XCTestCase { ] ) } + + func test_wrapJava_inheritFromBiFunction() async throws { + let classpathURL = try await compileJava( + """ + package com.example; + + import java.util.function.BiFunction; + + interface CallMe extends BiFunction { + @Override + ValueType apply( + ValueType newest, + ValueType oldest + ); + } + """) + + try assertWrapJavaOutput( + javaClassNames: [ + "java.util.function.BiFunction", + "com.example.CallMe", + ], + classpath: [classpathURL], + expectedChunks: [ + """ + @JavaInterface("com.example.CallMe", extends: BiFunction.self) + public struct CallMe { + /** + * Java method `apply`. + * + * ### Java method signature + * ```java + * public abstract ValueType com.example.CallMe.apply(ValueType,ValueType) + * ``` + */ + @JavaMethod(typeErasedResult: "ValueType!") + public func apply(_ arg0: ValueType?, _ arg1: ValueType?) -> ValueType! + } + """, + ] + ) + } }