Skip to content

Commit 4569c7f

Browse files
committed
Enable swift module interfaces if the package author enables library evolution via unsafe flags
There isn't currently a way for package authors to enable library evolution or module interfaces from the package manifest. They can pass `-enable-library-evolution` in their unsafe flags, but because `-emit-module-interface` requires a path parameter, it isn't something that can be set in the manifest. This adds a way to infer XCBuild settings based on values set in manifest-declared settings. The idea is to implement semantics appropriately for each platform based on generalized flags passed from the manifest. rdar://78773077
1 parent 30f8a7f commit 4569c7f

File tree

8 files changed

+110
-5
lines changed

8 files changed

+110
-5
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// swift-tools-version:5.1
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "LibraryEvolution",
6+
products: [
7+
],
8+
targets: [
9+
.target(name: "A", dependencies: [], swiftSettings: [.unsafeFlags(["-enable-library-evolution"])]),
10+
.target(name: "B", dependencies: ["A"], swiftSettings: [.unsafeFlags(["-enable-library-evolution"])]),
11+
])

Fixtures/Miscellaneous/LibraryEvolution/Sources/A/A.swift

Whitespace-only changes.

Fixtures/Miscellaneous/LibraryEvolution/Sources/B/B.swift

Whitespace-only changes.

Sources/Build/BuildPlan.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -778,14 +778,14 @@ public final class SwiftTargetBuildDescription {
778778
args += ["-color-diagnostics"]
779779
}
780780

781-
// Add the output for the `.swiftinterface`, if requested.
782-
if buildParameters.enableParseableModuleInterfaces {
783-
args += ["-emit-parseable-module-interface-path", parseableModuleInterfaceOutputPath.pathString]
784-
}
785-
786781
// Add agruments from declared build settings.
787782
args += self.buildSettingsFlags()
788783

784+
// Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other way.
785+
if buildParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") {
786+
args += ["-emit-module-interface-path", parseableModuleInterfaceOutputPath.pathString]
787+
}
788+
789789
// User arguments (from -Xswiftc) should follow generated arguments to allow user overrides
790790
args += buildParameters.swiftCompilerFlags
791791
return args

Sources/XCBuildSupport/PIF.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ public enum PIF {
936936
case WATCHOS_DEPLOYMENT_TARGET
937937
case MARKETING_VERSION
938938
case CURRENT_PROJECT_VERSION
939+
case SWIFT_EMIT_MODULE_INTERFACE
939940
}
940941

941942
public enum MultipleValueSetting: String, Codable {

Sources/XCBuildSupport/PIFBuilder.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,21 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
812812

813813
return bundleName
814814
}
815+
816+
// Add inferred build settings for a particular value for a manifest setting and value.
817+
private func addInferredBuildSettings(
818+
for setting: PIF.BuildSettings.MultipleValueSetting,
819+
value: [String],
820+
platform: PIF.BuildSettings.Platform? = nil,
821+
configuration: BuildConfiguration,
822+
settings: inout PIF.BuildSettings
823+
) {
824+
// Automatically set SWIFT_EMIT_MODULE_INTERFACE if the package author uses unsafe flags to enable
825+
// library evolution (this is needed until there is a way to specify this in the package manifest).
826+
if setting == .OTHER_SWIFT_FLAGS && value.contains("-enable-library-evolution") {
827+
settings[.SWIFT_EMIT_MODULE_INTERFACE] = "YES"
828+
}
829+
}
815830

816831
// Apply target-specific build settings defined in the manifest.
817832
private func addManifestBuildSettings(
@@ -833,8 +848,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
833848
switch configuration {
834849
case .debug:
835850
debugSettings[setting, for: platform, default: ["$(inherited)"]] += value
851+
addInferredBuildSettings(for: setting, value: value, platform: platform, configuration: .debug, settings: &debugSettings)
836852
case .release:
837853
releaseSettings[setting, for: platform, default: ["$(inherited)"]] += value
854+
addInferredBuildSettings(for: setting, value: value, platform: platform, configuration: .release, settings: &releaseSettings)
838855
}
839856
}
840857

@@ -847,8 +864,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
847864
switch configuration {
848865
case .debug:
849866
debugSettings[setting, default: ["$(inherited)"]] += value
867+
addInferredBuildSettings(for: setting, value: value, configuration: .debug, settings: &debugSettings)
850868
case .release:
851869
releaseSettings[setting, default: ["$(inherited)"]] += value
870+
addInferredBuildSettings(for: setting, value: value, configuration: .release, settings: &releaseSettings)
852871
}
853872
}
854873

Tests/CommandsTests/BuildToolTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,18 @@ final class BuildToolTests: XCTestCase {
246246
}
247247
}
248248

249+
func testAutomaticParseableInterfacesWithLibraryEvolution() {
250+
fixture(name: "Miscellaneous/LibraryEvolution") { path in
251+
do {
252+
let result = try build([], packagePath: path)
253+
XCTAssert(result.binContents.contains("A.swiftinterface"))
254+
XCTAssert(result.binContents.contains("B.swiftinterface"))
255+
} catch SwiftPMProductError.executionFailure(_, _, let stderr) {
256+
XCTFail(stderr)
257+
}
258+
}
259+
}
260+
249261
func testBuildCompleteMessage() {
250262
fixture(name: "DependencyResolution/Internal/Simple") { path in
251263
do {

Tests/XCBuildSupportTests/PIFBuilderTests.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,6 +2224,68 @@ class PIFBuilderTests: XCTestCase {
22242224
}
22252225
}
22262226
}
2227+
2228+
/// Tests that the inference of XCBuild build settings based on the package manifest's declared unsafe settings
2229+
/// works as expected.
2230+
func testUnsafeFlagsBuildSettingInference() throws {
2231+
let fs = InMemoryFileSystem(emptyFiles:
2232+
"/MyLib/Sources/MyLib/Foo.swift"
2233+
)
2234+
2235+
let diagnostics = DiagnosticsEngine()
2236+
let graph = try loadPackageGraph(
2237+
fs: fs,
2238+
diagnostics: diagnostics,
2239+
manifests: [
2240+
Manifest.createManifest(
2241+
name: "MyLib",
2242+
path: "/MyLib",
2243+
packageKind: .root,
2244+
packageLocation: "/MyLib",
2245+
v: .v5,
2246+
products: [
2247+
.init(name: "MyLib", type: .library(.automatic), targets: ["MyLib"]),
2248+
],
2249+
targets: [
2250+
.init(name: "MyLib", settings: [
2251+
.init(
2252+
tool: .swift,
2253+
name: .unsafeFlags,
2254+
value: ["-enable-library-evolution"],
2255+
condition: .init(config: "release")),
2256+
]),
2257+
]),
2258+
],
2259+
shouldCreateMultipleTestProducts: true
2260+
)
2261+
2262+
let builder = PIFBuilder(graph: graph, parameters: .mock(), diagnostics: diagnostics)
2263+
let pif = try builder.construct()
2264+
2265+
XCTAssertNoDiagnostics(diagnostics)
2266+
2267+
PIFTester(pif) { workspace in
2268+
workspace.checkProject("PACKAGE:/MyLib") { project in
2269+
project.checkTarget("PACKAGE-TARGET:MyLib") { target in
2270+
target.checkBuildConfiguration("Debug") { configuration in
2271+
configuration.checkBuildSettings { settings in
2272+
// Check that the `-enable-library-evolution` setting for Release didn't affect Debug.
2273+
XCTAssertEqual(settings[.SWIFT_EMIT_MODULE_INTERFACE], nil)
2274+
XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil)
2275+
}
2276+
}
2277+
target.checkBuildConfiguration("Release") { configuration in
2278+
configuration.checkBuildSettings { settings in
2279+
// Check that the `-enable-library-evolution` setting for Release also set SWIFT_EMIT_MODULE_INTERFACE.
2280+
XCTAssertEqual(settings[.SWIFT_EMIT_MODULE_INTERFACE], "YES")
2281+
XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], ["$(inherited)", "-enable-library-evolution"])
2282+
}
2283+
}
2284+
}
2285+
}
2286+
}
2287+
}
2288+
22272289
#endif
22282290
}
22292291

0 commit comments

Comments
 (0)