diff --git a/Package.resolved b/Package.resolved index eb90ff03..08a456d1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "903b6688321bae457eebe89dd170b8e84c2b492ea8715d843afaaaa1a64c58b5", + "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" } }, { @@ -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" + "revision" : "128d90e4633a8e6941586dea75426e177dfb92e6", + "version" : "9.0.0" } }, { @@ -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..659dd481 100644 --- a/Package.swift +++ b/Package.swift @@ -80,7 +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", .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")), diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift index d8f182c5..bf544144 100644 --- a/Sources/XcodeGraph/Models/Settings.swift +++ b/Sources/XcodeGraph/Models/Settings.swift @@ -145,19 +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 { public static func test( 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/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: 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..8a875e8b 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,74 +16,8 @@ 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 8c429113..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.string(for: key) { + 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.string(for: key) { + if let value = config.buildSettings[key]?.stringValue { results[key] = value break // Once found, move to the next key } 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. diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTarget+BuildSettings.swift index 29f22adf..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,18 +59,8 @@ 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: 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..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.string(for: .productBundleIdentifier) { + if let bundleId = debugBuildSettings[BuildSettingKey.productBundleIdentifier]?.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]?.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..319d33bc 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,8 @@ 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 { @@ -229,19 +222,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 ) } 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..e2627451 100644 --- a/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift +++ b/Tests/XcodeGraphMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift @@ -75,26 +75,6 @@ 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 { // Given diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Target/PBXTargetMapperTests.swift index 30181b1f..8764dc4f 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 @@ -319,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 @@ -457,7 +366,7 @@ struct PBXTargetMapperTests: Sendable { productType: .application, buildSettings: [ "PRODUCT_BUNDLE_IDENTIFIER": "com.example.app", - "INFOPLIST_FILE": relativePath.pathString, + "INFOPLIST_FILE": .string(relativePath.pathString), ] ) @@ -513,7 +422,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, 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) - } -}