108 lines
3.8 KiB
Swift
108 lines
3.8 KiB
Swift
import Foundation
|
|
|
|
struct CompilerArgumentsExtractor {
|
|
static func allCompilerInvocations(compilerLogs: String) -> [[String]] {
|
|
var compilerInvocations = [[String]]()
|
|
compilerLogs.enumerateLines { line, _ in
|
|
if let swiftcIndex = line.range(of: "swiftc ")?.upperBound, line.contains(" -module-name ") {
|
|
let invocation = parseCLIArguments(String(line[swiftcIndex...]))
|
|
.expandingResponseFiles
|
|
.filteringCompilerArguments
|
|
compilerInvocations.append(invocation)
|
|
}
|
|
}
|
|
return compilerInvocations
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func parseCLIArguments(_ string: String) -> [String] {
|
|
let escapedSpacePlaceholder = "\u{0}"
|
|
let scanner = Scanner(string: string)
|
|
var str = ""
|
|
var didStart = false
|
|
while let result = scanner.scanUpToString("\"") {
|
|
if didStart {
|
|
str += result.replacingOccurrences(of: " ", with: escapedSpacePlaceholder)
|
|
str += " "
|
|
} else {
|
|
str += result
|
|
}
|
|
_ = scanner.scanString("\"")
|
|
didStart.toggle()
|
|
}
|
|
return str.trimmingCharacters(in: .whitespaces)
|
|
.replacingOccurrences(of: "\\ ", with: escapedSpacePlaceholder)
|
|
.components(separatedBy: " ")
|
|
.map { $0.replacingOccurrences(of: escapedSpacePlaceholder, with: " ") }
|
|
}
|
|
|
|
/**
|
|
Partially filters compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept.
|
|
|
|
- parameter args: Compiler arguments, as parsed from `xcodebuild`.
|
|
|
|
- returns: A tuple of partially filtered compiler arguments in `.0`, and whether or not there are
|
|
more flags to remove in `.1`.
|
|
*/
|
|
private func partiallyFilter(arguments args: [String]) -> ([String], Bool) {
|
|
guard let indexOfFlagToRemove = args.firstIndex(of: "-output-file-map") else {
|
|
return (args, false)
|
|
}
|
|
var args = args
|
|
args.remove(at: args.index(after: indexOfFlagToRemove))
|
|
args.remove(at: indexOfFlagToRemove)
|
|
return (args, true)
|
|
}
|
|
|
|
extension Array where Element == String {
|
|
/// Return the full list of compiler arguments, replacing any response files with their contents.
|
|
fileprivate var expandingResponseFiles: [String] {
|
|
return flatMap { arg -> [String] in
|
|
guard arg.starts(with: "@") else {
|
|
return [arg]
|
|
}
|
|
let responseFile = String(arg.dropFirst())
|
|
return (try? String(contentsOf: URL(fileURLWithPath: responseFile, isDirectory: false))).flatMap {
|
|
$0.trimmingCharacters(in: .newlines)
|
|
.components(separatedBy: "\n")
|
|
.expandingResponseFiles
|
|
} ?? [arg]
|
|
}
|
|
}
|
|
|
|
/// Returns filtered compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept.
|
|
var filteringCompilerArguments: [String] {
|
|
var args = self
|
|
if args.first == "swiftc" {
|
|
args.removeFirst()
|
|
}
|
|
|
|
// https://github.com/realm/SwiftLint/issues/3365
|
|
args = args.map { $0.replacingOccurrences(of: "\\=", with: "=") }
|
|
args = args.map { $0.replacingOccurrences(of: "\\ ", with: " ") }
|
|
args.append(contentsOf: ["-D", "DEBUG"])
|
|
var shouldContinueToFilterArguments = true
|
|
while shouldContinueToFilterArguments {
|
|
(args, shouldContinueToFilterArguments) = partiallyFilter(arguments: args)
|
|
}
|
|
|
|
return args.filter {
|
|
![
|
|
"-parseable-output",
|
|
"-incremental",
|
|
"-serialize-diagnostics",
|
|
"-emit-dependencies"
|
|
].contains($0)
|
|
}.map {
|
|
if $0 == "-O" {
|
|
return "-Onone"
|
|
} else if $0 == "-DNDEBUG=1" {
|
|
return "-DDEBUG=1"
|
|
}
|
|
return $0
|
|
}
|
|
}
|
|
}
|