Skip to content

Setup single dependency mode #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ public struct Driver {
/// The path to the pch for the imported Objective-C header.
public let bridgingPrecompiledHeader: VirtualPath?

/// Single dependencies file, input.
public let singleDependenciesFileInput: TypedVirtualPath?

/// Path to the dependencies file.
public let dependenciesFilePath: VirtualPath?

Expand Down Expand Up @@ -347,6 +350,8 @@ public struct Driver {
toolchain: toolchain,
targetTriple: targetTriple)

self.singleDependenciesFileInput = Self.computeSingleDependenciesInput(&parsedOptions, inputFiles: inputFiles)

// Supplemental outputs.
self.dependenciesFilePath = try Self.computeSupplementaryOutputPath(
&parsedOptions, type: .dependencies, isOutput: .emitDependencies,
Expand Down Expand Up @@ -1619,4 +1624,17 @@ extension Driver {

return try VirtualPath(path: moduleName.appendingFileTypeExtension(.swiftDocumentation))
}

static func computeSingleDependenciesInput(_ parsedOptions: inout ParsedOptions,
inputFiles: [TypedVirtualPath]) -> TypedVirtualPath? {
guard parsedOptions.hasArgument(.emitDependencies) else { return nil }

let hasOnlyOneDependenciesFile = parsedOptions.hasFlag(positive: .enableOnlyOneDependencyFile,
negative: .disableOnlyOneDependencyFile,
default: true)

guard hasOnlyOneDependenciesFile else { return nil }

return inputFiles.first { $0.type.isPartOfSwiftCompilation }
}
}
50 changes: 36 additions & 14 deletions Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,30 +206,37 @@ extension Driver {
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], primaryInputs: [TypedVirtualPath]) throws -> [TypedVirtualPath] {
var outputs: [TypedVirtualPath] = []

/// Add output of a particular type, if needed.
func addOutputOfType(
outputType: FileType, finalOutputPath: VirtualPath?,
input: VirtualPath?, flag: String
) {
/// Compute output path for output of a particular type.
func computeOutputOfType(outputType: FileType, finalOutputPath: VirtualPath?, input: VirtualPath?) -> VirtualPath? {
// If there is no final output, there's nothing to do.
guard let finalOutputPath = finalOutputPath else { return }
guard let finalOutputPath = finalOutputPath else { return nil }

// If the whole of the compiler output is this type, there's nothing to
// do.
if outputType == compilerOutputType { return }

// Add the appropriate flag.
commandLine.appendFlag(flag)
if outputType == compilerOutputType { return nil }

// Compute the output path based on the input path (if there is one), or
// use the final output.
let outputPath: VirtualPath
if let input = input {
outputPath = (outputFileMap ?? OutputFileMap())
return (outputFileMap ?? OutputFileMap())
.getOutput(inputFile: input, outputType: outputType)
} else {
outputPath = finalOutputPath
return finalOutputPath
}
}

/// Add output of a particular type, if needed.
func addOutputOfType(
outputType: FileType, finalOutputPath: VirtualPath?,
input: VirtualPath?, flag: String
) {
// Skip creating output if there is no path.
guard let outputPath = computeOutputOfType(outputType: outputType,
finalOutputPath: finalOutputPath,
input: input) else { return }

// Add the appropriate flag.
commandLine.appendFlag(flag)

commandLine.append(.path(outputPath))
outputs.append(TypedVirtualPath(file: outputPath, type: outputType))
Expand All @@ -248,11 +255,26 @@ extension Driver {
finalOutputPath: moduleDocOutputPath,
input: input,
flag: "-emit-module-doc-path")
addOutputOfType(
if let singleDependenciesFileInput = singleDependenciesFileInput {
if input == singleDependenciesFileInput.file {
addOutputOfType(
outputType: .dependencies,
finalOutputPath: dependenciesFilePath,
input: input,
flag: "-emit-dependencies-path")
} else if let path = computeOutputOfType(outputType: .dependencies,
finalOutputPath: dependenciesFilePath,
input: input) {
// Create empty dummy dependencies file.
try? localFileSystem.writeFileContents(path, bytes: ByteString())
}
} else {
addOutputOfType(
outputType: .dependencies,
finalOutputPath: dependenciesFilePath,
input: input,
flag: "-emit-dependencies-path")
}
}

addOutputOfType(
Expand Down
14 changes: 13 additions & 1 deletion Sources/SwiftDriver/Jobs/Planning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ extension Driver {
/// Compute the partitions we'll use for batch mode.
private func batchPartitions(_ info: BatchModeInfo) -> BatchPartitions? {
let swiftInputFiles = inputFiles.filter { inputFile in
inputFile.type.isPartOfSwiftCompilation
inputFile.isBatchable(singleDependenciesFileInput: singleDependenciesFileInput)
}
let numPartitions = numberOfBatchPartitions(info, swiftInputFiles: swiftInputFiles)

Expand Down Expand Up @@ -437,3 +437,15 @@ extension Driver {
return BatchPartitions(assignment: assignment, partitions: partitions)
}
}

private extension TypedVirtualPath {
func isBatchable(singleDependenciesFileInput: TypedVirtualPath?) -> Bool {
guard type.isPartOfSwiftCompilation else { return false }

if let singleDependenciesFileInput = singleDependenciesFileInput {
return self != singleDependenciesFileInput
}

return true
}
}
4 changes: 4 additions & 0 deletions Sources/SwiftDriver/Utilities/VirtualPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ extension TSCBasic.FileSystem {
try resolvingVirtualPath(path, apply: readFileContents)
}

func writeFileContents(_ path: VirtualPath, bytes: ByteString) throws {
try resolvingVirtualPath(path) { try writeFileContents($0, bytes: bytes) }
}

func getFileInfo(_ path: VirtualPath) throws -> TSCBasic.FileInfo {
try resolvingVirtualPath(path, apply: getFileInfo)
}
Expand Down
98 changes: 97 additions & 1 deletion Tests/SwiftDriverTests/SwiftDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ final class SwiftDriverTests: XCTestCase {

func testMergeModulesOnly() throws {
do {
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module", "-disable-bridging-pch", "-import-objc-header", "TestInputHeader.h", "-emit-dependencies", "-emit-module-doc-path", "/foo/bar/Test.swiftdoc"])
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module", "-disable-bridging-pch", "-import-objc-header", "TestInputHeader.h", "-emit-dependencies", "-disable-only-one-dependency-file", "-emit-module-doc-path", "/foo/bar/Test.swiftdoc"])
let plannedJobs = try driver.planBuild()
XCTAssertEqual(plannedJobs.count, 3)
XCTAssertEqual(plannedJobs[0].outputs.count, 3)
Expand Down Expand Up @@ -1872,6 +1872,102 @@ final class SwiftDriverTests: XCTestCase {
XCTAssertEqual(plannedJobs[0].outputs[0].file, .relative(RelativePath("Test.pcm")))
}
}

func testOnlyOneDependencyFile() throws {
// With -disable-only-one-dependency-file
do {
try withTemporaryDirectory { dir in
let main = AbsolutePath("main.d", relativeTo: dir)
let a = AbsolutePath("A.d", relativeTo: dir)
let b = AbsolutePath("B.d", relativeTo: dir)
let contents = """
{
"main.swift": {
"dependencies": "\(main.pathString)"
},
"A.swift": {
"dependencies": "\(a.pathString)"
},
"B.swift": {
"dependencies": "\(b.pathString)"
}
}
"""
let ofm = AbsolutePath("ofm.json", relativeTo: dir)
try localFileSystem.writeFileContents(ofm) { $0 <<< contents }

var driver = try Driver(args: ["swiftc", "-disable-only-one-dependency-file", "-emit-dependencies", "-emit-dependencies-path", dir.pathString, "main.swift", "A.swift", "B.swift", "-output-file-map", ofm.pathString])
let plannedJobs = try driver.planBuild()

XCTAssertEqual(plannedJobs.count, 4)

XCTAssertEqual(plannedJobs[0].kind, .compile)
XCTAssertEqual(plannedJobs[0].outputs.count, 2)
XCTAssertEqual(plannedJobs[0].outputs[1].file, .absolute(main))

XCTAssertEqual(plannedJobs[1].kind, .compile)
XCTAssertEqual(plannedJobs[1].outputs.count, 2)
XCTAssertEqual(plannedJobs[1].outputs[1].file, .absolute(a))

XCTAssertEqual(plannedJobs[2].kind, .compile)
XCTAssertEqual(plannedJobs[2].outputs.count, 2)
XCTAssertEqual(plannedJobs[2].outputs[1].file, .absolute(b))

XCTAssertEqual(plannedJobs[3].kind, .link)

// Check no dummy files
XCTAssertFalse(localFileSystem.isFile(main))
XCTAssertFalse(localFileSystem.isFile(a))
XCTAssertFalse(localFileSystem.isFile(b))
}
}

// With -enable-only-one-dependency-file
do {
try withTemporaryDirectory { dir in
let main = AbsolutePath("main.d", relativeTo: dir)
let a = AbsolutePath("A.d", relativeTo: dir)
let b = AbsolutePath("B.d", relativeTo: dir)
let contents = """
{
"main.swift": {
"dependencies": "\(main.pathString)"
},
"A.swift": {
"dependencies": "\(a.pathString)"
},
"B.swift": {
"dependencies": "\(b.pathString)"
}
}
"""
let ofm = AbsolutePath("ofm.json", relativeTo: dir)
try localFileSystem.writeFileContents(ofm) { $0 <<< contents }

var driver = try Driver(args: ["swiftc", "-enable-only-one-dependency-file", "-emit-dependencies", "-emit-dependencies-path", dir.pathString, "main.swift", "A.swift", "B.swift", "-output-file-map", ofm.pathString])
let plannedJobs = try driver.planBuild()

XCTAssertEqual(plannedJobs.count, 4)

XCTAssertEqual(plannedJobs[0].kind, .compile)
XCTAssertEqual(plannedJobs[0].outputs.count, 2)
XCTAssertEqual(plannedJobs[0].outputs[1].file, .absolute(main))

XCTAssertEqual(plannedJobs[1].kind, .compile)
XCTAssertEqual(plannedJobs[1].outputs.count, 1)

XCTAssertEqual(plannedJobs[2].kind, .compile)
XCTAssertEqual(plannedJobs[2].outputs.count, 1)

XCTAssertEqual(plannedJobs[3].kind, .link)

// Check dummy files
XCTAssertFalse(localFileSystem.isFile(main))
XCTAssertTrue(localFileSystem.isFile(a))
XCTAssertTrue(localFileSystem.isFile(b))
}
}
}
}

func assertString(
Expand Down