Skip to content

Commit 30e122a

Browse files
committed
Add enableFutureFeature and enableExperimentalFeature Swift settings.
1 parent 5bd3355 commit 30e122a

File tree

6 files changed

+106
-3
lines changed

6 files changed

+106
-3
lines changed

Sources/PackageDescription/BuildSettings.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,53 @@ public struct SwiftSetting: Encodable {
296296
public static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> SwiftSetting {
297297
return SwiftSetting(name: "unsafeFlags", value: flags, condition: condition)
298298
}
299+
300+
/// Enable an future feature with the given name.
301+
///
302+
/// A future feature is one that has been accepted into Swift as of a
303+
/// certain language version, but is not available by default in prior
304+
/// language modes because it has some impact on source compatibility.
305+
///
306+
/// Multiple future features can be added to a given target, and can
307+
/// be used in a target without affecting its dependencies. An unknown
308+
/// future feature will be ignored by the implementation.
309+
///
310+
/// - Since: First available in PackageDescription 5.7.
311+
///
312+
/// - Parameters:
313+
/// - name: The name of the future feature, e.g., ConciseMagicFile.
314+
/// - condition: A condition that restricts the application of the build
315+
/// setting.
316+
public static func enableFutureFeature(
317+
_ name: String,
318+
_ condition: BuildSettingCondition? = nil
319+
) -> SwiftSetting {
320+
return SwiftSetting(
321+
name: "futureFeatures", value: [name], condition: condition)
322+
}
323+
324+
/// Enable an experimental feature with the given name.
325+
///
326+
/// An experimental feature is one that has is in development, but
327+
/// has not been accepted into Swift as a language feature.
328+
///
329+
/// Multiple experimental features can be added to a given target, and can
330+
/// be used in a target without affecting its dependencies. An unknown
331+
/// experimental feature will be ignored by the implementation.
332+
///
333+
/// - Since: First available in PackageDescription 5.7.
334+
///
335+
/// - Parameters:
336+
/// - name: The name of the experimental feature, e.g., VariadicGenerics.
337+
/// - condition: A condition that restricts the application of the build
338+
/// setting.
339+
public static func enableExperimentalFeature(
340+
_ name: String,
341+
_ condition: BuildSettingCondition? = nil
342+
) -> SwiftSetting {
343+
return SwiftSetting(
344+
name: "experimentalFeatures", value: [name], condition: condition)
345+
}
299346
}
300347

301348
/// A linker build setting.

Sources/PackageLoading/ManifestJSONParser.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ enum ManifestJSONParser {
425425
kind = .linkedFramework(value)
426426
case "unsafeFlags":
427427
kind = .unsafeFlags(values)
428+
case "futureFeatures":
429+
kind = .futureFeatures(values)
430+
case "experimentalFeatures":
431+
kind = .experimentalFeatures(values)
428432
default:
429433
throw InternalError("invalid build setting \(name)")
430434
}

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,30 @@ public final class PackageBuilder {
927927
case .linker:
928928
decl = .OTHER_LDFLAGS
929929
}
930+
931+
case .futureFeatures(let _values):
932+
switch setting.tool {
933+
case .c, .cxx, .linker:
934+
throw InternalError("only Swift supports future features")
935+
936+
case .swift:
937+
decl = .OTHER_SWIFT_FLAGS
938+
}
939+
940+
values = _values.precedeElements(with: "-enable-future-feature")
941+
942+
case .experimentalFeatures(let _values):
943+
switch setting.tool {
944+
case .c, .cxx, .linker:
945+
throw InternalError(
946+
"only Swift supports experimental features")
947+
948+
case .swift:
949+
decl = .OTHER_SWIFT_FLAGS
950+
}
951+
952+
values = _values.precedeElements(
953+
with: "-enable-experimental-feature")
930954
}
931955

932956
// Create an assignment for this setting.
@@ -1397,3 +1421,23 @@ extension PackageBuilder {
13971421
}
13981422
}
13991423
}
1424+
1425+
fileprivate extension Sequence {
1426+
/// Construct a new array where each of the elements in the \c self
1427+
/// sequence is preceded by the \c prefixElement.
1428+
///
1429+
/// For example:
1430+
/// ```
1431+
/// ["Alice", "Bob", "Charlie"].precedeElements(with: "Hi")
1432+
/// ```
1433+
///
1434+
/// produces `["Hi", "Alice", "Hi", "Bob", "Hi", "Charlie"]`.
1435+
func precedeElements(with prefixElement: Element) -> [Element] {
1436+
var results: [Element] = []
1437+
for element in self {
1438+
results.append(prefixElement)
1439+
results.append(element)
1440+
}
1441+
return results
1442+
}
1443+
}

Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public enum TargetBuildSettingDescription {
2929
case linkedFramework(String)
3030

3131
case unsafeFlags([String])
32+
case futureFeatures([String])
33+
case experimentalFeatures([String])
3234
}
3335

3436
/// An individual build setting.

Sources/PackageModel/ManifestSourceGeneration.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ fileprivate extension SourceCodeFragment {
481481
params.append(SourceCodeFragment(from: condition))
482482
}
483483
self.init(enum: setting.kind.name, subnodes: params)
484-
case .unsafeFlags(let values):
484+
case .unsafeFlags(let values), .futureFeatures(let values), .experimentalFeatures(let values):
485485
params.append(SourceCodeFragment(strings: values))
486486
if let condition = setting.condition {
487487
params.append(SourceCodeFragment(from: condition))
@@ -633,6 +633,10 @@ extension TargetBuildSettingDescription.Kind {
633633
return "linkedFramework"
634634
case .unsafeFlags:
635635
return "unsafeFlags"
636+
case .futureFeatures:
637+
return "futureFeatures"
638+
case .experimentalFeatures:
639+
return "experimentalFeatures"
636640
}
637641
}
638642
}

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,6 +2486,8 @@ final class BuildPlanTests: XCTestCase {
24862486
.init(tool: .swift, kind: .define("RLINUX"), condition: .init(platformNames: ["linux"], config: "release")),
24872487
.init(tool: .swift, kind: .define("DMACOS"), condition: .init(platformNames: ["macos"], config: "debug")),
24882488
.init(tool: .swift, kind: .unsafeFlags(["-Isfoo", "-L", "sbar"])),
2489+
.init(tool: .swift, kind: .futureFeatures(["BestFeature"])),
2490+
.init(tool: .swift, kind: .futureFeatures(["WorstFeature"]), condition: .init(platformNames: ["macos"], config: "debug"))
24892491
]
24902492
),
24912493
try TargetDescription(
@@ -2550,7 +2552,7 @@ final class BuildPlanTests: XCTestCase {
25502552
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", .end])
25512553

25522554
let bar = try result.target(for: "bar").swiftTarget().compileArguments()
2553-
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", .end])
2555+
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", "-enable-future-feature", "BestFeature", .end])
25542556

25552557
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
25562558
XCTAssertMatch(exe, [.anySequence, "-DFOO", .end])
@@ -2566,7 +2568,7 @@ final class BuildPlanTests: XCTestCase {
25662568
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", .end])
25672569

25682570
let bar = try result.target(for: "bar").swiftTarget().compileArguments()
2569-
XCTAssertMatch(bar, [.anySequence, "-DDMACOS", "-Isfoo", "-L", "sbar", .end])
2571+
XCTAssertMatch(bar, [.anySequence, "-DDMACOS", "-Isfoo", "-L", "sbar", "-enable-future-feature", "BestFeature", "-enable-future-feature", "WorstFeature", .end])
25702572

25712573
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
25722574
XCTAssertMatch(exe, [.anySequence, "-DFOO", .end])

0 commit comments

Comments
 (0)