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..33f8f4694 100644 --- a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -242,30 +242,40 @@ 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 + 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 { + return nil + } + return url.lastPathComponent + }.sorted() + } } 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! + } + """, + ] + ) + } }