From fbae0efea747c32fd1655f63098d96ee02bde8e7 Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Mon, 17 Feb 2025 20:29:56 -0500 Subject: [PATCH 1/9] Remove unused `toAny` method --- Sources/XcodeGraph/Models/Settings.swift | 12 ---------- .../Models/SettingsTests.swift | 22 ------------------- 2 files changed, 34 deletions(-) diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift index d8f182c5..c631c040 100644 --- a/Sources/XcodeGraph/Models/Settings.swift +++ b/Sources/XcodeGraph/Models/Settings.swift @@ -145,18 +145,6 @@ extension [BuildConfiguration: Configuration?] { } } -extension [String: SettingValue] { - public func toAny() -> [String: Any] { - mapValues { value in - switch value { - case let .array(array): - return array - case let .string(string): - return string - } - } - } -} #if DEBUG extension Configuration { diff --git a/Tests/XcodeGraphTests/Models/SettingsTests.swift b/Tests/XcodeGraphTests/Models/SettingsTests.swift index 0901d338..1111301a 100644 --- a/Tests/XcodeGraphTests/Models/SettingsTests.swift +++ b/Tests/XcodeGraphTests/Models/SettingsTests.swift @@ -153,25 +153,3 @@ final class SettingsTests: XCTestCase { Configuration(settings: [:], xcconfig: nil) } } - -final class DictionaryStringSettingValueExtensionTests: XCTestCase { - func testToAny() { - // Given - let buildConfig: [String: SettingValue] = [ - "A": ["A_VALUE_1", "A_VALUE_2"], - "B": "B_VALUE", - "C": ["C_VALUE"], - ] - let expected: [String: Any] = [ - "A": ["A_VALUE_1", "A_VALUE_2"], - "B": "B_VALUE", - "C": ["C_VALUE"], - ] - - // When - let got = buildConfig.toAny() - - // Then - XCTAssertEqualDictionaries(got, expected) - } -} From 14f7238ed14fe5820e69c2e28b4ad95c0623432e Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Fri, 28 Feb 2025 16:01:17 -0500 Subject: [PATCH 2/9] First draft of using Strogly Typed build settings in XcodeProj 9 --- Package.resolved | 18 +-- Package.swift | 3 +- .../Extensions/PBXProject+Extensions.swift | 4 +- .../Phases/PBXCopyFilesBuildPhaseMapper.swift | 2 +- .../Phases/PBXHeadersBuildPhaseMapper.swift | 2 +- .../Phases/PBXSourcesBuildPhaseMapper.swift | 5 +- .../Mappers/Project/ProjectAttribute.swift | 2 +- .../Mappers/Settings/BuildSettings.swift | 143 +++++++++--------- .../XCConfigurationList+Helpers.swift | 4 +- .../Targets/PBXTarget+BuildSettings.swift | 34 ++--- .../Targets/PBXTarget+GraphMapping.swift | 36 +---- .../Mappers/Targets/PBXTargetMapper.swift | 15 +- 12 files changed, 116 insertions(+), 152 deletions(-) diff --git a/Package.resolved b/Package.resolved index eb90ff03..4efea982 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "903b6688321bae457eebe89dd170b8e84c2b492ea8715d843afaaaa1a64c58b5", + "originHash" : "5595944e8be7d3f216476672222bb48dda3ea7521f57fcbd42c2db44a3f9b7f1", "pins" : [ { "identity" : "aexml", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/p-x9/MachOKit", "state" : { - "revision" : "7ea55264554310709a4669091be21e5b5423aa44", - "version" : "0.29.1" + "revision" : "62d22e1ecef3dda702c039f03dd83f674ad59bfc", + "version" : "0.30.0" } }, { @@ -159,8 +159,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "c8a44d836fe7913603e246acab7c528c2e780168", - "version" : "1.4.0" + "revision" : "a34201439c74b53f0fd71ef11741af7e7caf01e1", + "version" : "1.4.2" } }, { @@ -168,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tuist/XcodeProj", "state" : { - "revision" : "02bc2dd6224aa59147941d85fdc45a7677af62f6", - "version" : "8.27.3" + "branch" : "waltflanagan/StrongTypes", + "revision" : "d04f4ffe6616c6239f9e1f7ae1e2949f6f0033d6" } }, { @@ -177,8 +177,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", - "version" : "1.4.3" + "revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4", + "version" : "1.5.2" } }, { diff --git a/Package.swift b/Package.swift index 957fb98d..a8cd4b40 100644 --- a/Package.swift +++ b/Package.swift @@ -80,7 +80,8 @@ let package = Package( dependencies: [ .package(url: "https://github.com/Flight-School/AnyCodable", .upToNextMajor(from: "0.6.7")), .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.8")), - .package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "8.27.7")), + .package(url: "https://github.com/tuist/XcodeProj", branch: "waltflanagan/StrongTypes"), +// .package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "8.27.7")), .package(url: "https://github.com/tuist/Command.git", from: "0.13.0"), .package(url: "https://github.com/tuist/FileSystem.git", .upToNextMajor(from: "0.7.7")), .package(url: "https://github.com/apple/swift-service-context", .upToNextMajor(from: "1.2.0")), diff --git a/Sources/XcodeGraphMapper/Extensions/PBXProject+Extensions.swift b/Sources/XcodeGraphMapper/Extensions/PBXProject+Extensions.swift index c6cbe15e..9aec190f 100644 --- a/Sources/XcodeGraphMapper/Extensions/PBXProject+Extensions.swift +++ b/Sources/XcodeGraphMapper/Extensions/PBXProject+Extensions.swift @@ -5,7 +5,7 @@ extension PBXProject { /// /// - Parameter attr: The attribute key to look up. /// - Returns: The value of the attribute if it exists, or `nil` if not found. - func attribute(for attr: ProjectAttribute) -> String? { - attributes[attr.rawValue] as? String + func attribute(for attr: ProjectAttributeKey) -> String? { + attributes[attr.rawValue]?.stringValue } } diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift index 4074dac4..02a262c7 100644 --- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift @@ -59,7 +59,7 @@ struct PBXCopyFilesBuildPhaseMapper: PBXCopyFilesBuildPhaseMapping { } let absolutePath = try AbsolutePath(validating: pathString) - let attributes = buildFile.settings?.stringArray(for: .attributes) + let attributes = buildFile.attributes let codeSignOnCopy = attributes?.contains(BuildFileAttribute.codeSignOnCopy.rawValue) ?? false return .file(path: absolutePath, condition: nil, codeSignOnCopy: codeSignOnCopy) diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift index 379b0515..bb458233 100644 --- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift @@ -54,7 +54,7 @@ struct PBXHeadersBuildPhaseMapper: PBXHeadersBuildPhaseMapping { } let absolutePath = try AbsolutePath(validating: pathString) - let attributes = buildFile.settings?.stringArray(for: .attributes) + let attributes = buildFile.attributes let visibility: HeaderInfo.HeaderVisibility if attributes?.contains(HeaderAttribute.public.rawValue) == true { diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift index dc0e5745..01169059 100644 --- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift @@ -40,9 +40,8 @@ struct PBXSourcesBuildPhaseMapper: PBXSourcesBuildPhaseMapping { } let path = try AbsolutePath(validating: pathString) - let settings = buildFile.settings ?? [:] - let compilerFlags: String? = settings.string(for: .compilerFlags) - let attributes: [String]? = settings.stringArray(for: .attributes) + let compilerFlags: String? = buildFile.compilerFlags + let attributes: [String]? = buildFile.attributes return SourceFile( path: path, diff --git a/Sources/XcodeGraphMapper/Mappers/Project/ProjectAttribute.swift b/Sources/XcodeGraphMapper/Mappers/Project/ProjectAttribute.swift index ae33a42f..2c1fb51d 100644 --- a/Sources/XcodeGraphMapper/Mappers/Project/ProjectAttribute.swift +++ b/Sources/XcodeGraphMapper/Mappers/Project/ProjectAttribute.swift @@ -1,7 +1,7 @@ import Foundation /// Attributes for project settings that can be retrieved from a `PBXProject`. -enum ProjectAttribute: String { +enum ProjectAttributeKey: String { case classPrefix = "CLASSPREFIX" case organization = "ORGANIZATIONNAME" case lastUpgradeCheck = "LastUpgradeCheck" diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift index e5652b26..6205abc5 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift @@ -27,74 +27,75 @@ enum BuildSettingKey: String { case visionOSDeploymentTarget = "VISIONOS_DEPLOYMENT_TARGET" } -/// A protocol representing a type that can parse a build setting value from a generic `Any`. -protocol BuildSettingValue { - associatedtype Value - static func parse(_ any: Any) -> Value? -} - -/// A type that parses build settings as strings. -enum BuildSettingString: BuildSettingValue { - static func parse(_ any: Any) -> String? { - any as? String - } -} - -/// A type that parses build settings as arrays of strings. -enum BuildSettingStringArray: BuildSettingValue { - static func parse(_ any: Any) -> [String]? { - let arr = any as? [Any] - return arr?.compactMap { $0 as? String } - } -} - -/// A type that parses build settings as booleans. -enum BuildSettingBool: BuildSettingValue { - static func parse(_ any: Any) -> Bool? { - any as? Bool - } -} - -/// A type that parses build settings as dictionaries of strings to strings. -enum BuildSettingStringDict: BuildSettingValue { - static func parse(_ any: Any) -> [String: String]? { - any as? [String: String] - } -} - -extension [String: Any] { - /// Extracts a build setting value of a specified type from the dictionary. - /// - /// - Parameters: - /// - key: The `BuildSettingKey` to look up. - /// - type: The type conforming to `BuildSettingValue` indicating the expected value type. - /// - Returns: The parsed value if found and valid, or `nil` otherwise. - func extractBuildSetting(_ key: BuildSettingKey, as _: T.Type = T.self) - -> T.Value? - { - guard let value = self[key.rawValue] else { return nil } - return T.parse(value) - } -} - -extension [String: Any] { - /// Retrieves a string value for the given build setting key. - func string(for key: BuildSettingKey) -> String? { - extractBuildSetting(key, as: BuildSettingString.self) - } - - /// Retrieves an array of strings for the given build setting key. - func stringArray(for key: BuildSettingKey) -> [String]? { - extractBuildSetting(key, as: BuildSettingStringArray.self) - } - - /// Retrieves a boolean value for the given build setting key. - func bool(for key: BuildSettingKey) -> Bool? { - extractBuildSetting(key, as: BuildSettingBool.self) - } - - /// Retrieves a dictionary of strings for the given build setting key. - func stringDict(for key: BuildSettingKey) -> [String: String]? { - extractBuildSetting(key, as: BuildSettingStringDict.self) - } -} +// +///// A protocol representing a type that can parse a build setting value from a generic `Any`. +//protocol BuildSettingValue { +// associatedtype Value +// static func parse(_ any: Any) -> Value? +//} +// +///// A type that parses build settings as strings. +//enum BuildSettingString: BuildSettingValue { +// static func parse(_ any: Any) -> String? { +// any as? String +// } +//} +// +///// A type that parses build settings as arrays of strings. +//enum BuildSettingStringArray: BuildSettingValue { +// static func parse(_ any: Any) -> [String]? { +// let arr = any as? [Any] +// return arr?.compactMap { $0 as? String } +// } +//} +// +///// A type that parses build settings as booleans. +//enum BuildSettingBool: BuildSettingValue { +// static func parse(_ any: Any) -> Bool? { +// any as? Bool +// } +//} +// +///// A type that parses build settings as dictionaries of strings to strings. +//enum BuildSettingStringDict: BuildSettingValue { +// static func parse(_ any: Any) -> [String: String]? { +// any as? [String: String] +// } +//} +// +//extension [String: Any] { +// /// Extracts a build setting value of a specified type from the dictionary. +// /// +// /// - Parameters: +// /// - key: The `BuildSettingKey` to look up. +// /// - type: The type conforming to `BuildSettingValue` indicating the expected value type. +// /// - Returns: The parsed value if found and valid, or `nil` otherwise. +// func extractBuildSetting(_ key: BuildSettingKey, as _: T.Type = T.self) +// -> T.Value? +// { +// guard let value = self[key.rawValue] else { return nil } +// return T.parse(value) +// } +//} +// +//extension [String: Any] { +// /// Retrieves a string value for the given build setting key. +// func string(for key: BuildSettingKey) -> String? { +// extractBuildSetting(key, as: BuildSettingString.self) +// } +// +// /// Retrieves an array of strings for the given build setting key. +// func stringArray(for key: BuildSettingKey) -> [String]? { +// extractBuildSetting(key, as: BuildSettingStringArray.self) +// } +// +// /// Retrieves a boolean value for the given build setting key. +// func bool(for key: BuildSettingKey) -> Bool? { +// extractBuildSetting(key, as: BuildSettingBool.self) +// } +// +// /// Retrieves a dictionary of strings for the given build setting key. +// func stringDict(for key: BuildSettingKey) -> [String: String]? { +// extractBuildSetting(key, as: BuildSettingStringDict.self) +// } +//} diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift index 8c429113..58eeaab3 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift @@ -10,7 +10,7 @@ extension XCConfigurationList { let configurationMatcher = ConfigurationMatcher() var results = [BuildConfiguration: String]() for config in buildConfigurations { - if let value = config.buildSettings.string(for: key) { + if let value = config.buildSettings[key.rawValue]?.stringValue { let variant = configurationMatcher.variant(for: config.name) let buildConfig = BuildConfiguration(name: config.name, variant: variant) results[buildConfig] = value @@ -29,7 +29,7 @@ extension XCConfigurationList { for key in keys { for config in buildConfigurations { - if let value = config.buildSettings.string(for: key) { + if let value = config.buildSettings[key.rawValue]?.stringValue { results[key] = value break // Once found, move to the next key } diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift index 29f22adf..ae742d84 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift @@ -4,16 +4,16 @@ import XcodeGraph import XcodeProj extension PBXTarget { - enum EnvironmentExtractor { - static func extract(from buildSettings: BuildSettings) -> [String: EnvironmentVariable] { - guard let envVars = buildSettings.stringDict(for: .environmentVariables) else { - return [:] - } - return envVars.reduce(into: [:]) { result, pair in - result[pair.key] = EnvironmentVariable(value: pair.value, isEnabled: true) - } - } - } +// enum EnvironmentExtractor { +// static func extract(from buildSettings: BuildSettings) -> [String: EnvironmentVariable] { +// guard let envVars = buildSettings.stringDict(for: .environmentVariables) else { +// return [:] +// } +// return envVars.reduce(into: [:]) { result, pair in +// result[pair.key] = EnvironmentVariable(value: pair.value, isEnabled: true) +// } +// } +// } /// Retrieves the path to the Info.plist file from the target's build settings. /// @@ -73,15 +73,15 @@ extension PBXTarget { /// Extracts environment variables from all build configurations of the target. /// /// If multiple configurations define the same environment variable, the last processed configuration takes precedence. - func extractEnvironmentVariables() -> [String: EnvironmentVariable] { - buildConfigurationList?.buildConfigurations.reduce(into: [:]) { result, config in - result.merge(EnvironmentExtractor.extract(from: config.buildSettings)) { current, _ in current - } - } ?? [:] - } +// func extractEnvironmentVariables() -> [String: EnvironmentVariable] { +// buildConfigurationList?.buildConfigurations.reduce(into: [:]) { result, config in +// result.merge(EnvironmentExtractor.extract(from: config.buildSettings)) { current, _ in current +// } +// } ?? [:] +// } /// Returns the build settings from the "Debug" build configuration, or an empty dictionary if not present. - var debugBuildSettings: [String: Any] { + var debugBuildSettings: [String: BuildSetting] { buildConfigurationList?.buildConfigurations.first(where: { $0.name == "Debug" })?.buildSettings ?? [:] } diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift index 2de70f79..5caf8c3a 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift @@ -6,7 +6,7 @@ import XcodeProj extension PBXTarget { /// Attempts to retrieve the bundle identifier from the target's debug build settings, or throws an error if missing. func bundleIdentifier() throws -> String { - if let bundleId = debugBuildSettings.string(for: .productBundleIdentifier) { + if let bundleId = debugBuildSettings[BuildSettingKey.productBundleIdentifier.rawValue]?.stringValue { return bundleId } else { return "Unknown" @@ -18,45 +18,13 @@ extension PBXTarget { buildPhases.compactMap { $0 as? PBXCopyFilesBuildPhase } } - func launchArguments() throws -> [LaunchArgument] { - guard let buildConfigList = buildConfigurationList else { return [] } - var launchArguments: [LaunchArgument] = [] - for buildConfig in buildConfigList.buildConfigurations { - if let args = buildConfig.buildSettings.stringArray(for: .launchArguments) { - launchArguments.append(contentsOf: args.map { LaunchArgument(name: $0, isEnabled: true) }) - } - } - return launchArguments.uniqued() - } - - func prune() throws -> Bool { - debugBuildSettings.bool(for: .prune) ?? false - } - func mergedBinaryType() throws -> MergedBinaryType { - let mergedBinaryTypeString = debugBuildSettings.string(for: .mergedBinaryType) + let mergedBinaryTypeString = debugBuildSettings[BuildSettingKey.mergedBinaryType.rawValue]?.stringValue return mergedBinaryTypeString == "automatic" ? .automatic : .disabled } - func mergeable() throws -> Bool { - debugBuildSettings.bool(for: .mergeable) ?? false - } - func onDemandResourcesTags() throws -> OnDemandResourcesTags? { // Currently returns nil, could be extended if needed return nil } - - func metadata() throws -> TargetMetadata { - var tags: Set = [] - for buildConfig in buildConfigurationList?.buildConfigurations ?? [] { - if let tagsString = buildConfig.buildSettings.string(for: .tags) { - let extractedTags = tagsString - .split(separator: ",") - .map { $0.trimmingCharacters(in: .whitespaces) } - tags.formUnion(extractedTags) - } - } - return .metadata(tags: tags) - } } diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift index c2630a27..2d2b18ce 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift @@ -190,8 +190,8 @@ struct PBXTargetMapper: PBXTargetMapping { let buildRules = try pbxTarget.buildRules.compactMap { try buildRuleMapper.map($0) } // Environment & Launch - let environmentVariables = pbxTarget.extractEnvironmentVariables() - let launchArguments = try pbxTarget.launchArguments() +// let environmentVariables = pbxTarget.extractEnvironmentVariables() +// let launchArguments = try pbxTarget.launchArguments() // Files group let filesGroup = try extractFilesGroup(from: pbxTarget, xcodeProj: xcodeProj) @@ -200,11 +200,11 @@ struct PBXTargetMapper: PBXTargetMapping { let playgrounds = try extractPlaygrounds(from: pbxTarget, xcodeProj: xcodeProj) // Misc - let prune = try pbxTarget.prune() +// let prune = try pbxTarget.prune() let mergedBinaryType = try pbxTarget.mergedBinaryType() - let mergeable = try pbxTarget.mergeable() +// let mergeable = try pbxTarget.mergeable() let onDemandResourcesTags = try pbxTarget.onDemandResourcesTags() - let metadata = try pbxTarget.metadata() +// let metadata = try pbxTarget.metadata() // Dependencies let projectNativeTargets = try pbxTarget.dependencies.compactMap { @@ -229,19 +229,14 @@ struct PBXTargetMapper: PBXTargetMapping { headers: headers, coreDataModels: coreDataModels, scripts: scripts, - environmentVariables: environmentVariables, - launchArguments: launchArguments, filesGroup: filesGroup, dependencies: allDependencies, rawScriptBuildPhases: rawScriptBuildPhases, playgrounds: playgrounds, additionalFiles: additionalFiles, buildRules: buildRules, - prune: prune, mergedBinaryType: mergedBinaryType, - mergeable: mergeable, onDemandResourcesTags: onDemandResourcesTags, - metadata: metadata, packages: packages ) } From 0cb7afa95872dcd6a71bbe168afda3a77fb5b510 Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sun, 2 Mar 2025 11:35:03 -0500 Subject: [PATCH 3/9] Fix an issue that stripped all attributes --- .../Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift index fbc24a6f..0317421f 100644 --- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift @@ -65,7 +65,7 @@ struct PBXFrameworksBuildPhaseMapper: PBXFrameworksBuildPhaseMapping { ) if let path = fileRef.path { let name = path.replacingOccurrences(of: ".framework", with: "") - let linkingStatus: LinkingStatus = (buildFile.settings?["ATTRIBUTES"] as? [String])? + let linkingStatus: LinkingStatus = buildFile.attributes? .contains("Weak") == true ? .optional : .required switch fileRef.sourceTree { case .buildProductsDir: From c078d84f8d213cbf2fd44839e7ad17aeee9897a1 Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sun, 2 Mar 2025 11:35:21 -0500 Subject: [PATCH 4/9] Update for strongly typed build settings mapping --- .../Settings/XCConfigurationMapper.swift | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationMapper.swift b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationMapper.swift index 5a5b683c..cb1ae39e 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationMapper.swift @@ -84,35 +84,17 @@ final class XCConfigurationMapper: SettingsMapping { /// - Parameter buildSettings: A dictionary of raw build settings. /// - Returns: A `SettingsDictionary` containing `SettingValue`-typed settings. /// - Throws: If a setting value cannot be mapped (this is typically non-fatal; most values can be stringified). - func mapBuildSettings(_ buildSettings: [String: Any]) throws -> SettingsDictionary { + func mapBuildSettings(_ buildSettings: [String: BuildSetting]) throws -> SettingsDictionary { var settingsDict = SettingsDictionary() for (key, value) in buildSettings { - settingsDict[key] = try mapSettingValue(value) + settingsDict[key] = switch value { + case let .string(string): .string(string) + case let .array(array): .array(array) + } } return settingsDict } - /// Maps a single raw setting value into a `SettingValue`. - /// - /// - If the value is a `String`, it becomes a `SettingValue.string`. - /// - If the value is an `Array`, each element is converted to a string if possible, resulting in `SettingValue.array`. - /// - Otherwise, the value is stringified using `String(describing:)` and returned as `SettingValue.string`. - /// - /// - Parameter value: A raw setting value from the build settings dictionary. - /// - Returns: A `SettingValue` representing the processed setting. - private func mapSettingValue(_ value: Any) throws -> SettingValue { - if let stringValue = value as? String { - return .string(stringValue) - } else if let arrayValue = value as? [Any] { - let stringArray = arrayValue.compactMap { $0 as? String } - return .array(stringArray) - } else { - // Fallback: convert unknown types to strings - let stringValue = String(describing: value) - return .string(stringValue) - } - } - /// Determines a `BuildConfiguration.Variant` (e.g., `.debug` or `.release`) from a configuration name. /// /// Uses `ConfigurationMatcher` to infer the variant by analyzing the configuration name for known keywords. From b500a34d0dbed348137140b7d1a40b27746d3218 Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sat, 8 Mar 2025 11:01:17 -0500 Subject: [PATCH 5/9] Update Tests for strongly typed build settings --- .../Project/PBXProjectMapperTests.swift | 2 +- .../Schemes/XCSchemeMapperTests.swift | 4 +- .../Settings/BuildSettingsTests.swift | 104 ------------------ .../Settings/XCConfigurationMapperTests.swift | 18 --- .../Target/PBXTargetMapperTests.swift | 68 +----------- .../Mocks/MockDefaults.swift | 6 +- .../TestData/PBXProject+TestData.swift | 4 +- 7 files changed, 10 insertions(+), 196 deletions(-) delete mode 100644 Tests/XcodeGraphMapperTests/MapperTests/Settings/BuildSettingsTests.swift diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Project/PBXProjectMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Project/PBXProjectMapperTests.swift index b59bd3f8..231e67c7 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Project/PBXProjectMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Project/PBXProjectMapperTests.swift @@ -34,7 +34,7 @@ struct PBXProjectMapperTests: Sendable { let mapper = PBXProjectMapper() - let customAttributes: [String: Any] = [ + let customAttributes: [String: ProjectAttribute] = [ "ORGANIZATIONNAME": "Example Org", "CLASSPREFIX": "EX", "LastUpgradeCheck": "1500", diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Schemes/XCSchemeMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Schemes/XCSchemeMapperTests.swift index 81d9d9d2..6ed030f3 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Schemes/XCSchemeMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Schemes/XCSchemeMapperTests.swift @@ -4,8 +4,8 @@ import Path import Testing import XcodeGraph -@testable import XcodeGraphMapper -@testable import XcodeProj +@testable @preconcurrency import XcodeGraphMapper +@testable @preconcurrency import XcodeProj @Suite struct XCSchemeMapperTests: Sendable { diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Settings/BuildSettingsTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Settings/BuildSettingsTests.swift deleted file mode 100644 index f27be5f9..00000000 --- a/Tests/XcodeGraphMapperTests/MapperTests/Settings/BuildSettingsTests.swift +++ /dev/null @@ -1,104 +0,0 @@ -import Path -import Testing -import XcodeGraph -import XcodeProj -@testable import XcodeGraphMapper - -@Suite -struct BuildSettingsTests { - @Test("Extracts a string value from build settings") - func testStringExtraction() async throws { - // Given - let settings: [String: Any] = ["COMPILER_FLAGS": "-ObjC"] - - // When - let value = settings.string(for: .compilerFlags) - - // Then - try #require(value != nil) - #expect(value == "-ObjC") - } - - @Test("Extracts a boolean value from build settings and returns nil for invalid types") - func testBoolExtraction() { - // Given - let settings: [String: Any] = ["PRUNE": true] - let invalidSettings: [String: Any] = ["PRUNE": "notABool"] - - // When - let boolValue = settings.bool(for: .prune) - let invalidBool = invalidSettings.bool(for: .prune) - - // Then - #expect(boolValue == true) - #expect(invalidBool == nil) - } - - @Test("Extracts a string array from build settings") - func testStringArrayExtraction() async throws { - // Given - let settings: [String: Any] = ["LAUNCH_ARGUMENTS": ["-enableFeature", "-verbose"]] - - // When - let args = settings.stringArray(for: .launchArguments) - - // Then - try #require(args != nil) - #expect(args?.count == 2) - #expect(args?.contains("-verbose") == true) - } - - @Test("Extracts a dictionary of strings (e.g., environment variables) from build settings") - func testStringDictExtraction() async throws { - // Given - let settings: [String: Any] = ["ENVIRONMENT_VARIABLES": ["KEY": "VALUE"]] - - // When - let envVars = settings.stringDict(for: .environmentVariables) - - // Then - try #require(envVars != nil) - #expect(envVars?["KEY"] == "VALUE") - } - - @Test("Returns nil when keys are missing in build settings") - func testMissingKeyReturnsNil() { - // Given - let settings: [String: Any] = ["TAGS": "some,tags"] - - // When / Then - // No key for productBundleIdentifier or mergeable, so returns nil - #expect(settings.string(for: .productBundleIdentifier) == nil) - #expect(settings.bool(for: .mergeable) == nil) - } - - @Test("Coerces any array elements to strings, discarding non-string values") - func testCoerceAnyArrayToStringArray() async throws { - // Given - let settings: [String: Any] = ["LAUNCH_ARGUMENTS": ["-flag", 42, true]] - - // When - let args = settings.stringArray(for: .launchArguments) - - // Then - try #require(args != nil) - // Non-string elements are discarded, leaving only ["-flag"] - #expect(args == ["-flag"]) - } - - @Test( - "Extracts the SDKROOT build setting as a string", - arguments: Platform.allCases - ) - func testExtractSDKROOT(platform: Platform) throws { - // Given - let settings: [String: Any] = ["SDKROOT": platform.xcodeSdkRoot] - - // When - let sdkroot = settings.string(for: .sdkroot) - - // Then - try #require(sdkroot != nil) - #expect(sdkroot == platform.xcodeSdkRoot) - } -} diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift index 82b2f832..371dd661 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift @@ -75,25 +75,7 @@ struct XCConfigurationMapperTests { #expect(releaseConfig?.settings["PRODUCT_BUNDLE_IDENTIFIER"] == "com.example.release") } - @Test("Coerces non-string values to strings in build settings") - func testCoercionOfNonStringValues() async throws { - // Given - let pbxProj = PBXProj() - let config: XCBuildConfiguration = .testDebug( - buildSettings: ["SOME_NUMBER": 42, "A_BOOL": true] - ).add(to: pbxProj) - let configList = XCConfigurationList.test(buildConfigurations: [config]).add(to: pbxProj) - let xcodeProj = try await XcodeProj.test(configurationList: configList) - // When - let settings = try mapper.map(xcodeProj: xcodeProj, configurationList: configList) - - // Then - let debugKey = try #require(settings.configurations.keys.first { $0.name == "Debug" }) - let debugConfig = try #require(settings.configurations[debugKey]) - - #expect(debugConfig?.settings["SOME_NUMBER"] == "42") - } @Test("Resolves XCConfig file paths correctly") func testXCConfigPathResolution() async throws { diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift index 30181b1f..51bfa18a 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift @@ -69,70 +69,6 @@ struct PBXTargetMapperTests: Sendable { #expect(mapped.bundleId == "Unknown") } - @Test("Maps a target with environment variables") - func testMapTargetWithEnvironmentVariables() async throws { - // Given - let xcodeProj = try await XcodeProj.test() - let target = createTarget( - name: "App", - xcodeProj: xcodeProj, - productType: .application, - buildSettings: [ - "PRODUCT_BUNDLE_IDENTIFIER": "com.example.app", - "ENVIRONMENT_VARIABLES": ["TEST_VAR": "test_value"], - ] - ) - let mapper = PBXTargetMapper() - - // When - let mapped = try #require( - try await mapper.map( - pbxTarget: target, - xcodeProj: xcodeProj, - projectNativeTargets: [:], - packages: [] - ) - ) - - // Then - #expect(mapped.environmentVariables["TEST_VAR"]?.value == "test_value") - #expect(mapped.environmentVariables["TEST_VAR"]?.isEnabled == true) - } - - @Test("Maps a target with launch arguments") - func testMapTargetWithLaunchArguments() async throws { - // Given - let xcodeProj = try await XcodeProj.test() - - let target = createTarget( - name: "App", - xcodeProj: xcodeProj, - productType: .application, - buildSettings: [ - "PRODUCT_BUNDLE_IDENTIFIER": "com.example.app", - "LAUNCH_ARGUMENTS": ["-debug", "--verbose"], - ] - ) - let mapper = PBXTargetMapper() - - // When - let mapped = try #require( - try await mapper.map( - pbxTarget: target, - xcodeProj: xcodeProj, - projectNativeTargets: [:], - packages: [] - ) - ) - - // Then - let expected = [ - LaunchArgument(name: "-debug", isEnabled: true), - LaunchArgument(name: "--verbose", isEnabled: true), - ] - #expect(mapped.launchArguments == expected) - } - @Test("Maps a target with source files") func testMapTargetWithSourceFiles() async throws { // Given @@ -457,7 +393,7 @@ struct PBXTargetMapperTests: Sendable { productType: .application, buildSettings: [ "PRODUCT_BUNDLE_IDENTIFIER": "com.example.app", - "INFOPLIST_FILE": relativePath.pathString, + "INFOPLIST_FILE": .string(relativePath.pathString), ] ) @@ -513,7 +449,7 @@ struct PBXTargetMapperTests: Sendable { xcodeProj: XcodeProj, productType: PBXProductType, buildPhases: [PBXBuildPhase] = [], - buildSettings: [String: Any] = [:], + buildSettings: [String: BuildSetting] = [:], dependencies: [PBXTargetDependency] = [] ) -> PBXNativeTarget { let debugConfig = XCBuildConfiguration( diff --git a/Tests/XcodeGraphMapperTests/Mocks/MockDefaults.swift b/Tests/XcodeGraphMapperTests/Mocks/MockDefaults.swift index 75ff48b6..564c3356 100644 --- a/Tests/XcodeGraphMapperTests/Mocks/MockDefaults.swift +++ b/Tests/XcodeGraphMapperTests/Mocks/MockDefaults.swift @@ -4,19 +4,19 @@ import XcodeGraph @testable import XcodeProj enum MockDefaults { - nonisolated(unsafe) static let defaultDebugSettings: [String: Any] = [ + static let defaultDebugSettings: [String: BuildSetting] = [ "PRODUCT_NAME": "$(TARGET_NAME)", "ENABLE_STRICT_OBJC_MSGSEND": "YES", "PRODUCT_BUNDLE_IDENTIFIER": "com.example.debug", ] - nonisolated(unsafe) static let defaultReleaseSettings: [String: Any] = [ + static let defaultReleaseSettings: [String: BuildSetting] = [ "PRODUCT_NAME": "$(TARGET_NAME)", "VALIDATE_PRODUCT": "YES", "PRODUCT_BUNDLE_IDENTIFIER": "com.example.release", ] - nonisolated(unsafe) static let defaultProjectAttributes: [String: Any] = [ + nonisolated(unsafe) static let defaultProjectAttributes: [String: ProjectAttribute] = [ "BuildIndependentTargetsInParallel": "YES", ] } diff --git a/Tests/XcodeGraphMapperTests/TestData/PBXProject+TestData.swift b/Tests/XcodeGraphMapperTests/TestData/PBXProject+TestData.swift index 4311c332..f223ba88 100644 --- a/Tests/XcodeGraphMapperTests/TestData/PBXProject+TestData.swift +++ b/Tests/XcodeGraphMapperTests/TestData/PBXProject+TestData.swift @@ -18,9 +18,9 @@ extension PBXProject { ], projectRoots: [String] = [""], targets: [PBXTarget] = [], - attributes: [String: Any] = MockDefaults.defaultProjectAttributes, + attributes: [String: ProjectAttribute] = MockDefaults.defaultProjectAttributes, packageReferences: [XCRemoteSwiftPackageReference] = [], - targetAttributes: [PBXTarget: [String: Any]] = [:] + targetAttributes: [PBXTarget: [String: ProjectAttribute]] = [:] ) -> PBXProject { PBXProject( name: name, From c03864f8fe5910010b6caa9ad3d499769f983b1e Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sat, 8 Mar 2025 11:05:02 -0500 Subject: [PATCH 6/9] Finish removal of tuist specific information from build settings --- .../Mappers/Settings/BuildSettings.swift | 91 ++----------------- .../XCConfigurationList+Helpers.swift | 4 +- .../Targets/PBXTarget+BuildSettings.swift | 21 ----- .../Targets/PBXTarget+GraphMapping.swift | 4 +- .../Mappers/Targets/PBXTargetMapper.swift | 8 +- 5 files changed, 12 insertions(+), 116 deletions(-) diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift index 6205abc5..007afc02 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift @@ -1,22 +1,11 @@ import Foundation +import XcodeProj /// Keys representing various build settings that may appear in an Xcode project or workspace configuration. enum BuildSettingKey: String { case sdkroot = "SDKROOT" - case compilerFlags = "COMPILER_FLAGS" - case attributes = "ATTRIBUTES" - case environmentVariables = "ENVIRONMENT_VARIABLES" case codeSignOnCopy = "CODE_SIGN_ON_COPY" - case dependencyFile = "DEPENDENCY_FILE" - case inputPaths = "INPUT_PATHS" - case outputPaths = "OUTPUT_PATHS" - case showEnvVarsInLog = "SHOW_ENV_VARS_IN_LOG" - case shellPath = "SHELL_PATH" - case launchArguments = "LAUNCH_ARGUMENTS" - case tags = "TAGS" case mergedBinaryType = "MERGED_BINARY_TYPE" - case prune = "PRUNE" - case mergeable = "MERGEABLE" case productBundleIdentifier = "PRODUCT_BUNDLE_IDENTIFIER" case infoPlistFile = "INFOPLIST_FILE" case codeSignEntitlements = "CODE_SIGN_ENTITLEMENTS" @@ -27,75 +16,9 @@ enum BuildSettingKey: String { case visionOSDeploymentTarget = "VISIONOS_DEPLOYMENT_TARGET" } -// -///// A protocol representing a type that can parse a build setting value from a generic `Any`. -//protocol BuildSettingValue { -// associatedtype Value -// static func parse(_ any: Any) -> Value? -//} -// -///// A type that parses build settings as strings. -//enum BuildSettingString: BuildSettingValue { -// static func parse(_ any: Any) -> String? { -// any as? String -// } -//} -// -///// A type that parses build settings as arrays of strings. -//enum BuildSettingStringArray: BuildSettingValue { -// static func parse(_ any: Any) -> [String]? { -// let arr = any as? [Any] -// return arr?.compactMap { $0 as? String } -// } -//} -// -///// A type that parses build settings as booleans. -//enum BuildSettingBool: BuildSettingValue { -// static func parse(_ any: Any) -> Bool? { -// any as? Bool -// } -//} -// -///// A type that parses build settings as dictionaries of strings to strings. -//enum BuildSettingStringDict: BuildSettingValue { -// static func parse(_ any: Any) -> [String: String]? { -// any as? [String: String] -// } -//} -// -//extension [String: Any] { -// /// Extracts a build setting value of a specified type from the dictionary. -// /// -// /// - Parameters: -// /// - key: The `BuildSettingKey` to look up. -// /// - type: The type conforming to `BuildSettingValue` indicating the expected value type. -// /// - Returns: The parsed value if found and valid, or `nil` otherwise. -// func extractBuildSetting(_ key: BuildSettingKey, as _: T.Type = T.self) -// -> T.Value? -// { -// guard let value = self[key.rawValue] else { return nil } -// return T.parse(value) -// } -//} -// -//extension [String: Any] { -// /// Retrieves a string value for the given build setting key. -// func string(for key: BuildSettingKey) -> String? { -// extractBuildSetting(key, as: BuildSettingString.self) -// } -// -// /// Retrieves an array of strings for the given build setting key. -// func stringArray(for key: BuildSettingKey) -> [String]? { -// extractBuildSetting(key, as: BuildSettingStringArray.self) -// } -// -// /// Retrieves a boolean value for the given build setting key. -// func bool(for key: BuildSettingKey) -> Bool? { -// extractBuildSetting(key, as: BuildSettingBool.self) -// } -// -// /// Retrieves a dictionary of strings for the given build setting key. -// func stringDict(for key: BuildSettingKey) -> [String: String]? { -// extractBuildSetting(key, as: BuildSettingStringDict.self) -// } -//} + +extension BuildSettings { + subscript(_ key: BuildSettingKey) -> BuildSetting? { + self[key.rawValue] + } +} diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift index 58eeaab3..8b85ac7f 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/XCConfigurationList+Helpers.swift @@ -10,7 +10,7 @@ extension XCConfigurationList { let configurationMatcher = ConfigurationMatcher() var results = [BuildConfiguration: String]() for config in buildConfigurations { - if let value = config.buildSettings[key.rawValue]?.stringValue { + if let value = config.buildSettings[key]?.stringValue { let variant = configurationMatcher.variant(for: config.name) let buildConfig = BuildConfiguration(name: config.name, variant: variant) results[buildConfig] = value @@ -29,7 +29,7 @@ extension XCConfigurationList { for key in keys { for config in buildConfigurations { - if let value = config.buildSettings[key.rawValue]?.stringValue { + if let value = config.buildSettings[key]?.stringValue { results[key] = value break // Once found, move to the next key } diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift index ae742d84..4d91e148 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift @@ -4,17 +4,6 @@ import XcodeGraph import XcodeProj extension PBXTarget { -// enum EnvironmentExtractor { -// static func extract(from buildSettings: BuildSettings) -> [String: EnvironmentVariable] { -// guard let envVars = buildSettings.stringDict(for: .environmentVariables) else { -// return [:] -// } -// return envVars.reduce(into: [:]) { result, pair in -// result[pair.key] = EnvironmentVariable(value: pair.value, isEnabled: true) -// } -// } -// } - /// Retrieves the path to the Info.plist file from the target's build settings. /// /// - Returns: The `INFOPLIST_FILE` value if present, otherwise `nil`. @@ -70,16 +59,6 @@ extension PBXTarget { ) } - /// Extracts environment variables from all build configurations of the target. - /// - /// If multiple configurations define the same environment variable, the last processed configuration takes precedence. -// func extractEnvironmentVariables() -> [String: EnvironmentVariable] { -// buildConfigurationList?.buildConfigurations.reduce(into: [:]) { result, config in -// result.merge(EnvironmentExtractor.extract(from: config.buildSettings)) { current, _ in current -// } -// } ?? [:] -// } - /// Returns the build settings from the "Debug" build configuration, or an empty dictionary if not present. var debugBuildSettings: [String: BuildSetting] { buildConfigurationList?.buildConfigurations.first(where: { $0.name == "Debug" })?.buildSettings diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift index 5caf8c3a..6f0129f0 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+GraphMapping.swift @@ -6,7 +6,7 @@ import XcodeProj extension PBXTarget { /// Attempts to retrieve the bundle identifier from the target's debug build settings, or throws an error if missing. func bundleIdentifier() throws -> String { - if let bundleId = debugBuildSettings[BuildSettingKey.productBundleIdentifier.rawValue]?.stringValue { + if let bundleId = debugBuildSettings[BuildSettingKey.productBundleIdentifier]?.stringValue { return bundleId } else { return "Unknown" @@ -19,7 +19,7 @@ extension PBXTarget { } func mergedBinaryType() throws -> MergedBinaryType { - let mergedBinaryTypeString = debugBuildSettings[BuildSettingKey.mergedBinaryType.rawValue]?.stringValue + let mergedBinaryTypeString = debugBuildSettings[BuildSettingKey.mergedBinaryType]?.stringValue return mergedBinaryTypeString == "automatic" ? .automatic : .disabled } diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift index 2d2b18ce..c92eb82b 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift @@ -189,10 +189,6 @@ struct PBXTargetMapper: PBXTargetMapping { // Build Rules let buildRules = try pbxTarget.buildRules.compactMap { try buildRuleMapper.map($0) } - // Environment & Launch -// let environmentVariables = pbxTarget.extractEnvironmentVariables() -// let launchArguments = try pbxTarget.launchArguments() - // Files group let filesGroup = try extractFilesGroup(from: pbxTarget, xcodeProj: xcodeProj) @@ -200,11 +196,9 @@ struct PBXTargetMapper: PBXTargetMapping { let playgrounds = try extractPlaygrounds(from: pbxTarget, xcodeProj: xcodeProj) // Misc -// let prune = try pbxTarget.prune() let mergedBinaryType = try pbxTarget.mergedBinaryType() -// let mergeable = try pbxTarget.mergeable() let onDemandResourcesTags = try pbxTarget.onDemandResourcesTags() -// let metadata = try pbxTarget.metadata() + // Dependencies let projectNativeTargets = try pbxTarget.dependencies.compactMap { From 6476dff331b11c1869f02019db79bbc0a78df81f Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sat, 8 Mar 2025 11:06:55 -0500 Subject: [PATCH 7/9] Linting --- Sources/XcodeGraph/Models/Settings.swift | 1 - Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift | 1 - Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift | 1 - .../MapperTests/Settings/XCConfigurationMapperTests.swift | 2 -- 4 files changed, 5 deletions(-) diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift index c631c040..bf544144 100644 --- a/Sources/XcodeGraph/Models/Settings.swift +++ b/Sources/XcodeGraph/Models/Settings.swift @@ -145,7 +145,6 @@ extension [BuildConfiguration: Configuration?] { } } - #if DEBUG extension Configuration { public static func test( diff --git a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift index 007afc02..8a875e8b 100644 --- a/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift +++ b/Sources/XcodeGraphMapper/Mappers/Settings/BuildSettings.swift @@ -16,7 +16,6 @@ enum BuildSettingKey: String { case visionOSDeploymentTarget = "VISIONOS_DEPLOYMENT_TARGET" } - extension BuildSettings { subscript(_ key: BuildSettingKey) -> BuildSetting? { self[key.rawValue] diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift index c92eb82b..319d33bc 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift @@ -199,7 +199,6 @@ struct PBXTargetMapper: PBXTargetMapping { let mergedBinaryType = try pbxTarget.mergedBinaryType() let onDemandResourcesTags = try pbxTarget.onDemandResourcesTags() - // Dependencies let projectNativeTargets = try pbxTarget.dependencies.compactMap { try dependencyMapper.map($0, xcodeProj: xcodeProj) diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift index 371dd661..e2627451 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift @@ -75,8 +75,6 @@ struct XCConfigurationMapperTests { #expect(releaseConfig?.settings["PRODUCT_BUNDLE_IDENTIFIER"] == "com.example.release") } - - @Test("Resolves XCConfig file paths correctly") func testXCConfigPathResolution() async throws { // Given From bb80553c93505f55e983054c4db4d326fdaa0a6e Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Sun, 9 Mar 2025 10:01:48 -0400 Subject: [PATCH 8/9] Remove leftover test --- .../Target/PBXTargetMapperTests.swift | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift index 51bfa18a..8764dc4f 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift @@ -255,33 +255,6 @@ struct PBXTargetMapperTests: Sendable { } } - @Test("Maps a target with metadata tags") - func testMapTargetWithMetadata() async throws { - // Given - let xcodeProj = try await XcodeProj.test() - let target = createTarget( - name: "App", - xcodeProj: xcodeProj, - productType: .application, - buildSettings: [ - "PRODUCT_BUNDLE_IDENTIFIER": "com.example.app", - "TAGS": "tag1, tag2, tag3", - ] - ) - let mapper = PBXTargetMapper() - - // When - let mapped = try await mapper.map( - pbxTarget: target, - xcodeProj: xcodeProj, - projectNativeTargets: [:], - packages: [] - ) - - // Then - #expect(mapped?.metadata.tags == Set(["tag1", "tag2", "tag3"])) - } - @Test("Maps entitlements when CODE_SIGN_ENTITLEMENTS is set") func testMapEntitlements() async throws { // Given From 320ded1cf583e6ceae0e9dc2a267d28bfe680e50 Mon Sep 17 00:00:00 2001 From: Mike Simons Date: Wed, 19 Mar 2025 08:26:22 -0400 Subject: [PATCH 9/9] Update to XcodeProj 9.0 --- Package.resolved | 10 +++++----- Package.swift | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Package.resolved b/Package.resolved index 4efea982..08a456d1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "5595944e8be7d3f216476672222bb48dda3ea7521f57fcbd42c2db44a3f9b7f1", + "originHash" : "6e9c67eb028efd58882f60208b868c006b6ab0a4c7006e49085a4c346df383b0", "pins" : [ { "identity" : "aexml", "kind" : "remoteSourceControl", "location" : "https://github.com/tadija/AEXML.git", "state" : { - "revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", - "version" : "4.6.1" + "revision" : "db806756c989760b35108146381535aec231092b", + "version" : "4.7.0" } }, { @@ -168,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tuist/XcodeProj", "state" : { - "branch" : "waltflanagan/StrongTypes", - "revision" : "d04f4ffe6616c6239f9e1f7ae1e2949f6f0033d6" + "revision" : "128d90e4633a8e6941586dea75426e177dfb92e6", + "version" : "9.0.0" } }, { diff --git a/Package.swift b/Package.swift index a8cd4b40..659dd481 100644 --- a/Package.swift +++ b/Package.swift @@ -80,8 +80,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/Flight-School/AnyCodable", .upToNextMajor(from: "0.6.7")), .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.8")), - .package(url: "https://github.com/tuist/XcodeProj", branch: "waltflanagan/StrongTypes"), -// .package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "8.27.7")), + .package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "9.0.0")), .package(url: "https://github.com/tuist/Command.git", from: "0.13.0"), .package(url: "https://github.com/tuist/FileSystem.git", .upToNextMajor(from: "0.7.7")), .package(url: "https://github.com/apple/swift-service-context", .upToNextMajor(from: "1.2.0")),