mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
05ac3c9d75
This will unblock using Swift Concurrency features and updating to the latest versions of Swift Argument Parser. This won't drop support for linting projects with an older toolchain / Xcode selected, as long as SwiftLint was _built_ with 5.6+ and is _running_ on macOS 12+. So the main breaking change for end users here is requiring macOS 12 to run. However, the upside to using Swift Concurrency features is worth the breaking change in my opinion. Also being able to stay on recent Swift Argument Parser releases.
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
|
|
}
|
|
}
|
|
}
|