Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,37 @@ extension FFMSwift2JavaGenerator {
}

package func writeSwiftThunkSources(printer: inout CodePrinter) throws {
let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava"
let moduleFilename = "\(moduleFilenameBase).swift"
do {
log.debug("Printing contents: \(moduleFilename)")
// Skip global thunks when generating for a single type
if config.singleType == nil {
let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava"
let moduleFilename = "\(moduleFilenameBase).swift"
do {
log.debug("Printing contents: \(moduleFilename)")

try printGlobalSwiftThunkSources(&printer)
try printGlobalSwiftThunkSources(&printer)

if let outputFile = try printer.writeContents(
outputDirectory: self.swiftOutputDirectory,
javaPackagePath: nil,
filename: moduleFilename,
) {
log.info("Generated: \(moduleFilenameBase.bold).swift (at \(outputFile.absoluteString))")
self.expectedOutputSwiftFileNames.remove(moduleFilename)
if let outputFile = try printer.writeContents(
outputDirectory: self.swiftOutputDirectory,
javaPackagePath: nil,
filename: moduleFilename,
) {
log.info("Generated: \(moduleFilenameBase.bold).swift (at \(outputFile.absoluteString))")
self.expectedOutputSwiftFileNames.remove(moduleFilename)
}
} catch {
log.warning("Failed to write to Swift thunks: \(moduleFilename)")
}
} catch {
log.warning("Failed to write to Swift thunks: \(moduleFilename)")
}

// === All types
// We have to write all types to their corresponding output file that matches the file they were declared in,
// because otherwise SwiftPM plugins will not pick up files apropriately -- we expect 1 output +SwiftJava.swift file for every input.
let filteredTypes = self.analysis.importedTypes
let filteredTypes: [String: ImportedNominalType]
if let singleType = config.singleType {
filteredTypes = self.analysis.importedTypes.filter { $0.key == singleType }
} else {
filteredTypes = self.analysis.importedTypes
}

for group: (key: String, value: [Dictionary<String, ImportedNominalType>.Element]) in Dictionary(
grouping: filteredTypes,
Expand Down
32 changes: 21 additions & 11 deletions Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,15 @@ extension FFMSwift2JavaGenerator {

/// Every imported public type becomes a public class in its own file in Java.
package func writeExportedJavaSources(printer: inout CodePrinter) throws {
let typesToExport = analysis.importedTypes
.sorted(by: { $0.key < $1.key })
let typesToExport: [(key: String, value: ImportedNominalType)]
if let singleType = config.singleType {
typesToExport = analysis.importedTypes
.filter { $0.key == singleType }
.sorted(by: { $0.key < $1.key })
} else {
typesToExport = analysis.importedTypes
.sorted(by: { $0.key < $1.key })
}

for (_, ty) in typesToExport {
let javaName = javaClassName(for: ty)
Expand All @@ -186,16 +193,19 @@ extension FFMSwift2JavaGenerator {
}
}

let filename = "\(self.swiftModuleName).java"
log.debug("Printing contents: \(filename)")
printModule(&printer)
// Skip the module-level .java file when generating for a single type
if config.singleType == nil {
let filename = "\(self.swiftModuleName).java"
log.debug("Printing contents: \(filename)")
printModule(&printer)

if let outputFile = try printer.writeContents(
outputDirectory: javaOutputDirectory,
javaPackagePath: javaPackagePath,
filename: filename,
) {
log.info("Generated: \((self.swiftModuleName + ".java").bold) (at \(outputFile.absoluteString))")
if let outputFile = try printer.writeContents(
outputDirectory: javaOutputDirectory,
javaPackagePath: javaPackagePath,
filename: filename,
) {
log.info("Generated: \((self.swiftModuleName + ".java").bold) (at \(outputFile.absoluteString))")
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/SwiftJavaConfigurationShared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public struct Configuration: Codable {

public var generatedJavaSourcesListFileOutput: String?

/// If set, only generate bindings for this single Swift type name
public var singleType: String?

/// If set, JExtract (JNI mode) will write a linker version script to this
/// path, listing all generated JNI ``@_cdecl`` entry-point symbols as
/// global exports and hiding everything else with `local: *`. Pass this
Expand Down
16 changes: 10 additions & 6 deletions Sources/SwiftJavaTool/Commands/JExtractCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension SwiftJava {
struct JExtractCommand: SwiftJavaBaseAsyncParsableCommand, HasCommonOptions {
static let configuration = CommandConfiguration(
commandName: "jextract", // TODO: wrap-swift?
abstract: "Wrap Swift functions and types with Java bindings, making them available to be called from Java"
abstract: "Wrap Swift functions and types with Java bindings, making them available to be called from Java",
)

@OptionGroup var commonOptions: SwiftJava.CommonOptions
Expand All @@ -61,7 +61,7 @@ extension SwiftJava {
@Flag(
inversion: .prefixedNo,
help:
"Some build systems require an output to be present when it was 'expected', even if empty. This is used by the JExtractSwiftPlugin build plugin, but otherwise should not be necessary."
"Some build systems require an output to be present when it was 'expected', even if empty. This is used by the JExtractSwiftPlugin build plugin, but otherwise should not be necessary.",
)
var writeEmptyFiles: Bool?

Expand Down Expand Up @@ -92,7 +92,7 @@ extension SwiftJava {
@Flag(
inversion: .prefixedNo,
help:
"By enabling this mode, JExtract will generate Java code that allows you to implement Swift protocols using Java classes. This feature requires disabling the SwiftPM Sandbox (!). This feature is onl supported in 'jni' mode."
"By enabling this mode, JExtract will generate Java code that allows you to implement Swift protocols using Java classes. This feature requires disabling the SwiftPM Sandbox (!). This feature is onl supported in 'jni' mode.",
)
var enableJavaCallbacks: Bool?

Expand All @@ -117,7 +117,7 @@ extension SwiftJava {
Patterns are matched against relative file paths (without .swift extension). \
Supports * (single-segment wildcard) and ** (recursive wildcard). \
Example: --filter-include 'Models/**'
"""
""",
)
var filterInclude: [String] = []

Expand All @@ -127,9 +127,12 @@ extension SwiftJava {
Exclude Swift source files matching these patterns during jextract. \
Same pattern syntax as --filter-include. \
Example: --filter-exclude 'Internal/*'
"""
""",
)
var filterExclude: [String] = []

@Option(help: "If specified, only generate bindings for this single Swift type name")
var singleType: String?
}
}

Expand All @@ -151,6 +154,7 @@ extension SwiftJava.JExtractCommand {
configure(&config.linkerExportListOutput, overrideWith: self.linkerExportListOutput)
configure(&config.swiftFilterInclude, append: self.filterInclude)
configure(&config.swiftFilterExclude, append: self.filterExclude)
configure(&config.singleType, overrideWith: self.singleType)

try checkModeCompatibility(config: config)

Expand Down Expand Up @@ -196,7 +200,7 @@ struct IncompatibleModeError: Error {
extension SwiftJava.JExtractCommand {
func jextractSwift(
config: Configuration,
dependentConfigs: [Configuration]
dependentConfigs: [Configuration],
) throws {
try SwiftToJava(config: config, dependentConfigs: dependentConfigs).run()
}
Expand Down
8 changes: 4 additions & 4 deletions scripts/swiftkit-ffm-generate-bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
JAVA_OUTPUT="${REPO_ROOT}/SwiftKitFFM/src/main/java"
JAVA_PACKAGE="org.swift.swiftkit.ffm.generated"

# Declare types to generate: SWIFT_MODULE FILTER_INCLUDE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR
# Declare types to generate: SWIFT_MODULE SINGLE_TYPE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR
TYPES=(
"SwiftRuntimeFunctions SwiftJavaError Sources/SwiftRuntimeFunctions Sources/SwiftRuntimeFunctions/generated"
)

for entry in "${TYPES[@]}"; do
read -r MODULE FILTER INPUT_SWIFT OUTPUT_SWIFT <<< "$entry"
read -r MODULE SINGLE_TYPE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry"

echo "==> Generating ${FILTER} (module: ${MODULE})..."
echo "==> Generating ${SINGLE_TYPE} (module: ${MODULE})..."

xcrun swift run swift-java jextract \
--mode ffm \
--filter-include "$FILTER" \
--single-type "$SINGLE_TYPE" \
--swift-module "$MODULE" \
--input-swift "${REPO_ROOT}/${INPUT_SWIFT}" \
--output-swift "${REPO_ROOT}/${OUTPUT_SWIFT}" \
Expand Down
Loading