Skip to content

Commit

Permalink
Use XCConfig to evaluate config file contents
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmassicotte committed Dec 1, 2023
1 parent 5f458dc commit 84f9f15
Show file tree
Hide file tree
Showing 12 changed files with 489 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"location" : "https://github.com/mattmassicotte/XCConfig",
"state" : {
"branch" : "main",
"revision" : "ffe291c9237407f3f2695d10e567df63ad8b81fd"
"revision" : "2379629e1f44a6476afc709d9fcffb02bf7fa724"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/tuist/XcodeProj", from: "8.15.0"),
.package(url: "https://github.com/mattmassicotte/XCConfig", branch: "main"),
.package(url: "https://github.com/mattmassicotte/XCConfig", revision: "2379629e1f44a6476afc709d9fcffb02bf7fa724"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.3"),
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.0"),
],
Expand Down
42 changes: 42 additions & 0 deletions Sources/XCLinting/Extensions/PBXProj+BuildSettings.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation

import XCConfig
import XcodeProj

extension PBXProj {
Expand All @@ -16,4 +17,45 @@ extension PBXProj {
try block(target.name, list)
}
}

func enumerateTargets(_ block: (PBXProject, PBXTarget) throws -> Void) rethrows {
for proj in projects {
for target in proj.targets {
try block(proj, target)
}
}
}

func enumerateBuildSettingStatements(
rootURL: URL,
_ block: (PBXProject, PBXTarget, XCBuildConfiguration, [[Statement]]) throws -> Void
) throws {
let sourceRootPath = rootURL.path(percentEncoded: false)

for proj in projects {
let projConfigList = proj.buildConfigurationList

for target in proj.targets {
for config in target.buildConfigurationList?.buildConfigurations ?? [] {
print("\(target.name)-\(config.name)")

let projConfig = projConfigList?.configuration(name: config.name)
let projConfigStatements = try projConfig?.baseConfigurationStatements(with: sourceRootPath) ?? []
let projStatements = projConfig?.buildSettingsStatements ?? []

let configStatements = try config.baseConfigurationStatements(with: sourceRootPath)
let statements = config.buildSettingsStatements

let heirarchy = [
projConfigStatements,
projStatements,
configStatements,
statements
]

try block(proj, target, config, heirarchy)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

import XCConfig
import XcodeProj

extension XCBuildConfiguration {
func baseConfigurationContent(with sourceRoot: String) throws -> String? {
guard let fullPath = try baseConfiguration?.fullPath(sourceRoot: sourceRoot) else {
return nil
}

return try String(contentsOf: URL(filePath: fullPath))
}

func baseConfigurationStatements(with sourceRoot: String) throws -> [Statement] {
guard let content = try baseConfigurationContent(with: sourceRoot) else {
return []
}

return Parser().parse(content)
}

var buildSettingsStatements: [Statement] {
buildSettings.compactMap { (key, value) -> Statement? in
guard let value = value as? String else { return nil }

return Statement.assignment(Assignment(key: key, value: value))
}
}
}
45 changes: 28 additions & 17 deletions Sources/XCLinting/Rules/ValidateBuildSettingsRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,50 @@ struct ValidateBuildSettingsRule {
func run(_ environment: XCLinter.Environment) throws -> [Violation] {
var violations = [Violation]()

// check top-level
for config in environment.project.pbxproj.buildConfigurations {
violations.append(contentsOf: evaluateTargetSettings("Project", config: config))
}

// check targets
environment.project.pbxproj.enumerateBuildConfigurations { name, configList in
for config in configList.buildConfigurations {
violations.append(contentsOf: evaluateTargetSettings(name, config: config))
}
try enumerateSettings(with: environment) { target, config, settings in
print("\(target.name), \(config.name), \(settings)")

violations.append(contentsOf: evaluateTargetSettings(target.name, settings: settings))
}

return violations
}

func evaluateTargetSettings(_ name: String, config: XCBuildConfiguration) -> [Violation] {
func evaluateTargetSettings(_ targetName: String, settings: [BuildSetting: String]) -> [Violation] {
var violations = [Violation]()

for pair in config.buildSettings {
guard let setting = BuildSetting(rawValue: pair.key) else { continue }

let status = setting.evaluateValue(pair.value as? String ?? "")
for (setting, value) in settings {
let name = setting.rawValue
let status = setting.evaluateValue(value)

switch status {
case .deprecated:
violations.append(.init("\(name):\(pair.key) = \(pair.value) is deprecated"))
violations.append(.init("\(targetName):\(name) = \(value) is deprecated"))
case .invalid:
violations.append(.init("\(name):\(pair.key) = \(pair.value) is invalid"))
violations.append(.init("\(targetName):\(name) = \(value) is invalid"))
case .valid:
break
}
}

return violations
}

func enumerateSettings(
with environment: XCLinter.Environment,
block: (PBXTarget, XCBuildConfiguration, [BuildSetting: String]) throws -> Void
) throws {
let project = environment.project
let sourceRootURL = environment.projectRootURL.deletingLastPathComponent()
let evaluator = Evaluator(rootURL: sourceRootURL)
let platformStatements: [Statement] = []

try project.pbxproj.enumerateBuildSettingStatements(rootURL: sourceRootURL) { proj, target, config, statementsList in
let heirarchy = [platformStatements] + statementsList

let settings = try evaluator.evaluate(heirarchy: heirarchy)

try block(target, config, settings)
}
}
}
1 change: 1 addition & 0 deletions Tests/XCLintTests/TestData/XCConfFile-Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALWAYS_SEARCH_USER_PATHS = NO
1 change: 1 addition & 0 deletions Tests/XCLintTests/TestData/XCConfFile-Release.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALWAYS_SEARCH_USER_PATHS = YES
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading

0 comments on commit 84f9f15

Please sign in to comment.