Skip to content

Commit e47991f

Browse files
committed
Add new 'VirtualPath' variant for a driver-computed build artifact with known contents
Similarly to a 'temporaryWithKnownContents', but at an explicitly-determined-by-the-driver 'AbsolutePath'. This is useful for when the driver is able to determine that build planning will produce an input to compilation which must be placed in a specific location. For example, the chained bridging header content and output path are determined during build planning (specifically, dependency scanning) and must be fed into compilation tasks as inputs. This new file type will allow the driver to compute the new file and its contents and then have them get written to disk when resolving input paths to said compilation tasks.
1 parent c55c52f commit e47991f

File tree

6 files changed

+150
-23
lines changed

6 files changed

+150
-23
lines changed

Sources/SwiftDriver/Driver/Driver.swift

+6-10
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ public struct Driver {
330330
case .relative(let path):
331331
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory
332332
return cwd.map { AbsolutePath($0, path) }
333-
case .standardInput, .standardOutput, .temporary, .temporaryWithKnownContents, .fileList:
333+
case .standardInput, .standardOutput, .temporary, .temporaryWithKnownContents, .fileList, .buildArtifactWithKnownContents:
334334
fatalError("Frontend target information will never include a path of this type.")
335335
}
336336
}
@@ -3087,15 +3087,11 @@ extension Driver {
30873087
chainedBridgingHeader: ChainedBridgingHeaderFile?) throws -> VirtualPath.Handle? {
30883088
// handle chained bridging header.
30893089
if let chainedHeader = chainedBridgingHeader, !chainedHeader.path.isEmpty {
3090-
let path = try VirtualPath(path: chainedHeader.path)
3091-
let dirExists = try fileSystem.exists(path.parentDirectory)
3092-
if !dirExists, let dirToCreate = path.parentDirectory.absolutePath {
3093-
try fileSystem.createDirectory(dirToCreate, recursive: true)
3094-
}
3095-
try fileSystem.writeFileContents(path,
3096-
bytes: ByteString(encodingAsUTF8: chainedHeader.content),
3097-
atomically: true)
3098-
return path.intern()
3090+
let path = try AbsolutePath(validating: chainedHeader.path)
3091+
if !fileSystem.exists(path.parentDirectory) {
3092+
try fileSystem.createDirectory(path.parentDirectory, recursive: true)
3093+
}
3094+
return try VirtualPath.createBuildProductFileWithKnownContents(path, chainedHeader.content.data(using: .utf8)!).intern()
30993095
}
31003096
return originalObjCHeaderFile
31013097
}

Sources/SwiftDriver/Execution/ArgsResolver.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public final class ArgsResolver {
134134
try createFileList(path: actualPath, contents: items)
135135
case let .fileList(_, .outputFileMap(map)):
136136
try createFileList(path: actualPath, outputFileMap: map)
137-
case .relative, .absolute, .standardInput, .standardOutput:
137+
case .relative, .absolute, .standardInput, .standardOutput, .buildArtifactWithKnownContents:
138138
fatalError("Not a temporary path.")
139139
}
140140

@@ -143,6 +143,12 @@ public final class ArgsResolver {
143143
return result
144144
}
145145

146+
// First time resolving a build product with a known path and driver-computed
147+
// contents, write it to the filesystem.
148+
if case let .buildArtifactWithKnownContents(absolutePath, contents) = path {
149+
try fileSystem.writeFileContents(absolutePath, bytes: .init(contents))
150+
}
151+
146152
// Otherwise, return the path.
147153
let result = path.name
148154
pathMapping[path] = result

Sources/SwiftDriver/Jobs/Planning.swift

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ extension Driver {
120120
supportsExplicitInterfaceBuild:
121121
isFrontendArgSupported(.explicitInterfaceModuleBuild),
122122
cas: cas,
123+
prefixMap: prefixMapping,
123124
supportsBridgingHeaderPCHCommand:
124125
interModuleDependencyOracle.supportsBridgingHeaderPCHCommand)
125126
} else {

Sources/SwiftDriver/Utilities/VirtualPath.swift

+41-12
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public enum VirtualPath: Hashable {
4747
/// Standard output
4848
case standardOutput
4949

50+
/// ACTODO: Comment
51+
case buildArtifactWithKnownContents(AbsolutePath, Data)
52+
5053
/// We would like to direct clients to use the temporary file creation utilities `createUniqueTemporaryFile`, etc.
5154
/// To ensure temporary files are unique.
5255
/// TODO: If/When Swift gains enum access control, we can prohibit direct instantiation of temporary file cases,
@@ -72,6 +75,8 @@ public enum VirtualPath: Hashable {
7275
case .relative(let path), .temporary(let path),
7376
.temporaryWithKnownContents(let path, _), .fileList(let path, _):
7477
return path.extension
78+
case .buildArtifactWithKnownContents(let path, _):
79+
return path.extension
7580
case .absolute(let path):
7681
return path.extension
7782
case .standardInput, .standardOutput:
@@ -82,7 +87,7 @@ public enum VirtualPath: Hashable {
8287
/// Whether this virtual path is to a temporary.
8388
public var isTemporary: Bool {
8489
switch self {
85-
case .relative, .absolute, .standardInput, .standardOutput:
90+
case .relative, .absolute, .standardInput, .standardOutput, .buildArtifactWithKnownContents:
8691
return false
8792
case .temporary, .temporaryWithKnownContents, .fileList:
8893
return true
@@ -91,7 +96,7 @@ public enum VirtualPath: Hashable {
9196

9297
public var absolutePath: AbsolutePath? {
9398
switch self {
94-
case let .absolute(absolutePath):
99+
case .absolute(let absolutePath), .buildArtifactWithKnownContents(let absolutePath, _):
95100
return absolutePath
96101
case .relative, .temporary, .temporaryWithKnownContents, .fileList, .standardInput, .standardOutput:
97102
return nil
@@ -111,15 +116,15 @@ public enum VirtualPath: Hashable {
111116
.fileList(let name, _),
112117
.temporaryWithKnownContents(let name, _):
113118
return name
114-
case .absolute, .relative, .standardInput, .standardOutput:
119+
case .absolute, .relative, .standardInput, .standardOutput, .buildArtifactWithKnownContents:
115120
return nil
116121
}
117122
}
118123

119124
/// Retrieve the basename of the path.
120125
public var basename: String {
121126
switch self {
122-
case .absolute(let path):
127+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
123128
return path.basename
124129
case .relative(let path), .temporary(let path), .temporaryWithKnownContents(let path, _), .fileList(let path, _):
125130
return path.basename
@@ -131,7 +136,7 @@ public enum VirtualPath: Hashable {
131136
/// Retrieve the basename of the path without the extension.
132137
public var basenameWithoutExt: String {
133138
switch self {
134-
case .absolute(let path):
139+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
135140
return path.basenameWithoutExt
136141
case .relative(let path), .temporary(let path), .temporaryWithKnownContents(let path, _), .fileList(let path, _):
137142
return path.basenameWithoutExt
@@ -143,7 +148,7 @@ public enum VirtualPath: Hashable {
143148
/// Retrieve the path to the parent directory.
144149
public var parentDirectory: VirtualPath {
145150
switch self {
146-
case .absolute(let path):
151+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
147152
return .absolute(path.parentDirectory)
148153
case .relative(let path):
149154
return .relative(try! RelativePath(validating: path.dirname))
@@ -162,7 +167,7 @@ public enum VirtualPath: Hashable {
162167
/// This should not be used with `.standardInput` or `.standardOutput`.
163168
public func appending(component: String) -> VirtualPath {
164169
switch self {
165-
case .absolute(let path):
170+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
166171
return .absolute(path.appending(component: component))
167172
case .relative(let path):
168173
return .relative(path.appending(component: component))
@@ -180,7 +185,7 @@ public enum VirtualPath: Hashable {
180185

181186
public func appending(components: String...) -> VirtualPath {
182187
switch self {
183-
case .absolute(let path):
188+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
184189
return .absolute(path.appending(components: components))
185190
case .relative(let path):
186191
return .relative(path.appending(components: components))
@@ -201,7 +206,7 @@ public enum VirtualPath: Hashable {
201206
/// This should not be used with `.standardInput` or `.standardOutput`.
202207
public func appendingToBaseName(_ suffix: String) throws -> VirtualPath {
203208
switch self {
204-
case let .absolute(path):
209+
case let .absolute(path), .buildArtifactWithKnownContents(let path, _):
205210
return .absolute(try AbsolutePath(validating: path.pathString + suffix))
206211
case let .relative(path):
207212
return .relative(try RelativePath(validating: path.pathString + suffix))
@@ -297,6 +302,8 @@ extension VirtualPath {
297302
return path.pathString
298303
case .absolute(let path):
299304
return path.pathString
305+
case .buildArtifactWithKnownContents(let path, _):
306+
return "buildArtifactWithKnownContents:" + path.pathString
300307
case .temporary(let path):
301308
// N.B. Mangle in a discrimintor for temporaries so they intern apart
302309
// from normal kinds of paths.
@@ -429,6 +436,13 @@ extension VirtualPath {
429436
}
430437
}
431438

439+
extension VirtualPath {
440+
public static func createBuildProductFileWithKnownContents(_ path: AbsolutePath, _ data: Data)
441+
throws -> VirtualPath {
442+
return .buildArtifactWithKnownContents(path, data)
443+
}
444+
}
445+
432446
// MARK: Temporary File Creation
433447

434448
/// Most client contexts require temporary files they request to be unique (e.g. auxiliary compile outputs).
@@ -511,7 +525,7 @@ extension VirtualPath.Handle: Hashable {}
511525
extension VirtualPath: Codable {
512526
private enum CodingKeys: String, CodingKey {
513527
case relative, absolute, standardInput, standardOutput, temporary,
514-
temporaryWithKnownContents, fileList
528+
temporaryWithKnownContents, buildProductWithKnownContents, fileList
515529
}
516530

517531
public func encode(to encoder: Encoder) throws {
@@ -534,6 +548,10 @@ extension VirtualPath: Codable {
534548
var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .temporaryWithKnownContents)
535549
try unkeyedContainer.encode(path)
536550
try unkeyedContainer.encode(contents)
551+
case let .buildArtifactWithKnownContents(path, contents):
552+
var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .buildProductWithKnownContents)
553+
try unkeyedContainer.encode(path)
554+
try unkeyedContainer.encode(contents)
537555
case .fileList(let path, let fileList):
538556
var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .fileList)
539557
try unkeyedContainer.encode(path)
@@ -568,6 +586,11 @@ extension VirtualPath: Codable {
568586
let path = try unkeyedValues.decode(RelativePath.self)
569587
let contents = try unkeyedValues.decode(Data.self)
570588
self = .temporaryWithKnownContents(path, contents)
589+
case .buildProductWithKnownContents:
590+
var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key)
591+
let path = try unkeyedValues.decode(AbsolutePath.self)
592+
let contents = try unkeyedValues.decode(Data.self)
593+
self = .buildArtifactWithKnownContents(path, contents)
571594
case .fileList:
572595
var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key)
573596
let path = try unkeyedValues.decode(RelativePath.self)
@@ -593,7 +616,7 @@ public struct TextualVirtualPath: Codable, Hashable {
593616
public func encode(to encoder: Encoder) throws {
594617
var container = encoder.singleValueContainer()
595618
switch VirtualPath.lookup(self.path) {
596-
case .absolute(let path):
619+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
597620
try container.encode(path.pathString)
598621
case .relative(let path):
599622
try container.encode(path.pathString)
@@ -612,7 +635,7 @@ extension VirtualPath: CustomStringConvertible {
612635
case .relative(let path):
613636
return path.pathString
614637

615-
case .absolute(let path):
638+
case .absolute(let path), .buildArtifactWithKnownContents(let path, _):
616639
return path.pathString
617640

618641
case .standardInput, .standardOutput:
@@ -632,6 +655,8 @@ extension VirtualPath: CustomDebugStringConvertible {
632655
return ".relative(\(path.pathString))"
633656
case .absolute(let path):
634657
return ".absolute(\(path.pathString))"
658+
case .buildArtifactWithKnownContents(let path, _):
659+
return "buildProductWithKnownContents(\(path.pathString))"
635660
case .standardInput:
636661
return ".standardInput"
637662
case .standardOutput:
@@ -653,6 +678,8 @@ extension VirtualPath {
653678
switch self {
654679
case let .absolute(path):
655680
return .absolute(try AbsolutePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType)))
681+
case let .buildArtifactWithKnownContents(path, content):
682+
return .buildArtifactWithKnownContents(try AbsolutePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType)), content)
656683
case let .relative(path):
657684
return .relative(try RelativePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType)))
658685
case let .temporary(path):
@@ -700,6 +727,8 @@ extension TSCBasic.FileSystem {
700727
switch path {
701728
case let .absolute(absPath):
702729
return try f(absPath)
730+
case let .buildArtifactWithKnownContents(absPath, _):
731+
return try f(absPath)
703732
case let .relative(relPath):
704733
guard let cwd = currentWorkingDirectory else {
705734
throw FileSystemError.noCurrentWorkingDirectory

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

+91
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,97 @@ final class ExplicitModuleBuildTests: XCTestCase {
24572457
}
24582458
}
24592459

2460+
func testTargetVariantEmitModuleExplicit() throws {
2461+
let (stdlibPath, shimsPath, _, _) = try getDriverArtifactsForScanning()
2462+
let cHeadersPath: AbsolutePath =
2463+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
2464+
.appending(component: "CHeaders")
2465+
let swiftModuleInterfacesPath: AbsolutePath =
2466+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
2467+
.appending(component: "Swift")
2468+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
2469+
2470+
do {
2471+
try withTemporaryDirectory { path in
2472+
let main = path.appending(component: "testDependencyScanning.swift")
2473+
try localFileSystem.writeFileContents(main, bytes:
2474+
"""
2475+
import C;\
2476+
import E;\
2477+
import G;
2478+
"""
2479+
)
2480+
2481+
var driver = try Driver(args: ["swiftc",
2482+
"-target", "x86_64-apple-macosx10.14",
2483+
"-target-variant", "x86_64-apple-ios13.1-macabi",
2484+
"-enable-library-evolution", "-emit-module", "-emit-module-interface",
2485+
"-emit-module-path", "foo.swiftmodule/target.swiftmodule",
2486+
"-emit-variant-module-path", "foo.swiftmodule/variant.swiftmodule",
2487+
"-emit-module-interface-path", "foo.swiftmodule/target.swiftinterface",
2488+
"-emit-variant-module-interface-path", "foo.swiftmodule/variant.swiftinterface",
2489+
"-I", cHeadersPath.nativePathString(escaped: true),
2490+
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
2491+
"-I", stdlibPath.nativePathString(escaped: true),
2492+
"-I", shimsPath.nativePathString(escaped: true),
2493+
"-explicit-module-build",
2494+
main.pathString] + sdkArgumentsForTesting)
2495+
2496+
let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs()
2497+
XCTAssertEqual(plannedJobs.count, 32)
2498+
2499+
let emitModuleJobs = try plannedJobs.findJobs(.emitModule)
2500+
let targetModuleJob = emitModuleJobs[0]
2501+
let variantModuleJob = emitModuleJobs[1]
2502+
2503+
XCTAssert(targetModuleJob.commandLine.contains(.flag("-emit-module")))
2504+
XCTAssert(variantModuleJob.commandLine.contains(.flag("-emit-module")))
2505+
2506+
XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftdoc")))))
2507+
XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftsourceinfo")))))
2508+
XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.abi.json")))))
2509+
XCTAssertTrue(targetModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/target.swiftmodule")))]))
2510+
2511+
XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftdoc")))))
2512+
XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo")))))
2513+
XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.abi.json")))))
2514+
XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))]))
2515+
2516+
let verifyModuleJobs = try plannedJobs.findJobs(.verifyModuleInterface)
2517+
let verifyTargetModuleJob = verifyModuleJobs[0]
2518+
let verifyVariantModuleJob = verifyModuleJobs[1]
2519+
XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("-typecheck-module-from-interface")))
2520+
XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("-typecheck-module-from-interface")))
2521+
2522+
XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("-target")))
2523+
XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("x86_64-apple-macosx10.14")))
2524+
XCTAssert(verifyTargetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftinterface")))))
2525+
2526+
XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("-target")))
2527+
XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi")))
2528+
XCTAssert(verifyVariantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftinterface")))))
2529+
2530+
let interfaceCompilationJobs = try plannedJobs.findJobs(.compileModuleFromInterface)
2531+
let targetAModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) &&
2532+
$0.commandLine.contains(.flag("x86_64-apple-macosx10.14"))})
2533+
let variantAModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) &&
2534+
$0.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi"))})
2535+
let targetGModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) &&
2536+
$0.commandLine.contains(.flag("x86_64-apple-macosx10.14"))})
2537+
let variantGModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) &&
2538+
$0.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi"))})
2539+
let targetEModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("E")]) &&
2540+
$0.commandLine.contains(.flag("x86_64-apple-macosx10.14"))})
2541+
let variantEModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("E")]) &&
2542+
$0.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi"))})
2543+
let targetSwiftModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("Swift")]) &&
2544+
$0.commandLine.contains(.flag("x86_64-apple-macosx10.14"))})
2545+
let variantSwiftModuleJob = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("Swift")]) &&
2546+
$0.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi"))})
2547+
}
2548+
}
2549+
}
2550+
24602551
// We only care about prebuilt modules in macOS.
24612552
#if os(macOS)
24622553
func testPrebuiltModuleGenerationJobs() throws {

0 commit comments

Comments
 (0)