From 50350bb4785e321e7a113b15b9361bd38e5368c9 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 5 Jun 2025 14:59:28 +0900 Subject: [PATCH 1/8] rename Java2SwiftLib to SwiftJavaLib --- Package.swift | 16 ++++++++-------- .../JavaClassTranslator.swift | 0 .../JavaTranslator+Configuration.swift | 0 .../JavaTranslator+Validation.swift | 0 .../JavaTranslator.swift | 0 .../MethodVariance.swift | 0 .../OptionalKind.swift | 0 .../StringExtras.swift | 0 .../TranslationError.swift | 0 .../String+Extensions.swift | 4 ++-- .../SwiftJava+EmitConfiguration.swift} | 5 ++--- .../SwiftJava+FetchDependencies.swift} | 6 +++--- .../SwiftJava+GenerateWrappers.swift} | 6 +++--- .../SwiftJava.swift} | 8 ++++---- .../Java2SwiftTests.swift | 2 +- .../JavaTranslatorValidationTests.swift | 2 +- 16 files changed, 24 insertions(+), 25 deletions(-) rename Sources/{Java2SwiftLib => SwiftJavaLib}/JavaClassTranslator.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/JavaTranslator+Configuration.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/JavaTranslator+Validation.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/JavaTranslator.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/MethodVariance.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/OptionalKind.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/StringExtras.swift (100%) rename Sources/{Java2SwiftLib => SwiftJavaLib}/TranslationError.swift (100%) rename Sources/{Java2Swift => SwiftJavaTool}/String+Extensions.swift (97%) rename Sources/{Java2Swift/JavaToSwift+EmitConfiguration.swift => SwiftJavaTool/SwiftJava+EmitConfiguration.swift} (98%) rename Sources/{Java2Swift/JavaToSwift+FetchDependencies.swift => SwiftJavaTool/SwiftJava+FetchDependencies.swift} (99%) rename Sources/{Java2Swift/JavaToSwift+GenerateWrappers.swift => SwiftJavaTool/SwiftJava+GenerateWrappers.swift} (98%) rename Sources/{Java2Swift/JavaToSwift.swift => SwiftJavaTool/SwiftJava.swift} (99%) rename Tests/{Java2SwiftTests => SwiftJavaTests}/Java2SwiftTests.swift (99%) rename Tests/{Java2SwiftTests => SwiftJavaTests}/JavaTranslatorValidationTests.swift (98%) diff --git a/Package.swift b/Package.swift index 42b74041..05cd4309 100644 --- a/Package.swift +++ b/Package.swift @@ -92,8 +92,8 @@ let package = Package( ), .executable( - name: "Java2Swift", - targets: ["Java2Swift"] + name: "swift-java", + targets: ["SwiftJavaTool"] ), // ==== Plugin for building Java code @@ -274,7 +274,7 @@ let package = Package( name: "Java2SwiftPlugin", capability: .buildTool(), dependencies: [ - "Java2Swift" + "SwiftJavaTool" ] ), @@ -312,7 +312,7 @@ let package = Package( ), .target( - name: "Java2SwiftLib", + name: "SwiftJavaLib", dependencies: [ .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntax", package: "swift-syntax"), @@ -334,7 +334,7 @@ let package = Package( ), .executableTarget( - name: "Java2Swift", + name: "SwiftJavaTool", dependencies: [ .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntax", package: "swift-syntax"), @@ -343,7 +343,7 @@ let package = Package( "JavaKit", "JavaKitJar", "JavaKitNetwork", - "Java2SwiftLib", + "SwiftJavaLib", "JavaKitShared", ], @@ -427,8 +427,8 @@ let package = Package( ), .testTarget( - name: "Java2SwiftTests", - dependencies: ["Java2SwiftLib"], + name: "SwiftJavaTests", + dependencies: ["SwiftJavaLib"], swiftSettings: [ .swiftLanguageMode(.v5), .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) diff --git a/Sources/Java2SwiftLib/JavaClassTranslator.swift b/Sources/SwiftJavaLib/JavaClassTranslator.swift similarity index 100% rename from Sources/Java2SwiftLib/JavaClassTranslator.swift rename to Sources/SwiftJavaLib/JavaClassTranslator.swift diff --git a/Sources/Java2SwiftLib/JavaTranslator+Configuration.swift b/Sources/SwiftJavaLib/JavaTranslator+Configuration.swift similarity index 100% rename from Sources/Java2SwiftLib/JavaTranslator+Configuration.swift rename to Sources/SwiftJavaLib/JavaTranslator+Configuration.swift diff --git a/Sources/Java2SwiftLib/JavaTranslator+Validation.swift b/Sources/SwiftJavaLib/JavaTranslator+Validation.swift similarity index 100% rename from Sources/Java2SwiftLib/JavaTranslator+Validation.swift rename to Sources/SwiftJavaLib/JavaTranslator+Validation.swift diff --git a/Sources/Java2SwiftLib/JavaTranslator.swift b/Sources/SwiftJavaLib/JavaTranslator.swift similarity index 100% rename from Sources/Java2SwiftLib/JavaTranslator.swift rename to Sources/SwiftJavaLib/JavaTranslator.swift diff --git a/Sources/Java2SwiftLib/MethodVariance.swift b/Sources/SwiftJavaLib/MethodVariance.swift similarity index 100% rename from Sources/Java2SwiftLib/MethodVariance.swift rename to Sources/SwiftJavaLib/MethodVariance.swift diff --git a/Sources/Java2SwiftLib/OptionalKind.swift b/Sources/SwiftJavaLib/OptionalKind.swift similarity index 100% rename from Sources/Java2SwiftLib/OptionalKind.swift rename to Sources/SwiftJavaLib/OptionalKind.swift diff --git a/Sources/Java2SwiftLib/StringExtras.swift b/Sources/SwiftJavaLib/StringExtras.swift similarity index 100% rename from Sources/Java2SwiftLib/StringExtras.swift rename to Sources/SwiftJavaLib/StringExtras.swift diff --git a/Sources/Java2SwiftLib/TranslationError.swift b/Sources/SwiftJavaLib/TranslationError.swift similarity index 100% rename from Sources/Java2SwiftLib/TranslationError.swift rename to Sources/SwiftJavaLib/TranslationError.swift diff --git a/Sources/Java2Swift/String+Extensions.swift b/Sources/SwiftJavaTool/String+Extensions.swift similarity index 97% rename from Sources/Java2Swift/String+Extensions.swift rename to Sources/SwiftJavaTool/String+Extensions.swift index 26a20241..f2bb9e72 100644 --- a/Sources/Java2Swift/String+Extensions.swift +++ b/Sources/SwiftJavaTool/String+Extensions.swift @@ -14,10 +14,10 @@ import Foundation import ArgumentParser -import Java2SwiftLib +import SwiftJavaLib import JavaKit import JavaKitJar -import Java2SwiftLib +import SwiftJavaLib import JavaKitConfigurationShared extension String { diff --git a/Sources/Java2Swift/JavaToSwift+EmitConfiguration.swift b/Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift similarity index 98% rename from Sources/Java2Swift/JavaToSwift+EmitConfiguration.swift rename to Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift index 6754d381..e029d2db 100644 --- a/Sources/Java2Swift/JavaToSwift+EmitConfiguration.swift +++ b/Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift @@ -14,13 +14,12 @@ import Foundation import ArgumentParser -import Java2SwiftLib +import SwiftJavaLib import JavaKit import JavaKitJar -import Java2SwiftLib import JavaKitConfigurationShared -extension JavaToSwift { +extension SwiftJava { // TODO: make this perhaps "emit type mappings" mutating func emitConfiguration( diff --git a/Sources/Java2Swift/JavaToSwift+FetchDependencies.swift b/Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift similarity index 99% rename from Sources/Java2Swift/JavaToSwift+FetchDependencies.swift rename to Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift index 2a9694c0..47570b19 100644 --- a/Sources/Java2Swift/JavaToSwift+FetchDependencies.swift +++ b/Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift @@ -13,16 +13,16 @@ //===----------------------------------------------------------------------===// import Foundation -import Java2SwiftLib +import SwiftJavaLib import JavaKit import Foundation import JavaKitJar -import Java2SwiftLib +import SwiftJavaLib import JavaKitConfigurationShared import JavaKitShared import _Subprocess -extension JavaToSwift { +extension SwiftJava { var SwiftJavaClasspathPrefix: String { "SWIFT_JAVA_CLASSPATH:" } diff --git a/Sources/Java2Swift/JavaToSwift+GenerateWrappers.swift b/Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift similarity index 98% rename from Sources/Java2Swift/JavaToSwift+GenerateWrappers.swift rename to Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift index 67aa3f9c..a57644de 100644 --- a/Sources/Java2Swift/JavaToSwift+GenerateWrappers.swift +++ b/Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift @@ -14,13 +14,13 @@ import Foundation import ArgumentParser -import Java2SwiftLib +import SwiftJavaLib import JavaKit import JavaKitJar -import Java2SwiftLib +import SwiftJavaLib import JavaKitConfigurationShared -extension JavaToSwift { +extension SwiftJava { mutating func generateWrappers( config: Configuration, classpath: String, diff --git a/Sources/Java2Swift/JavaToSwift.swift b/Sources/SwiftJavaTool/SwiftJava.swift similarity index 99% rename from Sources/Java2Swift/JavaToSwift.swift rename to Sources/SwiftJavaTool/SwiftJava.swift index 536b3fd1..c8be85a5 100644 --- a/Sources/Java2Swift/JavaToSwift.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -14,7 +14,7 @@ import ArgumentParser import Foundation -import Java2SwiftLib +import SwiftJavaLib import JavaKit import JavaKitJar import JavaKitNetwork @@ -26,8 +26,8 @@ import JavaKitShared /// Command-line utility to drive the export of Java classes into Swift types. @main -struct JavaToSwift: AsyncParsableCommand { - static var _commandName: String { "Java2Swift" } +struct SwiftJava: AsyncParsableCommand { + static var _commandName: String { "swift-java" } @Option(help: "The name of the Swift module into which the resulting Swift types will be generated.") var moduleName: String? @@ -382,7 +382,7 @@ struct JavaToSwift: AsyncParsableCommand { } } -extension JavaToSwift { +extension SwiftJava { /// Get base configuration, depending on if we are to 'amend' or 'overwrite' the existing configuration. package func getBaseConfigurationForWrite() throws -> (Bool, Configuration) { guard let actualOutputDirectory = self.actualOutputDirectory else { diff --git a/Tests/Java2SwiftTests/Java2SwiftTests.swift b/Tests/SwiftJavaTests/Java2SwiftTests.swift similarity index 99% rename from Tests/Java2SwiftTests/Java2SwiftTests.swift rename to Tests/SwiftJavaTests/Java2SwiftTests.swift index 48440522..e2b68a34 100644 --- a/Tests/Java2SwiftTests/Java2SwiftTests.swift +++ b/Tests/SwiftJavaTests/Java2SwiftTests.swift @@ -14,7 +14,7 @@ @_spi(Testing) import JavaKit -import Java2SwiftLib +import SwiftJavaLib import XCTest // NOTE: Workaround for https://github.com/swiftlang/swift-java/issues/43 /// Handy reference to the JVM abstraction. diff --git a/Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift b/Tests/SwiftJavaTests/JavaTranslatorValidationTests.swift similarity index 98% rename from Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift rename to Tests/SwiftJavaTests/JavaTranslatorValidationTests.swift index e5c3a951..a203486a 100644 --- a/Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift +++ b/Tests/SwiftJavaTests/JavaTranslatorValidationTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import Java2SwiftLib +import SwiftJavaLib import XCTest final class JavaTranslatorValidationTests: XCTestCase { From 929c6c45f7a62b1f999ff90baf5915518b7d6e6c Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 5 Jun 2025 17:15:57 +0900 Subject: [PATCH 2/8] rename swift2java plugin to SwiftJavaPlugin --- Package.swift | 6 +++--- .../SwiftJavaPlugin.swift} | 6 +++--- .../{Java2SwiftPlugin => SwiftJavaPlugin}/_PluginsShared | 0 Samples/JavaDependencySampleApp/Package.swift | 4 ++-- Samples/JavaKitSampleApp/Package.swift | 2 +- Samples/JavaProbablyPrime/Package.swift | 2 +- Samples/JavaSieve/Package.swift | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) rename Plugins/{Java2SwiftPlugin/Java2SwiftPlugin.swift => SwiftJavaPlugin/SwiftJavaPlugin.swift} (97%) rename Plugins/{Java2SwiftPlugin => SwiftJavaPlugin}/_PluginsShared (100%) diff --git a/Package.swift b/Package.swift index 05cd4309..48abbdb8 100644 --- a/Package.swift +++ b/Package.swift @@ -106,9 +106,9 @@ let package = Package( // ==== Plugin for wrapping Java classes in Swift .plugin( - name: "Java2SwiftPlugin", + name: "SwiftJavaPlugin", targets: [ - "Java2SwiftPlugin" + "SwiftJavaPlugin" ] ), @@ -271,7 +271,7 @@ let package = Package( ), .plugin( - name: "Java2SwiftPlugin", + name: "SwiftJavaPlugin", capability: .buildTool(), dependencies: [ "SwiftJavaTool" diff --git a/Plugins/Java2SwiftPlugin/Java2SwiftPlugin.swift b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift similarity index 97% rename from Plugins/Java2SwiftPlugin/Java2SwiftPlugin.swift rename to Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift index 88507f2e..05e6db0d 100644 --- a/Plugins/Java2SwiftPlugin/Java2SwiftPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -18,7 +18,7 @@ import PackagePlugin fileprivate let SwiftJavaConfigFileName = "swift-java.config" @main -struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { +struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { var pluginName: String = "swift-java" var verbose: Bool = getEnvironmentBool("SWIFT_JAVA_VERBOSE") @@ -27,7 +27,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { log("Create build commands for target '\(target.name)'") guard let sourceModule = target.sourceModule else { return [] } - let executable = try context.tool(named: "Java2Swift").url + let executable = try context.tool(named: "SwiftJavaTool").url var commands: [Command] = [] // Note: Target doesn't have a directoryURL counterpart to directory, @@ -199,7 +199,7 @@ struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { } } -extension Java2SwiftBuildToolPlugin { +extension SwiftJavaBuildToolPlugin { func argumentsModuleName(sourceModule: Target) -> [String] { return [ "--module-name", sourceModule.name diff --git a/Plugins/Java2SwiftPlugin/_PluginsShared b/Plugins/SwiftJavaPlugin/_PluginsShared similarity index 100% rename from Plugins/Java2SwiftPlugin/_PluginsShared rename to Plugins/SwiftJavaPlugin/_PluginsShared diff --git a/Samples/JavaDependencySampleApp/Package.swift b/Samples/JavaDependencySampleApp/Package.swift index 2b5ae361..b39e7b81 100644 --- a/Samples/JavaDependencySampleApp/Package.swift +++ b/Samples/JavaDependencySampleApp/Package.swift @@ -76,7 +76,7 @@ let package = Package( .swiftLanguageMode(.v5), ], plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), @@ -96,7 +96,7 @@ let package = Package( ], plugins: [ // .plugin(name: "SwiftJavaBootstrapJavaPlugin", package: "swift-java"), - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), diff --git a/Samples/JavaKitSampleApp/Package.swift b/Samples/JavaKitSampleApp/Package.swift index e51867cc..0956290c 100644 --- a/Samples/JavaKitSampleApp/Package.swift +++ b/Samples/JavaKitSampleApp/Package.swift @@ -77,7 +77,7 @@ let package = Package( plugins: [ .plugin(name: "JavaCompilerPlugin", package: "swift-java"), .plugin(name: "JExtractSwiftPlugin", package: "swift-java"), - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), ] diff --git a/Samples/JavaProbablyPrime/Package.swift b/Samples/JavaProbablyPrime/Package.swift index e837bfdb..4cc887f8 100644 --- a/Samples/JavaProbablyPrime/Package.swift +++ b/Samples/JavaProbablyPrime/Package.swift @@ -34,7 +34,7 @@ let package = Package( .swiftLanguageMode(.v5) ], plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), ] diff --git a/Samples/JavaSieve/Package.swift b/Samples/JavaSieve/Package.swift index 35dcd19c..cd65f82e 100644 --- a/Samples/JavaSieve/Package.swift +++ b/Samples/JavaSieve/Package.swift @@ -58,7 +58,7 @@ let package = Package( .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ], plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), @@ -75,7 +75,7 @@ let package = Package( .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ], plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), ] From 67fd870912e4bd7774fa185a8d0d633a81dd8987 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 5 Jun 2025 17:21:08 +0900 Subject: [PATCH 3/8] rename JExtractSwift to JExtractSwiftLib as we'll depend on it in SwiftJava --- Package.swift | 10 +- .../CDeclLowering/CRepresentation.swift | 0 ...wift2JavaTranslator+FunctionLowering.swift | 0 .../CTypes/CEnum.swift | 0 .../CTypes/CFunction.swift | 0 .../CTypes/CParameter.swift | 0 .../CTypes/CStruct.swift | 0 .../CTypes/CTag.swift | 0 .../CTypes/CType.swift | 0 .../CTypes/CUnion.swift | 0 .../CodePrinter.swift | 0 .../Convenience/Collection+Extensions.swift | 0 .../Convenience/String+Extensions.swift | 0 .../Convenience/SwiftSyntax+Extensions.swift | 0 .../ConversionStep.swift | 0 .../ImportedDecls.swift | 0 .../JavaConstants/ForeignValueLayouts.swift | 0 .../JavaConstants/JavaTypes.swift | 0 .../Logger.swift | 0 .../Swift2Java.swift | 0 ...2JavaTranslator+JavaBindingsPrinting.swift | 0 ...Swift2JavaTranslator+JavaTranslation.swift | 0 .../Swift2JavaTranslator+Printing.swift | 0 ...ft2JavaTranslator+SwiftThunkPrinting.swift | 0 .../Swift2JavaTranslator.swift | 0 .../Swift2JavaVisitor.swift | 0 .../SwiftKit+Printing.swift | 0 .../SwiftThunkTranslator.swift | 191 ++++++++++++++++++ .../SwiftTypes/SwiftFunctionSignature.swift | 0 .../SwiftTypes/SwiftFunctionType.swift | 0 .../SwiftTypes/SwiftModuleSymbolTable.swift | 0 .../SwiftNominalTypeDeclaration.swift | 0 .../SwiftTypes/SwiftParameter.swift | 0 .../SwiftParsedModuleSymbolTable.swift | 0 .../SwiftTypes/SwiftResult.swift | 0 .../SwiftStandardLibraryTypes.swift | 0 .../SwiftTypes/SwiftSymbolTable.swift | 0 .../SwiftTypes/SwiftType.swift | 0 .../ThunkNameRegistry.swift | 0 .../Asserts/LoweringAssertions.swift | 2 +- .../Asserts/TextAssertions.swift | 2 +- Tests/JExtractSwiftTests/CTypeTests.swift | 2 +- .../ClassPrintingTests.swift | 2 +- .../FuncCallbackImportTests.swift | 2 +- .../FunctionDescriptorImportTests.swift | 2 +- .../FunctionLoweringTests.swift | 2 +- .../MethodImportTests.swift | 2 +- .../JExtractSwiftTests/MethodThunkTests.swift | 2 +- .../StringPassingTests.swift | 2 +- .../SwiftSymbolTableTests.swift | 2 +- .../VariableImportTests.swift | 2 +- 51 files changed, 208 insertions(+), 17 deletions(-) rename Sources/{JExtractSwift => JExtractSwiftLib}/CDeclLowering/CRepresentation.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CEnum.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CFunction.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CParameter.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CStruct.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CTag.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CType.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CTypes/CUnion.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/CodePrinter.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Convenience/Collection+Extensions.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Convenience/String+Extensions.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Convenience/SwiftSyntax+Extensions.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/ConversionStep.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/ImportedDecls.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/JavaConstants/ForeignValueLayouts.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/JavaConstants/JavaTypes.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Logger.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2Java.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaTranslator+JavaBindingsPrinting.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaTranslator+JavaTranslation.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaTranslator+Printing.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaTranslator+SwiftThunkPrinting.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaTranslator.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/Swift2JavaVisitor.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftKit+Printing.swift (100%) create mode 100644 Sources/JExtractSwiftLib/SwiftThunkTranslator.swift rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftFunctionSignature.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftFunctionType.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftModuleSymbolTable.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftNominalTypeDeclaration.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftParameter.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftParsedModuleSymbolTable.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftResult.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftStandardLibraryTypes.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftSymbolTable.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/SwiftTypes/SwiftType.swift (100%) rename Sources/{JExtractSwift => JExtractSwiftLib}/ThunkNameRegistry.swift (100%) diff --git a/Package.swift b/Package.swift index 48abbdb8..c2580296 100644 --- a/Package.swift +++ b/Package.swift @@ -127,8 +127,8 @@ let package = Package( ), .library( - name: "JExtractSwift", - targets: ["JExtractSwift"] + name: "JExtractSwiftLib", + targets: ["JExtractSwiftLib"] ), // ==== Plugin for wrapping Java classes in Swift @@ -355,7 +355,7 @@ let package = Package( ), .target( - name: "JExtractSwift", + name: "JExtractSwiftLib", dependencies: [ .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntax", package: "swift-syntax"), @@ -373,7 +373,7 @@ let package = Package( .executableTarget( name: "JExtractSwiftTool", dependencies: [ - "JExtractSwift", + "JExtractSwiftLib", ], swiftSettings: [ .swiftLanguageMode(.v5) @@ -438,7 +438,7 @@ let package = Package( .testTarget( name: "JExtractSwiftTests", dependencies: [ - "JExtractSwift" + "JExtractSwiftLib" ], swiftSettings: [ .swiftLanguageMode(.v5), diff --git a/Sources/JExtractSwift/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/CDeclLowering/CRepresentation.swift similarity index 100% rename from Sources/JExtractSwift/CDeclLowering/CRepresentation.swift rename to Sources/JExtractSwiftLib/CDeclLowering/CRepresentation.swift diff --git a/Sources/JExtractSwift/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift b/Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift similarity index 100% rename from Sources/JExtractSwift/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift rename to Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift diff --git a/Sources/JExtractSwift/CTypes/CEnum.swift b/Sources/JExtractSwiftLib/CTypes/CEnum.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CEnum.swift rename to Sources/JExtractSwiftLib/CTypes/CEnum.swift diff --git a/Sources/JExtractSwift/CTypes/CFunction.swift b/Sources/JExtractSwiftLib/CTypes/CFunction.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CFunction.swift rename to Sources/JExtractSwiftLib/CTypes/CFunction.swift diff --git a/Sources/JExtractSwift/CTypes/CParameter.swift b/Sources/JExtractSwiftLib/CTypes/CParameter.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CParameter.swift rename to Sources/JExtractSwiftLib/CTypes/CParameter.swift diff --git a/Sources/JExtractSwift/CTypes/CStruct.swift b/Sources/JExtractSwiftLib/CTypes/CStruct.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CStruct.swift rename to Sources/JExtractSwiftLib/CTypes/CStruct.swift diff --git a/Sources/JExtractSwift/CTypes/CTag.swift b/Sources/JExtractSwiftLib/CTypes/CTag.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CTag.swift rename to Sources/JExtractSwiftLib/CTypes/CTag.swift diff --git a/Sources/JExtractSwift/CTypes/CType.swift b/Sources/JExtractSwiftLib/CTypes/CType.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CType.swift rename to Sources/JExtractSwiftLib/CTypes/CType.swift diff --git a/Sources/JExtractSwift/CTypes/CUnion.swift b/Sources/JExtractSwiftLib/CTypes/CUnion.swift similarity index 100% rename from Sources/JExtractSwift/CTypes/CUnion.swift rename to Sources/JExtractSwiftLib/CTypes/CUnion.swift diff --git a/Sources/JExtractSwift/CodePrinter.swift b/Sources/JExtractSwiftLib/CodePrinter.swift similarity index 100% rename from Sources/JExtractSwift/CodePrinter.swift rename to Sources/JExtractSwiftLib/CodePrinter.swift diff --git a/Sources/JExtractSwift/Convenience/Collection+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/Collection+Extensions.swift similarity index 100% rename from Sources/JExtractSwift/Convenience/Collection+Extensions.swift rename to Sources/JExtractSwiftLib/Convenience/Collection+Extensions.swift diff --git a/Sources/JExtractSwift/Convenience/String+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/String+Extensions.swift similarity index 100% rename from Sources/JExtractSwift/Convenience/String+Extensions.swift rename to Sources/JExtractSwiftLib/Convenience/String+Extensions.swift diff --git a/Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift similarity index 100% rename from Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift rename to Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift diff --git a/Sources/JExtractSwift/ConversionStep.swift b/Sources/JExtractSwiftLib/ConversionStep.swift similarity index 100% rename from Sources/JExtractSwift/ConversionStep.swift rename to Sources/JExtractSwiftLib/ConversionStep.swift diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift similarity index 100% rename from Sources/JExtractSwift/ImportedDecls.swift rename to Sources/JExtractSwiftLib/ImportedDecls.swift diff --git a/Sources/JExtractSwift/JavaConstants/ForeignValueLayouts.swift b/Sources/JExtractSwiftLib/JavaConstants/ForeignValueLayouts.swift similarity index 100% rename from Sources/JExtractSwift/JavaConstants/ForeignValueLayouts.swift rename to Sources/JExtractSwiftLib/JavaConstants/ForeignValueLayouts.swift diff --git a/Sources/JExtractSwift/JavaConstants/JavaTypes.swift b/Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift similarity index 100% rename from Sources/JExtractSwift/JavaConstants/JavaTypes.swift rename to Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift diff --git a/Sources/JExtractSwift/Logger.swift b/Sources/JExtractSwiftLib/Logger.swift similarity index 100% rename from Sources/JExtractSwift/Logger.swift rename to Sources/JExtractSwiftLib/Logger.swift diff --git a/Sources/JExtractSwift/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift similarity index 100% rename from Sources/JExtractSwift/Swift2Java.swift rename to Sources/JExtractSwiftLib/Swift2Java.swift diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift rename to Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaTranslation.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift rename to Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaTranslation.swift diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift rename to Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift rename to Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaTranslator.swift rename to Sources/JExtractSwiftLib/Swift2JavaTranslator.swift diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift similarity index 100% rename from Sources/JExtractSwift/Swift2JavaVisitor.swift rename to Sources/JExtractSwiftLib/Swift2JavaVisitor.swift diff --git a/Sources/JExtractSwift/SwiftKit+Printing.swift b/Sources/JExtractSwiftLib/SwiftKit+Printing.swift similarity index 100% rename from Sources/JExtractSwift/SwiftKit+Printing.swift rename to Sources/JExtractSwiftLib/SwiftKit+Printing.swift diff --git a/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift b/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift new file mode 100644 index 00000000..51547ba8 --- /dev/null +++ b/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift @@ -0,0 +1,191 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxBuilder + +extension Swift2JavaTranslator { + public func writeSwiftThunkSources(outputDirectory: String) throws { + var printer = CodePrinter() + + try writeSwiftThunkSources(outputDirectory: outputDirectory, printer: &printer) + } + + public func writeSwiftThunkSources(outputDirectory: String, printer: inout CodePrinter) throws { + let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" + let moduleFilename = "\(moduleFilenameBase).swift" + do { + log.info("Printing contents: \(moduleFilename)") + + try printGlobalSwiftThunkSources(&printer) + + if let outputFile = try printer.writeContents( + outputDirectory: outputDirectory, + javaPackagePath: nil, + filename: moduleFilename) + { + print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile)") + } + } catch { + log.warning("Failed to write to Swift thunks: \(moduleFilename)") + } + + // === All types + for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { + let fileNameBase = "\(ty.swiftNominal.qualifiedName)+SwiftJava" + let filename = "\(fileNameBase).swift" + log.info("Printing contents: \(filename)") + + do { + try printSwiftThunkSources(&printer, ty: ty) + + if let outputFile = try printer.writeContents( + outputDirectory: outputDirectory, + javaPackagePath: nil, + filename: filename) + { + print("[swift-java] Generated: \(fileNameBase.bold).swift (at \(outputFile)") + } + } catch { + log.warning("Failed to write to Swift thunks: \(filename)") + } + } + } + + public func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { + let stt = SwiftThunkTranslator(self) + + printer.print( + """ + // Generated by swift-java + + import SwiftKitSwift + + """) + + for thunk in stt.renderGlobalThunks() { + printer.print(thunk) + printer.println() + } + } + + public func printSwiftThunkSources(_ printer: inout CodePrinter, decl: ImportedFunc) { + let stt = SwiftThunkTranslator(self) + + for thunk in stt.render(forFunc: decl) { + printer.print(thunk) + printer.println() + } + } + + package func printSwiftThunkSources(_ printer: inout CodePrinter, ty: ImportedNominalType) throws { + let stt = SwiftThunkTranslator(self) + + printer.print( + """ + // Generated by swift-java + + import SwiftKitSwift + + """ + ) + + for thunk in stt.renderThunks(forType: ty) { + printer.print("\(thunk)") + printer.print("") + } + } +} + +struct SwiftThunkTranslator { + + let st: Swift2JavaTranslator + + init(_ st: Swift2JavaTranslator) { + self.st = st + } + + func renderGlobalThunks() -> [DeclSyntax] { + var decls: [DeclSyntax] = [] + decls.reserveCapacity( + st.importedGlobalVariables.count + st.importedGlobalFuncs.count + ) + + for decl in st.importedGlobalVariables { + decls.append(contentsOf: render(forFunc: decl)) + } + + for decl in st.importedGlobalFuncs { + decls.append(contentsOf: render(forFunc: decl)) + } + + return decls + } + + /// Render all the thunks that make Swift methods accessible to Java. + func renderThunks(forType nominal: ImportedNominalType) -> [DeclSyntax] { + var decls: [DeclSyntax] = [] + decls.reserveCapacity( + 1 + nominal.initializers.count + nominal.variables.count + nominal.methods.count + ) + + decls.append(renderSwiftTypeAccessor(nominal)) + + for decl in nominal.initializers { + decls.append(contentsOf: render(forFunc: decl)) + } + + for decl in nominal.variables { + decls.append(contentsOf: render(forFunc: decl)) + } + + for decl in nominal.methods { + decls.append(contentsOf: render(forFunc: decl)) + } + + return decls + } + + /// Accessor to get the `T.self` of the Swift type, without having to rely on mangled name lookups. + func renderSwiftTypeAccessor(_ nominal: ImportedNominalType) -> DeclSyntax { + let funcName = SwiftKitPrinting.Names.getType( + module: st.swiftModuleName, + nominal: nominal) + + return + """ + @_cdecl("\(raw: funcName)") + public func \(raw: funcName)() -> UnsafeMutableRawPointer /* Any.Type */ { + return unsafeBitCast(\(raw: nominal.swiftNominal.qualifiedName).self, to: UnsafeMutableRawPointer.self) + } + """ + } + + func render(forFunc decl: ImportedFunc) -> [DeclSyntax] { + st.log.trace("Rendering thunks for: \(decl.displayName)") + + let thunkName = st.thunkNameRegistry.functionThunkName(decl: decl) + guard let translatedSignatures = st.translatedSignature(for: decl) else { + return [] + } + + let thunkFunc = translatedSignatures.loweredSignature.cdeclThunk( + cName: thunkName, + swiftAPIName: decl.name, + as: decl.apiKind, + stdlibTypes: st.swiftStdlibTypes + ) + return [DeclSyntax(thunkFunc)] + } +} diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftFunctionSignature.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftFunctionSignature.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftFunctionType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionType.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftFunctionType.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionType.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftModuleSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftModuleSymbolTable.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftNominalTypeDeclaration.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftParameter.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParameter.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftParameter.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftParameter.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftParsedModuleSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTable.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftParsedModuleSymbolTable.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTable.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftResult.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftResult.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftResult.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftResult.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftStandardLibraryTypes.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypes.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftStandardLibraryTypes.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypes.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftSymbolTable.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift diff --git a/Sources/JExtractSwift/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift similarity index 100% rename from Sources/JExtractSwift/SwiftTypes/SwiftType.swift rename to Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift diff --git a/Sources/JExtractSwift/ThunkNameRegistry.swift b/Sources/JExtractSwiftLib/ThunkNameRegistry.swift similarity index 100% rename from Sources/JExtractSwift/ThunkNameRegistry.swift rename to Sources/JExtractSwiftLib/ThunkNameRegistry.swift diff --git a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift index cbdcc6dd..b306689d 100644 --- a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -@_spi(Testing) import JExtractSwift +@_spi(Testing) import JExtractSwiftLib import SwiftSyntax import Testing diff --git a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift index 1686f0ab..3923cf3e 100644 --- a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing import struct Foundation.CharacterSet diff --git a/Tests/JExtractSwiftTests/CTypeTests.swift b/Tests/JExtractSwiftTests/CTypeTests.swift index 569296d6..a2339e34 100644 --- a/Tests/JExtractSwiftTests/CTypeTests.swift +++ b/Tests/JExtractSwiftTests/CTypeTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing @Suite("C type system tests") diff --git a/Tests/JExtractSwiftTests/ClassPrintingTests.swift b/Tests/JExtractSwiftTests/ClassPrintingTests.swift index 94cd8a40..1edcdaf0 100644 --- a/Tests/JExtractSwiftTests/ClassPrintingTests.swift +++ b/Tests/JExtractSwiftTests/ClassPrintingTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing struct ClassPrintingTests { diff --git a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift index 50022c55..8b401ce3 100644 --- a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing final class FuncCallbackImportTests { diff --git a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift index 9cdcdf58..2963ce9e 100644 --- a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift +++ b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing @Suite diff --git a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift index dce594ae..b7855e43 100644 --- a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift +++ b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import SwiftSyntax import Testing diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index 25c028ce..48515ff2 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing final class MethodImportTests { diff --git a/Tests/JExtractSwiftTests/MethodThunkTests.swift b/Tests/JExtractSwiftTests/MethodThunkTests.swift index d57e449e..f6f5ce71 100644 --- a/Tests/JExtractSwiftTests/MethodThunkTests.swift +++ b/Tests/JExtractSwiftTests/MethodThunkTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing final class MethodThunkTests { diff --git a/Tests/JExtractSwiftTests/StringPassingTests.swift b/Tests/JExtractSwiftTests/StringPassingTests.swift index 08190399..ae131b94 100644 --- a/Tests/JExtractSwiftTests/StringPassingTests.swift +++ b/Tests/JExtractSwiftTests/StringPassingTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing final class StringPassingTests { diff --git a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift index 5d1e5e2b..2bbaa913 100644 --- a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift +++ b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -@_spi(Testing) import JExtractSwift +@_spi(Testing) import JExtractSwiftLib import SwiftSyntax import SwiftParser import Testing diff --git a/Tests/JExtractSwiftTests/VariableImportTests.swift b/Tests/JExtractSwiftTests/VariableImportTests.swift index 55724293..9d8650b4 100644 --- a/Tests/JExtractSwiftTests/VariableImportTests.swift +++ b/Tests/JExtractSwiftTests/VariableImportTests.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib import Testing final class VariableImportTests { From 1c927ce551d727dbf11df952c2d6dd28d545aff2 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 5 Jun 2025 17:24:42 +0900 Subject: [PATCH 4/8] remove JExtractSwiftTool and move it into SwiftJava tool --- Package.swift | 17 ----------------- .../JExtractSwiftTool.swift | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) rename Sources/{JExtractSwiftTool => SwiftJavaTool}/JExtractSwiftTool.swift (96%) diff --git a/Package.swift b/Package.swift index c2580296..21c71671 100644 --- a/Package.swift +++ b/Package.swift @@ -112,13 +112,6 @@ let package = Package( ] ), - // ==== 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", @@ -370,16 +363,6 @@ let package = Package( ] ), - .executableTarget( - name: "JExtractSwiftTool", - dependencies: [ - "JExtractSwiftLib", - ], - swiftSettings: [ - .swiftLanguageMode(.v5) - ] - ), - .plugin( name: "JExtractSwiftPlugin", capability: .buildTool(), diff --git a/Sources/JExtractSwiftTool/JExtractSwiftTool.swift b/Sources/SwiftJavaTool/JExtractSwiftTool.swift similarity index 96% rename from Sources/JExtractSwiftTool/JExtractSwiftTool.swift rename to Sources/SwiftJavaTool/JExtractSwiftTool.swift index f219cc8c..615ad4c5 100644 --- a/Sources/JExtractSwiftTool/JExtractSwiftTool.swift +++ b/Sources/SwiftJavaTool/JExtractSwiftTool.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import JExtractSwift +import JExtractSwiftLib @main struct JExtractSwift { From 9185bfed4250d8de9d5d767b2bf4f1fcce13c266 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 5 Jun 2025 17:45:21 +0900 Subject: [PATCH 5/8] WIP start introducing jextract mode in swift-java tool --- Package.swift | 5 ++- .../JExtractSwiftCommandPlugin.swift | 2 +- .../JExtractSwiftPlugin.swift | 2 +- .../Configuration.swift | 4 ++ ...iftTool.swift => SwiftJava+JExtract.swift} | 27 +++++++++--- Sources/SwiftJavaTool/SwiftJava.swift | 43 ++++++++++++++++--- 6 files changed, 68 insertions(+), 15 deletions(-) rename Sources/SwiftJavaTool/{JExtractSwiftTool.swift => SwiftJava+JExtract.swift} (50%) diff --git a/Package.swift b/Package.swift index 21c71671..dc5dc974 100644 --- a/Package.swift +++ b/Package.swift @@ -337,6 +337,7 @@ let package = Package( "JavaKitJar", "JavaKitNetwork", "SwiftJavaLib", + "JExtractSwiftLib", "JavaKitShared", ], @@ -367,7 +368,7 @@ let package = Package( name: "JExtractSwiftPlugin", capability: .buildTool(), dependencies: [ - "JExtractSwiftTool" + "SwiftJavaTool" ] ), .plugin( @@ -377,7 +378,7 @@ let package = Package( permissions: [ ]), dependencies: [ - "JExtractSwiftTool" + "SwiftJavaTool" ] ), diff --git a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift index d6cfb7cb..1eed7bd3 100644 --- a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift +++ b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift @@ -130,7 +130,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin func runExtract(context: PluginContext, target: Target, arguments: [String]) throws { let process = Process() - process.executableURL = try context.tool(named: "JExtractSwiftTool").url + process.executableURL = try context.tool(named: "SwiftJavaTool").url process.arguments = arguments do { diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index f4255ff5..30f8019b 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -22,7 +22,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { var verbose: Bool = getEnvironmentBool("SWIFT_JAVA_VERBOSE") func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { - let toolURL = try context.tool(named: "JExtractSwiftTool").url + let toolURL = try context.tool(named: "SwiftJavaTool").url guard let sourceModule = target.sourceModule else { return [] } diff --git a/Sources/JavaKitConfigurationShared/Configuration.swift b/Sources/JavaKitConfigurationShared/Configuration.swift index 13c7fd9b..21d0b03b 100644 --- a/Sources/JavaKitConfigurationShared/Configuration.swift +++ b/Sources/JavaKitConfigurationShared/Configuration.swift @@ -27,6 +27,10 @@ public struct Configuration: Codable { public var javaPackage: String? + public var inputSwift: String? + public var outputSwift: String? + public var outputJava: String? + // ==== java 2 swift --------------------------------------------------------- /// The Java class path that should be passed along to the Java2Swift tool. diff --git a/Sources/SwiftJavaTool/JExtractSwiftTool.swift b/Sources/SwiftJavaTool/SwiftJava+JExtract.swift similarity index 50% rename from Sources/SwiftJavaTool/JExtractSwiftTool.swift rename to Sources/SwiftJavaTool/SwiftJava+JExtract.swift index 615ad4c5..7cb15c8e 100644 --- a/Sources/SwiftJavaTool/JExtractSwiftTool.swift +++ b/Sources/SwiftJavaTool/SwiftJava+JExtract.swift @@ -12,12 +12,29 @@ // //===----------------------------------------------------------------------===// +import Foundation +import ArgumentParser +import SwiftJavaLib +import JavaKit +import JavaKitJar +import SwiftJavaLib import JExtractSwiftLib +import JavaKitConfigurationShared + +/// Extract Java bindings from Swift sources or interface files. +/// +/// Example usage: +/// ``` +/// > swift-java --input-swift Sources/SwiftyBusiness \ +/// --output-swift .build/.../outputs/SwiftyBusiness \ +/// --output-Java .build/.../outputs/Java +/// ``` +extension SwiftJava { + + mutating func jextractSwift( + config: Configuration + ) throws { -@main -struct JExtractSwift { - static func main() throws { - let command = SwiftToJava.parseOrExit(CommandLine.arguments) - try command.run() } + } diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index c8be85a5..a877575b 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -59,10 +59,19 @@ struct SwiftJava: AsyncParsableCommand { ) var swiftNativeImplementation: [String] = [] - @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the Java2Swift configuration file.") + @Option(name: .shortAndLong, help: "Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift.") + var inputSwift: String? = nil + + @Option(name: .shortAndLong, help: "The directory where generated Swift files should be written. Generally used with jextract mode.") + var outputSwift: String? = nil + + @Option(name: .shortAndLong, help: "The directory where generated Java files should be written. Generally used with jextract mode.") + var outputJava: String? = nil + + // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) + @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") var outputDirectory: String? = nil - @Option(name: .shortAndLong, help: "Directory where to write cached values (e.g. swift-java.classpath files)") var cacheDirectory: String? = nil @@ -87,8 +96,7 @@ struct SwiftJava: AsyncParsableCommand { var javaPackageFilter: String? = nil @Argument( - help: - "The input file, which is either a Java2Swift configuration file or (if '-jar' was specified) a Jar file." + help: "The input file, which is either a Java2Swift configuration file or (if '-jar' was specified) a Jar file." ) var input: String @@ -163,6 +171,9 @@ struct SwiftJava: AsyncParsableCommand { /// Fetch dependencies for a module case fetchDependencies + + /// Extract Java bindings from provided Swift sources. + case jextract // TODO: carry jextract specific config here? } mutating func run() async { @@ -172,7 +183,24 @@ struct SwiftJava: AsyncParsableCommand { // Determine the mode in which we'll execute. let toolMode: ToolMode - if jar { + // TODO: some options are exclusive to eachother so we should detect that + if let inputSwift { + guard let outputSwift else { + print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-swift directory was provided!") + return + } + guard let outputJava else { + print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-java directory was provided!") + return + } + var c = Configuration() + c.inputSwift = inputSwift + c.outputSwift = outputSwift + c.outputJava = outputJava + config = c + + toolMode = .jextract + } else if jar { if let moduleBaseDir { config = try readConfiguration(sourceDir: moduleBaseDir.path) } else { @@ -251,7 +279,7 @@ struct SwiftJava: AsyncParsableCommand { // print("[debug][swift-java] Found cached dependency resolver classpath: \(dependencyResolverClasspath)") // classpathEntries += dependencyResolverClasspath // } - case .classWrappers: + case .classWrappers, .jextract: break; } @@ -310,6 +338,9 @@ struct SwiftJava: AsyncParsableCommand { moduleName: moduleName, cacheDir: effectiveCacheDirectory, resolvedClasspath: dependencyClasspath) + + case .jextract: + try jextractSwift(config: config) } } catch { // We fail like this since throwing out of the run often ends up hiding the failure reason when it is executed as SwiftPM plugin (!) From 668cd4bfdc7f1ffede411bc52d23eb90b6e33b57 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Fri, 6 Jun 2025 13:47:03 +0900 Subject: [PATCH 6/8] [Package.swift] improve detecting JAVA_HOME on macos by using java_home --- Package.swift | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index dc5dc974..16c8a1a3 100644 --- a/Package.swift +++ b/Package.swift @@ -4,8 +4,7 @@ import CompilerPluginSupport import PackageDescription -import class Foundation.FileManager -import class Foundation.ProcessInfo +import Foundation // Note: the JAVA_HOME environment variable must be set to point to where // Java is installed, e.g., @@ -25,9 +24,53 @@ func findJavaHome() -> String { return home } + + if let home = getJavaHomeFromLibexecJavaHome(), + !home.isEmpty { + return home + } fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.") } + +/// On MacOS we can use the java_home tool as a fallback if we can't find JAVA_HOME environment variable. +func getJavaHomeFromLibexecJavaHome() -> String? { + let task = Process() + task.executableURL = URL(fileURLWithPath: "/usr/libexec/java_home") + + // Check if the executable exists before trying to run it + guard FileManager.default.fileExists(atPath: task.executableURL!.path) else { + print("/usr/libexec/java_home does not exist") + return nil + } + + let pipe = Pipe() + task.standardOutput = pipe + task.standardError = pipe // Redirect standard error to the same pipe for simplicity + + do { + try task.run() + task.waitUntilExit() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) + + if task.terminationStatus == 0 { + return output + } else { + print("java_home terminated with status: \(task.terminationStatus)") + // Optionally, log the error output for debugging + if let errorOutput = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) { + print("Error output: \(errorOutput)") + } + return nil + } + } catch { + print("Error running java_home: \(error)") + return nil + } +} + let javaHome = findJavaHome() let javaIncludePath = "\(javaHome)/include" From 772c21af2ffe9b763a5603bbe3c23a832971aa54 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Fri, 6 Jun 2025 16:46:54 +0900 Subject: [PATCH 7/8] Final cleanups connectign jextract to the swift-java tools --- .../JExtractSwiftCommandPlugin.swift | 16 +-- .../JExtractSwiftPlugin.swift | 19 ++-- Plugins/PluginsShared/PluginUtils.swift | 4 +- Sources/JExtractSwiftLib/Logger.swift | 46 ++------ Sources/JExtractSwiftLib/Swift2Java.swift | 85 +++++++------- .../Configuration.swift | 59 +++++++++- .../SwiftJavaTool/SwiftJava+JExtract.swift | 2 +- Sources/SwiftJavaTool/SwiftJava.swift | 104 +++++++++++++----- 8 files changed, 198 insertions(+), 137 deletions(-) diff --git a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift index 1eed7bd3..01201de6 100644 --- a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift +++ b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift @@ -69,21 +69,21 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin let sourceDir = target.directory.string let configuration = try readConfiguration(sourceDir: "\(sourceDir)") - guard let javaPackage = configuration.javaPackage else { - throw SwiftJavaPluginError.missingConfiguration(sourceDir: "\(sourceDir)", key: "javaPackage") - } var arguments: [String] = [ - "--swift-module", sourceModule.name, - "--package-name", javaPackage, - "--output-directory-java", context.outputDirectoryJava.path(percentEncoded: false), - "--output-directory-swift", context.outputDirectorySwift.path(percentEncoded: false), + "--input-swift", sourceDir, + "--module-name", sourceModule.name, + "--output-java", context.outputJavaDirectory.path(percentEncoded: false), + "--output-swift", context.outputSwiftDirectory.path(percentEncoded: false), // TODO: "--build-cache-directory", ... // Since plugins cannot depend on libraries we cannot detect what the output files will be, // as it depends on the contents of the input files. Therefore we have to implement this as a prebuild plugin. // We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc. ] - arguments.append(sourceDir) + // arguments.append(sourceDir) // TODO: we could do this shape maybe? to have the dirs last? + if let package = configuration.javaPackage, !package.isEmpty { + ["--java-package", package] + } return arguments } diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index 30f8019b..8b0c7d9d 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -50,20 +50,23 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { // We use the the usual maven-style structure of "src/[generated|main|test]/java/..." // that is common in JVM ecosystem - let outputDirectoryJava = context.outputDirectoryJava - let outputDirectorySwift = context.outputDirectorySwift + let outputJavaDirectory = context.outputJavaDirectory + let outputSwiftDirectory = context.outputSwiftDirectory var arguments: [String] = [ - "--swift-module", sourceModule.name, - "--package-name", javaPackage, - "--output-directory-java", outputDirectoryJava.path(percentEncoded: false), - "--output-directory-swift", outputDirectorySwift.path(percentEncoded: false), + "--input-swift", sourceDir, + "--module-name", sourceModule.name, + "--output-java", outputJavaDirectory.path(percentEncoded: false), + "--output-swift", outputSwiftDirectory.path(percentEncoded: false), // TODO: "--build-cache-directory", ... // Since plugins cannot depend on libraries we cannot detect what the output files will be, // as it depends on the contents of the input files. Therefore we have to implement this as a prebuild plugin. // We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc. ] - arguments.append(sourceDir) + // arguments.append(sourceDir) + if !javaPackage.isEmpty { + arguments.append(contentsOf: ["--java-package", javaPackage]) + } return [ .prebuildCommand( @@ -72,7 +75,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { arguments: arguments, // inputFiles: [ configFile ] + swiftFiles, // outputFiles: outputJavaFiles - outputFilesDirectory: outputDirectorySwift + outputFilesDirectory: outputSwiftDirectory ) ] } diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 6932253a..691d3375 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -60,14 +60,14 @@ func getEnvironmentBool(_ name: String) -> Bool { } extension PluginContext { - var outputDirectoryJava: URL { + var outputJavaDirectory: URL { self.pluginWorkDirectoryURL .appending(path: "src") .appending(path: "generated") .appending(path: "java") } - var outputDirectorySwift: URL { + var outputSwiftDirectory: URL { self.pluginWorkDirectoryURL .appending(path: "Sources") } diff --git a/Sources/JExtractSwiftLib/Logger.swift b/Sources/JExtractSwiftLib/Logger.swift index 9aceb201..180ffb54 100644 --- a/Sources/JExtractSwiftLib/Logger.swift +++ b/Sources/JExtractSwiftLib/Logger.swift @@ -14,6 +14,8 @@ import Foundation import SwiftSyntax +import ArgumentParser +import JavaKitConfigurationShared // Placeholder for some better logger, we could depend on swift-log public struct Logger { @@ -95,47 +97,17 @@ public struct Logger { } extension Logger { - public enum Level: String, Hashable { - case trace = "trace" - case debug = "debug" - case info = "info" - case notice = "notice" - case warning = "warning" - case error = "error" - case critical = "critical" - } + public typealias Level = JavaKitConfigurationShared.LogLevel } -extension Logger.Level { - public init(from decoder: any Decoder) throws { - var container = try decoder.unkeyedContainer() - let string = try container.decode(String.self) - switch string { - case "trace": self = .trace - case "debug": self = .debug - case "info": self = .info - case "notice": self = .notice - case "warning": self = .warning - case "error": self = .error - case "critical": self = .critical - default: fatalError("Unknown value for \(Logger.Level.self): \(string)") - } +extension Logger.Level: ExpressibleByArgument { + public var defaultValueDescription: String { + "log level" } + public private(set) static var allValueStrings: [String] = + ["trace", "debug", "info", "notice", "warning", "error", "critical"] - public func encode(to encoder: any Encoder) throws { - var container = encoder.singleValueContainer() - let text = - switch self { - case .trace: "trace" - case .debug: "debug" - case .info: "info" - case .notice: "notice" - case .warning: "warning" - case .error: "error" - case .critical: "critical" - } - try container.encode(text) - } + public private(set) static var defaultCompletionKind: CompletionKind = .default } extension Logger.Level { diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 6525fc0a..79cb0fec 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -12,57 +12,49 @@ // //===----------------------------------------------------------------------===// -import ArgumentParser import Foundation import SwiftSyntax import SwiftSyntaxBuilder import JavaKitShared +import JavaKitConfigurationShared // TODO: this should become SwiftJavaConfigurationShared -/// Command-line utility, similar to `jextract` to export Swift types to Java. -public struct SwiftToJava: ParsableCommand { - public init() {} +public struct SwiftToJava { + let config: Configuration - public static var _commandName: String { - "jextract-swift" + public init(config: Configuration) { + self.config = config } - @Option(help: "The package the generated Java code should be emitted into.") - var packageName: String - - @Option( - name: .shortAndLong, - help: "The directory in which to output the generated Swift files and manifest.") - var outputDirectoryJava: String = ".build/jextract-swift/generated" - - @Option(help: "Swift output directory") - var outputDirectorySwift: String - - @Option( - name: .long, - help: "Name of the Swift module to import (and the swift interface files belong to)") - var swiftModule: String - - @Option(name: .shortAndLong, help: "Configure the level of lots that should be printed") - var logLevel: Logger.Level = .info - - @Argument(help: "The Swift files or directories to recursively export to Java.") - var input: [String] - public func run() throws { - let inputPaths = self.input.dropFirst().map { URL(string: $0)! } + guard let swiftModule = config.swiftModule else { + fatalError("Missing '--swift-module' name.") + } let translator = Swift2JavaTranslator( - javaPackage: packageName, + javaPackage: config.javaPackage ?? "", // no package is ok, we'd generate all into top level swiftModuleName: swiftModule ) - translator.log.logLevel = logLevel + translator.log.logLevel = config.logLevel ?? .info + + if config.javaPackage == nil || config.javaPackage!.isEmpty { + translator.log.warning("Configured java package is '', consider specifying concrete package for generated sources.") + } + + print("===== CONFIG ==== \(config)") + + guard let inputSwift = config.inputSwiftDirectory else { + fatalError("Missing '--swift-input' directory!") + } + + let inputPaths = inputSwift.split(separator: ",").map { URL(string: String($0))! } + translator.log.info("Input paths = \(inputPaths)") var allFiles: [URL] = [] let fileManager = FileManager.default let log = translator.log - + for path in inputPaths { - log.debug("Input path: \(path)") + log.info("Input path: \(path)") if isDirectory(url: path) { if let enumerator = fileManager.enumerator(at: path, includingPropertiesForKeys: nil) { for case let fileURL as URL in enumerator { @@ -88,10 +80,21 @@ public struct SwiftToJava: ParsableCommand { translator.add(filePath: file.path, text: text) } + guard let outputSwiftDirectory = config.outputSwiftDirectory else { + fatalError("Missing --output-swift directory!") + } + guard let outputJavaDirectory = config.outputJavaDirectory else { + fatalError("Missing --output-java directory!") + } + try translator.analyze() - try translator.writeSwiftThunkSources(outputDirectory: outputDirectorySwift) - try translator.writeExportedJavaSources(outputDirectory: outputDirectoryJava) - print("[swift-java] Generated Java sources (\(packageName)) in: \(outputDirectoryJava)/") + + try translator.writeSwiftThunkSources(outputDirectory: outputSwiftDirectory) + print("[swift-java] Generated Swift sources (module: '\(config.swiftModule ?? "")') in: \(outputSwiftDirectory)/") + + try translator.writeExportedJavaSources(outputDirectory: outputJavaDirectory) + print("[swift-java] Generated Java sources (package: '\(config.javaPackage ?? "")') in: \(outputJavaDirectory)/") + print("[swift-java] Imported Swift module '\(swiftModule)': " + "done.".green) } @@ -102,16 +105,6 @@ public struct SwiftToJava: ParsableCommand { } -extension Logger.Level: ExpressibleByArgument { - public var defaultValueDescription: String { - "log level" - } - public private(set) static var allValueStrings: [String] = - ["trace", "debug", "info", "notice", "warning", "error", "critical"] - - public private(set) static var defaultCompletionKind: CompletionKind = .default -} - func isDirectory(url: URL) -> Bool { var isDirectory: ObjCBool = false _ = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) diff --git a/Sources/JavaKitConfigurationShared/Configuration.swift b/Sources/JavaKitConfigurationShared/Configuration.swift index 21d0b03b..5771b53f 100644 --- a/Sources/JavaKitConfigurationShared/Configuration.swift +++ b/Sources/JavaKitConfigurationShared/Configuration.swift @@ -21,15 +21,22 @@ import Foundation public typealias JavaVersion = Int -/// Configuration for the SwiftJava plugins, provided on a per-target basis. +/// Configuration for the SwiftJava tools and plugins, provided on a per-target basis. public struct Configuration: Codable { - // ==== swift 2 java --------------------------------------------------------- + + public var logLevel: LogLevel? + + // ==== swift 2 java / jextract swift --------------------------------------- public var javaPackage: String? - public var inputSwift: String? - public var outputSwift: String? - public var outputJava: String? + public var swiftModule: String? + + public var inputSwiftDirectory: String? + + public var outputSwiftDirectory: String? + + public var outputJavaDirectory: String? // ==== java 2 swift --------------------------------------------------------- @@ -204,3 +211,45 @@ public struct ConfigurationError: Error { self.line = line } } + +public enum LogLevel: String, Codable, Hashable { + case trace = "trace" + case debug = "debug" + case info = "info" + case notice = "notice" + case warning = "warning" + case error = "error" + case critical = "critical" +} + +extension LogLevel { + public init(from decoder: any Decoder) throws { + var container = try decoder.unkeyedContainer() + let string = try container.decode(String.self) + switch string { + case "trace": self = .trace + case "debug": self = .debug + case "info": self = .info + case "notice": self = .notice + case "warning": self = .warning + case "error": self = .error + case "critical": self = .critical + default: fatalError("Unknown value for \(LogLevel.self): \(string)") + } + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + let text = + switch self { + case .trace: "trace" + case .debug: "debug" + case .info: "info" + case .notice: "notice" + case .warning: "warning" + case .error: "error" + case .critical: "critical" + } + try container.encode(text) + } +} \ No newline at end of file diff --git a/Sources/SwiftJavaTool/SwiftJava+JExtract.swift b/Sources/SwiftJavaTool/SwiftJava+JExtract.swift index 7cb15c8e..79c96d3e 100644 --- a/Sources/SwiftJavaTool/SwiftJava+JExtract.swift +++ b/Sources/SwiftJavaTool/SwiftJava+JExtract.swift @@ -34,7 +34,7 @@ extension SwiftJava { mutating func jextractSwift( config: Configuration ) throws { - + try SwiftToJava(config: config).run() } } diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index a877575b..b8e29ed8 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -15,6 +15,7 @@ import ArgumentParser import Foundation import SwiftJavaLib +import JExtractSwiftLib import JavaKit import JavaKitJar import JavaKitNetwork @@ -30,7 +31,7 @@ struct SwiftJava: AsyncParsableCommand { static var _commandName: String { "swift-java" } @Option(help: "The name of the Swift module into which the resulting Swift types will be generated.") - var moduleName: String? + var moduleName: String? // TODO: rename to --swift-module? @Option( help: @@ -59,22 +60,28 @@ struct SwiftJava: AsyncParsableCommand { ) var swiftNativeImplementation: [String] = [] - @Option(name: .shortAndLong, help: "Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift.") + @Option(help: "Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift.") var inputSwift: String? = nil - @Option(name: .shortAndLong, help: "The directory where generated Swift files should be written. Generally used with jextract mode.") + @Option(help: "The directory where generated Swift files should be written. Generally used with jextract mode.") var outputSwift: String? = nil - @Option(name: .shortAndLong, help: "The directory where generated Java files should be written. Generally used with jextract mode.") + @Option(help: "The directory where generated Java files should be written. Generally used with jextract mode.") var outputJava: String? = nil + @Option(help: "The Java package the generated Java code should be emitted into.") + var javaPackage: String? = nil + // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") var outputDirectory: String? = nil @Option(name: .shortAndLong, help: "Directory where to write cached values (e.g. swift-java.classpath files)") var cacheDirectory: String? = nil - + + @Option(name: .shortAndLong, help: "Configure the level of logs that should be printed") + var logLevel: Logger.Level = .info + var effectiveCacheDirectory: String? { if let cacheDirectory { return cacheDirectory @@ -98,7 +105,7 @@ struct SwiftJava: AsyncParsableCommand { @Argument( help: "The input file, which is either a Java2Swift configuration file or (if '-jar' was specified) a Jar file." ) - var input: String + var input: String? /// Whether we have ensured that the output directory exists. var createdOutputDirectory: Bool = false @@ -151,6 +158,7 @@ struct SwiftJava: AsyncParsableCommand { // The configuration file goes at the top level. let outputDir: Foundation.URL if jar { + precondition(self.input != nil, "-jar mode requires path to jar to be specified as input path") outputDir = baseDir } else { outputDir = baseDir @@ -179,35 +187,48 @@ struct SwiftJava: AsyncParsableCommand { mutating func run() async { print("[info][swift-java] Run: \(CommandLine.arguments.joined(separator: " "))") do { - let config: Configuration - + var config: Configuration = Configuration() + if let moduleBaseDir { + print("[debug][swift-java] Load config from module base directory: \(moduleBaseDir.path)") + config = try readConfiguration(sourceDir: moduleBaseDir.path) + } else if let inputSwift { + print("[debug][swift-java] Load config from module swift input directory: \(inputSwift)") + config = try readConfiguration(sourceDir: inputSwift) + } + + config.logLevel = self.logLevel + if let javaPackage { + config.javaPackage = javaPackage + } + assert(config.javaPackage != nil, "java-package was nil!") + // Determine the mode in which we'll execute. let toolMode: ToolMode - // TODO: some options are exclusive to eachother so we should detect that + // TODO: some options are exclusive to each other so we should detect that if let inputSwift { guard let outputSwift else { - print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-swift directory was provided!") + print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-swift directory was provided!\n\(Self.helpMessage())") return } guard let outputJava else { - print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-java directory was provided!") + print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-java directory was provided!\n\(Self.helpMessage())") return } - var c = Configuration() - c.inputSwift = inputSwift - c.outputSwift = outputSwift - c.outputJava = outputJava - config = c + config.swiftModule = self.moduleName // FIXME: rename the moduleName + config.inputSwiftDirectory = self.inputSwift + config.outputSwiftDirectory = self.outputSwift + config.outputJavaDirectory = self.outputJava toolMode = .jextract } else if jar { - if let moduleBaseDir { - config = try readConfiguration(sourceDir: moduleBaseDir.path) - } else { - config = Configuration() + guard let input else { + fatalError("Mode -jar requires path\n\(Self.helpMessage())") } toolMode = .configuration(extraClasspath: input) } else if fetch { + guard let input else { + fatalError("Mode -jar requires path\n\(Self.helpMessage())") + } config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) guard let dependencies = config.dependencies else { print("[swift-java] Running in 'fetch dependencies' mode but dependencies list was empty!") @@ -216,13 +237,23 @@ struct SwiftJava: AsyncParsableCommand { } toolMode = .fetchDependencies } else { + guard let input else { + fatalError("Mode -jar requires path\n\(Self.helpMessage())") + } config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) toolMode = .classWrappers } - let moduleName = self.moduleName ?? - input.split(separator: "/").dropLast().last.map(String.init) ?? - "__UnknownModule" + print("[debug][swift-java] Running swift-java in mode: " + "\(toolMode.prettyName)".bold) + + let moduleName: String = + if let name = self.moduleName { + name + } else if let input { + input.split(separator: "/").dropLast().last.map(String.init) ?? "__UnknownModule" + } else { + "__UnknownModule" + } // Load all of the dependent configurations and associate them with Swift // modules. @@ -283,14 +314,20 @@ struct SwiftJava: AsyncParsableCommand { break; } - // Bring up the Java VM. + // Bring up the Java VM when necessary // TODO: print only in verbose mode let classpath = classpathEntries.joined(separator: ":") - print("[debug][swift-java] Initialize JVM with classpath: \(classpath)") - let jvm = try JavaVirtualMachine.shared(classpath: classpathEntries) + let jvm: JavaVirtualMachine! + switch toolMode { + case .configuration, .classWrappers: + print("[debug][swift-java] Initialize JVM with classpath: \(classpath)") + jvm = try JavaVirtualMachine.shared(classpath: classpathEntries) + default: + jvm = nil + } - // * Classespaths from all dependent configuration files + // * Classpaths from all dependent configuration files for (_, config) in dependentConfigs { // TODO: may need to resolve the dependent configs rather than just get their configs // TODO: We should cache the resolved classpaths as well so we don't do it many times @@ -320,9 +357,6 @@ struct SwiftJava: AsyncParsableCommand { guard let dependencies = config.dependencies else { fatalError("Configuration for fetching dependencies must have 'dependencies' defined!") } - guard let moduleName = self.moduleName else { - fatalError("Fetching dependencies must specify module name (--module-name)!") - } guard let effectiveCacheDirectory else { fatalError("Fetching dependencies must effective cache directory! Specify --output-directory or --cache-directory") } @@ -456,3 +490,13 @@ extension JavaClass { public func getSystemClassLoader() -> ClassLoader? } +extension SwiftJava.ToolMode { + var prettyName: String { + switch self { + case .configuration: "Configuration" + case .fetchDependencies: "Fetch dependencies" + case .classWrappers: "Wrap Java classes" + case .jextract: "JExtract Swift for Java" + } + } +} From 896b2bee13018c6ecd2670d098a02ff497467445 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Mon, 9 Jun 2025 15:42:42 +0900 Subject: [PATCH 8/8] fix JavaDependencySampleApp in swift-java cli mode --- .../JExtractSwiftCommandPlugin.swift | 2 +- .../JExtractSwiftPlugin.swift | 2 +- Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift | 2 +- .../SwiftThunkTranslator.swift | 191 ------------------ .../Configuration.swift | 11 +- Sources/SwiftJavaTool/SwiftJava.swift | 16 +- 6 files changed, 21 insertions(+), 203 deletions(-) delete mode 100644 Sources/JExtractSwiftLib/SwiftThunkTranslator.swift diff --git a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift index 01201de6..3ea89886 100644 --- a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift +++ b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift @@ -81,7 +81,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin // We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc. ] // arguments.append(sourceDir) // TODO: we could do this shape maybe? to have the dirs last? - if let package = configuration.javaPackage, !package.isEmpty { + if let package = configuration?.javaPackage, !package.isEmpty { ["--java-package", package] } diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index 8b0c7d9d..a2cda352 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -42,7 +42,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { let sourceDir = target.directory.string let configuration = try readConfiguration(sourceDir: "\(sourceDir)") - guard let javaPackage = configuration.javaPackage else { + guard let javaPackage = configuration?.javaPackage else { // throw SwiftJavaPluginError.missingConfiguration(sourceDir: "\(sourceDir)", key: "javaPackage") log("Skipping jextract step, no 'javaPackage' configuration in \(getSwiftJavaConfigPath(target: target) ?? "")") return [] diff --git a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift index 05e6db0d..77d7058d 100644 --- a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -38,7 +38,7 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { // which we are generating Swift wrappers for Java classes. let configFile = URL(filePath: sourceDir) .appending(path: SwiftJavaConfigFileName) - let config = try readConfiguration(sourceDir: sourceDir) + let config = try readConfiguration(sourceDir: sourceDir) ?? Configuration() log("Config on path: \(configFile.path(percentEncoded: false))") log("Config was: \(config)") diff --git a/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift b/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift deleted file mode 100644 index 51547ba8..00000000 --- a/Sources/JExtractSwiftLib/SwiftThunkTranslator.swift +++ /dev/null @@ -1,191 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxBuilder - -extension Swift2JavaTranslator { - public func writeSwiftThunkSources(outputDirectory: String) throws { - var printer = CodePrinter() - - try writeSwiftThunkSources(outputDirectory: outputDirectory, printer: &printer) - } - - public func writeSwiftThunkSources(outputDirectory: String, printer: inout CodePrinter) throws { - let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" - let moduleFilename = "\(moduleFilenameBase).swift" - do { - log.info("Printing contents: \(moduleFilename)") - - try printGlobalSwiftThunkSources(&printer) - - if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, - javaPackagePath: nil, - filename: moduleFilename) - { - print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile)") - } - } catch { - log.warning("Failed to write to Swift thunks: \(moduleFilename)") - } - - // === All types - for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { - let fileNameBase = "\(ty.swiftNominal.qualifiedName)+SwiftJava" - let filename = "\(fileNameBase).swift" - log.info("Printing contents: \(filename)") - - do { - try printSwiftThunkSources(&printer, ty: ty) - - if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, - javaPackagePath: nil, - filename: filename) - { - print("[swift-java] Generated: \(fileNameBase.bold).swift (at \(outputFile)") - } - } catch { - log.warning("Failed to write to Swift thunks: \(filename)") - } - } - } - - public func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { - let stt = SwiftThunkTranslator(self) - - printer.print( - """ - // Generated by swift-java - - import SwiftKitSwift - - """) - - for thunk in stt.renderGlobalThunks() { - printer.print(thunk) - printer.println() - } - } - - public func printSwiftThunkSources(_ printer: inout CodePrinter, decl: ImportedFunc) { - let stt = SwiftThunkTranslator(self) - - for thunk in stt.render(forFunc: decl) { - printer.print(thunk) - printer.println() - } - } - - package func printSwiftThunkSources(_ printer: inout CodePrinter, ty: ImportedNominalType) throws { - let stt = SwiftThunkTranslator(self) - - printer.print( - """ - // Generated by swift-java - - import SwiftKitSwift - - """ - ) - - for thunk in stt.renderThunks(forType: ty) { - printer.print("\(thunk)") - printer.print("") - } - } -} - -struct SwiftThunkTranslator { - - let st: Swift2JavaTranslator - - init(_ st: Swift2JavaTranslator) { - self.st = st - } - - func renderGlobalThunks() -> [DeclSyntax] { - var decls: [DeclSyntax] = [] - decls.reserveCapacity( - st.importedGlobalVariables.count + st.importedGlobalFuncs.count - ) - - for decl in st.importedGlobalVariables { - decls.append(contentsOf: render(forFunc: decl)) - } - - for decl in st.importedGlobalFuncs { - decls.append(contentsOf: render(forFunc: decl)) - } - - return decls - } - - /// Render all the thunks that make Swift methods accessible to Java. - func renderThunks(forType nominal: ImportedNominalType) -> [DeclSyntax] { - var decls: [DeclSyntax] = [] - decls.reserveCapacity( - 1 + nominal.initializers.count + nominal.variables.count + nominal.methods.count - ) - - decls.append(renderSwiftTypeAccessor(nominal)) - - for decl in nominal.initializers { - decls.append(contentsOf: render(forFunc: decl)) - } - - for decl in nominal.variables { - decls.append(contentsOf: render(forFunc: decl)) - } - - for decl in nominal.methods { - decls.append(contentsOf: render(forFunc: decl)) - } - - return decls - } - - /// Accessor to get the `T.self` of the Swift type, without having to rely on mangled name lookups. - func renderSwiftTypeAccessor(_ nominal: ImportedNominalType) -> DeclSyntax { - let funcName = SwiftKitPrinting.Names.getType( - module: st.swiftModuleName, - nominal: nominal) - - return - """ - @_cdecl("\(raw: funcName)") - public func \(raw: funcName)() -> UnsafeMutableRawPointer /* Any.Type */ { - return unsafeBitCast(\(raw: nominal.swiftNominal.qualifiedName).self, to: UnsafeMutableRawPointer.self) - } - """ - } - - func render(forFunc decl: ImportedFunc) -> [DeclSyntax] { - st.log.trace("Rendering thunks for: \(decl.displayName)") - - let thunkName = st.thunkNameRegistry.functionThunkName(decl: decl) - guard let translatedSignatures = st.translatedSignature(for: decl) else { - return [] - } - - let thunkFunc = translatedSignatures.loweredSignature.cdeclThunk( - cName: thunkName, - swiftAPIName: decl.name, - as: decl.apiKind, - stdlibTypes: st.swiftStdlibTypes - ) - return [DeclSyntax(thunkFunc)] - } -} diff --git a/Sources/JavaKitConfigurationShared/Configuration.swift b/Sources/JavaKitConfigurationShared/Configuration.swift index 5771b53f..1c4fad56 100644 --- a/Sources/JavaKitConfigurationShared/Configuration.swift +++ b/Sources/JavaKitConfigurationShared/Configuration.swift @@ -113,7 +113,7 @@ public struct JavaDependencyDescriptor: Hashable, Codable { } } -public func readConfiguration(sourceDir: String, file: String = #fileID, line: UInt = #line) throws -> Configuration { +public func readConfiguration(sourceDir: String, file: String = #fileID, line: UInt = #line) throws -> Configuration? { // Workaround since filePath is macOS 13 let sourcePath = if sourceDir.hasPrefix("file://") { sourceDir } else { "file://" + sourceDir } @@ -122,12 +122,15 @@ public func readConfiguration(sourceDir: String, file: String = #fileID, line: U return try readConfiguration(configPath: configPath, file: file, line: line) } -public func readConfiguration(configPath: URL, file: String = #fileID, line: UInt = #line) throws -> Configuration { +public func readConfiguration(configPath: URL, file: String = #fileID, line: UInt = #line) throws -> Configuration? { + guard let configData = try? Data(contentsOf: configPath) else { + return nil + } + do { - let configData = try Data(contentsOf: configPath) return try JSONDecoder().decode(Configuration.self, from: configData) } catch { - throw ConfigurationError(message: "Failed to parse SwiftJava configuration at '\(configPath.absoluteURL)'!", error: error, + throw ConfigurationError(message: "Failed to parse SwiftJava configuration at '\(configPath.absoluteURL)'! \(#fileID):\(#line)", error: error, file: file, line: line) } } diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index b8e29ed8..badd5455 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -116,6 +116,7 @@ struct SwiftJava: AsyncParsableCommand { return nil } + print("[debug][swift-java] Module base directory based on outputDirectory!") return URL(fileURLWithPath: outputDirectory) } @@ -186,21 +187,23 @@ struct SwiftJava: AsyncParsableCommand { mutating func run() async { print("[info][swift-java] Run: \(CommandLine.arguments.joined(separator: " "))") + print("[info][swift-java] Current work directory: \(URL(fileURLWithPath: "."))") + print("[info][swift-java] Module base directory: \(moduleBaseDir)") do { - var config: Configuration = Configuration() + var earlyConfig: Configuration? if let moduleBaseDir { print("[debug][swift-java] Load config from module base directory: \(moduleBaseDir.path)") - config = try readConfiguration(sourceDir: moduleBaseDir.path) + earlyConfig = try readConfiguration(sourceDir: moduleBaseDir.path) } else if let inputSwift { print("[debug][swift-java] Load config from module swift input directory: \(inputSwift)") - config = try readConfiguration(sourceDir: inputSwift) + earlyConfig = try readConfiguration(sourceDir: inputSwift) } + var config = earlyConfig ?? Configuration() config.logLevel = self.logLevel if let javaPackage { config.javaPackage = javaPackage } - assert(config.javaPackage != nil, "java-package was nil!") // Determine the mode in which we'll execute. let toolMode: ToolMode @@ -461,7 +464,10 @@ extension SwiftJava { return (false, .init()) case .amend: let configPath = actualOutputDirectory - return (true, try readConfiguration(sourceDir: configPath.path)) + guard let config = try readConfiguration(sourceDir: configPath.path) else { + return (false, .init()) + } + return (true, config) } } }