Skip to content

Commit

Permalink
Add -supplementary-output-file-map
Browse files Browse the repository at this point in the history
  • Loading branch information
polac24 committed May 29, 2023
1 parent d95da61 commit 4a46d57
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public struct SwiftFrontendArgInput {
// .swiftsourceinfo and .swiftdoc will be placed next to the .swiftmodule
let sourceInfoPath: String?
let docPath: String?
let supplementaryOutputFileMap: String?

/// Manual initializer implementation required to be public
public init(
Expand All @@ -77,7 +78,8 @@ public struct SwiftFrontendArgInput {
dependenciesPaths: [String],
diagnosticsPaths: [String],
sourceInfoPath: String?,
docPath: String?
docPath: String?,
supplementaryOutputFileMap: String?
) {
self.compile = compile
self.emitModule = emitModule
Expand All @@ -91,6 +93,7 @@ public struct SwiftFrontendArgInput {
self.diagnosticsPaths = diagnosticsPaths
self.sourceInfoPath = sourceInfoPath
self.docPath = docPath
self.supplementaryOutputFileMap = supplementaryOutputFileMap
}

// swiftlint:disable:next cyclomatic_complexity function_body_length
Expand Down Expand Up @@ -139,23 +142,28 @@ public struct SwiftFrontendArgInput {
emitModule: nil
)

let compilationFileMap = (0..<primaryInputsCount).reduce([String: SwiftFileCompilationInfo]()) { prev, i in
var new = prev
new[primaryInputPaths[i]] = SwiftFileCompilationInfo(
file: primaryInputFilesURLs[i],
dependencies: dependenciesPaths.get(i).map(URL.init(fileURLWithPath:)),
object: outputPaths.get(i).map(URL.init(fileURLWithPath:)),
// for now - swift-dependencies are not requested in the driver compilation mode
swiftDependencies: nil
)
return new
let compilationFilesOutputs: SwiftcContext.CompilationFilesOutputs
if let compimentaryFileMa = supplementaryOutputFileMap {
compilationFilesOutputs = .supplementaryFileMap(compimentaryFileMa)
} else {
compilationFilesOutputs = .map((0..<primaryInputsCount).reduce([String: SwiftFileCompilationInfo]()) { prev, i in
var new = prev
new[primaryInputPaths[i]] = SwiftFileCompilationInfo(
file: primaryInputFilesURLs[i],
dependencies: dependenciesPaths.get(i).map(URL.init(fileURLWithPath:)),
object: outputPaths.get(i).map(URL.init(fileURLWithPath:)),
// for now - swift-dependencies are not requested in the driver compilation mode
swiftDependencies: nil
)
return new
})
}

return try .init(
config: config,
moduleName: moduleName,
steps: steps,
outputs: .map(compilationFileMap),
outputs: compilationFilesOutputs,
target: target,
inputs: .list(inputPaths),
exampleWorkspaceFilePath: outputPaths[0]
Expand Down
2 changes: 2 additions & 0 deletions Sources/XCRemoteCache/Commands/Swiftc/SwiftcContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public struct SwiftcContext {
public enum CompilationFilesOutputs {
/// defined in a separate file (via -output-file-map)
case fileMap(String)
/// defined in a separate file (via -supplementary-output-file-map)
case supplementaryFileMap(String)
/// explicitly passed in the invocation
case map([String: SwiftFileCompilationInfo])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
// under the License.

import Foundation
import Yams

/// Errors with reading swiftc inputs
enum SwiftcInputReaderError: Error {
case readingFailed
case invalidFormat
case invalidYamlFormat
case missingField(String)
}

Expand All @@ -49,7 +51,7 @@ struct SwiftModuleCompilationInfo: Encodable, Equatable {
let swiftDependencies: URL?
}

public struct SwiftFileCompilationInfo: Encodable, Equatable {
public struct SwiftFileCompilationInfo: Encodable, Hashable {
let file: URL
// not present for WMO builds
let dependencies: URL?
Expand All @@ -61,19 +63,26 @@ public struct SwiftFileCompilationInfo: Encodable, Equatable {

class SwiftcFilemapInputEditor: SwiftcInputReader, SwiftcInputWriter {

enum Format {
case json
case yaml
}

private let file: URL
private let fileFormat: Format
private let fileManager: FileManager

init(_ file: URL, fileManager: FileManager) {
init(_ file: URL, fileFormat: Format, fileManager: FileManager) {
self.file = file
self.fileFormat = fileFormat
self.fileManager = fileManager
}

func read() throws -> SwiftCompilationInfo {
guard let content = fileManager.contents(atPath: file.path) else {
throw SwiftcInputReaderError.readingFailed
}
guard let representation = try JSONSerialization.jsonObject(with: content, options: []) as? [String: Any] else {
guard let representation = try decodeFile(content: content) else {
throw SwiftcInputReaderError.invalidFormat
}
return try SwiftCompilationInfo(from: representation)
Expand All @@ -83,11 +92,20 @@ class SwiftcFilemapInputEditor: SwiftcInputReader, SwiftcInputWriter {
let data = try JSONSerialization.data(withJSONObject: info.dump(), options: [.prettyPrinted])
fileManager.createFile(atPath: file.path, contents: data, attributes: nil)
}

private func decodeFile(content: Data) throws -> [String: Any]? {
switch fileFormat {
case .json:
return try JSONSerialization.jsonObject(with: content, options: []) as? [String: Any]
case .yaml:
return try Yams.load(yaml: String(data: content, encoding: .utf8)!) as? [String: Any]
}
}
}

extension SwiftCompilationInfo {
init(from object: [String: Any]) throws {
info = try SwiftModuleCompilationInfo(from: object[""])
info = try SwiftModuleCompilationInfo(from: object["", default: [:]])
files = try object.reduce([]) { prev, new in
let (key, value) = new
if key.isEmpty {
Expand Down
4 changes: 3 additions & 1 deletion Sources/XCRemoteCache/Commands/Swiftc/XCSwiftc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ public class XCSwiftAbstract<InputArgs> {
// and the file/list is named "output"
switch context.outputs {
case .fileMap(let path):
inputReader = SwiftcFilemapInputEditor(URL(fileURLWithPath: path), fileManager: fileManager)
inputReader = SwiftcFilemapInputEditor(URL(fileURLWithPath: path), fileFormat: .json, fileManager: fileManager)
case .supplementaryFileMap(let path):
inputReader = SwiftcFilemapInputEditor(URL(fileURLWithPath: path), fileFormat: .yaml, fileManager: fileManager)
case .map(let map):
// static - passed via the arguments list
// TODO: check if first 2 ars can always be `nil`
Expand Down
6 changes: 5 additions & 1 deletion Sources/xcswift-frontend/XCSwiftcFrontendMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class XCSwiftcFrontendMain {
var diagnosticsPaths: [String] = []
var sourceInfoPath: String?
var docPath: String?
var supplementaryOutputFileMap: String?

for i in 0..<args.count {
let arg = args[i]
Expand Down Expand Up @@ -73,6 +74,8 @@ public class XCSwiftcFrontendMain {
case "-primary-file":
// .swift
primaryInputPaths.append(args[i + 1])
case "-supplementary-output-file-map":
supplementaryOutputFileMap = args[i + 1]
default:
if arg.hasSuffix(".swift") {
inputPaths.append(arg)
Expand All @@ -94,7 +97,8 @@ public class XCSwiftcFrontendMain {
dependenciesPaths: dependenciesPaths,
diagnosticsPaths: diagnosticsPaths,
sourceInfoPath: sourceInfoPath,
docPath: docPath
docPath: docPath,
supplementaryOutputFileMap: supplementaryOutputFileMap
)
// swift-frontened is first invoked with some "probing" args like
// -print-target-info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,21 @@ class SwiftcFilemapInputEditorTests: FileXCTestCase {
)
private let sampleInfoContentData = #"{"":{"swift-dependencies":"/"}}"#.data(using: .utf8)!
private var inputFile: URL!
private var editor: SwiftcFilemapInputEditor!
private var editorJson: SwiftcFilemapInputEditor!
private var editorYaml: SwiftcFilemapInputEditor!

override func setUpWithError() throws {
try super.setUpWithError()
try prepareTempDir()
inputFile = workingDirectory!.appendingPathComponent("swift.json")
editor = SwiftcFilemapInputEditor(inputFile, fileManager: fileManager)
editorJson = SwiftcFilemapInputEditor(inputFile, fileFormat: .json, fileManager: fileManager)
editorYaml = SwiftcFilemapInputEditor(inputFile, fileFormat: .yaml, fileManager: fileManager)
}

func testReading() throws {
try fileManager.spt_writeToFile(atPath: inputFile.path, contents: sampleInfoContentData)

let readInfo = try editor.read()
let readInfo = try editorJson.read()

XCTAssertEqual(readInfo, sampleInfo)
}
Expand Down Expand Up @@ -80,13 +82,13 @@ class SwiftcFilemapInputEditorTests: FileXCTestCase {
])
try fileManager.spt_writeToFile(atPath: inputFile.path, contents: infoContentData)

let readInfo = try editor.read()
let readInfo = try editorJson.read()

XCTAssertEqual(readInfo, expectedInfo)
}

func testWritingSavesContent() throws {
try editor.write(sampleInfo)
try editorJson.write(sampleInfo)

let savedContent = try Data(contentsOf: inputFile)
let content = try JSONSerialization.jsonObject(with: savedContent, options: []) as? [String: Any]
Expand All @@ -108,7 +110,7 @@ class SwiftcFilemapInputEditorTests: FileXCTestCase {
),
])

try editor.write(extendedInfo)
try editorJson.write(extendedInfo)

let savedContent = try Data(contentsOf: inputFile)
let content = try JSONSerialization.jsonObject(with: savedContent, options: []) as? [String: Any]
Expand All @@ -119,12 +121,50 @@ class SwiftcFilemapInputEditorTests: FileXCTestCase {
func testModifyingFileCompilationInfo() throws {
try fileManager.spt_writeToFile(atPath: inputFile.path, contents: sampleInfoContentData)

let originalInfo = try editor.read()
let originalInfo = try editorJson.read()
var modifiedInfo = originalInfo
modifiedInfo.files = [file]
try editor.write(modifiedInfo)
let finalInfo = try editor.read()
try editorJson.write(modifiedInfo)
let finalInfo = try editorJson.read()

XCTAssertEqual(finalInfo, modifiedInfo)
}

func testReadingSupplementaryInfoWithOptionalProperties() throws {
let infoContentData = #"""
"/file1.swift":
swift-dependencies: "/file1.swiftdeps"
dependencies: "/file1.d"
"/file2.swift":
dependencies: "/file2.d"
object: "/file2.o"
swift-dependencies: "/file2.swiftdeps"
"""#.data(using: .utf8)!
let expectedInfo = SwiftCompilationInfo(
info: SwiftModuleCompilationInfo(
dependencies: nil,
swiftDependencies: nil
),
files: [
SwiftFileCompilationInfo(
file: "/file1.swift",
dependencies: "/file1.d",
object: nil,
swiftDependencies: "/file1.swiftdeps"
),
SwiftFileCompilationInfo(
file: "/file2.swift",
dependencies: "/file2.d",
object: "/file2.o",
swiftDependencies: "/file2.swiftdeps"
),
])
try fileManager.spt_writeToFile(atPath: inputFile.path, contents: infoContentData)

let readInfo = try editorYaml.read()

// `files` order doesn't match
XCTAssertEqual(readInfo.info, expectedInfo.info)
XCTAssertEqual(Set(readInfo.files), Set(expectedInfo.files))
}
}

0 comments on commit 4a46d57

Please sign in to comment.