From f030ef2c873a35e2fa49570a49e7ba3bb37f9bb6 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 31 Mar 2026 12:36:32 +0900 Subject: [PATCH 01/16] Update swift-syntax to 603 --- Package.swift | 2 +- .../JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index ce340154..cb3aa93b 100644 --- a/Package.swift +++ b/Package.swift @@ -118,7 +118,7 @@ let package = Package( ], dependencies: [ swiftJavaJNICoreDep, - .package(url: "https://github.com/swiftlang/swift-syntax", from: "602.0.0"), + .package(url: "https://github.com/swiftlang/swift-syntax", from: "603.0.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"), .package(url: "https://github.com/apple/swift-system", from: "1.4.0"), .package(url: "https://github.com/apple/swift-log", from: "1.2.0"), diff --git a/Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift index 86ed4211..4b05971e 100644 --- a/Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift +++ b/Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift @@ -229,8 +229,10 @@ extension DeclSyntaxProtocol { } else { "var" } + case .unexpectedCodeDecl(let node): + node.trimmedDescription case .usingDecl(let node): - node.nameForDebug + node.trimmedDescription } } From e118d1b82f05a89e8b532d4541a992d566d98c73 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 12:54:17 +0900 Subject: [PATCH 02/16] Add JExtractDefaultBuildConfiguration --- .../JExtractDefaultBuildConfiguration.swift | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift diff --git a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift new file mode 100644 index 00000000..85ed57a2 --- /dev/null +++ b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 Foundation +import SwiftSyntax +import SwiftIfConfig + +/// A default, fixed build configuration during static analysis for interface extraction. +struct JExtractDefaultBuildConfiguration: BuildConfiguration { + private var base: StaticBuildConfiguration + + init() { + let decoder = JSONDecoder() + base = try! decoder.decode(StaticBuildConfiguration.self, from: printStaticBuildConfigOutput) + } + + func isCustomConditionSet(name: String) throws -> Bool { + base.isCustomConditionSet(name: name) + } + + func hasFeature(name: String) throws -> Bool { + base.hasFeature(name: name) + } + + func hasAttribute(name: String) throws -> Bool { + base.hasAttribute(name: name) + } + + func canImport(importPath: [(TokenSyntax, String)], version: CanImportVersion) throws -> Bool { + try base.canImport(importPath: importPath, version: version) + } + + func isActiveTargetOS(name: String) throws -> Bool { + true + } + + func isActiveTargetArchitecture(name: String) throws -> Bool { + true + } + + func isActiveTargetEnvironment(name: String) throws -> Bool { + true + } + + func isActiveTargetRuntime(name: String) throws -> Bool { + true + } + + func isActiveTargetPointerAuthentication(name: String) throws -> Bool { + true + } + + func isActiveTargetObjectFormat(name: String) throws -> Bool { + true + } + + var targetPointerBitWidth: Int { + base.targetPointerBitWidth + } + + var targetAtomicBitWidths: [Int] { + base.targetAtomicBitWidths + } + + var endianness: Endianness { + base.endianness + } + + var languageVersion: VersionTuple { + base.languageVersion + } + + var compilerVersion: VersionTuple { + base.compilerVersion + } +} + +fileprivate let shared = JExtractDefaultBuildConfiguration() + +extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration { + static var jextractDefault: JExtractDefaultBuildConfiguration { + shared + } +} + +// $ swift frontend -print-static-build-config -target aarch64-unknown-linux-gnu +fileprivate let printStaticBuildConfigOutput = Data(#"{"attributes":["GKInspectable","noDerivative","objcMembers","discardableResult","const","available","usableFromInline","preconcurrency","nonisolated","retroactive","frozen","unsafe","propertyWrapper","lifetime","_extern","inline","abi","storageRestrictions","_opaqueReturnTypeOf","objc","constInitialized","autoclosure","escaping","unchecked","requires_stored_property_inits","convention","attached","nonexhaustive","dynamicCallable","reasync","dynamicMemberLookup","NSCopying","transpose","warn_unqualified_access","c","globalActor","isolated","_local","rethrows","exclusivity","backDeployed","UIApplicationMain","main","nonobjc","resultBuilder","Sendable","_noMetadata","IBDesignable","IBOutlet","export","IBSegueAction","IBAction","derivative","NSApplicationMain","inlinable","concurrent","IBInspectable","NSManaged","_addressable","differentiable"],"compilerVersion":{"components":[6,3]},"customConditions":[],"endianness":"little","features":["BuiltinCreateAsyncTaskWithExecutor","BuiltinCreateAsyncTaskName","Macros","ValueGenericsNameLookup","FreestandingExpressionMacros","AssociatedTypeAvailability","NoncopyableGenerics2","BuiltinInterleave","OptionalIsolatedParameters","BuiltinAddressOfRawLayout","InoutLifetimeDependence","ExtensionMacros","BuiltinBuildMainExecutor","BuiltinStoreRaw","InheritActorContext","IsolatedAny","BuiltinExecutor","LayoutPrespecialization","NonexhaustiveAttribute","InlineAlways","BuiltinCreateTaskGroupWithFlags","ValueGenerics","InlineArrayTypeSugar","BuiltinCreateTask","NonescapableTypes","RetroactiveAttribute","BuiltinTaskRunInline","BuiltinBuildComplexEqualityExecutor","MemorySafetyAttributes","BuiltinBuildExecutor","ModuleSelector","ParameterPacks","MarkerProtocol","ConformanceSuppression","BitwiseCopyable","AddressOfProperty2","UnsafeInheritExecutor","BuiltinCreateAsyncTaskOwnedTaskExecutor","SpecializeAttributeWithAvailability","BuiltinJob","AlwaysInheritActorContext","AsyncExecutionBehaviorAttributes","AsyncSequenceFailure","BorrowingSwitch","PrimaryAssociatedTypes2","NewCxxMethodSafetyHeuristics","ExtensionMacroAttr","BuiltinStackAlloc","TypedThrows","BuiltinContinuation","BuiltinUnprotectedAddressOf","BuiltinSelect","ImplicitSelfCapture","MoveOnlyPartialConsumption","Actors","GeneralizedIsSameMetaTypeBuiltin","MoveOnly","GlobalActors","BuiltinCreateAsyncDiscardingTaskInGroupWithExecutor","NonfrozenEnumExhaustivity","RethrowsProtocol","IsolatedDeinit","BuiltinUnprotectedStackAlloc","BuiltinCreateAsyncTaskInGroupWithExecutor","BuiltinTaskGroupWithArgument","NoAsyncAvailability","ObjCImplementation","AttachedMacros","FreestandingMacros","AsyncAwait","EffectfulProp","BuiltinConcurrencyStackNesting","BuiltinCreateAsyncTaskInGroup","ConcurrentFunctions","BuiltinHopToActor","BuiltinIntLiteralAccessors","BodyMacros","BuiltinVectorsExternC","BuiltinCreateAsyncDiscardingTaskInGroup","RawIdentifiers","NonescapableAccessorOnTrivial","UnavailableFromAsync","IsolatedAny2","LexicalLifetimes","SendableCompletionHandlers","BuiltinAssumeAlignment","AssociatedTypeImplements","Sendable","ABIAttributeSE0479","BuiltinBuildTaskExecutorRef","LifetimeDependenceMutableAccessors","BitwiseCopyable2","IsolatedConformances","ExpressionMacroDefaultArguments","NoncopyableGenerics","SendingArgsAndResults","MoveOnlyResilientTypes","BuiltinEmplaceTypedThrows"],"languageMode":{"components":[5,10]},"targetArchitectures":["arm64"],"targetAtomicBitWidths":[128,64,32,16,8],"targetEnvironments":[],"targetOSs":["Linux"],"targetObjectFileFormats":["ELF"],"targetPointerAuthenticationSchemes":["_none"],"targetPointerBitWidth":64,"targetRuntimes":["_Native","_multithreaded"]}"#.utf8) From c47576994d5a7f9a6d25ff17f3b27ca74b80b85d Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 14:13:12 +0900 Subject: [PATCH 03/16] Add visiting ifConfigDecl logic --- .../JExtractSwiftLib/Swift2JavaVisitor.swift | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index 1a583b1d..4b068c7b 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -16,6 +16,7 @@ import Foundation import SwiftJavaConfigurationShared import SwiftParser import SwiftSyntax +import SwiftIfConfig final class Swift2JavaVisitor { let translator: Swift2JavaTranslator @@ -70,7 +71,8 @@ final class Swift2JavaVisitor { self.visit(subscriptDecl: node, in: parent) case .enumCaseDecl(let node): self.visit(enumCaseDecl: node, in: parent) - + case .ifConfigDecl(let node): + self.visit(ifConfigDecl: node, in: parent, sourceFilePath: sourceFilePath) default: break } @@ -366,6 +368,37 @@ final class Swift2JavaVisitor { } } + private func visit( + ifConfigDecl node: IfConfigDeclSyntax, + in parent: ImportedNominalType?, + sourceFilePath: String + ) { + let (clause, diagnostics) = node.activeClause(in: .jextractDefault) + for diagnostic in diagnostics { + self.log.info(diagnostic.debugDescription) + } + if let clause, let elements = clause.elements { + switch elements { + case .statements(let statements): + for codeItem in statements { + if let declNode = codeItem.item.as(DeclSyntax.self) { + self.visit(decl: declNode, in: parent, sourceFilePath: sourceFilePath) + } + } + case .switchCases: + break + case .decls(let decls): + for decl in decls { + self.visit(decl: decl.decl, in: parent, sourceFilePath: sourceFilePath) + } + case .postfixExpression: + break + case .attributes: + break + } + } + } + private func importAccessor( from node: DeclSyntax, in typeContext: ImportedNominalType?, From dda539a970b89d98ff715c06f52ce86d8c87f0f2 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 15:16:40 +0900 Subject: [PATCH 04/16] Add IfConfigTests --- .../JExtractSwiftLib/Swift2JavaVisitor.swift | 21 ++-- .../SwiftParsedModuleSymbolTableBuilder.swift | 46 +++++++-- Tests/JExtractSwiftTests/IfConfigTests.swift | 99 +++++++++++++++++++ 3 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 Tests/JExtractSwiftTests/IfConfigTests.swift diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index 4b068c7b..a530370f 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -373,27 +373,20 @@ final class Swift2JavaVisitor { in parent: ImportedNominalType?, sourceFilePath: String ) { - let (clause, diagnostics) = node.activeClause(in: .jextractDefault) - for diagnostic in diagnostics { - self.log.info(diagnostic.debugDescription) - } + let (clause, _) = node.activeClause(in: .jextractDefault) if let clause, let elements = clause.elements { switch elements { - case .statements(let statements): - for codeItem in statements { + case .statements(let codeBlock): + for codeItem in codeBlock { if let declNode = codeItem.item.as(DeclSyntax.self) { self.visit(decl: declNode, in: parent, sourceFilePath: sourceFilePath) } } - case .switchCases: - break - case .decls(let decls): - for decl in decls { - self.visit(decl: decl.decl, in: parent, sourceFilePath: sourceFilePath) + case .decls(let memberBlock): + for memberItem in memberBlock { + self.visit(decl: memberItem.decl, in: parent, sourceFilePath: sourceFilePath) } - case .postfixExpression: - break - case .attributes: + default: break } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 895a0a1b..8a2390a9 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import SwiftSyntax +import SwiftIfConfig struct SwiftParsedModuleSymbolTableBuilder { let log: Logger? @@ -56,17 +57,25 @@ extension SwiftParsedModuleSymbolTableBuilder { ) { // Find top-level type declarations. for statement in sourceFile.statements { - // We only care about declarations. - guard case .decl(let decl) = statement.item else { - continue - } + self.handle(codeBlockItem: statement.item, sourceFilePath: sourceFilePath) + } + } - if let nominalTypeNode = decl.asNominal { - self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalTypeNode, parent: nil) - } - if let extensionNode = decl.as(ExtensionDeclSyntax.self) { - self.handle(extensionDecl: extensionNode, sourceFilePath: sourceFilePath) - } + mutating func handle( + codeBlockItem node: CodeBlockItemSyntax.Item, + sourceFilePath: String + ) { + // We only care about declarations. + guard case .decl(let decl) = node else { + return + } + + if let nominalTypeNode = decl.asNominal { + self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalTypeNode, parent: nil) + } else if let extensionNode = decl.as(ExtensionDeclSyntax.self) { + self.handle(extensionDecl: extensionNode, sourceFilePath: sourceFilePath) + } else if let ifConfigNode = decl.as(IfConfigDeclSyntax.self) { + self.handle(ifConfig: ifConfigNode, sourceFilePath: sourceFilePath) } } @@ -152,6 +161,23 @@ extension SwiftParsedModuleSymbolTableBuilder { return true } + mutating func handle( + ifConfig node: IfConfigDeclSyntax, + sourceFilePath: String + ) { + let (clause, _) = node.activeClause(in: .jextractDefault) + if let clause, let elements = clause.elements { + switch elements { + case .statements(let codeBlock): + for codeItem in codeBlock { + self.handle(codeBlockItem: codeItem.item, sourceFilePath: sourceFilePath) + } + default: + break + } + } + } + /// Finalize the symbol table and return it. mutating func finalize() -> SwiftModuleSymbolTable { // Handle the unresolved extensions. diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift new file mode 100644 index 00000000..004effb2 --- /dev/null +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 JExtractSwiftLib +import Testing + +@Suite +struct IfConfigTests { + @Test + func evaluateIfConfigs() throws { + try assertOutput( + input: """ + #if os(Android) + public func androidFunc() + #else + public func notAndroidFunc() + #endif + + #if canImport(Foundation) + public struct CanImport { + } + #else + public struct CannotImport { + } + #endif + + #if DEBUG + public struct IsDebug { + } + #else + public struct IsNotDebug { + #if os(Linux) + public var linuxVar: Int + #elseif os(iOS) || os(macOS) + public var iOSorMacOSVar: Int + #else + #error("unsupported platform") + #endif + } + #endif + """, + .ffm, + .java, + detectChunkByInitialLines: 1, + expectedChunks: [ + "public static void androidFunc()", + "public final class CannotImport", + "public final class IsNotDebug", + "public long getLinuxVar()" + ], + notExpectedChunks: [ + "public static void notAndroidFunc()", + "public final class CanImport", + "public final class IsDebug", + "public long getIOSorMacOSVar() " + ] + ) + } + + @Test + func swiftinterfaceCommonPattern() throws { + try assertOutput( + input: """ + public enum Foundation { + public struct URL {} + } + + public struct AppStore : Swift.Sendable, Swift.Codable { + public var storeURL: Foundation.URL? + #if compiler(>=5.3) && $NonescapableTypes + public init(storeURL: Foundation.URL?) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws + } + """, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_AppStore__00024init__J") + public func Java_com_example_swift_AppStore__00024init__J(environment: UnsafeMutablePointer!, thisClass: jclass, storeURL: jlong) -> jlong + """ + ] + ) + } +} From 4b6e407f6839367c4aee5d19951abbfc47c840b8 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 15:21:42 +0900 Subject: [PATCH 05/16] swift format --- .../JExtractDefaultBuildConfiguration.swift | 9 ++++++--- Sources/JExtractSwiftLib/Swift2JavaVisitor.swift | 2 +- .../SwiftParsedModuleSymbolTableBuilder.swift | 2 +- Tests/JExtractSwiftTests/IfConfigTests.swift | 10 +++++----- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift index 85ed57a2..f20d76d4 100644 --- a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift +++ b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// import Foundation -import SwiftSyntax import SwiftIfConfig +import SwiftSyntax /// A default, fixed build configuration during static analysis for interface extraction. struct JExtractDefaultBuildConfiguration: BuildConfiguration { @@ -86,7 +86,7 @@ struct JExtractDefaultBuildConfiguration: BuildConfiguration { } } -fileprivate let shared = JExtractDefaultBuildConfiguration() +private let shared = JExtractDefaultBuildConfiguration() extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration { static var jextractDefault: JExtractDefaultBuildConfiguration { @@ -95,4 +95,7 @@ extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration { } // $ swift frontend -print-static-build-config -target aarch64-unknown-linux-gnu -fileprivate let printStaticBuildConfigOutput = Data(#"{"attributes":["GKInspectable","noDerivative","objcMembers","discardableResult","const","available","usableFromInline","preconcurrency","nonisolated","retroactive","frozen","unsafe","propertyWrapper","lifetime","_extern","inline","abi","storageRestrictions","_opaqueReturnTypeOf","objc","constInitialized","autoclosure","escaping","unchecked","requires_stored_property_inits","convention","attached","nonexhaustive","dynamicCallable","reasync","dynamicMemberLookup","NSCopying","transpose","warn_unqualified_access","c","globalActor","isolated","_local","rethrows","exclusivity","backDeployed","UIApplicationMain","main","nonobjc","resultBuilder","Sendable","_noMetadata","IBDesignable","IBOutlet","export","IBSegueAction","IBAction","derivative","NSApplicationMain","inlinable","concurrent","IBInspectable","NSManaged","_addressable","differentiable"],"compilerVersion":{"components":[6,3]},"customConditions":[],"endianness":"little","features":["BuiltinCreateAsyncTaskWithExecutor","BuiltinCreateAsyncTaskName","Macros","ValueGenericsNameLookup","FreestandingExpressionMacros","AssociatedTypeAvailability","NoncopyableGenerics2","BuiltinInterleave","OptionalIsolatedParameters","BuiltinAddressOfRawLayout","InoutLifetimeDependence","ExtensionMacros","BuiltinBuildMainExecutor","BuiltinStoreRaw","InheritActorContext","IsolatedAny","BuiltinExecutor","LayoutPrespecialization","NonexhaustiveAttribute","InlineAlways","BuiltinCreateTaskGroupWithFlags","ValueGenerics","InlineArrayTypeSugar","BuiltinCreateTask","NonescapableTypes","RetroactiveAttribute","BuiltinTaskRunInline","BuiltinBuildComplexEqualityExecutor","MemorySafetyAttributes","BuiltinBuildExecutor","ModuleSelector","ParameterPacks","MarkerProtocol","ConformanceSuppression","BitwiseCopyable","AddressOfProperty2","UnsafeInheritExecutor","BuiltinCreateAsyncTaskOwnedTaskExecutor","SpecializeAttributeWithAvailability","BuiltinJob","AlwaysInheritActorContext","AsyncExecutionBehaviorAttributes","AsyncSequenceFailure","BorrowingSwitch","PrimaryAssociatedTypes2","NewCxxMethodSafetyHeuristics","ExtensionMacroAttr","BuiltinStackAlloc","TypedThrows","BuiltinContinuation","BuiltinUnprotectedAddressOf","BuiltinSelect","ImplicitSelfCapture","MoveOnlyPartialConsumption","Actors","GeneralizedIsSameMetaTypeBuiltin","MoveOnly","GlobalActors","BuiltinCreateAsyncDiscardingTaskInGroupWithExecutor","NonfrozenEnumExhaustivity","RethrowsProtocol","IsolatedDeinit","BuiltinUnprotectedStackAlloc","BuiltinCreateAsyncTaskInGroupWithExecutor","BuiltinTaskGroupWithArgument","NoAsyncAvailability","ObjCImplementation","AttachedMacros","FreestandingMacros","AsyncAwait","EffectfulProp","BuiltinConcurrencyStackNesting","BuiltinCreateAsyncTaskInGroup","ConcurrentFunctions","BuiltinHopToActor","BuiltinIntLiteralAccessors","BodyMacros","BuiltinVectorsExternC","BuiltinCreateAsyncDiscardingTaskInGroup","RawIdentifiers","NonescapableAccessorOnTrivial","UnavailableFromAsync","IsolatedAny2","LexicalLifetimes","SendableCompletionHandlers","BuiltinAssumeAlignment","AssociatedTypeImplements","Sendable","ABIAttributeSE0479","BuiltinBuildTaskExecutorRef","LifetimeDependenceMutableAccessors","BitwiseCopyable2","IsolatedConformances","ExpressionMacroDefaultArguments","NoncopyableGenerics","SendingArgsAndResults","MoveOnlyResilientTypes","BuiltinEmplaceTypedThrows"],"languageMode":{"components":[5,10]},"targetArchitectures":["arm64"],"targetAtomicBitWidths":[128,64,32,16,8],"targetEnvironments":[],"targetOSs":["Linux"],"targetObjectFileFormats":["ELF"],"targetPointerAuthenticationSchemes":["_none"],"targetPointerBitWidth":64,"targetRuntimes":["_Native","_multithreaded"]}"#.utf8) +private let printStaticBuildConfigOutput = Data( + #"{"attributes":["GKInspectable","noDerivative","objcMembers","discardableResult","const","available","usableFromInline","preconcurrency","nonisolated","retroactive","frozen","unsafe","propertyWrapper","lifetime","_extern","inline","abi","storageRestrictions","_opaqueReturnTypeOf","objc","constInitialized","autoclosure","escaping","unchecked","requires_stored_property_inits","convention","attached","nonexhaustive","dynamicCallable","reasync","dynamicMemberLookup","NSCopying","transpose","warn_unqualified_access","c","globalActor","isolated","_local","rethrows","exclusivity","backDeployed","UIApplicationMain","main","nonobjc","resultBuilder","Sendable","_noMetadata","IBDesignable","IBOutlet","export","IBSegueAction","IBAction","derivative","NSApplicationMain","inlinable","concurrent","IBInspectable","NSManaged","_addressable","differentiable"],"compilerVersion":{"components":[6,3]},"customConditions":[],"endianness":"little","features":["BuiltinCreateAsyncTaskWithExecutor","BuiltinCreateAsyncTaskName","Macros","ValueGenericsNameLookup","FreestandingExpressionMacros","AssociatedTypeAvailability","NoncopyableGenerics2","BuiltinInterleave","OptionalIsolatedParameters","BuiltinAddressOfRawLayout","InoutLifetimeDependence","ExtensionMacros","BuiltinBuildMainExecutor","BuiltinStoreRaw","InheritActorContext","IsolatedAny","BuiltinExecutor","LayoutPrespecialization","NonexhaustiveAttribute","InlineAlways","BuiltinCreateTaskGroupWithFlags","ValueGenerics","InlineArrayTypeSugar","BuiltinCreateTask","NonescapableTypes","RetroactiveAttribute","BuiltinTaskRunInline","BuiltinBuildComplexEqualityExecutor","MemorySafetyAttributes","BuiltinBuildExecutor","ModuleSelector","ParameterPacks","MarkerProtocol","ConformanceSuppression","BitwiseCopyable","AddressOfProperty2","UnsafeInheritExecutor","BuiltinCreateAsyncTaskOwnedTaskExecutor","SpecializeAttributeWithAvailability","BuiltinJob","AlwaysInheritActorContext","AsyncExecutionBehaviorAttributes","AsyncSequenceFailure","BorrowingSwitch","PrimaryAssociatedTypes2","NewCxxMethodSafetyHeuristics","ExtensionMacroAttr","BuiltinStackAlloc","TypedThrows","BuiltinContinuation","BuiltinUnprotectedAddressOf","BuiltinSelect","ImplicitSelfCapture","MoveOnlyPartialConsumption","Actors","GeneralizedIsSameMetaTypeBuiltin","MoveOnly","GlobalActors","BuiltinCreateAsyncDiscardingTaskInGroupWithExecutor","NonfrozenEnumExhaustivity","RethrowsProtocol","IsolatedDeinit","BuiltinUnprotectedStackAlloc","BuiltinCreateAsyncTaskInGroupWithExecutor","BuiltinTaskGroupWithArgument","NoAsyncAvailability","ObjCImplementation","AttachedMacros","FreestandingMacros","AsyncAwait","EffectfulProp","BuiltinConcurrencyStackNesting","BuiltinCreateAsyncTaskInGroup","ConcurrentFunctions","BuiltinHopToActor","BuiltinIntLiteralAccessors","BodyMacros","BuiltinVectorsExternC","BuiltinCreateAsyncDiscardingTaskInGroup","RawIdentifiers","NonescapableAccessorOnTrivial","UnavailableFromAsync","IsolatedAny2","LexicalLifetimes","SendableCompletionHandlers","BuiltinAssumeAlignment","AssociatedTypeImplements","Sendable","ABIAttributeSE0479","BuiltinBuildTaskExecutorRef","LifetimeDependenceMutableAccessors","BitwiseCopyable2","IsolatedConformances","ExpressionMacroDefaultArguments","NoncopyableGenerics","SendingArgsAndResults","MoveOnlyResilientTypes","BuiltinEmplaceTypedThrows"],"languageMode":{"components":[5,10]},"targetArchitectures":["arm64"],"targetAtomicBitWidths":[128,64,32,16,8],"targetEnvironments":[],"targetOSs":["Linux"],"targetObjectFileFormats":["ELF"],"targetPointerAuthenticationSchemes":["_none"],"targetPointerBitWidth":64,"targetRuntimes":["_Native","_multithreaded"]}"# + .utf8 +) diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index a530370f..59b4c5bd 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// import Foundation +import SwiftIfConfig import SwiftJavaConfigurationShared import SwiftParser import SwiftSyntax -import SwiftIfConfig final class Swift2JavaVisitor { let translator: Swift2JavaTranslator diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 8a2390a9..9f5761f2 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -import SwiftSyntax import SwiftIfConfig +import SwiftSyntax struct SwiftParsedModuleSymbolTableBuilder { let log: Logger? diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift index 004effb2..20ee167b 100644 --- a/Tests/JExtractSwiftTests/IfConfigTests.swift +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -26,7 +26,7 @@ struct IfConfigTests { #else public func notAndroidFunc() #endif - + #if canImport(Foundation) public struct CanImport { } @@ -34,7 +34,7 @@ struct IfConfigTests { public struct CannotImport { } #endif - + #if DEBUG public struct IsDebug { } @@ -57,13 +57,13 @@ struct IfConfigTests { "public static void androidFunc()", "public final class CannotImport", "public final class IsNotDebug", - "public long getLinuxVar()" + "public long getLinuxVar()", ], notExpectedChunks: [ "public static void notAndroidFunc()", "public final class CanImport", "public final class IsDebug", - "public long getIOSorMacOSVar() " + "public long getIOSorMacOSVar() ", ] ) } @@ -75,7 +75,7 @@ struct IfConfigTests { public enum Foundation { public struct URL {} } - + public struct AppStore : Swift.Sendable, Swift.Codable { public var storeURL: Foundation.URL? #if compiler(>=5.3) && $NonescapableTypes From 8690e90516c88714c4904404818a7611082fbf50 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 17:05:51 +0900 Subject: [PATCH 06/16] Add static build configuration option --- Sources/JExtractSwiftLib/Swift2Java.swift | 1 - .../Swift2JavaTranslator.swift | 18 ++++++++++++++++++ .../JExtractSwiftLib/Swift2JavaVisitor.swift | 2 +- .../SwiftParsedModuleSymbolTableBuilder.swift | 7 ++++++- .../SwiftTypes/SwiftSymbolTable.swift | 4 ++++ .../Configuration.swift | 4 ++++ .../Commands/JExtractCommand.swift | 7 +++++++ 7 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 5a1b2da2..909b671f 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -34,7 +34,6 @@ public struct SwiftToJava { } let translator = Swift2JavaTranslator(config: config) - translator.log.logLevel = config.logLevel ?? .info let log = translator.log if config.javaPackage == nil || config.javaPackage!.isEmpty { diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index a5d96a1a..106247a4 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -14,6 +14,7 @@ import Foundation import SwiftBasicFormat +import SwiftIfConfig import SwiftJavaConfigurationShared import SwiftJavaJNICore import SwiftParser @@ -27,6 +28,9 @@ public final class Swift2JavaTranslator { let config: Configuration + /// The build configuration used to resolve #if conditional compilation blocks. + let buildConfig: any BuildConfiguration + /// The name of the Swift module being translated. let swiftModuleName: String @@ -70,6 +74,19 @@ public final class Swift2JavaTranslator { self.log = Logger(label: "translator", logLevel: config.logLevel ?? .info) self.config = config self.swiftModuleName = swiftModule + + if let staticBuildConfigPath = config.staticBuildConfigurationFile { + do { + let data = try Data(contentsOf: URL(fileURLWithPath: staticBuildConfigPath)) + let decoder = JSONDecoder() + self.buildConfig = try decoder.decode(StaticBuildConfiguration.self, from: data) + self.log.info("Using custom static build configuration from: \(staticBuildConfigPath)") + } catch { + fatalError("Failed to load static build configuration from '\(staticBuildConfigPath)': \(error)") + } + } else { + self.buildConfig = .jextractDefault + } } } @@ -152,6 +169,7 @@ extension Swift2JavaTranslator { moduleName: self.swiftModuleName, inputs + [dependenciesSource], config: self.config, + buildConfig: self.buildConfig, log: self.log, ) self.lookupContext = SwiftTypeLookupContext(symbolTable: symbolTable) diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index 59b4c5bd..c6390bc1 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -373,7 +373,7 @@ final class Swift2JavaVisitor { in parent: ImportedNominalType?, sourceFilePath: String ) { - let (clause, _) = node.activeClause(in: .jextractDefault) + let (clause, _) = node.activeClause(in: translator.buildConfig) if let clause, let elements = clause.elements { switch elements { case .statements(let codeBlock): diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 9f5761f2..f8e5927c 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -24,6 +24,9 @@ struct SwiftParsedModuleSymbolTableBuilder { /// Imported modules to resolve type syntax. let importedModules: [String: SwiftModuleSymbolTable] + /// The build configuration used to resolve #if conditional compilation blocks. + let buildConfig: any BuildConfiguration + /// Extension decls their extended type hasn't been resolved. var unresolvedExtensions: [ExtensionDeclSyntax] @@ -32,6 +35,7 @@ struct SwiftParsedModuleSymbolTableBuilder { requiredAvailablityOfModuleWithName: String? = nil, alternativeModules: SwiftModuleSymbolTable.AlternativeModuleNamesData? = nil, importedModules: [String: SwiftModuleSymbolTable], + buildConfig: any BuildConfiguration = .jextractDefault, log: Logger? = nil ) { self.log = log @@ -41,6 +45,7 @@ struct SwiftParsedModuleSymbolTableBuilder { alternativeModules: alternativeModules ) self.importedModules = importedModules + self.buildConfig = buildConfig self.unresolvedExtensions = [] } @@ -165,7 +170,7 @@ extension SwiftParsedModuleSymbolTableBuilder { ifConfig node: IfConfigDeclSyntax, sourceFilePath: String ) { - let (clause, _) = node.activeClause(in: .jextractDefault) + let (clause, _) = node.activeClause(in: buildConfig) if let clause, let elements = clause.elements { switch elements { case .statements(let codeBlock): diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift index d1a5d9e0..5ba824c4 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import CodePrinting +import SwiftIfConfig import SwiftJavaConfigurationShared import SwiftParser import SwiftSyntax @@ -69,6 +70,7 @@ extension SwiftSymbolTable { moduleName: String, _ inputFiles: some Collection, config: Configuration?, + buildConfig: any BuildConfiguration = .jextractDefault, log: Logger, ) -> SwiftSymbolTable { @@ -104,6 +106,7 @@ extension SwiftSymbolTable { var stubBuilder = SwiftParsedModuleSymbolTableBuilder( moduleName: stubModuleName, importedModules: ["Swift": importedModules["Swift"]!], + buildConfig: buildConfig, ) stubBuilder.handle(sourceFile: sourceFile, sourceFilePath: "\(stubModuleName)_stub.swift") let stubModule = stubBuilder.finalize() @@ -122,6 +125,7 @@ extension SwiftSymbolTable { var builder = SwiftParsedModuleSymbolTableBuilder( moduleName: moduleName, importedModules: importedModules, + buildConfig: buildConfig, log: log, ) // First, register top-level and nested nominal types to the symbol table. diff --git a/Sources/SwiftJavaConfigurationShared/Configuration.swift b/Sources/SwiftJavaConfigurationShared/Configuration.swift index 502996c4..24beabaa 100644 --- a/Sources/SwiftJavaConfigurationShared/Configuration.swift +++ b/Sources/SwiftJavaConfigurationShared/Configuration.swift @@ -132,6 +132,10 @@ public struct Configuration: Codable { /// ``` public var specialize: [String: SpecializationConfigEntry]? + /// If set, use this JSON file as the static build configuration for jextract. + /// This allows users to provide a custom StaticBuildConfiguration for #if resolution. + public var staticBuildConfigurationFile: String? + // ==== wrap-java --------------------------------------------------------- /// The Java class path that should be passed along to the swift-java tool. diff --git a/Sources/SwiftJavaTool/Commands/JExtractCommand.swift b/Sources/SwiftJavaTool/Commands/JExtractCommand.swift index 93dc860f..b1789cc3 100644 --- a/Sources/SwiftJavaTool/Commands/JExtractCommand.swift +++ b/Sources/SwiftJavaTool/Commands/JExtractCommand.swift @@ -133,6 +133,12 @@ extension SwiftJava { @Option(help: "If specified, only generate bindings for this single Swift type name") var singleType: String? + + @Option( + help: + "Path to a JSON file containing a StaticBuildConfiguration. Used to resolve #if conditional compilation blocks." + ) + var staticBuildConfig: String? } } @@ -155,6 +161,7 @@ extension SwiftJava.JExtractCommand { configure(&config.swiftFilterInclude, append: self.filterInclude) configure(&config.swiftFilterExclude, append: self.filterExclude) configure(&config.singleType, overrideWith: self.singleType) + configure(&config.staticBuildConfigurationFile, overrideWith: self.staticBuildConfig) try checkModeCompatibility(config: config) From e3e6f348565429f06858e77a6f1ced576fe902e9 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 17:41:03 +0900 Subject: [PATCH 07/16] Add overrideWithStaticBuildConfigurationFile test case --- .../JExtractSwiftPlugin.swift | 9 ++- .../Asserts/TextAssertions.swift | 12 +++- Tests/JExtractSwiftTests/IfConfigTests.swift | 72 +++++++++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index 9c2f48a5..43b6a250 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -46,7 +46,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { // The name of the configuration file SwiftJava.config from the target for // which we are generating Swift wrappers for Java classes. let configFile = sourceDir.appending(path: "swift-java.config") - let configuration = try readConfiguration(sourceDir: sourceDir) + let configuration = try readConfiguration(configPath: configFile) // We use the the usual maven-style structure of "src/[generated|main|test]/java/..." // that is common in JVM ecosystem @@ -71,6 +71,13 @@ struct JExtractSwiftBuildToolPlugin: 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. ] + if let staticBuildConfig = configuration?.staticBuildConfigurationFile { + guard let resolvedURL = URL(string: staticBuildConfig, relativeTo: configFile) else { + fatalError("Could not resolve 'staticBuildConfigurationFile' url") + } + arguments += ["--static-build-config", resolvedURL.absoluteURL.path(percentEncoded: false)] + } + let dependentConfigFilesArguments = dependentConfigFiles.flatMap { moduleAndConfigFile in let (moduleName, configFile) = moduleAndConfigFile return [ diff --git a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift index f0e1e72a..1f10b2aa 100644 --- a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift @@ -87,9 +87,17 @@ func assertOutput( let sourceLocation = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column) for notExpectedChunk in notExpectedChunks { + let outputNotContainsNotExpectedChunk = !output.contains(notExpectedChunk) #expect( - !output.contains(notExpectedChunk), - "Output must not contain:\n\(notExpectedChunk)\n\nGot output:\n\(output)", + outputNotContainsNotExpectedChunk, + """ + \("error: Output must not contain not expected chunk!".red) + ==== Not Expected output ----------------------------------------------- + \(notExpectedChunk.yellow) + ==== Got output ---------------------------------------------------- + \(output) + ==== --------------------------------------------------------------- + """, sourceLocation: sourceLocation ) } diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift index 20ee167b..d2f2a934 100644 --- a/Tests/JExtractSwiftTests/IfConfigTests.swift +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -14,6 +14,8 @@ import JExtractSwiftLib import Testing +import SwiftJavaConfigurationShared +import Foundation @Suite struct IfConfigTests { @@ -68,6 +70,60 @@ struct IfConfigTests { ) } + @Test + func overrideWithStaticBuildConfigurationFile() throws { + try withTemporaryFile( + suffix: "json", + contents: """ + { + "attributes": [], + "compilerVersion": { + "components": [6, 3] + }, + "customConditions": [ + "DEBUG" + ], + "endianness": "little", + "features": [], + "languageMode": { + "components": [5, 10] + }, + "targetArchitectures": [], + "targetAtomicBitWidths": [], + "targetEnvironments": [], + "targetOSs": [], + "targetObjectFileFormats": [], + "targetPointerAuthenticationSchemes": [], + "targetPointerBitWidth": 64, + "targetRuntimes": [] + } + """ + ) { staticBuildConfigFile in + var config = Configuration() + config.staticBuildConfigurationFile = staticBuildConfigFile.absoluteURL.path(percentEncoded: false) + + try assertOutput( + input: """ + #if DEBUG + public struct IsDebug {} + #else + public struct IsNotDebug {} + #endif + """, + config: config, + .ffm, + .java, + detectChunkByInitialLines: 1, + expectedChunks: [ + "public final class IsDebug", + ], + notExpectedChunks: [ + "public final class IsNotDebug", + ] + ) + } + } + @Test func swiftinterfaceCommonPattern() throws { try assertOutput( @@ -97,3 +153,19 @@ struct IfConfigTests { ) } } + +private func withTemporaryFile( + suffix: String, + contents: String = "", + in tempDirectory: URL = FileManager.default.temporaryDirectory, + _ perform: (URL) throws -> Void +) throws { + let tempFileName = "tmp_\(UUID().uuidString).\(suffix)" + let tempFileURL = tempDirectory.appendingPathComponent(tempFileName) + + try contents.write(to: tempFileURL, atomically: true, encoding: .utf8) + defer { + try? FileManager.default.removeItem(at: tempFileURL) + } + try perform(tempFileURL) +} From 8d291bd58c31450d8ff852a13b8ebf45dfdff8d3 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 2 Apr 2026 17:41:26 +0900 Subject: [PATCH 08/16] swift format --- Tests/JExtractSwiftTests/IfConfigTests.swift | 66 ++++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift index d2f2a934..f84668c6 100644 --- a/Tests/JExtractSwiftTests/IfConfigTests.swift +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -12,10 +12,10 @@ // //===----------------------------------------------------------------------===// +import Foundation import JExtractSwiftLib -import Testing import SwiftJavaConfigurationShared -import Foundation +import Testing @Suite struct IfConfigTests { @@ -75,50 +75,50 @@ struct IfConfigTests { try withTemporaryFile( suffix: "json", contents: """ - { - "attributes": [], - "compilerVersion": { - "components": [6, 3] - }, - "customConditions": [ - "DEBUG" - ], - "endianness": "little", - "features": [], - "languageMode": { - "components": [5, 10] - }, - "targetArchitectures": [], - "targetAtomicBitWidths": [], - "targetEnvironments": [], - "targetOSs": [], - "targetObjectFileFormats": [], - "targetPointerAuthenticationSchemes": [], - "targetPointerBitWidth": 64, - "targetRuntimes": [] - } - """ + { + "attributes": [], + "compilerVersion": { + "components": [6, 3] + }, + "customConditions": [ + "DEBUG" + ], + "endianness": "little", + "features": [], + "languageMode": { + "components": [5, 10] + }, + "targetArchitectures": [], + "targetAtomicBitWidths": [], + "targetEnvironments": [], + "targetOSs": [], + "targetObjectFileFormats": [], + "targetPointerAuthenticationSchemes": [], + "targetPointerBitWidth": 64, + "targetRuntimes": [] + } + """ ) { staticBuildConfigFile in var config = Configuration() config.staticBuildConfigurationFile = staticBuildConfigFile.absoluteURL.path(percentEncoded: false) try assertOutput( input: """ - #if DEBUG - public struct IsDebug {} - #else - public struct IsNotDebug {} - #endif - """, + #if DEBUG + public struct IsDebug {} + #else + public struct IsNotDebug {} + #endif + """, config: config, .ffm, .java, detectChunkByInitialLines: 1, expectedChunks: [ - "public final class IsDebug", + "public final class IsDebug" ], notExpectedChunks: [ - "public final class IsNotDebug", + "public final class IsNotDebug" ] ) } From c0d775860f89cb86ae05876765902828487bd621 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 09:45:23 +0900 Subject: [PATCH 09/16] Update Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift Co-authored-by: Konrad `ktoso` Malawski --- Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index 43b6a250..dd5d2f2b 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -73,7 +73,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { if let staticBuildConfig = configuration?.staticBuildConfigurationFile { guard let resolvedURL = URL(string: staticBuildConfig, relativeTo: configFile) else { - fatalError("Could not resolve 'staticBuildConfigurationFile' url") + fatalError("Could not resolve 'staticBuildConfigurationFile' url: \(staticBuildConfig)") } arguments += ["--static-build-config", resolvedURL.absoluteURL.path(percentEncoded: false)] } From e6fb03ab4487dda17445a4a8fd96518ade8eaaef Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 09:46:28 +0900 Subject: [PATCH 10/16] Move .shared into the type itself --- .../JExtractDefaultBuildConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift index f20d76d4..c3704065 100644 --- a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift +++ b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift @@ -18,6 +18,8 @@ import SwiftSyntax /// A default, fixed build configuration during static analysis for interface extraction. struct JExtractDefaultBuildConfiguration: BuildConfiguration { + static let shared = JExtractDefaultBuildConfiguration() + private var base: StaticBuildConfiguration init() { @@ -86,11 +88,9 @@ struct JExtractDefaultBuildConfiguration: BuildConfiguration { } } -private let shared = JExtractDefaultBuildConfiguration() - extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration { static var jextractDefault: JExtractDefaultBuildConfiguration { - shared + .shared } } From b93b3b1675ca4d85e6d91b71b48daf64a7d2c947 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 11:38:23 +0900 Subject: [PATCH 11/16] Introduce build tools plugin to capture static build configuration --- Package.swift | 22 +++++- .../_StaticBuildConfigPlugin.swift | 33 +++++++++ .../JExtractDefaultBuildConfiguration.swift | 12 ++-- .../StaticBuildConfigPluginExecutable.swift | 70 +++++++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift create mode 100644 Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift diff --git a/Package.swift b/Package.swift index cb3aa93b..73ac9744 100644 --- a/Package.swift +++ b/Package.swift @@ -123,7 +123,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-system", from: "1.4.0"), .package(url: "https://github.com/apple/swift-log", from: "1.2.0"), .package(url: "https://github.com/apple/swift-collections", .upToNextMinor(from: "1.3.0")), // primarily for ordered collections - .package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.2.1", traits: ["SubprocessFoundation"]), + .package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.4.0", traits: ["SubprocessFoundation"]), // Benchmarking .package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")), @@ -320,6 +320,7 @@ let package = Package( dependencies: [ .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftLexicalLookup", package: "swift-syntax"), + .product(name: "SwiftIfConfig", package: "swift-syntax"), .product(name: "SwiftSyntax", package: "swift-syntax"), .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), .product(name: "ArgumentParser", package: "swift-argument-parser"), @@ -331,6 +332,25 @@ let package = Package( ], swiftSettings: [ .swiftLanguageMode(.v5) + ], + plugins: [ + .plugin(name: "_StaticBuildConfigPlugin") + ] + ), + + .executableTarget( + name: "StaticBuildConfigPluginExecutable", + dependencies: [ + .product(name: "Subprocess", package: "swift-subprocess"), + .product(name: "SwiftIfConfig", package: "swift-syntax"), + ] + ), + + .plugin( + name: "_StaticBuildConfigPlugin", + capability: .buildTool(), + dependencies: [ + "StaticBuildConfigPluginExecutable" ] ), diff --git a/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift b/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift new file mode 100644 index 00000000..4816f59c --- /dev/null +++ b/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 Foundation +import PackagePlugin + +@main +struct _StaticBuildConfigPlugin: BuildToolPlugin { + func createBuildCommands(context: PluginContext, target: any Target) async throws -> [Command] { + let outSwift = context.pluginWorkDirectoryURL.appending(path: "StaticBuildConfiguration+embedded.swift") + return [ + .buildCommand( + displayName: "Run -print-static-build-config", + executable: try context.tool(named: "StaticBuildConfigPluginExecutable").url, + arguments: [outSwift.absoluteURL.path(percentEncoded: false)], + environment: [:], + inputFiles: [], + outputFiles: [outSwift.absoluteURL] + ) + ] + } +} diff --git a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift index c3704065..cdca4355 100644 --- a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift +++ b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift @@ -24,7 +24,11 @@ struct JExtractDefaultBuildConfiguration: BuildConfiguration { init() { let decoder = JSONDecoder() - base = try! decoder.decode(StaticBuildConfiguration.self, from: printStaticBuildConfigOutput) + do { + base = try decoder.decode(StaticBuildConfiguration.self, from: StaticBuildConfiguration.embedded) + } catch { + fatalError("Embedded StaticBuildConfiguration is broken! data: \(String(data: StaticBuildConfiguration.embedded, encoding: .utf8) ?? "")") + } } func isCustomConditionSet(name: String) throws -> Bool { @@ -93,9 +97,3 @@ extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration { .shared } } - -// $ swift frontend -print-static-build-config -target aarch64-unknown-linux-gnu -private let printStaticBuildConfigOutput = Data( - #"{"attributes":["GKInspectable","noDerivative","objcMembers","discardableResult","const","available","usableFromInline","preconcurrency","nonisolated","retroactive","frozen","unsafe","propertyWrapper","lifetime","_extern","inline","abi","storageRestrictions","_opaqueReturnTypeOf","objc","constInitialized","autoclosure","escaping","unchecked","requires_stored_property_inits","convention","attached","nonexhaustive","dynamicCallable","reasync","dynamicMemberLookup","NSCopying","transpose","warn_unqualified_access","c","globalActor","isolated","_local","rethrows","exclusivity","backDeployed","UIApplicationMain","main","nonobjc","resultBuilder","Sendable","_noMetadata","IBDesignable","IBOutlet","export","IBSegueAction","IBAction","derivative","NSApplicationMain","inlinable","concurrent","IBInspectable","NSManaged","_addressable","differentiable"],"compilerVersion":{"components":[6,3]},"customConditions":[],"endianness":"little","features":["BuiltinCreateAsyncTaskWithExecutor","BuiltinCreateAsyncTaskName","Macros","ValueGenericsNameLookup","FreestandingExpressionMacros","AssociatedTypeAvailability","NoncopyableGenerics2","BuiltinInterleave","OptionalIsolatedParameters","BuiltinAddressOfRawLayout","InoutLifetimeDependence","ExtensionMacros","BuiltinBuildMainExecutor","BuiltinStoreRaw","InheritActorContext","IsolatedAny","BuiltinExecutor","LayoutPrespecialization","NonexhaustiveAttribute","InlineAlways","BuiltinCreateTaskGroupWithFlags","ValueGenerics","InlineArrayTypeSugar","BuiltinCreateTask","NonescapableTypes","RetroactiveAttribute","BuiltinTaskRunInline","BuiltinBuildComplexEqualityExecutor","MemorySafetyAttributes","BuiltinBuildExecutor","ModuleSelector","ParameterPacks","MarkerProtocol","ConformanceSuppression","BitwiseCopyable","AddressOfProperty2","UnsafeInheritExecutor","BuiltinCreateAsyncTaskOwnedTaskExecutor","SpecializeAttributeWithAvailability","BuiltinJob","AlwaysInheritActorContext","AsyncExecutionBehaviorAttributes","AsyncSequenceFailure","BorrowingSwitch","PrimaryAssociatedTypes2","NewCxxMethodSafetyHeuristics","ExtensionMacroAttr","BuiltinStackAlloc","TypedThrows","BuiltinContinuation","BuiltinUnprotectedAddressOf","BuiltinSelect","ImplicitSelfCapture","MoveOnlyPartialConsumption","Actors","GeneralizedIsSameMetaTypeBuiltin","MoveOnly","GlobalActors","BuiltinCreateAsyncDiscardingTaskInGroupWithExecutor","NonfrozenEnumExhaustivity","RethrowsProtocol","IsolatedDeinit","BuiltinUnprotectedStackAlloc","BuiltinCreateAsyncTaskInGroupWithExecutor","BuiltinTaskGroupWithArgument","NoAsyncAvailability","ObjCImplementation","AttachedMacros","FreestandingMacros","AsyncAwait","EffectfulProp","BuiltinConcurrencyStackNesting","BuiltinCreateAsyncTaskInGroup","ConcurrentFunctions","BuiltinHopToActor","BuiltinIntLiteralAccessors","BodyMacros","BuiltinVectorsExternC","BuiltinCreateAsyncDiscardingTaskInGroup","RawIdentifiers","NonescapableAccessorOnTrivial","UnavailableFromAsync","IsolatedAny2","LexicalLifetimes","SendableCompletionHandlers","BuiltinAssumeAlignment","AssociatedTypeImplements","Sendable","ABIAttributeSE0479","BuiltinBuildTaskExecutorRef","LifetimeDependenceMutableAccessors","BitwiseCopyable2","IsolatedConformances","ExpressionMacroDefaultArguments","NoncopyableGenerics","SendingArgsAndResults","MoveOnlyResilientTypes","BuiltinEmplaceTypedThrows"],"languageMode":{"components":[5,10]},"targetArchitectures":["arm64"],"targetAtomicBitWidths":[128,64,32,16,8],"targetEnvironments":[],"targetOSs":["Linux"],"targetObjectFileFormats":["ELF"],"targetPointerAuthenticationSchemes":["_none"],"targetPointerBitWidth":64,"targetRuntimes":["_Native","_multithreaded"]}"# - .utf8 -) diff --git a/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift b/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift new file mode 100644 index 00000000..3c747965 --- /dev/null +++ b/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 Foundation +import Subprocess +import SwiftIfConfig + +@main struct StaticBuildConfigPluginExecutable { + static func main() async throws { + let args = CommandLine.arguments + guard args.count > 1 else { + print("Usage: \(args[0]) ") + return + } + let dst = URL(fileURLWithPath: args[1]) + + let data = try await loadStaticBuildConfig() + let template = #""" + import Foundation + import SwiftIfConfig + + extension StaticBuildConfiguration { + static var embedded: Data { + Data(#"\#(data)"#.utf8) + } + } + """# + try template.write(to: dst, atomically: true, encoding: .utf8) + } + + static func loadStaticBuildConfig() async throws -> String { + #if compiler(>=6.3) + let result = try await run( + .name("swift"), + arguments: ["frontend", "-print-static-build-config", "-target", "aarch64-unknown-linux-gnu"], + output: .string(limit: 65536), + error: .string(limit: 65536) + ) + if let error = result.standardError, !error.isEmpty { + fatalError(error) + } + return result.standardOutput ?? "" + #else + #if compiler(>=6.2) + let configuration = StaticBuildConfiguration( + languageVersion: .init(components: [5, 10]), + compilerVersion: .init(components: [6, 2]) + ) + #else + let configuration = StaticBuildConfiguration( + languageVersion: .init(components: [5, 10]), + compilerVersion: .init(components: [6, 1]) + ) + #endif + let encoder = JSONEncoder() + return String(data: try encoder.encode(configuration), encoding: .utf8) ?? "" + #endif + } +} From aee4cc75fcc8389f492e9ecb943535bc1a35f38c Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 12:22:55 +0900 Subject: [PATCH 12/16] Skip swiftinterfaceCommonPattern test for old compiler --- Tests/JExtractSwiftTests/IfConfigTests.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift index f84668c6..1e2e74ed 100644 --- a/Tests/JExtractSwiftTests/IfConfigTests.swift +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -17,6 +17,12 @@ import JExtractSwiftLib import SwiftJavaConfigurationShared import Testing +#if compiler(>=6.3) +private let `compiler(>=6.3)` = true +#else +private let `compiler(>=6.3)` = false +#endif + @Suite struct IfConfigTests { @Test @@ -124,7 +130,7 @@ struct IfConfigTests { } } - @Test + @Test(.enabled(if: `compiler(>=6.3)`)) func swiftinterfaceCommonPattern() throws { try assertOutput( input: """ From ca64cd981bfaa49aaddcda1111d1a2c323e6663c Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 14:14:16 +0900 Subject: [PATCH 13/16] Fix for swift 6.1 --- Tests/JExtractSwiftTests/IfConfigTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/JExtractSwiftTests/IfConfigTests.swift b/Tests/JExtractSwiftTests/IfConfigTests.swift index 1e2e74ed..6eddc08b 100644 --- a/Tests/JExtractSwiftTests/IfConfigTests.swift +++ b/Tests/JExtractSwiftTests/IfConfigTests.swift @@ -18,9 +18,9 @@ import SwiftJavaConfigurationShared import Testing #if compiler(>=6.3) -private let `compiler(>=6.3)` = true +private let has6_3compilerFeature = true #else -private let `compiler(>=6.3)` = false +private let has6_3compilerFeature = false #endif @Suite @@ -130,7 +130,7 @@ struct IfConfigTests { } } - @Test(.enabled(if: `compiler(>=6.3)`)) + @Test(.enabled(if: has6_3compilerFeature)) func swiftinterfaceCommonPattern() throws { try assertOutput( input: """ From 711b13e6d25d5620b62ba869e3d211a66aa0b715 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 6 Apr 2026 09:43:44 +0900 Subject: [PATCH 14/16] Add document for --static-build-config --- .../Documentation.docc/SupportedFeatures.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md index c320bedc..4a681d86 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md @@ -464,3 +464,40 @@ public final class FishBox ... { > NOTE: Currently no helpers are available to convert between unspecialized types to specialized ones, but this can be offered > as additional `box.as(FishBox.class)` conversion methods in the future. + +### Evaluating `#if` + +In jextract, `#if` branches are evaluated using [SwiftIfConfig](https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftIfConfig/SwiftIfConfig.docc/SwiftIfConfig.md). +The evaluation parameters are fixed; for example, the `os` expression always evaluates to true, so in the following case the value of the variable will be `Linux`. + +```swift +#if os(Linux) +let os = "Linux" +#elseif os(Android) +let os = "Android" +#else +let os = "Other" +#endif +``` + +If you want the above situation to be evaluated as `Android`, you can override the evaluation parameters. +First, obtain a [StaticBuildConfiguration](https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftIfConfig/StaticBuildConfiguration.swift) with the following command and save it to a file. +(Adjust `-target` to match the environment you want to build for. This command is available from Swift 6.3.) + +```sh +swift frontend -print-static-build-config -target aarch64-unknown-linux-android28 > static-build-config.json +``` + +Then pass the path to that file when running jextract. + +- When using the jextract command: `--static-build-config ` +- When configuring via `swift-java.config`: + ```json + { + ... + "staticBuildConfigurationFile": "" // Relative path from `swift-java.config` + } + ``` + +As a result, jextract will evaluate `os` as `Android`. + From e7203ee636698522d4022ffce8dafb30cfd85da7 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 7 Apr 2026 11:19:26 +0900 Subject: [PATCH 15/16] Use module bundle to avoid SwiftPM bug --- Package.swift | 3 +++ .../_StaticBuildConfigPlugin.swift | 9 +++++---- .../MySwiftLibrary/MySwiftLibrary.swift | 2 ++ .../JExtractDefaultBuildConfiguration.swift | 10 +++++++--- Sources/JExtractSwiftLib/Resources/dummy | 1 + .../StaticBuildConfigPluginExecutable.swift | 20 +++++-------------- 6 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 Sources/JExtractSwiftLib/Resources/dummy diff --git a/Package.swift b/Package.swift index 73ac9744..376ed040 100644 --- a/Package.swift +++ b/Package.swift @@ -330,6 +330,9 @@ let package = Package( "SwiftJavaConfigurationShared", "CodePrinting", ], + resources: [ + .process("Resources") + ], swiftSettings: [ .swiftLanguageMode(.v5) ], diff --git a/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift b/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift index 4816f59c..7feb8392 100644 --- a/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift +++ b/Plugins/_StaticBuildConfigPlugin/_StaticBuildConfigPlugin.swift @@ -18,15 +18,16 @@ import PackagePlugin @main struct _StaticBuildConfigPlugin: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: any Target) async throws -> [Command] { - let outSwift = context.pluginWorkDirectoryURL.appending(path: "StaticBuildConfiguration+embedded.swift") + let executable = try context.tool(named: "StaticBuildConfigPluginExecutable").url + let out = context.pluginWorkDirectoryURL.appending(path: "static-build-config.json") return [ .buildCommand( displayName: "Run -print-static-build-config", - executable: try context.tool(named: "StaticBuildConfigPluginExecutable").url, - arguments: [outSwift.absoluteURL.path(percentEncoded: false)], + executable: executable, + arguments: [out.path(percentEncoded: false)], environment: [:], inputFiles: [], - outputFiles: [outSwift.absoluteURL] + outputFiles: [out] ) ] } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index b536abc5..5e35ac91 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -21,6 +21,8 @@ import Foundation #if os(Linux) import Glibc +#elseif os(Android) +import Android #else import Darwin.C #endif diff --git a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift index cdca4355..65adf8ff 100644 --- a/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift +++ b/Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift @@ -23,11 +23,15 @@ struct JExtractDefaultBuildConfiguration: BuildConfiguration { private var base: StaticBuildConfiguration init() { - let decoder = JSONDecoder() + guard let url = Bundle.module.url(forResource: "static-build-config", withExtension: "json") else { + fatalError("static-build-config.json is not found in module bundle") + } do { - base = try decoder.decode(StaticBuildConfiguration.self, from: StaticBuildConfiguration.embedded) + let data = try Data(contentsOf: url) + let decoder = JSONDecoder() + base = try decoder.decode(StaticBuildConfiguration.self, from: data) } catch { - fatalError("Embedded StaticBuildConfiguration is broken! data: \(String(data: StaticBuildConfiguration.embedded, encoding: .utf8) ?? "")") + fatalError("\(error)") } } diff --git a/Sources/JExtractSwiftLib/Resources/dummy b/Sources/JExtractSwiftLib/Resources/dummy new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Sources/JExtractSwiftLib/Resources/dummy @@ -0,0 +1 @@ + diff --git a/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift b/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift index 3c747965..52a1d07b 100644 --- a/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift +++ b/Sources/StaticBuildConfigPluginExecutable/StaticBuildConfigPluginExecutable.swift @@ -26,31 +26,21 @@ import SwiftIfConfig let dst = URL(fileURLWithPath: args[1]) let data = try await loadStaticBuildConfig() - let template = #""" - import Foundation - import SwiftIfConfig - - extension StaticBuildConfiguration { - static var embedded: Data { - Data(#"\#(data)"#.utf8) - } - } - """# - try template.write(to: dst, atomically: true, encoding: .utf8) + try data.write(to: dst, options: .atomic) } - static func loadStaticBuildConfig() async throws -> String { + static func loadStaticBuildConfig() async throws -> Data { #if compiler(>=6.3) let result = try await run( .name("swift"), arguments: ["frontend", "-print-static-build-config", "-target", "aarch64-unknown-linux-gnu"], - output: .string(limit: 65536), + output: .data(limit: 65536), error: .string(limit: 65536) ) if let error = result.standardError, !error.isEmpty { fatalError(error) } - return result.standardOutput ?? "" + return result.standardOutput #else #if compiler(>=6.2) let configuration = StaticBuildConfiguration( @@ -64,7 +54,7 @@ import SwiftIfConfig ) #endif let encoder = JSONEncoder() - return String(data: try encoder.encode(configuration), encoding: .utf8) ?? "" + return try encoder.encode(configuration) #endif } } From 01bd03e3f50e2c20b973ca7e38a463bf0b567603 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 7 Apr 2026 11:48:07 +0900 Subject: [PATCH 16/16] Avoid license header check --- Sources/JExtractSwiftLib/Resources/dummy | 1 - Sources/JExtractSwiftLib/Resources/dummy.json | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 Sources/JExtractSwiftLib/Resources/dummy create mode 100644 Sources/JExtractSwiftLib/Resources/dummy.json diff --git a/Sources/JExtractSwiftLib/Resources/dummy b/Sources/JExtractSwiftLib/Resources/dummy deleted file mode 100644 index 8b137891..00000000 --- a/Sources/JExtractSwiftLib/Resources/dummy +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Sources/JExtractSwiftLib/Resources/dummy.json b/Sources/JExtractSwiftLib/Resources/dummy.json new file mode 100644 index 00000000..8a373545 --- /dev/null +++ b/Sources/JExtractSwiftLib/Resources/dummy.json @@ -0,0 +1,2 @@ +// The actual resources will be generated by the buildToolsPlugin. This file is necessary for SwiftPM to generate a module bundle. +{}