Skip to content

Commit b93b3b1

Browse files
committed
Introduce build tools plugin to capture static build configuration
1 parent e6fb03a commit b93b3b1

4 files changed

Lines changed: 129 additions & 8 deletions

File tree

Package.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ let package = Package(
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"),
@@ -331,6 +332,25 @@ let package = Package(
331332
],
332333
swiftSettings: [
333334
.swiftLanguageMode(.v5)
335+
],
336+
plugins: [
337+
.plugin(name: "_StaticBuildConfigPlugin")
338+
]
339+
),
340+
341+
.executableTarget(
342+
name: "StaticBuildConfigPluginExecutable",
343+
dependencies: [
344+
.product(name: "Subprocess", package: "swift-subprocess"),
345+
.product(name: "SwiftIfConfig", package: "swift-syntax"),
346+
]
347+
),
348+
349+
.plugin(
350+
name: "_StaticBuildConfigPlugin",
351+
capability: .buildTool(),
352+
dependencies: [
353+
"StaticBuildConfigPluginExecutable"
334354
]
335355
),
336356

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 outSwift = context.pluginWorkDirectoryURL.appending(path: "StaticBuildConfiguration+embedded.swift")
22+
return [
23+
.buildCommand(
24+
displayName: "Run -print-static-build-config",
25+
executable: try context.tool(named: "StaticBuildConfigPluginExecutable").url,
26+
arguments: [outSwift.absoluteURL.path(percentEncoded: false)],
27+
environment: [:],
28+
inputFiles: [],
29+
outputFiles: [outSwift.absoluteURL]
30+
)
31+
]
32+
}
33+
}

Sources/JExtractSwiftLib/JExtractDefaultBuildConfiguration.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ struct JExtractDefaultBuildConfiguration: BuildConfiguration {
2424

2525
init() {
2626
let decoder = JSONDecoder()
27-
base = try! decoder.decode(StaticBuildConfiguration.self, from: printStaticBuildConfigOutput)
27+
do {
28+
base = try decoder.decode(StaticBuildConfiguration.self, from: StaticBuildConfiguration.embedded)
29+
} catch {
30+
fatalError("Embedded StaticBuildConfiguration is broken! data: \(String(data: StaticBuildConfiguration.embedded, encoding: .utf8) ?? "")")
31+
}
2832
}
2933

3034
func isCustomConditionSet(name: String) throws -> Bool {
@@ -93,9 +97,3 @@ extension BuildConfiguration where Self == JExtractDefaultBuildConfiguration {
9397
.shared
9498
}
9599
}
96-
97-
// $ swift frontend -print-static-build-config -target aarch64-unknown-linux-gnu
98-
private let printStaticBuildConfigOutput = Data(
99-
#"{"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"]}"#
100-
.utf8
101-
)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 Subprocess
17+
import SwiftIfConfig
18+
19+
@main struct StaticBuildConfigPluginExecutable {
20+
static func main() async throws {
21+
let args = CommandLine.arguments
22+
guard args.count > 1 else {
23+
print("Usage: \(args[0]) <destination_path>")
24+
return
25+
}
26+
let dst = URL(fileURLWithPath: args[1])
27+
28+
let data = try await loadStaticBuildConfig()
29+
let template = #"""
30+
import Foundation
31+
import SwiftIfConfig
32+
33+
extension StaticBuildConfiguration {
34+
static var embedded: Data {
35+
Data(#"\#(data)"#.utf8)
36+
}
37+
}
38+
"""#
39+
try template.write(to: dst, atomically: true, encoding: .utf8)
40+
}
41+
42+
static func loadStaticBuildConfig() async throws -> String {
43+
#if compiler(>=6.3)
44+
let result = try await run(
45+
.name("swift"),
46+
arguments: ["frontend", "-print-static-build-config", "-target", "aarch64-unknown-linux-gnu"],
47+
output: .string(limit: 65536),
48+
error: .string(limit: 65536)
49+
)
50+
if let error = result.standardError, !error.isEmpty {
51+
fatalError(error)
52+
}
53+
return result.standardOutput ?? ""
54+
#else
55+
#if compiler(>=6.2)
56+
let configuration = StaticBuildConfiguration(
57+
languageVersion: .init(components: [5, 10]),
58+
compilerVersion: .init(components: [6, 2])
59+
)
60+
#else
61+
let configuration = StaticBuildConfiguration(
62+
languageVersion: .init(components: [5, 10]),
63+
compilerVersion: .init(components: [6, 1])
64+
)
65+
#endif
66+
let encoder = JSONEncoder()
67+
return String(data: try encoder.encode(configuration), encoding: .utf8) ?? ""
68+
#endif
69+
}
70+
}

0 commit comments

Comments
 (0)