-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathVerifyNoBS.swift
executable file
·155 lines (130 loc) · 5.4 KB
/
VerifyNoBS.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/swift
// This script is meant to be called from an Xcode run script build phase
// It verifies there are no buildSettings embedded in the Xcode project
// as it is preferable to have build settings specified in .xcconfig files
// How to use:
// Put this script in a folder called 'buildscripts' next to your xcode project
// Then, add a Run script build phase to one of your targets with this as the script
//
// xcrun -sdk macosx swift buildscripts/VerifyNoBS.swift --xcode ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj/project.pbxproj
//
import Darwin
import Foundation
/// A message with its file name and location
struct LocatedMessage {
let message: String
let fileUrl: URL
let line: Int
}
/// Utility to process the pbxproj file
struct BuildSettingsVerifier {
public enum ProcessXcodeprojResult {
case foundBuildSettings([LocatedMessage])
case error(String)
case success(String)
}
/// Mode to run the utility in. Mode defines the output format
public enum Mode {
/// Write errors to stderr
case cmd
/// Write errors to stdout in a format that is picked up by Xcode
case xcode
}
/// The mode to run in
let mode: Mode
/// The absolute file URL to the pbxproj file
let projUrl: URL
init(mode: Mode, projUrl: URL) {
self.mode = mode
self.projUrl = projUrl
}
/// Reports an error either to stderr or to stdout, depending on the mode
func reportError(message: String, fileUrl: URL? = nil, line: Int? = nil) {
switch mode {
case .cmd:
let stderr = FileHandle.standardError
if let data = "\(message)\n".data(using: String.Encoding.utf8, allowLossyConversion: false) {
stderr.write(data)
} else {
print("There was an error. Could not convert error message to printable string")
}
case .xcode:
var messageParts = [String]()
if let fileUrl = fileUrl {
messageParts.append("\(fileUrl.path):")
}
if let line = line {
messageParts.append("\(line): ")
}
messageParts.append("error: \(message)")
print(messageParts.joined())
}
}
/// Inspect the pbxproj file for non-empty buildSettings
func processXcodeprojAt(url: URL) -> ProcessXcodeprojResult {
let startTime = Date()
guard let xcodeproj = try? String(contentsOf: url, encoding: String.Encoding.utf8) else {
return .error("Failed to read xcodeproj contents from \(url)")
}
let lines = xcodeproj.components(separatedBy: CharacterSet.newlines)
print("Found \(lines.count) lines")
var locatedMessages: [LocatedMessage] = []
var inBuildSettingsBlock = false
for (lineIndex, nthLine) in lines.enumerated() {
if inBuildSettingsBlock {
if nthLine.range(of: "\\u007d[:space:]*;", options: .regularExpression) != nil {
inBuildSettingsBlock = false
} else if nthLine.range(of: "CODE_SIGN_IDENTITY") != nil {
} else {
let message = mode == .cmd ? " \(nthLine)\n" : "Setting '\(nthLine.trimmingCharacters(in: .whitespacesAndNewlines))' should be in an xcconfig file"
locatedMessages.append(LocatedMessage(
message: message,
fileUrl: url,
line: lineIndex + 1
))
}
} else {
if nthLine.range(of: "buildSettings[:space:]*=", options: .regularExpression) != nil {
inBuildSettingsBlock = true
}
}
}
let timeInterval = Date().timeIntervalSince(startTime)
print("Process took \(timeInterval) seconds")
if locatedMessages.count > 0 {
return .foundBuildSettings(locatedMessages)
}
return .success(":-)")
}
public func verify() -> Int32 {
print("Verifying there are no build settings...")
let result = processXcodeprojAt(url: projUrl)
switch result {
case .error(let str):
reportError(message: "Error verifying build settings: \(str)")
return EXIT_FAILURE
case .foundBuildSettings(let locatedMessages):
reportError(message: "Found build settings in project file")
for msg in locatedMessages {
reportError(message: msg.message, fileUrl: msg.fileUrl, line: msg.line)
}
return EXIT_FAILURE
case .success:
print("No build settings found in project file")
return EXIT_SUCCESS
}
}
}
var commandLineArgs = CommandLine.arguments.dropFirst()
//print("processArgs were \(commandLineArgs)")
if commandLineArgs.count < 1 {
print("Usage: \(#file) [--xcode] /path/to/Project.xcodeproj/project.pbxproj")
exit(EXIT_FAILURE)
} else {
let xcodeProjFilePath = commandLineArgs.removeLast()
let mode: BuildSettingsVerifier.Mode = commandLineArgs.count > 0 && commandLineArgs.last == "--xcode" ? .xcode : .cmd
let myUrl = URL(fileURLWithPath: xcodeProjFilePath)
let verifier = BuildSettingsVerifier(mode: mode, projUrl: myUrl)
let exitCode = verifier.verify()
exit(exitCode)
}