From 0fe3ccbc1134e7303756b4610b7660fc3dd1109d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 5 Nov 2021 15:27:34 -0500 Subject: [PATCH] Update to the latest version of VerifyNoBS --- NetNewsWire.xcodeproj/project.pbxproj | 6 +- buildscripts/VerifyNoBS.swift | 155 +++++++++++++++++++++++ buildscripts/VerifyNoBuildSettings.swift | 84 ------------ 3 files changed, 156 insertions(+), 89 deletions(-) create mode 100755 buildscripts/VerifyNoBS.swift delete mode 100755 buildscripts/VerifyNoBuildSettings.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 175bf7e5c..4515b4e74 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -3760,7 +3760,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBuildSettings.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n"; + shellScript = "xcrun -sdk macosx swift buildscripts/VerifyNoBS.swift --xcode ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj/project.pbxproj\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -4742,8 +4742,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 65ED40F2235DF5E00081F399 /* NetNewsWire_macapp_target_macappstore.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = "Apple Development"; - PRODUCT_NAME = NetNewsWire; }; name = Debug; }; @@ -4751,8 +4749,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 65ED40F2235DF5E00081F399 /* NetNewsWire_macapp_target_macappstore.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = "Apple Development"; - PRODUCT_NAME = NetNewsWire; }; name = Release; }; diff --git a/buildscripts/VerifyNoBS.swift b/buildscripts/VerifyNoBS.swift new file mode 100755 index 000000000..c2c72ba22 --- /dev/null +++ b/buildscripts/VerifyNoBS.swift @@ -0,0 +1,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) +} diff --git a/buildscripts/VerifyNoBuildSettings.swift b/buildscripts/VerifyNoBuildSettings.swift deleted file mode 100755 index 82e839b46..000000000 --- a/buildscripts/VerifyNoBuildSettings.swift +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/swift - -// This script is originally from github.com/olofhellman/VerifyNoBS -// The idea is that all build settings should be kept in .xcconfig files, -// rather than in the xcode pbxproj file inside an Xcode project bundle -// Having the script run as part of a regular build ensures that the project file -// doesn't accidentally accumulate build settings - -import Darwin -import Foundation - -func reportError(message: String) { - print("error message was \(message)") - let stderr = FileHandle.standardError - if let data = message.data(using: String.Encoding.utf8, allowLossyConversion: false) { - stderr.write(data) - } else { - print("there was an error. script \"VerifyNoBuildSettings\" could not convert error message to printable string") - } -} - -public enum ProcessXcodeprojResult { - case FoundBuildSettings([String]) - case Error(String) - case OK(String) -} - -public func processXcodeprojAt(url: URL) -> ProcessXcodeprojResult { - let startTime = Date() - guard let xcodeproj = try? String(contentsOf: url, encoding: String.Encoding.utf8) else { - return .Error("script \"VerifyNoBuildSettings\" failed making xcodeproj from url") - } - let lines = xcodeproj.components(separatedBy: CharacterSet.newlines) - print ("found \(lines.count) lines") - - var badLines: [String] = [] - var inBuildSettingsBlock = false - for nthLine in lines { - if inBuildSettingsBlock { - if let _ = nthLine.range(of:"\\u007d[:space:]*;", options: .regularExpression) { - inBuildSettingsBlock = false - } else if let _ = nthLine.range(of:"CODE_SIGN_IDENTITY") { - - } else if let _ = nthLine.range(of:"PRODUCT_NAME") { - - } else { - badLines.append(nthLine) - } - } else { - if let _ = nthLine.range(of:"buildSettings[:space:]*=", options: .regularExpression) { - inBuildSettingsBlock = true - } - } - } - - let timeInterval = Date().timeIntervalSince(startTime) - print ("process took \(timeInterval) seconds") - if (badLines.count > 0) { - return .FoundBuildSettings(badLines) - } - return .OK(":-)") -} -print("Verifying no buildSettings...") - -let commandLineArgs = CommandLine.arguments -print("processArgs were \(commandLineArgs)") -let xcodeprojfilepath = commandLineArgs[1] -let myUrl = URL(fileURLWithPath:xcodeprojfilepath) -let result = processXcodeprojAt(url: myUrl) - -switch result { - case .Error(let str): - reportError (message: "error script \"VerifyNoBuildSettings\" encountered an error: \(str)") - exit(EXIT_FAILURE) - case .FoundBuildSettings(let badLines): - reportError (message: "script \"VerifyNoBuildSettings\" found build settings in the project file:") - for badLine in badLines { - reportError (message: " \(badLine)\n") - } - exit(EXIT_FAILURE) - case .OK: - print ("script \"VerifyNoBuildSettings\" verified the project contained no buildSettings") - exit(EXIT_SUCCESS) -}