Skip to content

Commit 5378f8d

Browse files
authored
PackageModel: relative paths for cross-compilation destinations (#5812)
Currently, `destination.json` files only support absolute paths, which makes it hard to move cross-compilation sysroots between directories and copying them to different machines. This change support for relative paths in v2 scheme of `destination.json` files. The path is then normalized relative to the location of `destination.json` file. `struct DestinationInfo` is renamed to `DestinationInfoV1`. New `struct DestinationInfoV2` is added to handle the new version that supports relative paths. This is also used as an opportunity to make the schema more consistent with manifests for artifact bundles, in which properties use `camelCase` instead of `dash-case` naming. Also new in schema version 2 are `linkerFlags` and renaming of `extraCPPFlags` to `extraCXXFlags`, to follow up on #5827 and #5837.
1 parent 567979c commit 5378f8d

File tree

5 files changed

+162
-31
lines changed

5 files changed

+162
-31
lines changed

Sources/PackageModel/Destination.swift

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ public struct Destination: Encodable, Equatable {
238238
}
239239
return _sdkPlatformFrameworkPath
240240
}
241+
241242
/// Cache storage for sdk platform path.
242243
private static var _sdkPlatformFrameworkPath: (fwk: AbsolutePath, lib: AbsolutePath)? = nil
243244

@@ -258,34 +259,55 @@ public struct Destination: Encodable, Equatable {
258259
}
259260

260261
extension Destination {
261-
/// Load a Destination description from a JSON representation from disk.
262+
/// Load a ``Destination`` description from a JSON representation from disk.
262263
public init(fromFile path: AbsolutePath, fileSystem: FileSystem) throws {
263264
let decoder = JSONDecoder.makeWithDefaults()
264265
let version = try decoder.decode(path: path, fileSystem: fileSystem, as: VersionInfo.self)
266+
265267
// Check schema version.
266-
guard version.version == 1 else {
268+
switch version.version {
269+
case 1:
270+
let destination = try decoder.decode(path: path, fileSystem: fileSystem, as: DestinationInfoV1.self)
271+
try self.init(
272+
destinationTriple: destination.target.map{ try Triple($0) },
273+
sdkRootDir: destination.sdk,
274+
toolchainBinDir: destination.binDir,
275+
extraFlags: .init(
276+
cCompilerFlags: destination.extraCCFlags,
277+
cxxCompilerFlags: destination.extraCPPFlags,
278+
swiftCompilerFlags: destination.extraSwiftCFlags
279+
)
280+
)
281+
case 2:
282+
let destination = try decoder.decode(path: path, fileSystem: fileSystem, as: DestinationInfoV2.self)
283+
let destinationDirectory = path.parentDirectory
284+
285+
// TODO support multiple host and destination triple.
286+
try self.init(
287+
hostTriple: destination.hostTriples.map(Triple.init).first,
288+
destinationTriple: destination.destinationTriples.map(Triple.init).first,
289+
sdkRootDir: AbsolutePath(validating: destination.sdkRootDir, relativeTo: destinationDirectory),
290+
toolchainBinDir: AbsolutePath(validating: destination.toolchainBinDir, relativeTo: destinationDirectory),
291+
extraFlags: .init(
292+
cCompilerFlags: destination.extraCCFlags,
293+
cxxCompilerFlags: destination.extraCXXFlags,
294+
swiftCompilerFlags: destination.extraSwiftCFlags,
295+
linkerFlags: destination.extraLinkerFlags
296+
)
297+
)
298+
default:
267299
throw DestinationError.invalidSchemaVersion
268300
}
269-
let destination = try decoder.decode(path: path, fileSystem: fileSystem, as: DestinationInfo.self)
270-
try self.init(
271-
destinationTriple: destination.target.map{ try Triple($0) },
272-
sdkRootDir: destination.sdk,
273-
toolchainBinDir: destination.binDir,
274-
extraFlags: BuildFlags(
275-
cCompilerFlags: destination.extraCCFlags,
276-
// maintaining `destination.extraCPPFlags` naming inconsistency for compatibility.
277-
cxxCompilerFlags: destination.extraCPPFlags,
278-
swiftCompilerFlags: destination.extraSwiftCFlags
279-
)
280-
)
281301
}
282302
}
283303

304+
/// Version of the schema of `destination.json` files used for cross-compilation.
284305
fileprivate struct VersionInfo: Codable {
285306
let version: Int
286307
}
287308

288-
fileprivate struct DestinationInfo: Codable {
309+
/// Represents v1 schema of `destination.json` files used for cross-compilation.
310+
fileprivate struct DestinationInfoV1: Codable {
289311
let target: String?
290312
let sdk: AbsolutePath?
291313
let binDir: AbsolutePath
@@ -302,3 +324,15 @@ fileprivate struct DestinationInfo: Codable {
302324
case extraCPPFlags = "extra-cpp-flags"
303325
}
304326
}
327+
328+
/// Represents v2 schema of `destination.json` files used for cross-compilation.
329+
fileprivate struct DestinationInfoV2: Codable {
330+
let sdkRootDir: String
331+
let toolchainBinDir: String
332+
let hostTriples: [String]
333+
let destinationTriples: [String]
334+
let extraCCFlags: [String]
335+
let extraSwiftCFlags: [String]
336+
let extraCXXFlags: [String]
337+
let extraLinkerFlags: [String]
338+
}

Sources/PackageModel/Manifest.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,14 @@ public final class Manifest {
180180
}
181181

182182
var requiredDependencies: Set<PackageIdentity> = []
183-
for target in self.targetsRequired(for: products) {
184-
for targetDependency in target.dependencies {
183+
for destinationTriple in self.targetsRequired(for: products) {
184+
for targetDependency in destinationTriple.dependencies {
185185
if let dependency = self.packageDependency(referencedBy: targetDependency) {
186186
requiredDependencies.insert(dependency.identity)
187187
}
188188
}
189189

190-
target.pluginUsages?.forEach {
190+
destinationTriple.pluginUsages?.forEach {
191191
if let dependency = self.packageDependency(referencedBy: $0) {
192192
requiredDependencies.insert(dependency.identity)
193193
}

Sources/PackageModel/UserToolchain.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ public final class UserToolchain: Toolchain {
258258
}
259259

260260
internal static func deriveSwiftCFlags(triple: Triple, destination: Destination, environment: EnvironmentVariables) throws -> [String] {
261-
guard let sdk = destination.sdkRootDir else {
261+
guard let sdkDir = destination.sdkRootDir else {
262262
if triple.isWindows() {
263263
// Windows uses a variable named SDKROOT to determine the root of
264264
// the SDK. This is not the same value as the SDKROOT parameter
@@ -336,7 +336,7 @@ public final class UserToolchain: Toolchain {
336336
}
337337

338338
return (triple.isDarwin() || triple.isAndroid() || triple.isWASI() || triple.isWindows()
339-
? ["-sdk", sdk.pathString]
339+
? ["-sdk", sdkDir.pathString]
340340
: [])
341341
+ destination.extraFlags.swiftCompilerFlags
342342
}
@@ -389,9 +389,9 @@ public final class UserToolchain: Toolchain {
389389

390390
self.extraFlags.swiftCompilerFlags = try Self.deriveSwiftCFlags(triple: triple, destination: destination, environment: environment)
391391

392-
if let sdk = destination.sdkRootDir {
392+
if let sdkDir = destination.sdkRootDir {
393393
self.extraFlags.cCompilerFlags = [
394-
triple.isDarwin() ? "-isysroot" : "--sysroot", sdk.pathString
394+
triple.isDarwin() ? "-isysroot" : "--sysroot", sdkDir.pathString
395395
] + destination.extraFlags.cCompilerFlags
396396

397397
self.extraFlags.cxxCompilerFlags = destination.extraFlags.cxxCompilerFlags
@@ -521,12 +521,12 @@ public final class UserToolchain: Toolchain {
521521
return try AbsolutePath(validating: path)
522522
}
523523
} else if triple.isWindows() {
524-
let sdkroot: AbsolutePath
524+
let sdkRoot: AbsolutePath
525525

526-
if let sdk = destination.sdkRootDir {
527-
sdkroot = sdk
528-
} else if let SDKROOT = environment["SDKROOT"], let sdk = try? AbsolutePath(validating: SDKROOT) {
529-
sdkroot = sdk
526+
if let sdkDir = destination.sdkRootDir {
527+
sdkRoot = sdkDir
528+
} else if let SDKROOT = environment["SDKROOT"], let sdkDir = try? AbsolutePath(validating: SDKROOT) {
529+
sdkRoot = sdkDir
530530
} else {
531531
return .none
532532
}
@@ -537,7 +537,7 @@ public final class UserToolchain: Toolchain {
537537
// Library/Developer/Platforms/[PLATFORM].platform/Developer/SDKs/[PLATFORM].sdk/...
538538
//
539539
// SDKROOT points to [PLATFORM].sdk
540-
let platform = sdkroot.parentDirectory.parentDirectory.parentDirectory
540+
let platform = sdkRoot.parentDirectory.parentDirectory.parentDirectory
541541

542542
if let info = WindowsPlatformInfo(reading: platform.appending(component: "Info.plist"),
543543
diagnostics: nil, filesystem: localFileSystem) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Basics
14+
@testable import PackageModel
15+
import TSCBasic
16+
import TSCUtility
17+
import XCTest
18+
19+
private let bundleRootPath = try! AbsolutePath(validating: "/tmp/cross-toolchain")
20+
private let toolchainBinDir = RelativePath("swift.xctoolchain/usr/bin")
21+
private let sdkRootDir = RelativePath("ubuntu-jammy.sdk")
22+
private let hostTriple = "arm64-apple-darwin22.1.0"
23+
private let destinationTriple = "x86_64-unknown-linux-gnu"
24+
private let extraFlags = BuildFlags(
25+
cCompilerFlags: ["-fintegrated-as"],
26+
cxxCompilerFlags: ["-fno-exceptions"],
27+
swiftCompilerFlags: ["-enable-experimental-cxx-interop", "-use-ld=lld"],
28+
linkerFlags: ["-R/usr/lib/swift/linux/"]
29+
)
30+
31+
private let destinationV1JSON =
32+
#"""
33+
{
34+
"version": 1,
35+
"sdk": "\#(bundleRootPath.appending(sdkRootDir))",
36+
"toolchain-bin-dir": "\#(bundleRootPath.appending(toolchainBinDir))",
37+
"target": "\#(destinationTriple)",
38+
"extra-cc-flags": \#(extraFlags.cCompilerFlags),
39+
"extra-swiftc-flags": \#(extraFlags.swiftCompilerFlags),
40+
"extra-cpp-flags": \#(extraFlags.cxxCompilerFlags)
41+
}
42+
"""#
43+
44+
private let destinationV2JSON =
45+
#"""
46+
{
47+
"version": 2,
48+
"sdkRootDir": "\#(sdkRootDir)",
49+
"toolchainBinDir": "\#(toolchainBinDir)",
50+
"hostTriples": ["\#(hostTriple)"],
51+
"destinationTriples": ["\#(destinationTriple)"],
52+
"extraCCFlags": \#(extraFlags.cCompilerFlags),
53+
"extraSwiftCFlags": \#(extraFlags.swiftCompilerFlags),
54+
"extraCXXFlags": \#(extraFlags.cxxCompilerFlags),
55+
"extraLinkerFlags": \#(extraFlags.linkerFlags)
56+
}
57+
"""#
58+
59+
final class DestinationTests: XCTestCase {
60+
func testDestinationCodable() throws {
61+
let fs = InMemoryFileSystem(files: [
62+
"\(bundleRootPath)/destinationV1.json": ByteString(encodingAsUTF8: destinationV1JSON),
63+
"\(bundleRootPath)/destinationV2.json": ByteString(encodingAsUTF8: destinationV2JSON),
64+
])
65+
66+
let destinationV1 = try Destination(fromFile: bundleRootPath.appending(.init("destinationV1.json")), fileSystem: fs)
67+
68+
var flagsWithoutLinkerFlags = extraFlags
69+
flagsWithoutLinkerFlags.linkerFlags = []
70+
71+
let sdkRootAbsolutePath = bundleRootPath.appending(sdkRootDir)
72+
let toolchainBinAbsolutePath = bundleRootPath.appending(toolchainBinDir)
73+
74+
XCTAssertEqual(
75+
destinationV1,
76+
Destination(
77+
destinationTriple: try Triple(destinationTriple),
78+
sdkRootDir: sdkRootAbsolutePath,
79+
toolchainBinDir: toolchainBinAbsolutePath,
80+
extraFlags: flagsWithoutLinkerFlags
81+
)
82+
)
83+
84+
let destinationV2 = try Destination(fromFile: bundleRootPath.appending(.init("destinationV2.json")), fileSystem: fs)
85+
86+
XCTAssertEqual(
87+
destinationV2,
88+
Destination(
89+
hostTriple: try Triple(hostTriple),
90+
destinationTriple: try Triple(destinationTriple),
91+
sdkRootDir: sdkRootAbsolutePath,
92+
toolchainBinDir: toolchainBinAbsolutePath,
93+
extraFlags: extraFlags
94+
)
95+
)
96+
}
97+
}

Tests/PackageModelTests/PackageModelTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,18 @@ class PackageModelTests: XCTestCase {
5757

5858
func testAndroidCompilerFlags() throws {
5959
let triple = try Triple("x86_64-unknown-linux-android")
60-
let sdk = AbsolutePath(path: "/some/path/to/an/SDK.sdk")
60+
let sdkDir = AbsolutePath(path: "/some/path/to/an/SDK.sdk")
6161
let toolchainPath = AbsolutePath(path: "/some/path/to/a/toolchain.xctoolchain")
6262

6363
let destination = Destination(
6464
destinationTriple: triple,
65-
sdkRootDir: sdk,
65+
sdkRootDir: sdkDir,
6666
toolchainBinDir: toolchainPath.appending(components: "usr", "bin")
6767
)
6868

6969
XCTAssertEqual(try UserToolchain.deriveSwiftCFlags(triple: triple, destination: destination, environment: .process()), [
7070
// Needed when cross‐compiling for Android. 2020‐03‐01
71-
"-sdk", sdk.pathString,
71+
"-sdk", sdkDir.pathString,
7272
])
7373
}
7474
}

0 commit comments

Comments
 (0)