Skip to content

Commit 0bfca6d

Browse files
authored
Merge branch 'main' into wip-array-array
2 parents 730aa92 + 2d5b9ce commit 0bfca6d

19 files changed

Lines changed: 566 additions & 22 deletions

File tree

.github/workflows/pull_request.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
strategy:
4848
fail-fast: true
4949
matrix:
50-
swift_version: ['6.2', '6.3', 'nightly']
50+
swift_version: ['6.2', '6.3'] # FIXME: reenable 'nightly' once snapshot toolchains are stable again
5151
os_version: ['jammy']
5252
jdk_vendor: ['corretto']
5353
container:
@@ -139,7 +139,7 @@ jobs:
139139
strategy:
140140
fail-fast: false
141141
matrix:
142-
swift_version: ['6.1.3', '6.2', '6.3', 'nightly']
142+
swift_version: ['6.1.3', '6.2', '6.3'] # FIXME: reenable 'nightly' once snapshot toolchains are stable again
143143
os_version: ['jammy']
144144
jdk_vendor: ['corretto']
145145
container:
@@ -189,7 +189,7 @@ jobs:
189189
strategy:
190190
fail-fast: false
191191
matrix:
192-
swift_version: ['nightly-main', 'nightly-6.3']
192+
swift_version: ['nightly-6.3'] # FIXME: reenable 'nightly-main' once snapshot toolchains are stable again
193193
os_version: ['jammy']
194194
jdk_vendor: ['corretto']
195195
sdk_triple: ['aarch64-unknown-linux-android28', 'x86_64-unknown-linux-android28', 'armv7-unknown-linux-android28']
@@ -213,7 +213,7 @@ jobs:
213213
strategy:
214214
fail-fast: false
215215
matrix:
216-
swift_version: ['6.1.3', '6.2', '6.3', 'nightly']
216+
swift_version: ['6.1.3', '6.2', '6.3'] # FIXME: reenable 'nightly' once snapshot toolchains are stable again
217217
os_version: ['jammy']
218218
jdk_vendor: ['corretto']
219219
sample_app: [ # TODO: use a reusable-workflow to generate those names

Package.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ let package = Package(
118118
],
119119
dependencies: [
120120
swiftJavaJNICoreDep,
121-
.package(url: "https://github.com/swiftlang/swift-syntax", from: "602.0.0"),
121+
.package(url: "https://github.com/swiftlang/swift-syntax", from: "603.0.0"),
122122
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
123123
.package(url: "https://github.com/apple/swift-system", from: "1.4.0"),
124124
.package(url: "https://github.com/apple/swift-log", from: "1.2.0"),
125125
.package(url: "https://github.com/apple/swift-collections", .upToNextMinor(from: "1.3.0")), // primarily for ordered collections
126-
.package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.2.1", traits: ["SubprocessFoundation"]),
126+
.package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.4.0", traits: ["SubprocessFoundation"]),
127127

128128
// Benchmarking
129129
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")),
@@ -320,6 +320,7 @@ let package = Package(
320320
dependencies: [
321321
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
322322
.product(name: "SwiftLexicalLookup", package: "swift-syntax"),
323+
.product(name: "SwiftIfConfig", package: "swift-syntax"),
323324
.product(name: "SwiftSyntax", package: "swift-syntax"),
324325
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
325326
.product(name: "ArgumentParser", package: "swift-argument-parser"),
@@ -329,8 +330,30 @@ let package = Package(
329330
"SwiftJavaConfigurationShared",
330331
"CodePrinting",
331332
],
333+
resources: [
334+
.process("Resources")
335+
],
332336
swiftSettings: [
333337
.swiftLanguageMode(.v5)
338+
],
339+
plugins: [
340+
.plugin(name: "_StaticBuildConfigPlugin")
341+
]
342+
),
343+
344+
.executableTarget(
345+
name: "StaticBuildConfigPluginExecutable",
346+
dependencies: [
347+
.product(name: "Subprocess", package: "swift-subprocess"),
348+
.product(name: "SwiftIfConfig", package: "swift-syntax"),
349+
]
350+
),
351+
352+
.plugin(
353+
name: "_StaticBuildConfigPlugin",
354+
capability: .buildTool(),
355+
dependencies: [
356+
"StaticBuildConfigPluginExecutable"
334357
]
335358
),
336359

Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
4646
// The name of the configuration file SwiftJava.config from the target for
4747
// which we are generating Swift wrappers for Java classes.
4848
let configFile = sourceDir.appending(path: "swift-java.config")
49-
let configuration = try readConfiguration(sourceDir: sourceDir)
49+
let configuration = try readConfiguration(configPath: configFile)
5050

5151
// We use the the usual maven-style structure of "src/[generated|main|test]/java/..."
5252
// that is common in JVM ecosystem
@@ -71,6 +71,13 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
7171
// We'll have to make up some caching inside the tool so we don't re-parse files which have not changed etc.
7272
]
7373

74+
if let staticBuildConfig = configuration?.staticBuildConfigurationFile {
75+
guard let resolvedURL = URL(string: staticBuildConfig, relativeTo: configFile) else {
76+
fatalError("Could not resolve 'staticBuildConfigurationFile' url: \(staticBuildConfig)")
77+
}
78+
arguments += ["--static-build-config", resolvedURL.absoluteURL.path(percentEncoded: false)]
79+
}
80+
7481
let dependentConfigFilesArguments = dependentConfigFiles.flatMap { moduleAndConfigFile in
7582
let (moduleName, configFile) = moduleAndConfigFile
7683
return [
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import PackagePlugin
17+
18+
@main
19+
struct _StaticBuildConfigPlugin: BuildToolPlugin {
20+
func createBuildCommands(context: PluginContext, target: any Target) async throws -> [Command] {
21+
let executable = try context.tool(named: "StaticBuildConfigPluginExecutable").url
22+
let out = context.pluginWorkDirectoryURL.appending(path: "static-build-config.json")
23+
return [
24+
.buildCommand(
25+
displayName: "Run -print-static-build-config",
26+
executable: executable,
27+
arguments: [out.path(percentEncoded: false)],
28+
environment: [:],
29+
inputFiles: [],
30+
outputFiles: [out]
31+
)
32+
]
33+
}
34+
}

Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import Foundation
2121

2222
#if os(Linux)
2323
import Glibc
24+
#elseif os(Android)
25+
import Android
2426
#else
2527
import Darwin.C
2628
#endif

Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,10 @@ extension DeclSyntaxProtocol {
229229
} else {
230230
"var"
231231
}
232+
case .unexpectedCodeDecl(let node):
233+
node.trimmedDescription
232234
case .usingDecl(let node):
233-
node.nameForDebug
235+
node.trimmedDescription
234236
}
235237
}
236238

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import SwiftIfConfig
17+
import SwiftSyntax
18+
19+
/// A default, fixed build configuration during static analysis for interface extraction.
20+
struct JExtractDefaultBuildConfiguration: BuildConfiguration {
21+
static let shared = JExtractDefaultBuildConfiguration()
22+
23+
private var base: StaticBuildConfiguration
24+
25+
init() {
26+
guard let url = Bundle.module.url(forResource: "static-build-config", withExtension: "json") else {
27+
fatalError("static-build-config.json is not found in module bundle")
28+
}
29+
do {
30+
let data = try Data(contentsOf: url)
31+
let decoder = JSONDecoder()
32+
base = try decoder.decode(StaticBuildConfiguration.self, from: data)
33+
} catch {
34+
fatalError("\(error)")
35+
}
36+
}
37+
38+
func isCustomConditionSet(name: String) throws -> Bool {
39+
base.isCustomConditionSet(name: name)
40+
}
41+
42+
func hasFeature(name: String) throws -> Bool {
43+
base.hasFeature(name: name)
44+
}
45+
46+
func hasAttribute(name: String) throws -> Bool {
47+
base.hasAttribute(name: name)
48+
}
49+
50+
func canImport(importPath: [(TokenSyntax, String)], version: CanImportVersion) throws -> Bool {
51+
try base.canImport(importPath: importPath, version: version)
52+
}
53+
54+
func isActiveTargetOS(name: String) throws -> Bool {
55+
true
56+
}
57+
58+
func isActiveTargetArchitecture(name: String) throws -> Bool {
59+
true
60+
}
61+
62+
func isActiveTargetEnvironment(name: String) throws -> Bool {
63+
true
64+
}
65+
66+
func isActiveTargetRuntime(name: String) throws -> Bool {
67+
true
68+
}
69+
70+
func isActiveTargetPointerAuthentication(name: String) throws -> Bool {
71+
true
72+
}
73+
74+
func isActiveTargetObjectFormat(name: String) throws -> Bool {
75+
true
76+
}
77+
78+
var targetPointerBitWidth: Int {
79+
base.targetPointerBitWidth
80+
}
81+
82+
var targetAtomicBitWidths: [Int] {
83+
base.targetAtomicBitWidths
84+
}
85+
86+
var endianness: Endianness {
87+
base.endianness
88+
}
89+
90+
var languageVersion: VersionTuple {
91+
base.languageVersion
92+
}
93+
94+
var compilerVersion: VersionTuple {
95+
base.compilerVersion
96+
}
97+
}
98+
99+
extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration {
100+
static var jextractDefault: JExtractDefaultBuildConfiguration {
101+
.shared
102+
}
103+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// The actual resources will be generated by the buildToolsPlugin. This file is necessary for SwiftPM to generate a module bundle.
2+
{}

Sources/JExtractSwiftLib/Swift2Java.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public struct SwiftToJava {
3434
}
3535

3636
let translator = Swift2JavaTranslator(config: config)
37-
translator.log.logLevel = config.logLevel ?? .info
3837
let log = translator.log
3938

4039
if config.javaPackage == nil || config.javaPackage!.isEmpty {

Sources/JExtractSwiftLib/Swift2JavaTranslator.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import Foundation
1616
import SwiftBasicFormat
17+
import SwiftIfConfig
1718
import SwiftJavaConfigurationShared
1819
import SwiftJavaJNICore
1920
import SwiftParser
@@ -27,6 +28,9 @@ public final class Swift2JavaTranslator {
2728

2829
let config: Configuration
2930

31+
/// The build configuration used to resolve #if conditional compilation blocks.
32+
let buildConfig: any BuildConfiguration
33+
3034
/// The name of the Swift module being translated.
3135
let swiftModuleName: String
3236

@@ -70,6 +74,19 @@ public final class Swift2JavaTranslator {
7074
self.log = Logger(label: "translator", logLevel: config.logLevel ?? .info)
7175
self.config = config
7276
self.swiftModuleName = swiftModule
77+
78+
if let staticBuildConfigPath = config.staticBuildConfigurationFile {
79+
do {
80+
let data = try Data(contentsOf: URL(fileURLWithPath: staticBuildConfigPath))
81+
let decoder = JSONDecoder()
82+
self.buildConfig = try decoder.decode(StaticBuildConfiguration.self, from: data)
83+
self.log.info("Using custom static build configuration from: \(staticBuildConfigPath)")
84+
} catch {
85+
fatalError("Failed to load static build configuration from '\(staticBuildConfigPath)': \(error)")
86+
}
87+
} else {
88+
self.buildConfig = .jextractDefault
89+
}
7390
}
7491
}
7592

@@ -152,6 +169,7 @@ extension Swift2JavaTranslator {
152169
moduleName: self.swiftModuleName,
153170
inputs + [dependenciesSource],
154171
config: self.config,
172+
buildConfig: self.buildConfig,
155173
log: self.log,
156174
)
157175
self.lookupContext = SwiftTypeLookupContext(symbolTable: symbolTable)

0 commit comments

Comments
 (0)