From 5fe5dbafefe641641ed9b24ca9363db7d74e2f66 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 11 Aug 2024 19:04:00 +0000
Subject: [PATCH 001/914] chore(deps): update actions/checkout action to v4
---
.github/workflows/conventional-pr.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/conventional-pr.yml b/.github/workflows/conventional-pr.yml
index 2076210d..bf0fd747 100644
--- a/.github/workflows/conventional-pr.yml
+++ b/.github/workflows/conventional-pr.yml
@@ -12,7 +12,7 @@ jobs:
lint-pr:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- uses: CondeNast/conventional-pull-request-action@v0.2.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
From c0783f69414d86cb9a50c9b0d740207fe9b4a830 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Fri, 16 Aug 2024 06:49:24 +0000
Subject: [PATCH 002/914] docs: update README.md [skip ci]
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index beba958c..a9e246f9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ፨ XcodeGraph
-[](#contributors-)
+[](#contributors-)
XcodeGraph is a Swift Package that contains data structures to model an Xcode projects graph.
@@ -33,6 +33,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Dariusz Rybicki 💻 |
 Binlogo 💻 |
 Filip Racki 💻 |
+  Gabriel Liévano 💻 |
From d098b336f20858508e29bb4633e1eeb3e2d5402e Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Fri, 16 Aug 2024 06:49:25 +0000
Subject: [PATCH 003/914] docs: update .all-contributorsrc [skip ci]
---
.all-contributorsrc | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 519323c8..3d0512d5 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -33,6 +33,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "rgnns",
+ "name": "Gabriel Liévano",
+ "avatar_url": "https://avatars.githubusercontent.com/u/811827?v=4",
+ "profile": "https://github.com/rgnns",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
From ba164b94d758bf05091fcaf2db08fd0465f12f37 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 19 Aug 2024 17:28:45 +0000
Subject: [PATCH 004/914] chore(deps): update dependency tuist to v4.24.0
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index a22507bb..fb0a5f18 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.23.0"
+tuist = "4.24.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 5fe58a479d590c0cdcae6f327af58b7698f66d3f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 20 Aug 2024 01:35:32 +0000
Subject: [PATCH 005/914] chore(deps): update dependency tuist/path to from:
"0.3.3"
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index 6fdbbd83..c021fd39 100644
--- a/Package.swift
+++ b/Package.swift
@@ -23,7 +23,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.2")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.3")),
],
targets: targets
)
From cecc548a7f4140bc4826ae71e2f0ee575071fac3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabriel=20Li=C3=A9vano?=
<811827+rgnns@users.noreply.github.com>
Date: Mon, 19 Aug 2024 23:49:56 -0700
Subject: [PATCH 006/914] refactor: unify status enums into one linking status
(#32)
---
.../DependenciesGraph/DependenciesGraph.swift | 2 +-
Sources/XcodeGraph/Graph/GraphDependency.swift | 14 +++++++-------
.../Models/Metadata/FrameworkMetadata.swift | 6 +++---
.../Models/Metadata/SystemFrameworkMetadata.swift | 4 ++--
.../Models/Metadata/XCFrameworkMetadata.swift | 6 +++---
Sources/XcodeGraph/Models/TargetDependency.swift | 13 ++++---------
6 files changed, 20 insertions(+), 25 deletions(-)
diff --git a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
index ad29684b..929ef3df 100644
--- a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
+++ b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
@@ -33,7 +33,7 @@ public struct DependenciesGraph: Equatable, Codable {
name: String = "Test",
// swiftlint:disable:next force_try
path: AbsolutePath = AbsolutePath.root.appending(try! RelativePath(validating: "Test.xcframework")),
- status: FrameworkStatus = .required
+ status: LinkingStatus = .required
) -> DependenciesGraph {
let externalDependencies = [name: [TargetDependency.xcframework(path: path, status: status)]]
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 9d99134c..19812126 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -8,7 +8,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public let primaryBinaryPath: AbsolutePath
public let linking: BinaryLinking
public let mergeable: Bool
- public let status: FrameworkStatus
+ public let status: LinkingStatus
public init(
path: AbsolutePath,
@@ -16,7 +16,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
primaryBinaryPath: AbsolutePath,
linking: BinaryLinking,
mergeable: Bool,
- status: FrameworkStatus,
+ status: LinkingStatus,
macroPath _: AbsolutePath?
) {
self.path = path
@@ -60,7 +60,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
bcsymbolmapPaths: [AbsolutePath],
linking: BinaryLinking,
architectures: [BinaryArchitecture],
- status: FrameworkStatus
+ status: LinkingStatus
)
/// A dependency that represents a pre-compiled library.
@@ -85,7 +85,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
case target(name: String, path: AbsolutePath)
/// A dependency that represents an SDK
- case sdk(name: String, path: AbsolutePath, status: SDKStatus, source: SDKSource)
+ case sdk(name: String, path: AbsolutePath, status: LinkingStatus, source: SDKSource)
public func hash(into hasher: inout Hasher) {
switch self {
@@ -294,7 +294,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
bcsymbolmapPaths: [AbsolutePath] = [],
linking: BinaryLinking = .dynamic,
architectures: [BinaryArchitecture] = [.armv7],
- status: FrameworkStatus = .required
+ status: LinkingStatus = .required
) -> GraphDependency {
GraphDependency.framework(
path: path,
@@ -319,7 +319,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
primaryBinaryPath: AbsolutePath = AbsolutePath.root
.appending(try! RelativePath(validating: "Test.xcframework/Test")),
linking: BinaryLinking = .dynamic,
- status: FrameworkStatus = .required,
+ status: LinkingStatus = .required,
macroPath: AbsolutePath? = nil
) -> GraphDependency {
.xcframework(
@@ -348,7 +348,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public static func testSDK(
name: String = "XCTest.framework",
path: AbsolutePath = AbsolutePath.root.appending(try! RelativePath(validating: "XCTest.framework")),
- status: SDKStatus = .required,
+ status: LinkingStatus = .required,
source: SDKSource = .system
) -> GraphDependency {
.sdk(
diff --git a/Sources/XcodeGraph/Models/Metadata/FrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/FrameworkMetadata.swift
index 46943cfa..a746d099 100644
--- a/Sources/XcodeGraph/Models/Metadata/FrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/FrameworkMetadata.swift
@@ -9,7 +9,7 @@ public struct FrameworkMetadata: Equatable {
public var bcsymbolmapPaths: [AbsolutePath]
public var linking: BinaryLinking
public var architectures: [BinaryArchitecture]
- public var status: FrameworkStatus
+ public var status: LinkingStatus
public init(
path: AbsolutePath,
@@ -18,7 +18,7 @@ public struct FrameworkMetadata: Equatable {
bcsymbolmapPaths: [AbsolutePath],
linking: BinaryLinking,
architectures: [BinaryArchitecture],
- status: FrameworkStatus
+ status: LinkingStatus
) {
self.path = path
self.binaryPath = binaryPath
@@ -41,7 +41,7 @@ public struct FrameworkMetadata: Equatable {
bcsymbolmapPaths: [AbsolutePath] = [],
linking: BinaryLinking = .dynamic,
architectures: [BinaryArchitecture] = [.arm64],
- status: FrameworkStatus = .required
+ status: LinkingStatus = .required
) -> FrameworkMetadata {
FrameworkMetadata(
path: path,
diff --git a/Sources/XcodeGraph/Models/Metadata/SystemFrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/SystemFrameworkMetadata.swift
index fa824940..7d1173d4 100644
--- a/Sources/XcodeGraph/Models/Metadata/SystemFrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/SystemFrameworkMetadata.swift
@@ -5,13 +5,13 @@ import Path
public struct SystemFrameworkMetadata: Equatable {
public var name: String
public var path: AbsolutePath
- public var status: SDKStatus
+ public var status: LinkingStatus
public var source: SDKSource
public init(
name: String,
path: AbsolutePath,
- status: SDKStatus,
+ status: LinkingStatus,
source: SDKSource
) {
self.name = name
diff --git a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
index 4e80a582..5239900d 100644
--- a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
@@ -8,7 +8,7 @@ public struct XCFrameworkMetadata: Equatable {
public var primaryBinaryPath: AbsolutePath
public var linking: BinaryLinking
public var mergeable: Bool
- public var status: FrameworkStatus
+ public var status: LinkingStatus
public var macroPath: AbsolutePath?
public init(
@@ -17,7 +17,7 @@ public struct XCFrameworkMetadata: Equatable {
primaryBinaryPath: AbsolutePath,
linking: BinaryLinking,
mergeable: Bool,
- status: FrameworkStatus,
+ status: LinkingStatus,
macroPath: AbsolutePath?
) {
self.path = path
@@ -41,7 +41,7 @@ public struct XCFrameworkMetadata: Equatable {
try! AbsolutePath(validating: "/XCFrameworks/XCFramework.xcframework/ios-arm64/XCFramework"),
linking: BinaryLinking = .dynamic,
mergeable: Bool = false,
- status: FrameworkStatus = .required,
+ status: LinkingStatus = .required,
macroPath: AbsolutePath? = nil
) -> XCFrameworkMetadata {
XCFrameworkMetadata(
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index 05763bc0..9c1c20a2 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -1,12 +1,7 @@
import Foundation
import Path
-public enum FrameworkStatus: String, Hashable, Codable {
- case required
- case optional
-}
-
-public enum SDKStatus: String, Codable {
+public enum LinkingStatus: String, Hashable, Codable {
case required
case optional
}
@@ -20,8 +15,8 @@ public enum TargetDependency: Equatable, Hashable, Codable {
case target(name: String, condition: PlatformCondition? = nil)
case project(target: String, path: AbsolutePath, condition: PlatformCondition? = nil)
- case framework(path: AbsolutePath, status: FrameworkStatus, condition: PlatformCondition? = nil)
- case xcframework(path: AbsolutePath, status: FrameworkStatus, condition: PlatformCondition? = nil)
+ case framework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
+ case xcframework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
case library(
path: AbsolutePath,
publicHeaders: AbsolutePath,
@@ -29,7 +24,7 @@ public enum TargetDependency: Equatable, Hashable, Codable {
condition: PlatformCondition? = nil
)
case package(product: String, type: PackageType, condition: PlatformCondition? = nil)
- case sdk(name: String, status: SDKStatus, condition: PlatformCondition? = nil)
+ case sdk(name: String, status: LinkingStatus, condition: PlatformCondition? = nil)
case xctest
public var condition: PlatformCondition? {
From 33fcceedf5d9d658656fc868b7b8d056dbf875c0 Mon Sep 17 00:00:00 2001
From: Pedro
Date: Tue, 20 Aug 2024 18:06:35 +0200
Subject: [PATCH 007/914] Add validSourceCompatibleFolderExtensions static
attribute to Target
---
Sources/XcodeGraph/Models/Target.swift | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 29fd5b96..746bff74 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -7,9 +7,11 @@ public struct Target: Equatable, Hashable, Comparable, Codable {
// Note: The `.docc` file type is technically both a valid source extension and folder extension
// in order to compile the documentation archive (including Tutorials, Articles, etc.)
+ public static let validSourceCompatibleFolderExtensions: [String] = [
+ "playground", "rcproject", "mlpackage", "docc", "xcmappingmodel",
+ ]
public static let validSourceExtensions: [String] = [
- "m", "swift", "mm", "cpp", "cc", "c", "d", "s", "intentdefinition", "xcmappingmodel", "metal", "mlmodel", "docc",
- "playground", "rcproject", "mlpackage",
+ "m", "swift", "mm", "cpp", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel",
]
public static let validFolderExtensions: [String] = [
"framework", "bundle", "app", "xcassets", "appiconset", "scnassets",
From f32008caac086d6435476ba2afd4e4d9863d4f92 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Tue, 20 Aug 2024 18:44:13 +0200
Subject: [PATCH 008/914] feat: unify status enums into one linking status
From 74010b5d3d9577605f2098d9656fb37860162644 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Thu, 22 Aug 2024 09:36:11 +0200
Subject: [PATCH 009/914] ci: fix deteremining whether a release should be run
(#38)
---
.github/workflows/release.yml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5935f524..a4cb4d61 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
version:
- description: 'The version to release'
+ description: "The version to release"
type: string
permissions:
@@ -19,7 +19,7 @@ permissions:
jobs:
release:
name: Release
- runs-on: 'ubuntu-latest'
+ runs-on: "ubuntu-latest"
timeout-minutes: 15
if: "!startsWith(github.event.head_commit.message, '[Release]')"
steps:
@@ -34,16 +34,16 @@ jobs:
run: |
bumped_output=$(git cliff --bump)
changelog_content=$(cat CHANGELOG.md)
-
+
bumped_hash=$(echo -n "$bumped_output" | shasum -a 256 | awk '{print $1}')
changelog_hash=$(echo -n "$changelog_content" | shasum -a 256 | awk '{print $1}')
if [ "$bumped_hash" != "$changelog_hash" ]; then
- echo "should-release=false" >> $GITHUB_ENV
- else
echo "should-release=true" >> $GITHUB_ENV
+ else
+ echo "should-release=false" >> $GITHUB_ENV
fi
-
+
- name: Get next version
id: next-version
if: env.should-release == 'true'
@@ -69,7 +69,7 @@ jobs:
uses: stefanzweifel/git-auto-commit-action@v5
if: env.should-release == 'true'
with:
- commit_options: '--allow-empty'
+ commit_options: "--allow-empty"
tagging_message: ${{ steps.next-version.outputs.NEXT_VERSION }}
skip_dirty_check: true
commit_message: "[Release] XcodeGraph ${{ steps.next-version.outputs.NEXT_VERSION }}"
@@ -82,4 +82,4 @@ jobs:
name: ${{ steps.next-version.outputs.NEXT_VERSION }}
tag_name: ${{ steps.next-version.outputs.NEXT_VERSION }}
body: ${{ steps.release-notes.outputs.RELEASE_NOTES }}
- target_commitish: ${{ steps.auto-commit-action.outputs.commit_hash }}
\ No newline at end of file
+ target_commitish: ${{ steps.auto-commit-action.outputs.commit_hash }}
From b3071725a7adf04c47682f6e27a0a89058d7516f Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Thu, 22 Aug 2024 07:36:26 +0000
Subject: [PATCH 010/914] [Release] XcodeGraph 0.11.0
---
CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2bd81ab..4f425b56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,41 +5,70 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.0] - 2024-08-22
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.3" by @renovate[bot] in [#35](https://github.com/tuist/XcodeGraph/pull/35)
+- Update dependency tuist to v4.24.0 by @renovate[bot] in [#34](https://github.com/tuist/XcodeGraph/pull/34)
+- Update actions/checkout action to v4 by @renovate[bot]
+- Update dependency tuist to v4.23.0 by @renovate[bot] in [#30](https://github.com/tuist/XcodeGraph/pull/30)
+- Update dependency tuist/path to from: "0.3.2" by @renovate[bot] in [#29](https://github.com/tuist/XcodeGraph/pull/29)
+
+#### Ci
+- Fix deteremining whether a release should be run by @fortmarek in [#38](https://github.com/tuist/XcodeGraph/pull/38)
+
+#### Docs
+- Update .all-contributorsrc [skip ci] by @allcontributors[bot]
+- Update README.md [skip ci] by @allcontributors[bot]
+
+#### Feat
+- Unify status enums into one linking status by @fortmarek
+
+#### Fix
+- Repository used by git-cliff to generate changelogs by @pepicrft
+- Releases being triggered when they are not needed by @pepicrft
+
+#### Refactor
+- Unify status enums into one linking status by @rgnns in [#32](https://github.com/tuist/XcodeGraph/pull/32)
+
+## New Contributors
+* @rgnns made their first contribution in [#32](https://github.com/tuist/XcodeGraph/pull/32)
## [0.10.1] - 2024-08-08
### Details
#### Docs
-- Update .all-contributorsrc [skip ci]
-- Update README.md [skip ci]
+- Update .all-contributorsrc [skip ci] by @allcontributors[bot]
+- Update README.md [skip ci] by @allcontributors[bot]
## [0.10.0] - 2024-08-07
### Details
#### Feat
-- Add Swift sdk type
+- Add Swift sdk type by @fortmarek
## [0.9.1] - 2024-08-07
### Details
#### Chore
-- Automate releases using git cliff
+- Automate releases using git cliff by @pepicrft in [#28](https://github.com/tuist/XcodeGraph/pull/28)
## [0.8.0] - 2024-07-30
### Details
#### Chore
-- Update mikepenz/release-changelog-builder-action action to v4
+- Update mikepenz/release-changelog-builder-action action to v4 by @renovate[bot]
#### Docs
-- Update .all-contributorsrc [skip ci]
-- Update README.md [skip ci]
+- Update .all-contributorsrc [skip ci] by @allcontributors[bot]
+- Update README.md [skip ci] by @allcontributors[bot]
## [0.7.0] - 2024-06-25
### Details
#### Docs
-- Create .all-contributorsrc [skip ci]
-- Update README.md [skip ci]
-
-[0.10.1]: https://github.com/tuist/command/compare/0.10.0..0.10.1
-[0.10.0]: https://github.com/tuist/command/compare/0.9.1..0.10.0
-[0.9.1]: https://github.com/tuist/command/compare/0.9.0..0.9.1
-[0.8.0]: https://github.com/tuist/command/compare/0.7.0..0.8.0
-[0.7.0]: https://github.com/tuist/command/compare/0.6.0..0.7.0
+- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
+- Update README.md [skip ci] by @allcontributors[bot]
+
+[0.11.0]: https://github.com/tuist/XcodeGraph/compare/0.10.1..0.11.0
+[0.10.1]: https://github.com/tuist/XcodeGraph/compare/0.10.0..0.10.1
+[0.10.0]: https://github.com/tuist/XcodeGraph/compare/0.9.1..0.10.0
+[0.9.1]: https://github.com/tuist/XcodeGraph/compare/0.9.0..0.9.1
+[0.8.0]: https://github.com/tuist/XcodeGraph/compare/0.7.0..0.8.0
+[0.7.0]: https://github.com/tuist/XcodeGraph/compare/0.6.0..0.7.0
From d36025916c89382b14fd91ad89aaf11e8e3b8da9 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 27 Aug 2024 15:10:51 +0000
Subject: [PATCH 011/914] chore(deps): update dependency tuist to v4.25.0 (#39)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index fb0a5f18..696aafb6 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.24.0"
+tuist = "4.25.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 7578c2b105b20055d8af62fff99ceede7f20e4ab Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Tue, 27 Aug 2024 15:11:07 +0000
Subject: [PATCH 012/914] [Release] XcodeGraph 0.11.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f425b56..95771d46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.1] - 2024-08-27
+### Details
+#### Chore
+- Update dependency tuist to v4.25.0 by @renovate[bot] in [#39](https://github.com/tuist/XcodeGraph/pull/39)
+
## [0.11.0] - 2024-08-22
### Details
#### Chore
@@ -64,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.1]: https://github.com/tuist/XcodeGraph/compare/0.11.0..0.11.1
[0.11.0]: https://github.com/tuist/XcodeGraph/compare/0.10.1..0.11.0
[0.10.1]: https://github.com/tuist/XcodeGraph/compare/0.10.0..0.10.1
[0.10.0]: https://github.com/tuist/XcodeGraph/compare/0.9.1..0.10.0
From 0678cb501538742d19526b36ac04e96cf06036c4 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 28 Aug 2024 01:54:40 +0000
Subject: [PATCH 013/914] chore(deps): update dependency tuist/path to from:
"0.3.4" (#40)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index c021fd39..a01955fb 100644
--- a/Package.swift
+++ b/Package.swift
@@ -23,7 +23,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.3")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.4")),
],
targets: targets
)
From d48cd47a121b7bd730058a38509d9fb12172925e Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 28 Aug 2024 01:54:54 +0000
Subject: [PATCH 014/914] [Release] XcodeGraph 0.11.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95771d46..47d97a41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.2] - 2024-08-28
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.4" by @renovate[bot] in [#40](https://github.com/tuist/XcodeGraph/pull/40)
+
## [0.11.1] - 2024-08-27
### Details
#### Chore
@@ -69,6 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.2]: https://github.com/tuist/XcodeGraph/compare/0.11.1..0.11.2
[0.11.1]: https://github.com/tuist/XcodeGraph/compare/0.11.0..0.11.1
[0.11.0]: https://github.com/tuist/XcodeGraph/compare/0.10.1..0.11.0
[0.10.1]: https://github.com/tuist/XcodeGraph/compare/0.10.0..0.10.1
From 65682fdd5e800ce899b46635b9d6d7aec181dc7f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 3 Sep 2024 18:03:10 +0000
Subject: [PATCH 015/914] chore(deps): update dependency tuist to v4.26.0 (#42)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 696aafb6..d9bcdaff 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.25.0"
+tuist = "4.26.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 4563d978a4ca56309be1509a20afc853cf6028d5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Tue, 3 Sep 2024 18:03:31 +0000
Subject: [PATCH 016/914] [Release] XcodeGraph 0.11.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47d97a41..6a51d68c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.3] - 2024-09-03
+### Details
+#### Chore
+- Update dependency tuist to v4.26.0 by @renovate[bot] in [#42](https://github.com/tuist/XcodeGraph/pull/42)
+
## [0.11.2] - 2024-08-28
### Details
#### Chore
@@ -74,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.3]: https://github.com/tuist/XcodeGraph/compare/0.11.2..0.11.3
[0.11.2]: https://github.com/tuist/XcodeGraph/compare/0.11.1..0.11.2
[0.11.1]: https://github.com/tuist/XcodeGraph/compare/0.11.0..0.11.1
[0.11.0]: https://github.com/tuist/XcodeGraph/compare/0.10.1..0.11.0
From 272ad3ba03b8f199de2591da486efebbd4fac1b8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 4 Sep 2024 00:24:01 +0000
Subject: [PATCH 017/914] chore(deps): update dependency tuist/path to from:
"0.3.5" (#43)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index a01955fb..78281de0 100644
--- a/Package.swift
+++ b/Package.swift
@@ -23,7 +23,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.4")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.5")),
],
targets: targets
)
From f364d91b07a32f800f034f3c3ce122b98579e59b Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 4 Sep 2024 00:24:21 +0000
Subject: [PATCH 018/914] [Release] XcodeGraph 0.11.4
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a51d68c..9f0d576c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.4] - 2024-09-04
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.5" by @renovate[bot] in [#43](https://github.com/tuist/XcodeGraph/pull/43)
+
## [0.11.3] - 2024-09-03
### Details
#### Chore
@@ -79,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.4]: https://github.com/tuist/XcodeGraph/compare/0.11.3..0.11.4
[0.11.3]: https://github.com/tuist/XcodeGraph/compare/0.11.2..0.11.3
[0.11.2]: https://github.com/tuist/XcodeGraph/compare/0.11.1..0.11.2
[0.11.1]: https://github.com/tuist/XcodeGraph/compare/0.11.0..0.11.1
From 529972cf868b87b287f83a05eb3534db902c3eca Mon Sep 17 00:00:00 2001
From: Mike Simons
Date: Mon, 16 Sep 2024 12:39:00 -0400
Subject: [PATCH 019/914] chore: sendability conformance (#44)
* Sendability Conformance
* Enable Strict Concurrency
* Linting
---
Package.swift | 3 +++
Project.swift | 4 +++-
.../DependenciesGraph/DependenciesGraph.swift | 2 +-
Sources/XcodeGraph/Models/AnalyzeAction.swift | 2 +-
Sources/XcodeGraph/Models/ArchiveAction.swift | 2 +-
Sources/XcodeGraph/Models/Arguments.swift | 2 +-
Sources/XcodeGraph/Models/BuildAction.swift | 2 +-
Sources/XcodeGraph/Models/BuildConfiguration.swift | 4 ++--
.../XcodeGraph/Models/BuildRule+CompilerSpec.swift | 2 +-
Sources/XcodeGraph/Models/BuildRule+FileType.swift | 2 +-
Sources/XcodeGraph/Models/BuildRule.swift | 2 +-
Sources/XcodeGraph/Models/CopyFileElement.swift | 2 +-
Sources/XcodeGraph/Models/CopyFilesAction.swift | 4 ++--
Sources/XcodeGraph/Models/CoreDataModel.swift | 2 +-
Sources/XcodeGraph/Models/DeploymentTargets.swift | 2 +-
Sources/XcodeGraph/Models/Destination.swift | 12 ++++++------
Sources/XcodeGraph/Models/EnvironmentVariable.swift | 2 +-
Sources/XcodeGraph/Models/ExecutionAction.swift | 2 +-
Sources/XcodeGraph/Models/FileCodeGen.swift | 2 +-
Sources/XcodeGraph/Models/FileElement.swift | 2 +-
Sources/XcodeGraph/Models/Headers.swift | 2 +-
Sources/XcodeGraph/Models/IDETemplateMacros.swift | 2 +-
Sources/XcodeGraph/Models/LaunchArgument.swift | 2 +-
Sources/XcodeGraph/Models/LaunchStyle.swift | 2 +-
Sources/XcodeGraph/Models/MergedBinaryType.swift | 2 +-
Sources/XcodeGraph/Models/MetalOptions.swift | 2 +-
.../XcodeGraph/Models/OnDemandResourcesTags.swift | 2 +-
Sources/XcodeGraph/Models/Package.swift | 2 +-
Sources/XcodeGraph/Models/PlatformCondition.swift | 2 +-
Sources/XcodeGraph/Models/PlatformFilter.swift | 2 +-
Sources/XcodeGraph/Models/Plist.swift | 8 ++++----
Sources/XcodeGraph/Models/PrivacyManifest.swift | 2 +-
Sources/XcodeGraph/Models/Product.swift | 2 +-
Sources/XcodeGraph/Models/ProfileAction.swift | 2 +-
Sources/XcodeGraph/Models/Project.swift | 2 +-
Sources/XcodeGraph/Models/ProjectGroup.swift | 2 +-
Sources/XcodeGraph/Models/ProjectOptions.swift | 8 ++++----
Sources/XcodeGraph/Models/RawScriptBuildPhase.swift | 2 +-
Sources/XcodeGraph/Models/Requirement.swift | 2 +-
Sources/XcodeGraph/Models/ResourceFileElement.swift | 2 +-
Sources/XcodeGraph/Models/ResourceFileElements.swift | 2 +-
Sources/XcodeGraph/Models/ResourceSynthesizer.swift | 10 +++++-----
Sources/XcodeGraph/Models/RunAction.swift | 2 +-
Sources/XcodeGraph/Models/RunActionOptions.swift | 4 ++--
Sources/XcodeGraph/Models/Scheme.swift | 2 +-
.../XcodeGraph/Models/SchemeDiagnosticsOptions.swift | 2 +-
Sources/XcodeGraph/Models/ScreenCaptureFormat.swift | 2 +-
Sources/XcodeGraph/Models/Settings.swift | 8 ++++----
Sources/XcodeGraph/Models/SimulatedLocation.swift | 2 +-
Sources/XcodeGraph/Models/SourceFile.swift | 2 +-
Sources/XcodeGraph/Models/Target.swift | 2 +-
Sources/XcodeGraph/Models/TargetDependency.swift | 6 +++---
Sources/XcodeGraph/Models/TargetReference.swift | 2 +-
Sources/XcodeGraph/Models/TargetScript.swift | 6 +++---
Sources/XcodeGraph/Models/TestAction.swift | 2 +-
Sources/XcodeGraph/Models/TestPlan.swift | 2 +-
Sources/XcodeGraph/Models/TestableTarget.swift | 2 +-
Sources/XcodeGraph/Models/TestingOptions.swift | 2 +-
58 files changed, 87 insertions(+), 82 deletions(-)
diff --git a/Package.swift b/Package.swift
index 78281de0..8ca337f9 100644
--- a/Package.swift
+++ b/Package.swift
@@ -8,6 +8,9 @@ var targets: [Target] = [
dependencies: [
"AnyCodable",
"Path",
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
]
),
]
diff --git a/Project.swift b/Project.swift
index b3b6513a..4b4cd374 100644
--- a/Project.swift
+++ b/Project.swift
@@ -1,7 +1,9 @@
import ProjectDescription
import ProjectDescriptionHelpers
-let baseSettings: SettingsDictionary = [:]
+let baseSettings: SettingsDictionary = [
+ "SWIFT_STRICT_CONCURRENCY": "complete",
+]
func debugSettings() -> SettingsDictionary {
var settings = baseSettings
diff --git a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
index 929ef3df..62972a92 100644
--- a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
+++ b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// A directed acyclic graph (DAG) that Tuist uses to represent the dependency tree.
-public struct DependenciesGraph: Equatable, Codable {
+public struct DependenciesGraph: Equatable, Codable, Sendable {
/// A dictionary where the keys are the supported platforms and the values are dictionaries where the keys are the names of
/// dependencies, and the values are the dependencies themselves.
public let externalDependencies: [String: [TargetDependency]]
diff --git a/Sources/XcodeGraph/Models/AnalyzeAction.swift b/Sources/XcodeGraph/Models/AnalyzeAction.swift
index eb63012b..c0d5577c 100644
--- a/Sources/XcodeGraph/Models/AnalyzeAction.swift
+++ b/Sources/XcodeGraph/Models/AnalyzeAction.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct AnalyzeAction: Equatable, Codable {
+public struct AnalyzeAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public let configurationName: String
diff --git a/Sources/XcodeGraph/Models/ArchiveAction.swift b/Sources/XcodeGraph/Models/ArchiveAction.swift
index a7e1ba65..99dc55a4 100644
--- a/Sources/XcodeGraph/Models/ArchiveAction.swift
+++ b/Sources/XcodeGraph/Models/ArchiveAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct ArchiveAction: Equatable, Codable {
+public struct ArchiveAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public let configurationName: String
diff --git a/Sources/XcodeGraph/Models/Arguments.swift b/Sources/XcodeGraph/Models/Arguments.swift
index c9aac9e2..5b939098 100644
--- a/Sources/XcodeGraph/Models/Arguments.swift
+++ b/Sources/XcodeGraph/Models/Arguments.swift
@@ -1,7 +1,7 @@
import Foundation
/// Arguments contain commandline arguments passed on launch and Environment variables.
-public struct Arguments: Codable {
+public struct Arguments: Codable, Sendable {
// MARK: - Attributes
/// Launch arguments that are passed by the scheme when running a scheme action.
diff --git a/Sources/XcodeGraph/Models/BuildAction.swift b/Sources/XcodeGraph/Models/BuildAction.swift
index 161e0d2e..ea782d38 100644
--- a/Sources/XcodeGraph/Models/BuildAction.swift
+++ b/Sources/XcodeGraph/Models/BuildAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct BuildAction: Equatable, Codable {
+public struct BuildAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public var targets: [TargetReference]
diff --git a/Sources/XcodeGraph/Models/BuildConfiguration.swift b/Sources/XcodeGraph/Models/BuildConfiguration.swift
index b49438bb..d51cf636 100644
--- a/Sources/XcodeGraph/Models/BuildConfiguration.swift
+++ b/Sources/XcodeGraph/Models/BuildConfiguration.swift
@@ -7,8 +7,8 @@ import Foundation
/// It hosts the name as well as the variant of
/// a configuration to help infer the appropriate
/// default settings.
-public struct BuildConfiguration: Codable {
- public enum Variant: String, Codable {
+public struct BuildConfiguration: Codable, Sendable {
+ public enum Variant: String, Codable, Sendable {
case debug, release
}
diff --git a/Sources/XcodeGraph/Models/BuildRule+CompilerSpec.swift b/Sources/XcodeGraph/Models/BuildRule+CompilerSpec.swift
index e615f9d1..6098f6a2 100644
--- a/Sources/XcodeGraph/Models/BuildRule+CompilerSpec.swift
+++ b/Sources/XcodeGraph/Models/BuildRule+CompilerSpec.swift
@@ -3,7 +3,7 @@ import Foundation
extension BuildRule {
/// Mapping of compiler specs supported by a build rule to Xcode's internal representation.
/// All values were coppied from `pbxproj`.
- public enum CompilerSpec: String, Codable {
+ public enum CompilerSpec: String, Codable, Sendable {
case appIntentsMetadataExtractor = "com.apple.compilers.appintentsmetadata"
case appShortcutStringsMetadataExtractor = "com.apple.compilers.appshortcutstringsmetadata"
case appleClang = "com.apple.compilers.llvm.clang.1_0"
diff --git a/Sources/XcodeGraph/Models/BuildRule+FileType.swift b/Sources/XcodeGraph/Models/BuildRule+FileType.swift
index 3f6f957b..d0f888bf 100644
--- a/Sources/XcodeGraph/Models/BuildRule+FileType.swift
+++ b/Sources/XcodeGraph/Models/BuildRule+FileType.swift
@@ -3,7 +3,7 @@ import Foundation
extension BuildRule {
/// Mapping of file types supported by a build rule to Xcode's internal representation.
/// All values were coppied from `pbxproj`.
- public enum FileType: String, Codable {
+ public enum FileType: String, Codable, Sendable {
case instrumentsPackageDefinition = "com.apple.instruments.package-definition"
case metalAIR = "compiled.air"
case machO = "compiled.mach-o"
diff --git a/Sources/XcodeGraph/Models/BuildRule.swift b/Sources/XcodeGraph/Models/BuildRule.swift
index 7464b4e1..5dcc324f 100644
--- a/Sources/XcodeGraph/Models/BuildRule.swift
+++ b/Sources/XcodeGraph/Models/BuildRule.swift
@@ -1,7 +1,7 @@
import Foundation
/// A BuildRule is used to specify a method for transforming an input file in to an output file(s).
-public struct BuildRule: Codable, Equatable {
+public struct BuildRule: Codable, Equatable, Sendable {
/// Element compiler spec.
public let compilerSpec: CompilerSpec
diff --git a/Sources/XcodeGraph/Models/CopyFileElement.swift b/Sources/XcodeGraph/Models/CopyFileElement.swift
index 649599e3..4c9451af 100644
--- a/Sources/XcodeGraph/Models/CopyFileElement.swift
+++ b/Sources/XcodeGraph/Models/CopyFileElement.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public enum CopyFileElement: Equatable, Hashable, Codable {
+public enum CopyFileElement: Equatable, Hashable, Codable, Sendable {
case file(path: AbsolutePath, condition: PlatformCondition? = nil, codeSignOnCopy: Bool = false)
case folderReference(path: AbsolutePath, condition: PlatformCondition? = nil, codeSignOnCopy: Bool = false)
diff --git a/Sources/XcodeGraph/Models/CopyFilesAction.swift b/Sources/XcodeGraph/Models/CopyFilesAction.swift
index d61b35d6..06ba1438 100644
--- a/Sources/XcodeGraph/Models/CopyFilesAction.swift
+++ b/Sources/XcodeGraph/Models/CopyFilesAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct CopyFilesAction: Equatable, Codable {
+public struct CopyFilesAction: Equatable, Codable, Sendable {
/// Name of the build phase when the project gets generated.
public var name: String
@@ -15,7 +15,7 @@ public struct CopyFilesAction: Equatable, Codable {
public var files: [CopyFileElement]
/// Destination path.
- public enum Destination: String, Equatable, Codable {
+ public enum Destination: String, Equatable, Codable, Sendable {
case absolutePath
case productsDirectory
case wrapper
diff --git a/Sources/XcodeGraph/Models/CoreDataModel.swift b/Sources/XcodeGraph/Models/CoreDataModel.swift
index b17b74e2..a8f111e3 100644
--- a/Sources/XcodeGraph/Models/CoreDataModel.swift
+++ b/Sources/XcodeGraph/Models/CoreDataModel.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// Represents a Core Data model
-public struct CoreDataModel: Equatable, Codable {
+public struct CoreDataModel: Equatable, Codable, Sendable {
// MARK: - Attributes
/// Relative path to the Core Data model.
diff --git a/Sources/XcodeGraph/Models/DeploymentTargets.swift b/Sources/XcodeGraph/Models/DeploymentTargets.swift
index 54db36d7..191db89b 100644
--- a/Sources/XcodeGraph/Models/DeploymentTargets.swift
+++ b/Sources/XcodeGraph/Models/DeploymentTargets.swift
@@ -2,7 +2,7 @@ import Foundation
// MARK: - DeploymentTargets
-public struct DeploymentTargets: Hashable, Codable {
+public struct DeploymentTargets: Hashable, Codable, Sendable {
public let iOS: String?
public let macOS: String?
public let watchOS: String?
diff --git a/Sources/XcodeGraph/Models/Destination.swift b/Sources/XcodeGraph/Models/Destination.swift
index 0d7b2028..a7a30e73 100644
--- a/Sources/XcodeGraph/Models/Destination.swift
+++ b/Sources/XcodeGraph/Models/Destination.swift
@@ -3,11 +3,11 @@ import Foundation
public typealias Destinations = Set
extension Destinations {
- public static var watchOS: Destinations = [.appleWatch]
- public static var iOS: Destinations = [.iPhone, .iPad, .macWithiPadDesign]
- public static var macOS: Destinations = [.mac]
- public static var tvOS: Destinations = [.appleTv]
- public static var visionOS: Destinations = [.appleVision]
+ public static let watchOS: Destinations = [.appleWatch]
+ public static let iOS: Destinations = [.iPhone, .iPad, .macWithiPadDesign]
+ public static let macOS: Destinations = [.mac]
+ public static let tvOS: Destinations = [.appleTv]
+ public static let visionOS: Destinations = [.appleVision]
}
extension Destinations {
@@ -18,7 +18,7 @@ extension Destinations {
}
/// A supported platform representation.
-public enum Destination: String, Codable, Equatable, CaseIterable {
+public enum Destination: String, Codable, Equatable, CaseIterable, Sendable {
case iPhone
case iPad
case mac
diff --git a/Sources/XcodeGraph/Models/EnvironmentVariable.swift b/Sources/XcodeGraph/Models/EnvironmentVariable.swift
index 9e40a70c..38f074e0 100644
--- a/Sources/XcodeGraph/Models/EnvironmentVariable.swift
+++ b/Sources/XcodeGraph/Models/EnvironmentVariable.swift
@@ -1,7 +1,7 @@
import Foundation
/// It represents an environment variable that is passed when running a scheme's action
-public struct EnvironmentVariable: Equatable, Codable, Hashable, ExpressibleByStringLiteral {
+public struct EnvironmentVariable: Equatable, Codable, Hashable, ExpressibleByStringLiteral, Sendable {
// MARK: - Attributes
/// The value of the environment variable
diff --git a/Sources/XcodeGraph/Models/ExecutionAction.swift b/Sources/XcodeGraph/Models/ExecutionAction.swift
index 5c1f9737..1b1a798c 100644
--- a/Sources/XcodeGraph/Models/ExecutionAction.swift
+++ b/Sources/XcodeGraph/Models/ExecutionAction.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// A execution action
-public struct ExecutionAction: Equatable, Codable {
+public struct ExecutionAction: Equatable, Codable, Sendable {
// MARK: - Attributes
/// Name of a script.
diff --git a/Sources/XcodeGraph/Models/FileCodeGen.swift b/Sources/XcodeGraph/Models/FileCodeGen.swift
index e947f212..2fe3c599 100644
--- a/Sources/XcodeGraph/Models/FileCodeGen.swift
+++ b/Sources/XcodeGraph/Models/FileCodeGen.swift
@@ -7,7 +7,7 @@ import Foundation
/// - `project`: project codegen attribute `settings = {ATTRIBUTES = (project_codegen, )}`
/// - `disabled`: disabled codegen attribute `settings = {ATTRIBUTES = (no_codegen, )}`
///
-public enum FileCodeGen: String, Codable, Equatable {
+public enum FileCodeGen: String, Codable, Equatable, Sendable {
case `public` = "codegen"
case `private` = "private_codegen"
case project = "project_codegen"
diff --git a/Sources/XcodeGraph/Models/FileElement.swift b/Sources/XcodeGraph/Models/FileElement.swift
index f7fe0379..1972e9e3 100644
--- a/Sources/XcodeGraph/Models/FileElement.swift
+++ b/Sources/XcodeGraph/Models/FileElement.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public enum FileElement: Equatable, Hashable, Codable {
+public enum FileElement: Equatable, Hashable, Codable, Sendable {
case file(path: AbsolutePath)
case folderReference(path: AbsolutePath)
diff --git a/Sources/XcodeGraph/Models/Headers.swift b/Sources/XcodeGraph/Models/Headers.swift
index 1a4c4dac..98fa08d0 100644
--- a/Sources/XcodeGraph/Models/Headers.swift
+++ b/Sources/XcodeGraph/Models/Headers.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// Headers
-public struct Headers: Equatable, Codable {
+public struct Headers: Equatable, Codable, Sendable {
// MARK: - Attributes
public let `public`: [AbsolutePath]
diff --git a/Sources/XcodeGraph/Models/IDETemplateMacros.swift b/Sources/XcodeGraph/Models/IDETemplateMacros.swift
index 3c4df637..28067ffa 100644
--- a/Sources/XcodeGraph/Models/IDETemplateMacros.swift
+++ b/Sources/XcodeGraph/Models/IDETemplateMacros.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct IDETemplateMacros: Codable, Hashable {
+public struct IDETemplateMacros: Codable, Hashable, Sendable {
private enum CodingKeys: String, CodingKey {
case fileHeader = "FILEHEADER"
}
diff --git a/Sources/XcodeGraph/Models/LaunchArgument.swift b/Sources/XcodeGraph/Models/LaunchArgument.swift
index 8ad8d697..7b204d97 100644
--- a/Sources/XcodeGraph/Models/LaunchArgument.swift
+++ b/Sources/XcodeGraph/Models/LaunchArgument.swift
@@ -1,7 +1,7 @@
import Foundation
/// It represents an argument that is passed when running a scheme's action
-public struct LaunchArgument: Equatable, Codable, Hashable {
+public struct LaunchArgument: Equatable, Codable, Hashable, Sendable {
// MARK: - Attributes
/// The name of the launch argument
diff --git a/Sources/XcodeGraph/Models/LaunchStyle.swift b/Sources/XcodeGraph/Models/LaunchStyle.swift
index 93e06b7c..ed054918 100644
--- a/Sources/XcodeGraph/Models/LaunchStyle.swift
+++ b/Sources/XcodeGraph/Models/LaunchStyle.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum LaunchStyle: Codable {
+public enum LaunchStyle: Codable, Sendable {
case automatically
case waitForExecutableToBeLaunched
}
diff --git a/Sources/XcodeGraph/Models/MergedBinaryType.swift b/Sources/XcodeGraph/Models/MergedBinaryType.swift
index 2de57535..9d3c0cc5 100644
--- a/Sources/XcodeGraph/Models/MergedBinaryType.swift
+++ b/Sources/XcodeGraph/Models/MergedBinaryType.swift
@@ -1,7 +1,7 @@
/// Represents the different options to configure a target for mergeable libraries
///
/// https://developer.apple.com/documentation/xcode/configuring-your-project-to-use-mergeable-libraries
-public enum MergedBinaryType: Equatable, Codable {
+public enum MergedBinaryType: Equatable, Codable, Sendable {
/// Target is never going to merge available dependencies
case disabled
diff --git a/Sources/XcodeGraph/Models/MetalOptions.swift b/Sources/XcodeGraph/Models/MetalOptions.swift
index 8d859085..6c199072 100644
--- a/Sources/XcodeGraph/Models/MetalOptions.swift
+++ b/Sources/XcodeGraph/Models/MetalOptions.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct MetalOptions: Equatable, Codable {
+public struct MetalOptions: Equatable, Codable, Sendable {
public var apiValidation: Bool
public var shaderValidation: Bool
public var showGraphicsOverview: Bool
diff --git a/Sources/XcodeGraph/Models/OnDemandResourcesTags.swift b/Sources/XcodeGraph/Models/OnDemandResourcesTags.swift
index 75da69d1..a7c0b6ce 100644
--- a/Sources/XcodeGraph/Models/OnDemandResourcesTags.swift
+++ b/Sources/XcodeGraph/Models/OnDemandResourcesTags.swift
@@ -1,4 +1,4 @@
-public struct OnDemandResourcesTags: Codable, Equatable {
+public struct OnDemandResourcesTags: Codable, Equatable, Sendable {
public let initialInstall: [String]?
public let prefetchOrder: [String]?
diff --git a/Sources/XcodeGraph/Models/Package.swift b/Sources/XcodeGraph/Models/Package.swift
index ac4aab84..c3811fdc 100644
--- a/Sources/XcodeGraph/Models/Package.swift
+++ b/Sources/XcodeGraph/Models/Package.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public enum Package: Equatable, Codable {
+public enum Package: Equatable, Codable, Sendable {
case remote(url: String, requirement: Requirement)
case local(path: AbsolutePath)
}
diff --git a/Sources/XcodeGraph/Models/PlatformCondition.swift b/Sources/XcodeGraph/Models/PlatformCondition.swift
index 442f28f1..64e1847a 100644
--- a/Sources/XcodeGraph/Models/PlatformCondition.swift
+++ b/Sources/XcodeGraph/Models/PlatformCondition.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct PlatformCondition: Codable, Hashable, Equatable, Comparable {
+public struct PlatformCondition: Codable, Hashable, Equatable, Comparable, Sendable {
public static func < (lhs: PlatformCondition, rhs: PlatformCondition) -> Bool {
lhs.platformFilters < rhs.platformFilters
}
diff --git a/Sources/XcodeGraph/Models/PlatformFilter.swift b/Sources/XcodeGraph/Models/PlatformFilter.swift
index 00f2b2ed..a7bff439 100644
--- a/Sources/XcodeGraph/Models/PlatformFilter.swift
+++ b/Sources/XcodeGraph/Models/PlatformFilter.swift
@@ -12,7 +12,7 @@ extension PlatformFilters: Comparable {
/// Defines a set of platforms that can be used to limit where things
/// like build files, resources, and dependencies are used.
/// Context: https://github.com/tuist/tuist/pull/3152
-public enum PlatformFilter: Comparable, Hashable, Codable, CaseIterable {
+public enum PlatformFilter: Comparable, Hashable, Codable, CaseIterable, Sendable {
case ios
case macos
case tvos
diff --git a/Sources/XcodeGraph/Models/Plist.swift b/Sources/XcodeGraph/Models/Plist.swift
index 1ac2ff82..5f1d743b 100644
--- a/Sources/XcodeGraph/Models/Plist.swift
+++ b/Sources/XcodeGraph/Models/Plist.swift
@@ -3,11 +3,11 @@ import Path
// MARK: - Plist
-public enum Plist {
+public enum Plist: Sendable {
case infoPlist(InfoPlist)
case entitlements(Entitlements)
- public indirect enum Value: Equatable, Codable {
+ public indirect enum Value: Equatable, Codable, Sendable {
case string(String)
case integer(Int)
case real(Double)
@@ -92,7 +92,7 @@ extension Dictionary where Value == Plist.Value {
// MARK: - InfoPlist
-public enum InfoPlist: Equatable, Codable {
+public enum InfoPlist: Equatable, Codable, Sendable {
// Path to a user defined info.plist file (already exists on disk).
case file(path: AbsolutePath)
@@ -129,7 +129,7 @@ extension InfoPlist: ExpressibleByStringLiteral {
// MARK: - Entitlements
-public enum Entitlements: Equatable, Codable {
+public enum Entitlements: Equatable, Codable, Sendable {
// Path to a user defined .entitlements file (already exists on disk).
case file(path: AbsolutePath)
diff --git a/Sources/XcodeGraph/Models/PrivacyManifest.swift b/Sources/XcodeGraph/Models/PrivacyManifest.swift
index be1e2fc3..135bd58d 100644
--- a/Sources/XcodeGraph/Models/PrivacyManifest.swift
+++ b/Sources/XcodeGraph/Models/PrivacyManifest.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct PrivacyManifest: Codable, Equatable {
+public struct PrivacyManifest: Codable, Equatable, Sendable {
public var tracking: Bool
public var trackingDomains: [String]
diff --git a/Sources/XcodeGraph/Models/Product.swift b/Sources/XcodeGraph/Models/Product.swift
index 322a0683..d3876861 100644
--- a/Sources/XcodeGraph/Models/Product.swift
+++ b/Sources/XcodeGraph/Models/Product.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum Product: String, CustomStringConvertible, CaseIterable, Codable {
+public enum Product: String, CustomStringConvertible, CaseIterable, Codable, Sendable {
case app
case staticLibrary = "static_library"
case dynamicLibrary = "dynamic_library"
diff --git a/Sources/XcodeGraph/Models/ProfileAction.swift b/Sources/XcodeGraph/Models/ProfileAction.swift
index d06c1435..1699b6df 100644
--- a/Sources/XcodeGraph/Models/ProfileAction.swift
+++ b/Sources/XcodeGraph/Models/ProfileAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct ProfileAction: Equatable, Codable {
+public struct ProfileAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public let configurationName: String
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index 2af8c99c..9ee70f60 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebugStringConvertible, Codable {
+public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebugStringConvertible, Codable, Sendable {
// MARK: - Attributes
/// Path to the folder that contains the project manifest.
diff --git a/Sources/XcodeGraph/Models/ProjectGroup.swift b/Sources/XcodeGraph/Models/ProjectGroup.swift
index cebcdb1f..cea9a37e 100644
--- a/Sources/XcodeGraph/Models/ProjectGroup.swift
+++ b/Sources/XcodeGraph/Models/ProjectGroup.swift
@@ -1,5 +1,5 @@
import Foundation
-public enum ProjectGroup: Hashable, Codable {
+public enum ProjectGroup: Hashable, Codable, Sendable {
case group(name: String)
}
diff --git a/Sources/XcodeGraph/Models/ProjectOptions.swift b/Sources/XcodeGraph/Models/ProjectOptions.swift
index 219aae9c..c91c386e 100644
--- a/Sources/XcodeGraph/Models/ProjectOptions.swift
+++ b/Sources/XcodeGraph/Models/ProjectOptions.swift
@@ -2,7 +2,7 @@ import Foundation
extension Project {
/// Additional options related to the `Project`
- public struct Options: Codable, Hashable {
+ public struct Options: Codable, Hashable, Sendable {
/// Defines how to generate automatic schemes
public let automaticSchemesOptions: AutomaticSchemesOptions
@@ -38,9 +38,9 @@ extension Project {
extension Project.Options {
/// The automatic schemes options
- public enum AutomaticSchemesOptions: Codable, Hashable {
+ public enum AutomaticSchemesOptions: Codable, Hashable, Sendable {
/// Defines how to group targets into scheme for autogenerated schemes
- public enum TargetSchemesGrouping: Codable, Hashable {
+ public enum TargetSchemesGrouping: Codable, Hashable, Sendable {
/// Generate a single scheme per project
case singleScheme
@@ -68,7 +68,7 @@ extension Project.Options {
}
/// The text settings options
- public struct TextSettings: Codable, Hashable {
+ public struct TextSettings: Codable, Hashable, Sendable {
/// Whether tabs should be used instead of spaces
public let usesTabs: Bool?
diff --git a/Sources/XcodeGraph/Models/RawScriptBuildPhase.swift b/Sources/XcodeGraph/Models/RawScriptBuildPhase.swift
index 808748f8..2555a244 100644
--- a/Sources/XcodeGraph/Models/RawScriptBuildPhase.swift
+++ b/Sources/XcodeGraph/Models/RawScriptBuildPhase.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// It represents a raw script build phase.
-public struct RawScriptBuildPhase: Equatable, Codable {
+public struct RawScriptBuildPhase: Equatable, Codable, Sendable {
/// The name of the build phase.
public let name: String
diff --git a/Sources/XcodeGraph/Models/Requirement.swift b/Sources/XcodeGraph/Models/Requirement.swift
index 6b3852f3..1672ce62 100644
--- a/Sources/XcodeGraph/Models/Requirement.swift
+++ b/Sources/XcodeGraph/Models/Requirement.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum Requirement: Equatable, Codable {
+public enum Requirement: Equatable, Codable, Sendable {
case upToNextMajor(String)
case upToNextMinor(String)
case range(from: String, to: String)
diff --git a/Sources/XcodeGraph/Models/ResourceFileElement.swift b/Sources/XcodeGraph/Models/ResourceFileElement.swift
index 5023f01e..b221a255 100644
--- a/Sources/XcodeGraph/Models/ResourceFileElement.swift
+++ b/Sources/XcodeGraph/Models/ResourceFileElement.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public enum ResourceFileElement: Equatable, Hashable, Codable {
+public enum ResourceFileElement: Equatable, Hashable, Codable, Sendable {
/// A file path (or glob pattern) to include, a list of file paths (or glob patterns) to exclude, ODR tags list and inclusion
/// condition.
/// For convenience, a string literal can be used as an alternate way to specify this option.
diff --git a/Sources/XcodeGraph/Models/ResourceFileElements.swift b/Sources/XcodeGraph/Models/ResourceFileElements.swift
index ec17e6fd..0ba59320 100644
--- a/Sources/XcodeGraph/Models/ResourceFileElements.swift
+++ b/Sources/XcodeGraph/Models/ResourceFileElements.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct ResourceFileElements: Codable, Equatable {
+public struct ResourceFileElements: Codable, Equatable, Sendable {
public var resources: [ResourceFileElement]
public var privacyManifest: PrivacyManifest?
diff --git a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
index f1ce4edd..df5397b7 100644
--- a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
+++ b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
@@ -1,19 +1,19 @@
-import AnyCodable
+@preconcurrency import AnyCodable
import Foundation
import Path
-public struct ResourceSynthesizer: Equatable, Hashable, Codable {
+public struct ResourceSynthesizer: Equatable, Hashable, Codable, Sendable {
public let parser: Parser
public let parserOptions: [String: Parser.Option]
public let extensions: Set
public let template: Template
- public enum Template: Equatable, Hashable, Codable {
+ public enum Template: Equatable, Hashable, Codable, Sendable {
case file(AbsolutePath)
case defaultTemplate(String)
}
- public enum Parser: String, Equatable, Hashable, Codable {
+ public enum Parser: String, Equatable, Hashable, Codable, Sendable {
case strings
case assets
case plists
@@ -24,7 +24,7 @@ public struct ResourceSynthesizer: Equatable, Hashable, Codable {
case yaml
case files
- public struct Option: Equatable, Hashable, Codable {
+ public struct Option: Equatable, Hashable, Codable, Sendable {
public var value: Any { anyCodableValue.value }
private let anyCodableValue: AnyCodable
diff --git a/Sources/XcodeGraph/Models/RunAction.swift b/Sources/XcodeGraph/Models/RunAction.swift
index cd0b95e1..a0c2e092 100644
--- a/Sources/XcodeGraph/Models/RunAction.swift
+++ b/Sources/XcodeGraph/Models/RunAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct RunAction: Equatable, Codable {
+public struct RunAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public let configurationName: String
diff --git a/Sources/XcodeGraph/Models/RunActionOptions.swift b/Sources/XcodeGraph/Models/RunActionOptions.swift
index 5cae4294..941bdb18 100644
--- a/Sources/XcodeGraph/Models/RunActionOptions.swift
+++ b/Sources/XcodeGraph/Models/RunActionOptions.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// Options for the `RunAction` action
-public struct RunActionOptions: Equatable, Codable {
+public struct RunActionOptions: Equatable, Codable, Sendable {
/// App Language.
public let language: String?
@@ -53,7 +53,7 @@ public struct RunActionOptions: Equatable, Codable {
}
extension RunActionOptions {
- public enum GPUFrameCaptureMode: String, Codable, Equatable {
+ public enum GPUFrameCaptureMode: String, Codable, Equatable, Sendable {
case autoEnabled
case metal
case openGL
diff --git a/Sources/XcodeGraph/Models/Scheme.swift b/Sources/XcodeGraph/Models/Scheme.swift
index fb40a136..24b4cfe1 100644
--- a/Sources/XcodeGraph/Models/Scheme.swift
+++ b/Sources/XcodeGraph/Models/Scheme.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct Scheme: Equatable, Codable {
+public struct Scheme: Equatable, Codable, Sendable {
// MARK: - Attributes
public var name: String
diff --git a/Sources/XcodeGraph/Models/SchemeDiagnosticsOptions.swift b/Sources/XcodeGraph/Models/SchemeDiagnosticsOptions.swift
index 02dd794c..ee64e64a 100644
--- a/Sources/XcodeGraph/Models/SchemeDiagnosticsOptions.swift
+++ b/Sources/XcodeGraph/Models/SchemeDiagnosticsOptions.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct SchemeDiagnosticsOptions: Equatable, Codable {
+public struct SchemeDiagnosticsOptions: Equatable, Codable, Sendable {
public let addressSanitizerEnabled: Bool
public let detectStackUseAfterReturnEnabled: Bool
public let threadSanitizerEnabled: Bool
diff --git a/Sources/XcodeGraph/Models/ScreenCaptureFormat.swift b/Sources/XcodeGraph/Models/ScreenCaptureFormat.swift
index 49e7f59a..7e04d2ec 100644
--- a/Sources/XcodeGraph/Models/ScreenCaptureFormat.swift
+++ b/Sources/XcodeGraph/Models/ScreenCaptureFormat.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum ScreenCaptureFormat: String, Codable {
+public enum ScreenCaptureFormat: String, Codable, Sendable {
case screenshots
case screenRecording
}
diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift
index a89d1ede..e1e87942 100644
--- a/Sources/XcodeGraph/Models/Settings.swift
+++ b/Sources/XcodeGraph/Models/Settings.swift
@@ -4,7 +4,7 @@ import Path
public typealias SettingsDictionary = [String: SettingValue]
public enum SettingValue: ExpressibleByStringInterpolation, ExpressibleByStringLiteral, ExpressibleByArrayLiteral, Equatable,
- Codable
+ Codable, Sendable
{
case string(String)
case array([String])
@@ -73,7 +73,7 @@ extension SettingsDictionary {
}
}
-public struct Configuration: Equatable, Codable {
+public struct Configuration: Equatable, Codable, Sendable {
// MARK: - Attributes
public var settings: SettingsDictionary
@@ -98,7 +98,7 @@ public struct Configuration: Equatable, Codable {
}
}
-public enum DefaultSettings: Codable, Equatable {
+public enum DefaultSettings: Codable, Equatable, Sendable {
case recommended(excluding: Set = [])
case essential(excluding: Set = [])
case none
@@ -114,7 +114,7 @@ extension DefaultSettings {
}
}
-public struct Settings: Equatable, Codable {
+public struct Settings: Equatable, Codable, Sendable {
public static let `default` = Settings(
configurations: [.release: nil, .debug: nil],
defaultSettings: .recommended
diff --git a/Sources/XcodeGraph/Models/SimulatedLocation.swift b/Sources/XcodeGraph/Models/SimulatedLocation.swift
index 0840c6ed..d275b46f 100644
--- a/Sources/XcodeGraph/Models/SimulatedLocation.swift
+++ b/Sources/XcodeGraph/Models/SimulatedLocation.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public enum SimulatedLocation {
+public enum SimulatedLocation: Sendable {
case gpxFile(AbsolutePath)
case reference(String)
diff --git a/Sources/XcodeGraph/Models/SourceFile.swift b/Sources/XcodeGraph/Models/SourceFile.swift
index 9defe25d..015ffce2 100644
--- a/Sources/XcodeGraph/Models/SourceFile.swift
+++ b/Sources/XcodeGraph/Models/SourceFile.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// A type that represents a source file.
-public struct SourceFile: ExpressibleByStringLiteral, Equatable, Codable {
+public struct SourceFile: ExpressibleByStringLiteral, Equatable, Codable, Sendable {
/// Source file path.
public var path: AbsolutePath
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 746bff74..bca9006c 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
// swiftlint:disable:next type_body_length
-public struct Target: Equatable, Hashable, Comparable, Codable {
+public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
// MARK: - Static
// Note: The `.docc` file type is technically both a valid source extension and folder extension
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index 9c1c20a2..817f5db1 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -1,13 +1,13 @@
import Foundation
import Path
-public enum LinkingStatus: String, Hashable, Codable {
+public enum LinkingStatus: String, Hashable, Codable, Sendable {
case required
case optional
}
-public enum TargetDependency: Equatable, Hashable, Codable {
- public enum PackageType: String, Equatable, Hashable, Codable {
+public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
+ public enum PackageType: String, Equatable, Hashable, Codable, Sendable {
case runtime
case plugin
case macro
diff --git a/Sources/XcodeGraph/Models/TargetReference.swift b/Sources/XcodeGraph/Models/TargetReference.swift
index 5ff8b4d7..0289e749 100644
--- a/Sources/XcodeGraph/Models/TargetReference.swift
+++ b/Sources/XcodeGraph/Models/TargetReference.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct TargetReference: Hashable, Codable {
+public struct TargetReference: Hashable, Codable, Sendable {
public var projectPath: AbsolutePath
public var name: String
diff --git a/Sources/XcodeGraph/Models/TargetScript.swift b/Sources/XcodeGraph/Models/TargetScript.swift
index a9cf89a5..378f095b 100644
--- a/Sources/XcodeGraph/Models/TargetScript.swift
+++ b/Sources/XcodeGraph/Models/TargetScript.swift
@@ -2,12 +2,12 @@ import Foundation
import Path
/// It represents a target script build phase
-public struct TargetScript: Equatable, Codable {
+public struct TargetScript: Equatable, Codable, Sendable {
/// Order when the script gets executed.
///
/// - pre: Before the sources and resources build phase.
/// - post: After the sources and resources build phase.
- public enum Order: String, Equatable, Codable {
+ public enum Order: String, Equatable, Codable, Sendable {
case pre
case post
}
@@ -17,7 +17,7 @@ public struct TargetScript: Equatable, Codable {
/// - tool: Executes the tool with the given arguments. Tuist will look up the tool on the environment's PATH.
/// - scriptPath: Executes the file at the path with the given arguments.
/// - embedded: Executes the embedded script. This should be a short command.
- public enum Script: Equatable, Codable {
+ public enum Script: Equatable, Codable, Sendable {
case tool(path: String, args: [String] = [])
case scriptPath(path: AbsolutePath, args: [String] = [])
case embedded(String)
diff --git a/Sources/XcodeGraph/Models/TestAction.swift b/Sources/XcodeGraph/Models/TestAction.swift
index 8575a4fb..2d5e2878 100644
--- a/Sources/XcodeGraph/Models/TestAction.swift
+++ b/Sources/XcodeGraph/Models/TestAction.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct TestAction: Equatable, Codable {
+public struct TestAction: Equatable, Codable, Sendable {
// MARK: - Attributes
public var testPlans: [TestPlan]?
diff --git a/Sources/XcodeGraph/Models/TestPlan.swift b/Sources/XcodeGraph/Models/TestPlan.swift
index 341603ae..6770089c 100644
--- a/Sources/XcodeGraph/Models/TestPlan.swift
+++ b/Sources/XcodeGraph/Models/TestPlan.swift
@@ -1,7 +1,7 @@
import Foundation
import Path
-public struct TestPlan: Hashable, Codable {
+public struct TestPlan: Hashable, Codable, Sendable {
public let name: String
public let path: AbsolutePath
public let testTargets: [TestableTarget]
diff --git a/Sources/XcodeGraph/Models/TestableTarget.swift b/Sources/XcodeGraph/Models/TestableTarget.swift
index c6522ab5..7d800202 100644
--- a/Sources/XcodeGraph/Models/TestableTarget.swift
+++ b/Sources/XcodeGraph/Models/TestableTarget.swift
@@ -2,7 +2,7 @@ import Foundation
import Path
/// Testable target describe target and tests information.
-public struct TestableTarget: Equatable, Hashable, Codable {
+public struct TestableTarget: Equatable, Hashable, Codable, Sendable {
/// The target name and its project path.
public let target: TargetReference
/// Skip test target from TestAction.
diff --git a/Sources/XcodeGraph/Models/TestingOptions.swift b/Sources/XcodeGraph/Models/TestingOptions.swift
index 675db447..8f032606 100644
--- a/Sources/XcodeGraph/Models/TestingOptions.swift
+++ b/Sources/XcodeGraph/Models/TestingOptions.swift
@@ -1,4 +1,4 @@
-public struct TestingOptions: OptionSet, Codable, Hashable {
+public struct TestingOptions: Sendable, OptionSet, Codable, Hashable {
public let rawValue: Int
public init(rawValue: Int) {
From 21000c6be81e80aeafd6d2cd42494d4a38c9e53f Mon Sep 17 00:00:00 2001
From: waltflanagan
Date: Mon, 16 Sep 2024 16:39:15 +0000
Subject: [PATCH 020/914] [Release] XcodeGraph 0.11.5
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f0d576c..fe12822b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.5] - 2024-09-16
+### Details
+#### Chore
+- Sendability conformance by @waltflanagan in [#44](https://github.com/tuist/XcodeGraph/pull/44)
+
+## New Contributors
+* @waltflanagan made their first contribution in [#44](https://github.com/tuist/XcodeGraph/pull/44)
## [0.11.4] - 2024-09-04
### Details
#### Chore
@@ -84,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.5]: https://github.com/tuist/XcodeGraph/compare/0.11.4..0.11.5
[0.11.4]: https://github.com/tuist/XcodeGraph/compare/0.11.3..0.11.4
[0.11.3]: https://github.com/tuist/XcodeGraph/compare/0.11.2..0.11.3
[0.11.2]: https://github.com/tuist/XcodeGraph/compare/0.11.1..0.11.2
From f58c747dff2a7e7f1be73d32e16f4f62e17e26e4 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 19 Sep 2024 16:58:41 +0000
Subject: [PATCH 021/914] chore(deps): update dependency tuist to v4.27.0 (#46)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index d9bcdaff..40bd3cc0 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.26.0"
+tuist = "4.27.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 5e0908bcb11404219338a7ecc2184993b005baeb Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 19 Sep 2024 16:59:01 +0000
Subject: [PATCH 022/914] [Release] XcodeGraph 0.11.6
---
CHANGELOG.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe12822b..5ca4ea43 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,13 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.6] - 2024-09-19
+### Details
+#### Chore
+- Update dependency tuist to v4.27.0 by @renovate[bot] in [#46](https://github.com/tuist/XcodeGraph/pull/46)
+
## [0.11.5] - 2024-09-16
### Details
#### Chore
- Sendability conformance by @waltflanagan in [#44](https://github.com/tuist/XcodeGraph/pull/44)
-## New Contributors
-* @waltflanagan made their first contribution in [#44](https://github.com/tuist/XcodeGraph/pull/44)
## [0.11.4] - 2024-09-04
### Details
#### Chore
@@ -91,6 +94,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.6]: https://github.com/tuist/XcodeGraph/compare/0.11.5..0.11.6
[0.11.5]: https://github.com/tuist/XcodeGraph/compare/0.11.4..0.11.5
[0.11.4]: https://github.com/tuist/XcodeGraph/compare/0.11.3..0.11.4
[0.11.3]: https://github.com/tuist/XcodeGraph/compare/0.11.2..0.11.3
From b12239d3f1b977cd74ec08fee8860f449edc5aac Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 19 Sep 2024 21:29:41 +0000
Subject: [PATCH 023/914] chore(deps): update dependency tuist/path to from:
"0.3.6" (#47)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index 8ca337f9..b6c42683 100644
--- a/Package.swift
+++ b/Package.swift
@@ -26,7 +26,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.5")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.6")),
],
targets: targets
)
From 85f928ca72bee8449334f344ca10d675b0bac97d Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 19 Sep 2024 21:30:00 +0000
Subject: [PATCH 024/914] [Release] XcodeGraph 0.11.7
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ca4ea43..53be9398 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.11.7] - 2024-09-19
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.6" by @renovate[bot] in [#47](https://github.com/tuist/XcodeGraph/pull/47)
+
## [0.11.6] - 2024-09-19
### Details
#### Chore
@@ -94,6 +99,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.11.7]: https://github.com/tuist/XcodeGraph/compare/0.11.6..0.11.7
[0.11.6]: https://github.com/tuist/XcodeGraph/compare/0.11.5..0.11.6
[0.11.5]: https://github.com/tuist/XcodeGraph/compare/0.11.4..0.11.5
[0.11.4]: https://github.com/tuist/XcodeGraph/compare/0.11.3..0.11.4
From cd9c8d79018d6d597bc111c53de4b6cdcd680f66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabriel=20Li=C3=A9vano?=
<811827+rgnns@users.noreply.github.com>
Date: Fri, 20 Sep 2024 03:38:33 -0700
Subject: [PATCH 025/914] feat: add linking status to target and project (#45)
---
Package.resolved | 4 ++--
Sources/XcodeGraph/Graph/GraphDependency.swift | 8 ++++----
Sources/XcodeGraph/Models/TargetDependency.swift | 16 ++++++++--------
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/Package.resolved b/Package.resolved
index 5d7f2870..e1716dab 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/tuist/Path.git",
"state" : {
- "revision" : "4490da629937fc3994f72dd787dcc3f50d6973fe",
- "version" : "0.3.0"
+ "revision" : "e137d6db7a31decd77077613f5c59c745102603e",
+ "version" : "0.3.5"
}
}
],
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 19812126..b0e7c2f7 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -82,7 +82,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
case packageProduct(path: AbsolutePath, product: String, type: PackageProductType)
/// A dependency that represents a target that is defined in the project at the given path.
- case target(name: String, path: AbsolutePath)
+ case target(name: String, path: AbsolutePath, status: LinkingStatus = .required)
/// A dependency that represents an SDK
case sdk(name: String, path: AbsolutePath, status: LinkingStatus, source: SDKSource)
@@ -107,7 +107,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
hasher.combine(path)
hasher.combine(product)
hasher.combine(isPlugin)
- case let .target(name, path):
+ case let .target(name, path, _):
hasher.combine("target")
hasher.combine(name)
hasher.combine(path)
@@ -225,7 +225,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public var targetDependency: (name: String, path: AbsolutePath)? {
switch self {
- case let .target(name: name, path: path):
+ case let .target(name: name, path: path, _):
return (name, path)
default:
return nil
@@ -269,7 +269,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
return path.basename
case let .packageProduct(_, product, _):
return product
- case let .target(name, _):
+ case let .target(name, _, _):
return name
case let .sdk(name, _, _, _):
return name
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index 817f5db1..c2fa9f0c 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -13,8 +13,8 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
case macro
}
- case target(name: String, condition: PlatformCondition? = nil)
- case project(target: String, path: AbsolutePath, condition: PlatformCondition? = nil)
+ case target(name: String, status: LinkingStatus = .required, condition: PlatformCondition? = nil)
+ case project(target: String, path: AbsolutePath, status: LinkingStatus = .required, condition: PlatformCondition? = nil)
case framework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
case xcframework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
case library(
@@ -29,9 +29,9 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
public var condition: PlatformCondition? {
switch self {
- case .target(name: _, condition: let condition):
+ case .target(name: _, status: _, condition: let condition):
condition
- case .project(target: _, path: _, condition: let condition):
+ case .project(target: _, path: _, status: _, condition: let condition):
condition
case .framework(path: _, status: _, condition: let condition):
condition
@@ -49,10 +49,10 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
public func withCondition(_ condition: PlatformCondition?) -> TargetDependency {
switch self {
- case .target(name: let name, condition: _):
- return .target(name: name, condition: condition)
- case .project(target: let target, path: let path, condition: _):
- return .project(target: target, path: path, condition: condition)
+ case .target(name: let name, status: let status, condition: _):
+ return .target(name: name, status: status, condition: condition)
+ case .project(target: let target, path: let path, status: let status, condition: _):
+ return .project(target: target, path: path, status: status, condition: condition)
case .framework(path: let path, status: let status, condition: _):
return .framework(path: path, status: status, condition: condition)
case .xcframework(path: let path, status: let status, condition: _):
From 9235c3335f9a3809f8a47b6def05e2f54dce0a10 Mon Sep 17 00:00:00 2001
From: danieleformichelli
Date: Fri, 20 Sep 2024 10:38:49 +0000
Subject: [PATCH 026/914] [Release] XcodeGraph 0.12.0
---
CHANGELOG.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53be9398..f7f57e64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.0] - 2024-09-20
+### Details
+#### Feat
+- Add linking status to target and project by @rgnns in [#45](https://github.com/tuist/XcodeGraph/pull/45)
+
## [0.11.7] - 2024-09-19
### Details
#### Chore
@@ -66,8 +71,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Refactor
- Unify status enums into one linking status by @rgnns in [#32](https://github.com/tuist/XcodeGraph/pull/32)
-## New Contributors
-* @rgnns made their first contribution in [#32](https://github.com/tuist/XcodeGraph/pull/32)
## [0.10.1] - 2024-08-08
### Details
#### Docs
@@ -99,6 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.0]: https://github.com/tuist/XcodeGraph/compare/0.11.7..0.12.0
[0.11.7]: https://github.com/tuist/XcodeGraph/compare/0.11.6..0.11.7
[0.11.6]: https://github.com/tuist/XcodeGraph/compare/0.11.5..0.11.6
[0.11.5]: https://github.com/tuist/XcodeGraph/compare/0.11.4..0.11.5
From e8c07d00f08ec70e165db1b40353f9a1ab738e29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Mon, 23 Sep 2024 18:03:15 +0200
Subject: [PATCH 027/914] fix: missing baseDebug in Settings.with (#49)
---
Package.resolved | 4 ++--
Sources/XcodeGraph/Models/Settings.swift | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Package.resolved b/Package.resolved
index e1716dab..7d07ba17 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/tuist/Path.git",
"state" : {
- "revision" : "e137d6db7a31decd77077613f5c59c745102603e",
- "version" : "0.3.5"
+ "revision" : "bee1e910422f5c817b58dc593c50d747c4637b9c",
+ "version" : "0.3.6"
}
}
],
diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift
index e1e87942..48616f3c 100644
--- a/Sources/XcodeGraph/Models/Settings.swift
+++ b/Sources/XcodeGraph/Models/Settings.swift
@@ -145,6 +145,7 @@ public struct Settings: Equatable, Codable, Sendable {
public func with(base: SettingsDictionary) -> Settings {
.init(
base: base,
+ baseDebug: baseDebug,
configurations: configurations,
defaultSettings: defaultSettings
)
From 12462c8fff97fc5efb8476ef4625f434acd5058f Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Mon, 23 Sep 2024 16:03:28 +0000
Subject: [PATCH 028/914] [Release] XcodeGraph 0.12.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f7f57e64..5277e0f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.1] - 2024-09-23
+### Details
+#### Fix
+- Missing baseDebug in Settings.with by @fortmarek in [#49](https://github.com/tuist/XcodeGraph/pull/49)
+
## [0.12.0] - 2024-09-20
### Details
#### Feat
@@ -102,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.1]: https://github.com/tuist/XcodeGraph/compare/0.12.0..0.12.1
[0.12.0]: https://github.com/tuist/XcodeGraph/compare/0.11.7..0.12.0
[0.11.7]: https://github.com/tuist/XcodeGraph/compare/0.11.6..0.11.7
[0.11.6]: https://github.com/tuist/XcodeGraph/compare/0.11.5..0.11.6
From 2d98d730ade3762a71d27954902fb11b0f7d7d91 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 25 Sep 2024 21:04:17 +0000
Subject: [PATCH 029/914] chore(deps): update dependency tuist to v4.28.0 (#50)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 40bd3cc0..1d05621a 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.27.0"
+tuist = "4.28.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From e76f44d844e1e3c1f7f0a8828c3541169345fa6d Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 25 Sep 2024 21:04:41 +0000
Subject: [PATCH 030/914] [Release] XcodeGraph 0.12.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5277e0f4..02667650 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.2] - 2024-09-25
+### Details
+#### Chore
+- Update dependency tuist to v4.28.0 by @renovate[bot] in [#50](https://github.com/tuist/XcodeGraph/pull/50)
+
## [0.12.1] - 2024-09-23
### Details
#### Fix
@@ -107,6 +112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.2]: https://github.com/tuist/XcodeGraph/compare/0.12.1..0.12.2
[0.12.1]: https://github.com/tuist/XcodeGraph/compare/0.12.0..0.12.1
[0.12.0]: https://github.com/tuist/XcodeGraph/compare/0.11.7..0.12.0
[0.11.7]: https://github.com/tuist/XcodeGraph/compare/0.11.6..0.11.7
From 37656d212ed2ed1075534cd5e2bf8b823236490b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 26 Sep 2024 05:07:07 +0000
Subject: [PATCH 031/914] chore(deps): update dependency tuist/path to from:
"0.3.7" (#51)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index b6c42683..6a087f79 100644
--- a/Package.swift
+++ b/Package.swift
@@ -26,7 +26,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.6")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.7")),
],
targets: targets
)
From c9dcf4dae5c7972f74706e2f7344c19e00d0a40c Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 26 Sep 2024 05:07:20 +0000
Subject: [PATCH 032/914] [Release] XcodeGraph 0.12.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02667650..794cae3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.3] - 2024-09-26
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.7" by @renovate[bot] in [#51](https://github.com/tuist/XcodeGraph/pull/51)
+
## [0.12.2] - 2024-09-25
### Details
#### Chore
@@ -112,6 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.3]: https://github.com/tuist/XcodeGraph/compare/0.12.2..0.12.3
[0.12.2]: https://github.com/tuist/XcodeGraph/compare/0.12.1..0.12.2
[0.12.1]: https://github.com/tuist/XcodeGraph/compare/0.12.0..0.12.1
[0.12.0]: https://github.com/tuist/XcodeGraph/compare/0.11.7..0.12.0
From 131bf156ae929dd019bfa9e15168975e4dc569fb Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 26 Sep 2024 16:04:17 +0000
Subject: [PATCH 033/914] chore(deps): update dependency tuist to v4.28.1 (#52)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 1d05621a..c7c16bf8 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.28.0"
+tuist = "4.28.1"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 008e2dd528c912af302f332edfaef16912d882f0 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 26 Sep 2024 16:04:33 +0000
Subject: [PATCH 034/914] [Release] XcodeGraph 0.12.4
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 794cae3b..df14eb22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.4] - 2024-09-26
+### Details
+#### Chore
+- Update dependency tuist to v4.28.1 by @renovate[bot] in [#52](https://github.com/tuist/XcodeGraph/pull/52)
+
## [0.12.3] - 2024-09-26
### Details
#### Chore
@@ -117,6 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.4]: https://github.com/tuist/XcodeGraph/compare/0.12.3..0.12.4
[0.12.3]: https://github.com/tuist/XcodeGraph/compare/0.12.2..0.12.3
[0.12.2]: https://github.com/tuist/XcodeGraph/compare/0.12.1..0.12.2
[0.12.1]: https://github.com/tuist/XcodeGraph/compare/0.12.0..0.12.1
From fed522490b09ce67171f3917fdaa638bc66bd742 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 26 Sep 2024 22:18:03 +0000
Subject: [PATCH 035/914] chore(deps): update dependency tuist/path to from:
"0.3.8" (#53)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index 6a087f79..e63cef20 100644
--- a/Package.swift
+++ b/Package.swift
@@ -26,7 +26,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.7")),
+ .package(url: "https://github.com/tuist/Path.git", .upToNextMajor(from: "0.3.8")),
],
targets: targets
)
From 11ce21a0ad35497ba943687ab75c3bd305dcba8a Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 26 Sep 2024 22:18:18 +0000
Subject: [PATCH 036/914] [Release] XcodeGraph 0.12.5
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df14eb22..26c685d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.5] - 2024-09-26
+### Details
+#### Chore
+- Update dependency tuist/path to from: "0.3.8" by @renovate[bot] in [#53](https://github.com/tuist/XcodeGraph/pull/53)
+
## [0.12.4] - 2024-09-26
### Details
#### Chore
@@ -122,6 +127,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.5]: https://github.com/tuist/XcodeGraph/compare/0.12.4..0.12.5
[0.12.4]: https://github.com/tuist/XcodeGraph/compare/0.12.3..0.12.4
[0.12.3]: https://github.com/tuist/XcodeGraph/compare/0.12.2..0.12.3
[0.12.2]: https://github.com/tuist/XcodeGraph/compare/0.12.1..0.12.2
From b32446048a456d408d6246a4bbf4933fde4c9501 Mon Sep 17 00:00:00 2001
From: Giovanni Filaferro
Date: Fri, 4 Oct 2024 15:00:20 +0200
Subject: [PATCH 037/914] fix: add c++ to valid source extensions (#55)
---
Sources/XcodeGraph/Models/Target.swift | 2 +-
Tests/XcodeGraphTests/Models/TargetTests.swift | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index bca9006c..2257fc9d 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -11,7 +11,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
"playground", "rcproject", "mlpackage", "docc", "xcmappingmodel",
]
public static let validSourceExtensions: [String] = [
- "m", "swift", "mm", "cpp", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel",
+ "m", "swift", "mm", "cpp", "c++", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel",
]
public static let validFolderExtensions: [String] = [
"framework", "bundle", "app", "xcassets", "appiconset", "scnassets",
diff --git a/Tests/XcodeGraphTests/Models/TargetTests.swift b/Tests/XcodeGraphTests/Models/TargetTests.swift
index 4b9c9d08..fb6a9d2c 100644
--- a/Tests/XcodeGraphTests/Models/TargetTests.swift
+++ b/Tests/XcodeGraphTests/Models/TargetTests.swift
@@ -20,6 +20,7 @@ final class TargetTests: XCTestCase {
"swift",
"mm",
"cpp",
+ "c++",
"cc",
"c",
"d",
From 2d5ea1a2ee50725c0a5208391f589461d46ae6ad Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 4 Oct 2024 15:00:51 +0200
Subject: [PATCH 038/914] chore(deps): update dependency tuist to v4.28.2 (#54)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index c7c16bf8..1e89393c 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.28.1"
+tuist = "4.28.2"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 344dd27c53a4af781a7f5bf6273733e4f5bf5730 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Mon, 7 Oct 2024 11:14:26 +0200
Subject: [PATCH 039/914] docs: add fila95 as a contributor for code (#56)
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
---------
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 3d0512d5..c710b92e 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -42,6 +42,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "fila95",
+ "name": "Giovanni Filaferro",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7265334?v=4",
+ "profile": "https://github.com/fila95",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index a9e246f9..5f75211a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ፨ XcodeGraph
-[](#contributors-)
+[](#contributors-)
XcodeGraph is a Swift Package that contains data structures to model an Xcode projects graph.
@@ -34,6 +34,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Binlogo 💻 |
 Filip Racki 💻 |
 Gabriel Liévano 💻 |
+  Giovanni Filaferro 💻 |
From 5ef8690d436ec3ab9e016b6d31bb9720d0a76bad Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Mon, 7 Oct 2024 12:22:47 +0000
Subject: [PATCH 040/914] [Release] XcodeGraph 0.12.6
---
CHANGELOG.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26c685d8..87b55646 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.6] - 2024-10-07
+### Details
+#### Chore
+- Update dependency tuist to v4.28.2 by @renovate[bot] in [#54](https://github.com/tuist/XcodeGraph/pull/54)
+
+#### Docs
+- Add fila95 as a contributor for code by @allcontributors[bot] in [#56](https://github.com/tuist/XcodeGraph/pull/56)
+
+#### Fix
+- Add c++ to valid source extensions by @fila95 in [#55](https://github.com/tuist/XcodeGraph/pull/55)
+
+## New Contributors
+* @fila95 made their first contribution in [#55](https://github.com/tuist/XcodeGraph/pull/55)
## [0.12.5] - 2024-09-26
### Details
#### Chore
@@ -127,6 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.6]: https://github.com/tuist/XcodeGraph/compare/0.12.5..0.12.6
[0.12.5]: https://github.com/tuist/XcodeGraph/compare/0.12.4..0.12.5
[0.12.4]: https://github.com/tuist/XcodeGraph/compare/0.12.3..0.12.4
[0.12.3]: https://github.com/tuist/XcodeGraph/compare/0.12.2..0.12.3
From bc0e3c5ac623ce10b3ece80ba21d7c8d2e8d979d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 7 Oct 2024 22:17:35 +0000
Subject: [PATCH 041/914] chore(deps): update actions/checkout digest to
eef6144 (#58)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a4cb4d61..0e3a9ec0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
timeout-minutes: 15
if: "!startsWith(github.event.head_commit.message, '[Release]')"
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 0
- uses: jdx/mise-action@v2
From 7683c1d83decc41ecce83412efb01e3858804cae Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Mon, 7 Oct 2024 22:17:52 +0000
Subject: [PATCH 042/914] [Release] XcodeGraph 0.12.7
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87b55646..6ef45ef5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.7] - 2024-10-07
+### Details
+#### Chore
+- Update actions/checkout digest to eef6144 by @renovate[bot] in [#58](https://github.com/tuist/XcodeGraph/pull/58)
+
## [0.12.6] - 2024-10-07
### Details
#### Chore
@@ -140,6 +145,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.7]: https://github.com/tuist/XcodeGraph/compare/0.12.6..0.12.7
[0.12.6]: https://github.com/tuist/XcodeGraph/compare/0.12.5..0.12.6
[0.12.5]: https://github.com/tuist/XcodeGraph/compare/0.12.4..0.12.5
[0.12.4]: https://github.com/tuist/XcodeGraph/compare/0.12.3..0.12.4
From ce70060e120421bcfa13da0bfd5f7ac9b254f6c6 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 8 Oct 2024 16:04:02 +0200
Subject: [PATCH 043/914] chore(deps): update dependency tuist to v4.29.0 (#59)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 1e89393c..be4901e8 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.28.2"
+tuist = "4.29.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From f74bdea3d7c4061d0673de8206249298e6433b45 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Tue, 8 Oct 2024 14:04:18 +0000
Subject: [PATCH 044/914] [Release] XcodeGraph 0.12.8
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ef45ef5..6df48027 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.12.8] - 2024-10-08
+### Details
+#### Chore
+- Update dependency tuist to v4.29.0 by @renovate[bot] in [#59](https://github.com/tuist/XcodeGraph/pull/59)
+
## [0.12.7] - 2024-10-07
### Details
#### Chore
@@ -145,6 +150,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.12.8]: https://github.com/tuist/XcodeGraph/compare/0.12.7..0.12.8
[0.12.7]: https://github.com/tuist/XcodeGraph/compare/0.12.6..0.12.7
[0.12.6]: https://github.com/tuist/XcodeGraph/compare/0.12.5..0.12.6
[0.12.5]: https://github.com/tuist/XcodeGraph/compare/0.12.4..0.12.5
From 765dce0ea0cb14d75631cfbf1cf3dcecb0172f25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabriel=20Li=C3=A9vano?=
<811827+rgnns@users.noreply.github.com>
Date: Thu, 10 Oct 2024 02:55:39 -0700
Subject: [PATCH 045/914] feat: add .none linking status (#60)
---
Package.resolved | 4 ++--
Sources/XcodeGraph/Models/TargetDependency.swift | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Package.resolved b/Package.resolved
index 7d07ba17..c1487b6c 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/tuist/Path.git",
"state" : {
- "revision" : "bee1e910422f5c817b58dc593c50d747c4637b9c",
- "version" : "0.3.6"
+ "revision" : "7c74ac435e03a927c3a73134c48b61e60221abcb",
+ "version" : "0.3.8"
}
}
],
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index c2fa9f0c..f8fbf617 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -4,6 +4,7 @@ import Path
public enum LinkingStatus: String, Hashable, Codable, Sendable {
case required
case optional
+ case none
}
public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
From fbdc809330e4e5caa3b7bc4797196b305d7afd9c Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Thu, 10 Oct 2024 09:55:54 +0000
Subject: [PATCH 046/914] [Release] XcodeGraph 0.13.0
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6df48027..26111b15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.13.0] - 2024-10-10
+### Details
+#### Feat
+- Add .none linking status by @rgnns in [#60](https://github.com/tuist/XcodeGraph/pull/60)
+
## [0.12.8] - 2024-10-08
### Details
#### Chore
@@ -150,6 +155,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.13.0]: https://github.com/tuist/XcodeGraph/compare/0.12.8..0.13.0
[0.12.8]: https://github.com/tuist/XcodeGraph/compare/0.12.7..0.12.8
[0.12.7]: https://github.com/tuist/XcodeGraph/compare/0.12.6..0.12.7
[0.12.6]: https://github.com/tuist/XcodeGraph/compare/0.12.5..0.12.6
From 3912a2b42c35c395506bdb08bbae302eeb6597c0 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 10 Oct 2024 16:07:29 +0000
Subject: [PATCH 047/914] chore(deps): update dependency tuist to v4.29.1 (#62)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index be4901e8..0a5913d3 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.29.0"
+tuist = "4.29.1"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 4e22c6060b7c38d6e9396a393202eb37c35e3d28 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 10 Oct 2024 16:07:47 +0000
Subject: [PATCH 048/914] [Release] XcodeGraph 0.13.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26111b15..6260d1c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.13.1] - 2024-10-10
+### Details
+#### Chore
+- Update dependency tuist to v4.29.1 by @renovate[bot] in [#62](https://github.com/tuist/XcodeGraph/pull/62)
+
## [0.13.0] - 2024-10-10
### Details
#### Feat
@@ -155,6 +160,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.13.1]: https://github.com/tuist/XcodeGraph/compare/0.13.0..0.13.1
[0.13.0]: https://github.com/tuist/XcodeGraph/compare/0.12.8..0.13.0
[0.12.8]: https://github.com/tuist/XcodeGraph/compare/0.12.7..0.12.8
[0.12.7]: https://github.com/tuist/XcodeGraph/compare/0.12.6..0.12.7
From 57622cf00454796c58faf460147986c466b3226e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Pi=C3=B1era=20Buend=C3=ADa?=
<663605+pepicrft@users.noreply.github.com>
Date: Mon, 14 Oct 2024 18:36:30 +0200
Subject: [PATCH 049/914] Revert "Revert breaking changes (#25)" (#61)
This reverts commit 003abc24318f92b0c49ff46be419b5d6a1a697c9.
---
Sources/XcodeGraph/Graph/GraphDependency.swift | 6 ------
.../XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift | 7 -------
Sources/XcodeGraph/Models/ResourceSynthesizer.swift | 1 +
3 files changed, 1 insertion(+), 13 deletions(-)
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index b0e7c2f7..0dfbf083 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -5,7 +5,6 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public struct XCFramework: Hashable, CustomStringConvertible, Comparable, Codable {
public let path: AbsolutePath
public let infoPlist: XCFrameworkInfoPlist
- public let primaryBinaryPath: AbsolutePath
public let linking: BinaryLinking
public let mergeable: Bool
public let status: LinkingStatus
@@ -13,7 +12,6 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public init(
path: AbsolutePath,
infoPlist: XCFrameworkInfoPlist,
- primaryBinaryPath: AbsolutePath,
linking: BinaryLinking,
mergeable: Bool,
status: LinkingStatus,
@@ -21,7 +19,6 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
) {
self.path = path
self.infoPlist = infoPlist
- self.primaryBinaryPath = primaryBinaryPath
self.linking = linking
self.mergeable = mergeable
self.status = status
@@ -316,8 +313,6 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public static func testXCFramework(
path: AbsolutePath = AbsolutePath.root.appending(try! RelativePath(validating: "Test.xcframework")),
infoPlist: XCFrameworkInfoPlist = .test(),
- primaryBinaryPath: AbsolutePath = AbsolutePath.root
- .appending(try! RelativePath(validating: "Test.xcframework/Test")),
linking: BinaryLinking = .dynamic,
status: LinkingStatus = .required,
macroPath: AbsolutePath? = nil
@@ -326,7 +321,6 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
GraphDependency.XCFramework(
path: path,
infoPlist: infoPlist,
- primaryBinaryPath: primaryBinaryPath,
linking: linking,
mergeable: false,
status: status,
diff --git a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
index 5239900d..49cd5b70 100644
--- a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
@@ -5,7 +5,6 @@ import Path
public struct XCFrameworkMetadata: Equatable {
public var path: AbsolutePath
public var infoPlist: XCFrameworkInfoPlist
- public var primaryBinaryPath: AbsolutePath
public var linking: BinaryLinking
public var mergeable: Bool
public var status: LinkingStatus
@@ -14,7 +13,6 @@ public struct XCFrameworkMetadata: Equatable {
public init(
path: AbsolutePath,
infoPlist: XCFrameworkInfoPlist,
- primaryBinaryPath: AbsolutePath,
linking: BinaryLinking,
mergeable: Bool,
status: LinkingStatus,
@@ -22,7 +20,6 @@ public struct XCFrameworkMetadata: Equatable {
) {
self.path = path
self.infoPlist = infoPlist
- self.primaryBinaryPath = primaryBinaryPath
self.linking = linking
self.mergeable = mergeable
self.status = status
@@ -36,9 +33,6 @@ public struct XCFrameworkMetadata: Equatable {
// swiftlint:disable:next force_try
path: AbsolutePath = try! AbsolutePath(validating: "/XCFrameworks/XCFramework.xcframework"),
infoPlist: XCFrameworkInfoPlist = .test(),
- primaryBinaryPath: AbsolutePath =
- // swiftlint:disable:next force_try
- try! AbsolutePath(validating: "/XCFrameworks/XCFramework.xcframework/ios-arm64/XCFramework"),
linking: BinaryLinking = .dynamic,
mergeable: Bool = false,
status: LinkingStatus = .required,
@@ -47,7 +41,6 @@ public struct XCFrameworkMetadata: Equatable {
XCFrameworkMetadata(
path: path,
infoPlist: infoPlist,
- primaryBinaryPath: primaryBinaryPath,
linking: linking,
mergeable: mergeable,
status: status,
diff --git a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
index df5397b7..9b8880e2 100644
--- a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
+++ b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
@@ -15,6 +15,7 @@ public struct ResourceSynthesizer: Equatable, Hashable, Codable, Sendable {
public enum Parser: String, Equatable, Hashable, Codable, Sendable {
case strings
+ case stringsCatalog
case assets
case plists
case fonts
From 0fc8f3cbc97f24139103078073bbac6467c9354d Mon Sep 17 00:00:00 2001
From: Pedro
Date: Mon, 14 Oct 2024 18:51:58 +0200
Subject: [PATCH 050/914] feat: support platform for XCFrameworkInfoPlist
From b70c3bf22e8e2350f874a3290f7792e47867091d Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Mon, 14 Oct 2024 16:52:15 +0000
Subject: [PATCH 051/914] [Release] XcodeGraph 0.14.0
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6260d1c5..ed4b057a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.14.0] - 2024-10-14
+### Details
+#### Feat
+- Support platform for XCFrameworkInfoPlist by @pepicrft
+
## [0.13.1] - 2024-10-10
### Details
#### Chore
@@ -160,6 +165,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.14.0]: https://github.com/tuist/XcodeGraph/compare/0.13.1..0.14.0
[0.13.1]: https://github.com/tuist/XcodeGraph/compare/0.13.0..0.13.1
[0.13.0]: https://github.com/tuist/XcodeGraph/compare/0.12.8..0.13.0
[0.12.8]: https://github.com/tuist/XcodeGraph/compare/0.12.7..0.12.8
From 6e90bd34ed26b9f3afae71317ccb5b6d14e7c131 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 16 Oct 2024 12:35:47 +0000
Subject: [PATCH 052/914] chore(deps): update dependency tuist to v4.30.0 (#63)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 0a5913d3..5c793105 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.29.1"
+tuist = "4.30.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 3a5d156ced35fce28d2994b8a21e73189e8b977e Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 16 Oct 2024 12:36:03 +0000
Subject: [PATCH 053/914] [Release] XcodeGraph 0.14.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed4b057a..f0b3e2e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.14.1] - 2024-10-16
+### Details
+#### Chore
+- Update dependency tuist to v4.30.0 by @renovate[bot] in [#63](https://github.com/tuist/XcodeGraph/pull/63)
+
## [0.14.0] - 2024-10-14
### Details
#### Feat
@@ -165,6 +170,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.14.1]: https://github.com/tuist/XcodeGraph/compare/0.14.0..0.14.1
[0.14.0]: https://github.com/tuist/XcodeGraph/compare/0.13.1..0.14.0
[0.13.1]: https://github.com/tuist/XcodeGraph/compare/0.13.0..0.13.1
[0.13.0]: https://github.com/tuist/XcodeGraph/compare/0.12.8..0.13.0
From 24575bed0920c457bf1debcd4bf93db5a09c4c96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Wei=C3=9F?=
Date: Tue, 22 Oct 2024 10:39:49 +0200
Subject: [PATCH 054/914] feat: expose findImplicitDependencies property on
BuildAction (#64)
* feat: Expose buildImplicitDependencies poperty
* chore: Change property name to findImplicitDependencies
---
Sources/XcodeGraph/Models/BuildAction.swift | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Sources/XcodeGraph/Models/BuildAction.swift b/Sources/XcodeGraph/Models/BuildAction.swift
index ea782d38..93f32ef2 100644
--- a/Sources/XcodeGraph/Models/BuildAction.swift
+++ b/Sources/XcodeGraph/Models/BuildAction.swift
@@ -8,6 +8,7 @@ public struct BuildAction: Equatable, Codable, Sendable {
public var preActions: [ExecutionAction]
public var postActions: [ExecutionAction]
public var runPostActionsOnFailure: Bool
+ public var findImplicitDependencies: Bool
// MARK: - Init
@@ -15,12 +16,14 @@ public struct BuildAction: Equatable, Codable, Sendable {
targets: [TargetReference] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = [],
- runPostActionsOnFailure: Bool = false
+ runPostActionsOnFailure: Bool = false,
+ findImplicitDependencies: Bool = true
) {
self.targets = targets
self.preActions = preActions
self.postActions = postActions
self.runPostActionsOnFailure = runPostActionsOnFailure
+ self.findImplicitDependencies = findImplicitDependencies
}
}
From b8920950e66ec77723da5c17134e698f80ed3db8 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Tue, 22 Oct 2024 08:40:05 +0000
Subject: [PATCH 055/914] [Release] XcodeGraph 0.15.0
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0b3e2e5..22042486 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.15.0] - 2024-10-22
+### Details
+#### Feat
+- Expose findImplicitDependencies property on BuildAction by @alexanderwe in [#64](https://github.com/tuist/XcodeGraph/pull/64)
+
+## New Contributors
+* @alexanderwe made their first contribution in [#64](https://github.com/tuist/XcodeGraph/pull/64)
## [0.14.1] - 2024-10-16
### Details
#### Chore
@@ -170,6 +177,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.15.0]: https://github.com/tuist/XcodeGraph/compare/0.14.1..0.15.0
[0.14.1]: https://github.com/tuist/XcodeGraph/compare/0.14.0..0.14.1
[0.14.0]: https://github.com/tuist/XcodeGraph/compare/0.13.1..0.14.0
[0.13.1]: https://github.com/tuist/XcodeGraph/compare/0.13.0..0.13.1
From 78fb8cb194982864f5e508686e6eb7afc29daade Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Oct 2024 14:55:57 +0200
Subject: [PATCH 056/914] chore(deps): update dependency tuist to v4.31.0 (#65)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 5c793105..35165038 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.30.0"
+tuist = "4.31.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From ac13f3f4046bc02a472ba5167067fb13dc9ad70a Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Wed, 23 Oct 2024 12:56:15 +0000
Subject: [PATCH 057/914] [Release] XcodeGraph 0.15.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22042486..a76be853 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.15.1] - 2024-10-23
+### Details
+#### Chore
+- Update dependency tuist to v4.31.0 by @renovate[bot] in [#65](https://github.com/tuist/XcodeGraph/pull/65)
+
## [0.15.0] - 2024-10-22
### Details
#### Feat
@@ -177,6 +182,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.15.1]: https://github.com/tuist/XcodeGraph/compare/0.15.0..0.15.1
[0.15.0]: https://github.com/tuist/XcodeGraph/compare/0.14.1..0.15.0
[0.14.1]: https://github.com/tuist/XcodeGraph/compare/0.14.0..0.14.1
[0.14.0]: https://github.com/tuist/XcodeGraph/compare/0.13.1..0.14.0
From f17574ea8e4d5b2e05782f84e0da848297c11603 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Mon, 28 Oct 2024 09:02:26 +0100
Subject: [PATCH 058/914] feat: add swiftModules and moduleMaps to
XCFrameworkMetadata (#67)
* Add swiftModules and moduleMaps to XCFrameworkMetadata
* Add extra metadata to GraphDependency.xcframework
* Add mergeable to GraphDependency.testXCFramework
---
.../XcodeGraph/Graph/GraphDependency.swift | 19 +++++++++++++++----
.../Models/Metadata/XCFrameworkMetadata.swift | 18 +++++++++++++++---
2 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 0dfbf083..0ca71bde 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -8,6 +8,8 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
public let linking: BinaryLinking
public let mergeable: Bool
public let status: LinkingStatus
+ public let swiftModules: [AbsolutePath]
+ public let moduleMaps: [AbsolutePath]
public init(
path: AbsolutePath,
@@ -15,13 +17,17 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
linking: BinaryLinking,
mergeable: Bool,
status: LinkingStatus,
- macroPath _: AbsolutePath?
+ macroPath _: AbsolutePath?,
+ swiftModules: [AbsolutePath],
+ moduleMaps: [AbsolutePath]
) {
self.path = path
self.infoPlist = infoPlist
self.linking = linking
self.mergeable = mergeable
self.status = status
+ self.swiftModules = swiftModules
+ self.moduleMaps = moduleMaps
}
public var description: String {
@@ -314,17 +320,22 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
path: AbsolutePath = AbsolutePath.root.appending(try! RelativePath(validating: "Test.xcframework")),
infoPlist: XCFrameworkInfoPlist = .test(),
linking: BinaryLinking = .dynamic,
+ mergeable: Bool = false,
status: LinkingStatus = .required,
- macroPath: AbsolutePath? = nil
+ macroPath: AbsolutePath? = nil,
+ swiftModules: [AbsolutePath] = [],
+ moduleMaps: [AbsolutePath] = []
) -> GraphDependency {
.xcframework(
GraphDependency.XCFramework(
path: path,
infoPlist: infoPlist,
linking: linking,
- mergeable: false,
+ mergeable: mergeable,
status: status,
- macroPath: macroPath
+ macroPath: macroPath,
+ swiftModules: swiftModules,
+ moduleMaps: moduleMaps
)
)
}
diff --git a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
index 49cd5b70..72e24715 100644
--- a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
@@ -9,6 +9,10 @@ public struct XCFrameworkMetadata: Equatable {
public var mergeable: Bool
public var status: LinkingStatus
public var macroPath: AbsolutePath?
+ /// All `.swiftmodule` files present in the `.xcframework`
+ public var swiftModules: [AbsolutePath]
+ /// All `.modulemap` files present in the `.xcframework`
+ public var moduleMaps: [AbsolutePath]
public init(
path: AbsolutePath,
@@ -16,7 +20,9 @@ public struct XCFrameworkMetadata: Equatable {
linking: BinaryLinking,
mergeable: Bool,
status: LinkingStatus,
- macroPath: AbsolutePath?
+ macroPath: AbsolutePath?,
+ swiftModules: [AbsolutePath] = [],
+ moduleMaps: [AbsolutePath] = []
) {
self.path = path
self.infoPlist = infoPlist
@@ -24,6 +30,8 @@ public struct XCFrameworkMetadata: Equatable {
self.mergeable = mergeable
self.status = status
self.macroPath = macroPath
+ self.swiftModules = swiftModules
+ self.moduleMaps = moduleMaps
}
}
@@ -36,7 +44,9 @@ public struct XCFrameworkMetadata: Equatable {
linking: BinaryLinking = .dynamic,
mergeable: Bool = false,
status: LinkingStatus = .required,
- macroPath: AbsolutePath? = nil
+ macroPath: AbsolutePath? = nil,
+ swiftModules: [AbsolutePath] = [],
+ moduleMaps: [AbsolutePath] = []
) -> XCFrameworkMetadata {
XCFrameworkMetadata(
path: path,
@@ -44,7 +54,9 @@ public struct XCFrameworkMetadata: Equatable {
linking: linking,
mergeable: mergeable,
status: status,
- macroPath: macroPath
+ macroPath: macroPath,
+ swiftModules: swiftModules,
+ moduleMaps: moduleMaps
)
}
}
From 7be8001a127582859dc71528402e178c65e5f28e Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Mon, 28 Oct 2024 08:02:40 +0000
Subject: [PATCH 059/914] [Release] XcodeGraph 0.16.0
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a76be853..2741bc02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.16.0] - 2024-10-28
+### Details
+#### Feat
+- Add swiftModules and moduleMaps to XCFrameworkMetadata by @fortmarek in [#67](https://github.com/tuist/XcodeGraph/pull/67)
+
## [0.15.1] - 2024-10-23
### Details
#### Chore
@@ -182,6 +187,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.16.0]: https://github.com/tuist/XcodeGraph/compare/0.15.1..0.16.0
[0.15.1]: https://github.com/tuist/XcodeGraph/compare/0.15.0..0.15.1
[0.15.0]: https://github.com/tuist/XcodeGraph/compare/0.14.1..0.15.0
[0.14.1]: https://github.com/tuist/XcodeGraph/compare/0.14.0..0.14.1
From 738b5dca13c877fb8a6c0d8f122d1e4f2e8440fd Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 31 Oct 2024 17:58:53 +0100
Subject: [PATCH 060/914] chore(deps): update dependency tuist to v4.32.0 (#69)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 35165038..451789f2 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.31.0"
+tuist = "4.32.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From ee471187ec13ece4293b9c406f023146fb3a0506 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Thu, 31 Oct 2024 16:59:13 +0000
Subject: [PATCH 061/914] [Release] XcodeGraph 0.16.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2741bc02..ea10ac11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.16.1] - 2024-10-31
+### Details
+#### Chore
+- Update dependency tuist to v4.32.0 by @renovate[bot] in [#69](https://github.com/tuist/XcodeGraph/pull/69)
+
## [0.16.0] - 2024-10-28
### Details
#### Feat
@@ -187,6 +192,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.16.1]: https://github.com/tuist/XcodeGraph/compare/0.16.0..0.16.1
[0.16.0]: https://github.com/tuist/XcodeGraph/compare/0.15.1..0.16.0
[0.15.1]: https://github.com/tuist/XcodeGraph/compare/0.15.0..0.15.1
[0.15.0]: https://github.com/tuist/XcodeGraph/compare/0.14.1..0.15.0
From 882c3c4516e23d83afac3028ebbae4d13fde6644 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 3 Nov 2024 00:36:46 +0000
Subject: [PATCH 062/914] chore(deps): update dependency tuist to v4.32.1 (#70)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 451789f2..656ea2f8 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.32.0"
+tuist = "4.32.1"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 2f5f98a799b221c31c74f53b1ef8ff14c57b394a Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Sun, 3 Nov 2024 00:37:01 +0000
Subject: [PATCH 063/914] [Release] XcodeGraph 0.16.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea10ac11..ff22b076 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.16.2] - 2024-11-03
+### Details
+#### Chore
+- Update dependency tuist to v4.32.1 by @renovate[bot] in [#70](https://github.com/tuist/XcodeGraph/pull/70)
+
## [0.16.1] - 2024-10-31
### Details
#### Chore
@@ -192,6 +197,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.16.2]: https://github.com/tuist/XcodeGraph/compare/0.16.1..0.16.2
[0.16.1]: https://github.com/tuist/XcodeGraph/compare/0.16.0..0.16.1
[0.16.0]: https://github.com/tuist/XcodeGraph/compare/0.15.1..0.16.0
[0.15.1]: https://github.com/tuist/XcodeGraph/compare/0.15.0..0.15.1
From 43b6d6c01ad9ad2a28d7264241e698b35f3563eb Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 7 Nov 2024 19:47:21 +0000
Subject: [PATCH 064/914] chore(deps): update dependency tuist to v4.33.0 (#71)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 656ea2f8..5e48cc09 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.32.1"
+tuist = "4.33.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 119026753f06f8a055f20c3faf204dbb3f50b686 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 7 Nov 2024 19:47:39 +0000
Subject: [PATCH 065/914] [Release] XcodeGraph 0.16.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff22b076..2d8c2790 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.16.3] - 2024-11-07
+### Details
+#### Chore
+- Update dependency tuist to v4.33.0 by @renovate[bot] in [#71](https://github.com/tuist/XcodeGraph/pull/71)
+
## [0.16.2] - 2024-11-03
### Details
#### Chore
@@ -197,6 +202,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.16.3]: https://github.com/tuist/XcodeGraph/compare/0.16.2..0.16.3
[0.16.2]: https://github.com/tuist/XcodeGraph/compare/0.16.1..0.16.2
[0.16.1]: https://github.com/tuist/XcodeGraph/compare/0.16.0..0.16.1
[0.16.0]: https://github.com/tuist/XcodeGraph/compare/0.15.1..0.16.0
From 777099564063319693a36434ec3e75f6432a208e Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <12799+Garfeild@users.noreply.github.com>
Date: Mon, 11 Nov 2024 12:04:29 +0100
Subject: [PATCH 066/914] feat: add new types for embeddable SPM package (#57)
* feat: extend TargetDependency to allow embedded packages
Co-authored-by: Anastasiia Kuzenkova
Co-authored-by: Anton Kolchunov
* feat: extend GraphDependency to allow embedded packages
Co-authored-by: Anastasiia Kuzenkova
Co-authored-by: Anton Kolchunov
---------
Co-authored-by: Anastasiia Kuzenkova
Co-authored-by: Anton Kolchunov
---
Sources/XcodeGraph/Graph/GraphDependency.swift | 2 ++
Sources/XcodeGraph/Models/TargetDependency.swift | 1 +
2 files changed, 3 insertions(+)
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 0ca71bde..005cc1e8 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -45,6 +45,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
}
case runtime = "runtime package product"
+ case runtimeEmbedded = "runtime embedded package product"
case plugin = "plugin package product"
case macro = "macro package product"
@@ -218,6 +219,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
let .library(path: _, publicHeaders: _, linking: linking, architectures: _, swiftModuleMap: _):
return linking == .dynamic
case .bundle: return false
+ case .packageProduct(_, _, type: .runtimeEmbedded): return true
case .packageProduct: return false
case .target: return false
case .sdk: return false
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index f8fbf617..1c60d0bb 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -10,6 +10,7 @@ public enum LinkingStatus: String, Hashable, Codable, Sendable {
public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
public enum PackageType: String, Equatable, Hashable, Codable, Sendable {
case runtime
+ case runtimeEmbedded
case plugin
case macro
}
From 3c4ec4bf0aa97dadc80cba7f7ddec057e5566196 Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Mon, 11 Nov 2024 11:04:46 +0000
Subject: [PATCH 067/914] [Release] XcodeGraph 0.17.0
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d8c2790..75d90c8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.17.0] - 2024-11-11
+### Details
+#### Feat
+- Add new types for embeddable SPM package by @Garfeild in [#57](https://github.com/tuist/XcodeGraph/pull/57)
+
+## New Contributors
+* @Garfeild made their first contribution in [#57](https://github.com/tuist/XcodeGraph/pull/57)
## [0.16.3] - 2024-11-07
### Details
#### Chore
@@ -202,6 +209,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.17.0]: https://github.com/tuist/XcodeGraph/compare/0.16.3..0.17.0
[0.16.3]: https://github.com/tuist/XcodeGraph/compare/0.16.2..0.16.3
[0.16.2]: https://github.com/tuist/XcodeGraph/compare/0.16.1..0.16.2
[0.16.1]: https://github.com/tuist/XcodeGraph/compare/0.16.0..0.16.1
From 2a010545f7a2400c621ad7f1e7747ec4d5175c24 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Mon, 11 Nov 2024 12:10:59 +0100
Subject: [PATCH 068/914] docs: add Garfeild as a contributor for code (#72)
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
---------
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index c710b92e..81f8a59e 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -51,6 +51,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "Garfeild",
+ "name": "Anton Kolchunov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12799?v=4",
+ "profile": "https://github.com/Garfeild",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 5f75211a..c36d4eb0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ፨ XcodeGraph
-[](#contributors-)
+[](#contributors-)
XcodeGraph is a Swift Package that contains data structures to model an Xcode projects graph.
@@ -35,6 +35,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Filip Racki 💻 |
 Gabriel Liévano 💻 |
 Giovanni Filaferro 💻 |
+  Anton Kolchunov 💻 |
From 8ffc6cd3ddff6a414bae43e7dc1df6bf1b581233 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 13 Nov 2024 19:04:51 +0100
Subject: [PATCH 069/914] chore(deps): update dependency tuist to v4.34.0 (#73)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 5e48cc09..1efb1547 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.33.0"
+tuist = "4.34.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.4.0"
\ No newline at end of file
From 00d53a0bf5cc27efcdb8693bd19cce7cd74e538b Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Wed, 13 Nov 2024 18:05:14 +0000
Subject: [PATCH 070/914] [Release] XcodeGraph 0.17.1
---
CHANGELOG.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75d90c8a..ceb594e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.17.1] - 2024-11-13
+### Details
+#### Chore
+- Update dependency tuist to v4.34.0 by @renovate[bot] in [#73](https://github.com/tuist/XcodeGraph/pull/73)
+
+#### Docs
+- Add Garfeild as a contributor for code by @allcontributors[bot] in [#72](https://github.com/tuist/XcodeGraph/pull/72)
+
## [0.17.0] - 2024-11-11
### Details
#### Feat
@@ -209,6 +217,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+[0.17.1]: https://github.com/tuist/XcodeGraph/compare/0.17.0..0.17.1
[0.17.0]: https://github.com/tuist/XcodeGraph/compare/0.16.3..0.17.0
[0.16.3]: https://github.com/tuist/XcodeGraph/compare/0.16.2..0.16.3
[0.16.2]: https://github.com/tuist/XcodeGraph/compare/0.16.1..0.16.2
From 05720c1b7a1d3f9e87fe1acfb18c3eb69f3344d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Pi=C3=B1era=20Buend=C3=ADa?=
<663605+pepicrft@users.noreply.github.com>
Date: Thu, 14 Nov 2024 18:04:09 +0100
Subject: [PATCH 071/914] [feat] Supersede `Project.external` with
`Project.type` (#74)
* [feat] supersed Project.isExternal with Project.type
Which allows passing a hash for external projects to speed up the hashing of the graph.
* Fix CI
* Use macOS 15
* Use Xcode 14
---
.github/workflows/XcodeGraph.yml | 82 +++++-----
.github/workflows/cache.yml | 22 +--
.github/workflows/release.yml | 1 +
.mise.toml | 2 +-
Sources/XcodeGraph/Graph/GraphTarget.swift | 2 +-
Sources/XcodeGraph/Models/Project.swift | 181 +++++++++++++++++++++
6 files changed, 239 insertions(+), 51 deletions(-)
diff --git a/.github/workflows/XcodeGraph.yml b/.github/workflows/XcodeGraph.yml
index be439d79..4d2070db 100644
--- a/.github/workflows/XcodeGraph.yml
+++ b/.github/workflows/XcodeGraph.yml
@@ -2,16 +2,16 @@ name: XcodeGraph
on:
push:
branches:
- - '**'
+ - "**"
tags-ignore:
- - '**'
+ - "**"
paths:
- - '**/*.swift'
- - '.github/workflows/*.yml'
+ - "**/*.swift"
+ - ".github/workflows/*.yml"
pull_request:
paths:
- - '**/*.swift'
- - '.github/workflows/*.yml'
+ - "**/*.swift"
+ - ".github/workflows/*.yml"
concurrency:
group: XcodeGraph-${{ github.workflow }}-${{ github.ref }}
@@ -19,7 +19,7 @@ concurrency:
env:
MISE_EXPERIMENTAL: 1
- TUIST_CONFIG_CLOUD_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
+ TUIST_CONFIG_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
jobs:
spm_build:
@@ -28,45 +28,45 @@ jobs:
matrix:
os:
- ubuntu-22.04
- - macOS-13
+ - macos-14
swift-version:
- - '5.9'
+ - "5.9"
swift-compat-ver:
- - '5'
+ - "5"
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v4
- - uses: SwiftyLab/setup-swift@latest
- with:
- swift-version: ${{ matrix.swift-version }}
- # DEBUG mode
- - name: Build with debug mode.
- id: debug_build
- run: swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
- continue-on-error: true
- - name: Retry build with debug mode if necessary
- if: steps.debug_build.outcome == 'failure'
- run: |
- swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
- # RELEASE mode
- - name: Build with release mode.
- id: release_build
- run: swift build --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
- continue-on-error: true
- - name: Retry build with release mode if necessary
- if: steps.release_build.outcome == 'failure'
- run: |
- swift build --configuration release -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ - uses: actions/checkout@v4
+ - uses: SwiftyLab/setup-swift@latest
+ with:
+ swift-version: ${{ matrix.swift-version }}
+ # DEBUG mode
+ - name: Build with debug mode.
+ id: debug_build
+ run: swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ continue-on-error: true
+ - name: Retry build with debug mode if necessary
+ if: steps.debug_build.outcome == 'failure'
+ run: |
+ swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ # RELEASE mode
+ - name: Build with release mode.
+ id: release_build
+ run: swift build --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ continue-on-error: true
+ - name: Retry build with release mode if necessary
+ if: steps.release_build.outcome == 'failure'
+ run: |
+ swift build --configuration release -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
tuist_build:
name: Tuist Build
strategy:
matrix:
os:
- - macOS-13
+ - macos-14
swift-version:
- - '5.9'
+ - "5.9"
swift-compat-ver:
- - '5'
+ - "5"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -74,6 +74,8 @@ jobs:
with:
swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
+ with:
+ version: 2024.11.8
- name: Install dependencies
run: tuist install
- name: Build
@@ -83,11 +85,11 @@ jobs:
strategy:
matrix:
os:
- - macOS-13
+ - macos-14
swift-version:
- - '5.9'
+ - "5.9"
swift-compat-ver:
- - '5'
+ - "5"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -95,5 +97,7 @@ jobs:
with:
swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
+ with:
+ version: 2024.11.8
- name: Lint
- run: mise run lint
\ No newline at end of file
+ run: mise run lint
diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml
index 6117f145..ded627e3 100644
--- a/.github/workflows/cache.yml
+++ b/.github/workflows/cache.yml
@@ -4,12 +4,12 @@ on:
branches:
- main
paths:
- - '**/*.swift'
- - '.github/workflows/*.yml'
+ - "**/*.swift"
+ - ".github/workflows/*.yml"
pull_request:
paths:
- - '**/*.swift'
- - '.github/workflows/*.yml'
+ - "**/*.swift"
+ - ".github/workflows/*.yml"
concurrency:
group: Cache-${{ github.workflow }}-${{ github.ref }}
@@ -17,19 +17,19 @@ concurrency:
env:
MISE_EXPERIMENTAL: 1
- TUIST_CONFIG_CLOUD_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
-
+ TUIST_CONFIG_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
+
jobs:
warm:
name: Warm
strategy:
matrix:
os:
- - macOS-13
+ - macOS-14
swift-version:
- - '5.9'
+ - "5.9"
swift-compat-ver:
- - '5'
+ - "5"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -37,7 +37,9 @@ jobs:
with:
swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
+ with:
+ version: 2024.11.8
- name: Install dependencies
run: tuist install
- name: Build
- run: tuist cache
\ No newline at end of file
+ run: tuist cache
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0e3a9ec0..baa09515 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -29,6 +29,7 @@ jobs:
- uses: jdx/mise-action@v2
with:
experimental: true
+ version: 2024.11.8
- name: Check if there are releasable changes
id: is-releasable
run: |
diff --git a/.mise.toml b/.mise.toml
index 1efb1547..a00fc97f 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -2,4 +2,4 @@
tuist = "4.34.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
-"git-cliff" = "2.4.0"
\ No newline at end of file
+"git-cliff" = "2.6.1"
diff --git a/Sources/XcodeGraph/Graph/GraphTarget.swift b/Sources/XcodeGraph/Graph/GraphTarget.swift
index 71d25639..78f81765 100644
--- a/Sources/XcodeGraph/Graph/GraphTarget.swift
+++ b/Sources/XcodeGraph/Graph/GraphTarget.swift
@@ -37,7 +37,7 @@ public struct GraphTarget: Equatable, Hashable, Comparable, CustomDebugStringCon
public static func test(
path: AbsolutePath = .root,
target: Target = .test(),
- project: Project = .test()
+ project: Project = .test(type: .local)
) -> GraphTarget {
GraphTarget(
path: path,
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index 9ee70f60..fa00ffd9 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -1,6 +1,22 @@
import Foundation
import Path
+public enum ProjectType: Hashable, Equatable, Codable, CustomStringConvertible, Sendable {
+ /// A project is a local project managed by the user.
+ case local
+ /// A project is external (e.g. represents a Swift Package project). In those cases,
+ /// a hash can be provided, from example from the resolved ref of the represented package,
+ /// to skip the file-system-based hashing.
+ case external(hash: String? = nil)
+
+ public var description: String {
+ switch self {
+ case .local: "local project"
+ case let .external(hash): "external project"
+ }
+ }
+}
+
public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebugStringConvertible, Codable, Sendable {
// MARK: - Attributes
@@ -59,8 +75,12 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
public var lastUpgradeCheck: Version?
/// Indicates whether the project is imported through `Package.swift`.
+ @available(*, deprecated, renamed: "type", message: "Use project type instead")
public var isExternal: Bool
+ /// It represents the type of project.
+ public var type: ProjectType?
+
// MARK: - Init
/// Initializes the project with its attributes.
@@ -85,6 +105,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
/// - resourceSynthesizers: `ResourceSynthesizers` that will be applied on individual target's resources
/// - lastUpgradeCheck: The version in which a check happened related to recommended settings after updating Xcode.
/// - isExternal: Indicates whether the project is imported through `Package.swift`.
+ @available(*, deprecated, message: "Use the constructor that takes type: insead of isExternal:")
public init(
path: AbsolutePath,
sourceRootPath: AbsolutePath,
@@ -127,6 +148,74 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
self.isExternal = isExternal
}
+ /// Initializes the project with its attributes.
+ ///
+ /// - Parameters:
+ /// - path: Path to the folder that contains the project manifest.
+ /// - sourceRootPath: Path to the directory where the Xcode project will be generated.
+ /// - xcodeProjPath: Path to the Xcode project that will be generated.
+ /// - name: Project name.
+ /// - organizationName: Organization name.
+ /// - defaultKnownRegions: Default known regions.
+ /// - developmentRegion: Development region.
+ /// - options: Additional project options.
+ /// - settings: The settings to apply at the project level
+ /// - filesGroup: The root group to place project files within
+ /// - targets: The project targets
+ /// *(Those won't be included in any build phases)*
+ /// - packages: Project swift packages.
+ /// - schemes: Project schemes.
+ /// - ideTemplateMacros: IDE template macros that represent content of IDETemplateMacros.plist.
+ /// - additionalFiles: The additional files to include in the project
+ /// - resourceSynthesizers: `ResourceSynthesizers` that will be applied on individual target's resources
+ /// - lastUpgradeCheck: The version in which a check happened related to recommended settings after updating Xcode.
+ /// - type: The type of project, either local or external. This attribute supersedes `isExternal`
+ public init(
+ path: AbsolutePath,
+ sourceRootPath: AbsolutePath,
+ xcodeProjPath: AbsolutePath,
+ name: String,
+ organizationName: String?,
+ classPrefix: String?,
+ defaultKnownRegions: [String]?,
+ developmentRegion: String?,
+ options: Options,
+ settings: Settings,
+ filesGroup: ProjectGroup,
+ targets: [Target],
+ packages: [Package],
+ schemes: [Scheme],
+ ideTemplateMacros: IDETemplateMacros?,
+ additionalFiles: [FileElement],
+ resourceSynthesizers: [ResourceSynthesizer],
+ lastUpgradeCheck: Version?,
+ type: ProjectType
+ ) {
+ self.path = path
+ self.sourceRootPath = sourceRootPath
+ self.xcodeProjPath = xcodeProjPath
+ self.name = name
+ self.organizationName = organizationName
+ self.classPrefix = classPrefix
+ self.defaultKnownRegions = defaultKnownRegions
+ self.developmentRegion = developmentRegion
+ self.options = options
+ self.targets = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
+ self.packages = packages
+ self.schemes = schemes
+ self.settings = settings
+ self.filesGroup = filesGroup
+ self.ideTemplateMacros = ideTemplateMacros
+ self.additionalFiles = additionalFiles
+ self.resourceSynthesizers = resourceSynthesizers
+ self.lastUpgradeCheck = lastUpgradeCheck
+ isExternal = switch type {
+ case .external: true
+ case .local: false
+ }
+ self.type = type
+ }
+
// MARK: - CustomStringConvertible
public var description: String {
@@ -157,6 +246,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
#if DEBUG
extension Project {
+ @available(*, deprecated, message: "Use test that takes a project type instead of isExternal attribute.")
public static func test(
path: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
@@ -202,6 +292,52 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
)
}
+ public static func test(
+ path: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
+ sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
+ // swiftlint:disable:next force_try
+ xcodeProjPath: AbsolutePath = try! AbsolutePath(validating: "/Project/Project.xcodeproj"),
+ name: String = "Project",
+ organizationName: String? = nil,
+ classPrefix: String? = nil,
+ defaultKnownRegions: [String]? = nil,
+ developmentRegion: String? = nil,
+ options: Options = .test(automaticSchemesOptions: .disabled),
+ settings: Settings = Settings.test(),
+ filesGroup: ProjectGroup = .group(name: "Project"),
+ targets: [Target] = [Target.test()],
+ packages: [Package] = [],
+ schemes: [Scheme] = [],
+ ideTemplateMacros: IDETemplateMacros? = nil,
+ additionalFiles: [FileElement] = [],
+ resourceSynthesizers: [ResourceSynthesizer] = [],
+ lastUpgradeCheck: Version? = nil,
+ type: ProjectType
+ ) -> Project {
+ Project(
+ path: path,
+ sourceRootPath: sourceRootPath,
+ xcodeProjPath: xcodeProjPath,
+ name: name,
+ organizationName: organizationName,
+ classPrefix: classPrefix,
+ defaultKnownRegions: defaultKnownRegions,
+ developmentRegion: developmentRegion,
+ options: options,
+ settings: settings,
+ filesGroup: filesGroup,
+ targets: targets,
+ packages: packages,
+ schemes: schemes,
+ ideTemplateMacros: ideTemplateMacros,
+ additionalFiles: additionalFiles,
+ resourceSynthesizers: resourceSynthesizers,
+ lastUpgradeCheck: lastUpgradeCheck,
+ type: type
+ )
+ }
+
+ @available(*, deprecated, message: "Use empty that takes a project type instead of isExternal attribute.")
public static func empty(
path: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
@@ -246,6 +382,51 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
isExternal: isExternal
)
}
+
+ public static func empty(
+ path: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
+ sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
+ // swiftlint:disable:next force_try
+ xcodeProjPath: AbsolutePath = try! AbsolutePath(validating: "/test/text.xcodeproj"),
+ name: String = "Project",
+ organizationName: String? = nil,
+ classPrefix: String? = nil,
+ defaultKnownRegions: [String]? = nil,
+ developmentRegion: String? = nil,
+ options: Options = .test(automaticSchemesOptions: .disabled),
+ settings: Settings = .default,
+ filesGroup: ProjectGroup = .group(name: "Project"),
+ targets: [Target] = [],
+ packages: [Package] = [],
+ schemes: [Scheme] = [],
+ ideTemplateMacros: IDETemplateMacros? = nil,
+ additionalFiles: [FileElement] = [],
+ resourceSynthesizers: [ResourceSynthesizer] = [],
+ lastUpgradeCheck: Version? = nil,
+ type: ProjectType
+ ) -> Project {
+ Project(
+ path: path,
+ sourceRootPath: sourceRootPath,
+ xcodeProjPath: xcodeProjPath,
+ name: name,
+ organizationName: organizationName,
+ classPrefix: classPrefix,
+ defaultKnownRegions: defaultKnownRegions,
+ developmentRegion: developmentRegion,
+ options: options,
+ settings: settings,
+ filesGroup: filesGroup,
+ targets: targets,
+ packages: packages,
+ schemes: schemes,
+ ideTemplateMacros: ideTemplateMacros,
+ additionalFiles: additionalFiles,
+ resourceSynthesizers: resourceSynthesizers,
+ lastUpgradeCheck: lastUpgradeCheck,
+ type: type
+ )
+ }
}
extension Project.Options {
From b1f78609de1759332754f2891dd1053d4efd8d1e Mon Sep 17 00:00:00 2001
From: Pedro
Date: Thu, 14 Nov 2024 18:06:17 +0100
Subject: [PATCH 072/914] force release
From 53c1c048d17f4584ef0010a3e080338a2d7d15cd Mon Sep 17 00:00:00 2001
From: Pedro
Date: Thu, 14 Nov 2024 18:06:59 +0100
Subject: [PATCH 073/914] feat: force release
From d263b467aa4700a9e4c103a877c5158f5b26bed7 Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Thu, 14 Nov 2024 17:07:20 +0000
Subject: [PATCH 074/914] [Release] XcodeGraph 0.18.0
---
CHANGELOG.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ceb594e2..bcf24f45 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.18.0] - 2024-11-14
+### Details
+#### Feat
+- Force release by @pepicrft
+
## [0.17.1] - 2024-11-13
### Details
#### Chore
@@ -125,6 +130,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Feat
- Add linking status to target and project by @rgnns in [#45](https://github.com/tuist/XcodeGraph/pull/45)
+## New Contributors
+* @danieleformichelli made their first contribution
## [0.11.7] - 2024-09-19
### Details
#### Chore
@@ -140,6 +147,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Chore
- Sendability conformance by @waltflanagan in [#44](https://github.com/tuist/XcodeGraph/pull/44)
+## New Contributors
+* @waltflanagan made their first contribution
## [0.11.4] - 2024-09-04
### Details
#### Chore
@@ -211,12 +220,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+## New Contributors
+* @filipracki made their first contribution
## [0.7.0] - 2024-06-25
### Details
#### Docs
- Create .all-contributorsrc [skip ci] by @allcontributors[bot]
- Update README.md [skip ci] by @allcontributors[bot]
+## New Contributors
+* @darrarski made their first contribution
+[0.18.0]: https://github.com/tuist/XcodeGraph/compare/0.17.1..0.18.0
[0.17.1]: https://github.com/tuist/XcodeGraph/compare/0.17.0..0.17.1
[0.17.0]: https://github.com/tuist/XcodeGraph/compare/0.16.3..0.17.0
[0.16.3]: https://github.com/tuist/XcodeGraph/compare/0.16.2..0.16.3
From 9c64887396d43aa905e49124a83b40682e70a919 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 14 Nov 2024 18:15:24 +0100
Subject: [PATCH 075/914] chore(deps): update actions/checkout digest to
11bd719 (#66)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index baa09515..e1260c47 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
timeout-minutes: 15
if: "!startsWith(github.event.head_commit.message, '[Release]')"
steps:
- - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- uses: jdx/mise-action@v2
From 853d83e0ebdccd9dcd5124679ec1241d95acc5be Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Thu, 14 Nov 2024 17:15:38 +0000
Subject: [PATCH 076/914] [Release] XcodeGraph 0.18.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcf24f45..94e35090 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.18.1] - 2024-11-14
+### Details
+#### Chore
+- Update actions/checkout digest to 11bd719 by @renovate[bot] in [#66](https://github.com/tuist/XcodeGraph/pull/66)
+
## [0.18.0] - 2024-11-14
### Details
#### Feat
@@ -230,6 +235,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.18.1]: https://github.com/tuist/XcodeGraph/compare/0.18.0..0.18.1
[0.18.0]: https://github.com/tuist/XcodeGraph/compare/0.17.1..0.18.0
[0.17.1]: https://github.com/tuist/XcodeGraph/compare/0.17.0..0.17.1
[0.17.0]: https://github.com/tuist/XcodeGraph/compare/0.16.3..0.17.0
From ad60641d7d700507152bf908ee0a4f3a75fdb17a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Fri, 15 Nov 2024 12:50:50 +0100
Subject: [PATCH 077/914] fix: invocation of Project.test() without specified
ProjectType should use new method (#75)
---
Sources/XcodeGraph/Models/Project.swift | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index fa00ffd9..35cef6aa 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -267,7 +267,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
additionalFiles: [FileElement] = [],
resourceSynthesizers: [ResourceSynthesizer] = [],
lastUpgradeCheck: Version? = nil,
- isExternal: Bool = false
+ isExternal: Bool
) -> Project {
Project(
path: path,
@@ -312,7 +312,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
additionalFiles: [FileElement] = [],
resourceSynthesizers: [ResourceSynthesizer] = [],
lastUpgradeCheck: Version? = nil,
- type: ProjectType
+ type: ProjectType = .local
) -> Project {
Project(
path: path,
From 3f1b43887c5ca1de25b2c935bebb50a94fcfdb65 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Fri, 15 Nov 2024 11:51:07 +0000
Subject: [PATCH 078/914] [Release] XcodeGraph 0.18.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94e35090..68931ed0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.18.2] - 2024-11-15
+### Details
+#### Fix
+- Invocation of Project.test() without specified ProjectType should use new method by @fortmarek in [#75](https://github.com/tuist/XcodeGraph/pull/75)
+
## [0.18.1] - 2024-11-14
### Details
#### Chore
@@ -235,6 +240,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.18.2]: https://github.com/tuist/XcodeGraph/compare/0.18.1..0.18.2
[0.18.1]: https://github.com/tuist/XcodeGraph/compare/0.18.0..0.18.1
[0.18.0]: https://github.com/tuist/XcodeGraph/compare/0.17.1..0.18.0
[0.17.1]: https://github.com/tuist/XcodeGraph/compare/0.17.0..0.17.1
From 0744c719e04a98a26cb7fef437d061100cd5140d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 15 Nov 2024 21:39:24 +0000
Subject: [PATCH 079/914] chore(deps): update dependency tuist to v4.34.3 (#76)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index a00fc97f..0192ed03 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.34.0"
+tuist = "4.34.3"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 11d7aa37be867b7a84deb8bb1fc2708dcb414f28 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Fri, 15 Nov 2024 21:39:40 +0000
Subject: [PATCH 080/914] [Release] XcodeGraph 0.18.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68931ed0..c765df92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.18.3] - 2024-11-15
+### Details
+#### Chore
+- Update dependency tuist to v4.34.3 by @renovate[bot] in [#76](https://github.com/tuist/XcodeGraph/pull/76)
+
## [0.18.2] - 2024-11-15
### Details
#### Fix
@@ -240,6 +245,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.18.3]: https://github.com/tuist/XcodeGraph/compare/0.18.2..0.18.3
[0.18.2]: https://github.com/tuist/XcodeGraph/compare/0.18.1..0.18.2
[0.18.1]: https://github.com/tuist/XcodeGraph/compare/0.18.0..0.18.1
[0.18.0]: https://github.com/tuist/XcodeGraph/compare/0.17.1..0.18.0
From 0297ae8271edf9223c8f3801253537c5d9e3058c Mon Sep 17 00:00:00 2001
From: Hilton Campbell
Date: Tue, 19 Nov 2024 10:16:25 -0800
Subject: [PATCH 081/914] feat: add new `Target.metadata` attribute (#41)
* Add target metadata
* Fix CI pipeline
* Use macOS 14
* Use macOS-15
* Only set up Swift in ubuntu
---------
Co-authored-by: Pedro
---
.github/workflows/XcodeGraph.yml | 34 ++++++-------------
.github/workflows/cache.yml | 9 +----
.../Models/Metadata/TargetMetadata.swift | 22 ++++++++++++
Sources/XcodeGraph/Models/Target.swift | 17 +++++++---
Tuist/Config.swift => Tuist.swift | 0
5 files changed, 45 insertions(+), 37 deletions(-)
create mode 100644 Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
rename Tuist/Config.swift => Tuist.swift (100%)
diff --git a/.github/workflows/XcodeGraph.yml b/.github/workflows/XcodeGraph.yml
index 4d2070db..2b422d93 100644
--- a/.github/workflows/XcodeGraph.yml
+++ b/.github/workflows/XcodeGraph.yml
@@ -28,51 +28,44 @@ jobs:
matrix:
os:
- ubuntu-22.04
- - macos-14
+ - macos-15
swift-version:
- "5.9"
- swift-compat-ver:
- - "5"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- - uses: SwiftyLab/setup-swift@latest
+ - name: Setup Swift
+ if: matrix.os == 'ubuntu-22.04'
+ uses: SwiftyLab/setup-swift@latest
with:
swift-version: ${{ matrix.swift-version }}
# DEBUG mode
- name: Build with debug mode.
id: debug_build
- run: swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ run: swift build --configuration debug
continue-on-error: true
- name: Retry build with debug mode if necessary
if: steps.debug_build.outcome == 'failure'
run: |
- swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ swift build --configuration debug
# RELEASE mode
- name: Build with release mode.
id: release_build
- run: swift build --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ run: swift build --configuration release
continue-on-error: true
- name: Retry build with release mode if necessary
if: steps.release_build.outcome == 'failure'
run: |
- swift build --configuration release -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
+ swift build --configuration release
tuist_build:
name: Tuist Build
strategy:
matrix:
os:
- - macos-14
- swift-version:
- - "5.9"
- swift-compat-ver:
- - "5"
+ - macos-15
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- - uses: SwiftyLab/setup-swift@latest
- with:
- swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
with:
version: 2024.11.8
@@ -85,17 +78,10 @@ jobs:
strategy:
matrix:
os:
- - macos-14
- swift-version:
- - "5.9"
- swift-compat-ver:
- - "5"
+ - macos-15
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- - uses: SwiftyLab/setup-swift@latest
- with:
- swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
with:
version: 2024.11.8
diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml
index ded627e3..00dd8252 100644
--- a/.github/workflows/cache.yml
+++ b/.github/workflows/cache.yml
@@ -25,17 +25,10 @@ jobs:
strategy:
matrix:
os:
- - macOS-14
- swift-version:
- - "5.9"
- swift-compat-ver:
- - "5"
+ - macos-15
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- - uses: SwiftyLab/setup-swift@latest
- with:
- swift-version: ${{ matrix.swift-version }}
- uses: jdx/mise-action@v2
with:
version: 2024.11.8
diff --git a/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
new file mode 100644
index 00000000..e72de39d
--- /dev/null
+++ b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
@@ -0,0 +1,22 @@
+/// The metadata associated with a target.
+public struct TargetMetadata: Codable, Equatable {
+ public var tags: Set
+
+ public init(
+ tags: Set
+ ) {
+ self.tags = tags
+ }
+}
+
+#if DEBUG
+ extension TargetMetadata {
+ public static func test(
+ tags: Set = []
+ ) -> TargetMetadata {
+ TargetMetadata(
+ tags: tags
+ )
+ }
+ }
+#endif
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 2257fc9d..2ddd943b 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -49,6 +49,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
public let mergedBinaryType: MergedBinaryType
public let mergeable: Bool
public let onDemandResourcesTags: OnDemandResourcesTags?
+ public let metadata: TargetMetadata
// MARK: - Init
@@ -79,7 +80,8 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
prune: Bool = false,
mergedBinaryType: MergedBinaryType = .disabled,
mergeable: Bool = false,
- onDemandResourcesTags: OnDemandResourcesTags? = nil
+ onDemandResourcesTags: OnDemandResourcesTags? = nil,
+ metadata: TargetMetadata = .init(tags: [])
) {
self.name = name
self.product = product
@@ -108,6 +110,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
self.mergedBinaryType = mergedBinaryType
self.mergeable = mergeable
self.onDemandResourcesTags = onDemandResourcesTags
+ self.metadata = metadata
}
/// Given a target name, it obtains the product name by turning "-" characters into "_" and "/" into "_"
@@ -403,7 +406,8 @@ extension Sequence {
additionalFiles: [FileElement] = [],
prune: Bool = false,
mergedBinaryType: MergedBinaryType = .disabled,
- mergeable: Bool = false
+ mergeable: Bool = false,
+ metadata: TargetMetadata = .test()
) -> Target {
Target(
name: name,
@@ -430,7 +434,8 @@ extension Sequence {
additionalFiles: additionalFiles,
prune: prune,
mergedBinaryType: mergedBinaryType,
- mergeable: mergeable
+ mergeable: mergeable,
+ metadata: metadata
)
}
@@ -461,7 +466,8 @@ extension Sequence {
additionalFiles: [FileElement] = [],
prune: Bool = false,
mergedBinaryType: MergedBinaryType = .disabled,
- mergeable: Bool = false
+ mergeable: Bool = false,
+ metadata: TargetMetadata = .test()
) -> Target {
Target(
name: name,
@@ -488,7 +494,8 @@ extension Sequence {
additionalFiles: additionalFiles,
prune: prune,
mergedBinaryType: mergedBinaryType,
- mergeable: mergeable
+ mergeable: mergeable,
+ metadata: metadata
)
}
diff --git a/Tuist/Config.swift b/Tuist.swift
similarity index 100%
rename from Tuist/Config.swift
rename to Tuist.swift
From 1b912a8b581c2a370b07c21f2ce8613755a7b838 Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Tue, 19 Nov 2024 18:16:40 +0000
Subject: [PATCH 082/914] [Release] XcodeGraph 0.19.0
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c765df92..2b979d98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.0] - 2024-11-19
+### Details
+#### Feat
+- Add new `Target.metadata` attribute by @hiltonc in [#41](https://github.com/tuist/XcodeGraph/pull/41)
+
+## New Contributors
+* @hiltonc made their first contribution in [#41](https://github.com/tuist/XcodeGraph/pull/41)
## [0.18.3] - 2024-11-15
### Details
#### Chore
@@ -245,6 +252,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.0]: https://github.com/tuist/XcodeGraph/compare/0.18.3..0.19.0
[0.18.3]: https://github.com/tuist/XcodeGraph/compare/0.18.2..0.18.3
[0.18.2]: https://github.com/tuist/XcodeGraph/compare/0.18.1..0.18.2
[0.18.1]: https://github.com/tuist/XcodeGraph/compare/0.18.0..0.18.1
From 120ff29cbd45bc6c5a2f6528892e9a34fb518254 Mon Sep 17 00:00:00 2001
From: Pedro
Date: Tue, 19 Nov 2024 19:23:17 +0100
Subject: [PATCH 083/914] chore: fix release pipeline
---
.github/workflows/release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e1260c47..6a896dcc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
- name: Check if there are releasable changes
id: is-releasable
run: |
- bumped_output=$(git cliff --bump)
+ bumped_output=$(git cliff --bump --unreleased)
changelog_content=$(cat CHANGELOG.md)
bumped_hash=$(echo -n "$bumped_output" | shasum -a 256 | awk '{print $1}')
From cd2c44640039c0b06f327a4dfc8052d871a088cc Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Tue, 19 Nov 2024 18:23:45 +0000
Subject: [PATCH 084/914] [Release] XcodeGraph 0.19.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b979d98..bc2f4399 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.1] - 2024-11-19
+### Details
+#### Chore
+- Fix release pipeline by @pepicrft
+
## [0.19.0] - 2024-11-19
### Details
#### Feat
@@ -252,6 +257,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.1]: https://github.com/tuist/XcodeGraph/compare/0.19.0..0.19.1
[0.19.0]: https://github.com/tuist/XcodeGraph/compare/0.18.3..0.19.0
[0.18.3]: https://github.com/tuist/XcodeGraph/compare/0.18.2..0.18.3
[0.18.2]: https://github.com/tuist/XcodeGraph/compare/0.18.1..0.18.2
From 7d15c3b6aea30a090c7f0ada106b4395e2bc1424 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Wed, 20 Nov 2024 12:46:59 +0100
Subject: [PATCH 085/914] fix: invocation of Project.empty() without specified
ProjectType should use new method (#77)
---
Sources/XcodeGraph/Models/Project.swift | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index 35cef6aa..c3693537 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -358,7 +358,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
additionalFiles: [FileElement] = [],
resourceSynthesizers: [ResourceSynthesizer] = [],
lastUpgradeCheck: Version? = nil,
- isExternal: Bool = false
+ isExternal: Bool
) -> Project {
Project(
path: path,
@@ -403,7 +403,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
additionalFiles: [FileElement] = [],
resourceSynthesizers: [ResourceSynthesizer] = [],
lastUpgradeCheck: Version? = nil,
- type: ProjectType
+ type: ProjectType = .external(hash: "project-hash")
) -> Project {
Project(
path: path,
From 2354aa5705203ae9289c42c1bc6ac83956b53c6a Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Wed, 20 Nov 2024 11:47:18 +0000
Subject: [PATCH 086/914] [Release] XcodeGraph 0.19.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc2f4399..df34bc78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.2] - 2024-11-20
+### Details
+#### Fix
+- Invocation of Project.empty() without specified ProjectType should use new method by @fortmarek in [#77](https://github.com/tuist/XcodeGraph/pull/77)
+
## [0.19.1] - 2024-11-19
### Details
#### Chore
@@ -257,6 +262,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.2]: https://github.com/tuist/XcodeGraph/compare/0.19.1..0.19.2
[0.19.1]: https://github.com/tuist/XcodeGraph/compare/0.19.0..0.19.1
[0.19.0]: https://github.com/tuist/XcodeGraph/compare/0.18.3..0.19.0
[0.18.3]: https://github.com/tuist/XcodeGraph/compare/0.18.2..0.18.3
From 7bee9cd5eac84dc7b5d964a6873d4d42438a7150 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Wed, 20 Nov 2024 12:51:14 +0100
Subject: [PATCH 087/914] fix: make Project.type non-optional (#78)
---
Sources/XcodeGraph/Models/Project.swift | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index c3693537..db1dff06 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -79,7 +79,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
public var isExternal: Bool
/// It represents the type of project.
- public var type: ProjectType?
+ public var type: ProjectType
// MARK: - Init
@@ -146,6 +146,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
self.resourceSynthesizers = resourceSynthesizers
self.lastUpgradeCheck = lastUpgradeCheck
self.isExternal = isExternal
+ type = .external(hash: nil)
}
/// Initializes the project with its attributes.
From aa1eaa209cbd308d1b8f6162b4d020ae8bd36058 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Wed, 20 Nov 2024 11:51:30 +0000
Subject: [PATCH 088/914] [Release] XcodeGraph 0.19.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df34bc78..0b71bb7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.3] - 2024-11-20
+### Details
+#### Fix
+- Make Project.type non-optional by @fortmarek in [#78](https://github.com/tuist/XcodeGraph/pull/78)
+
## [0.19.2] - 2024-11-20
### Details
#### Fix
@@ -262,6 +267,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.3]: https://github.com/tuist/XcodeGraph/compare/0.19.2..0.19.3
[0.19.2]: https://github.com/tuist/XcodeGraph/compare/0.19.1..0.19.2
[0.19.1]: https://github.com/tuist/XcodeGraph/compare/0.19.0..0.19.1
[0.19.0]: https://github.com/tuist/XcodeGraph/compare/0.18.3..0.19.0
From b38d27da3422e15514e765d1ae1a139676b4c0c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Thu, 21 Nov 2024 13:55:54 +0100
Subject: [PATCH 089/914] fix: overlay platform-specific settings when the
value does not exist (#79)
---
.../SettingsDictionary+Extras.swift | 40 +++++++++++++++++
Sources/XcodeGraph/Models/Settings.swift | 43 -------------------
.../SettingsDictionary+ExtrasTests.swift | 29 +++++++++++++
3 files changed, 69 insertions(+), 43 deletions(-)
create mode 100644 Sources/XcodeGraph/Extensions/SettingsDictionary+Extras.swift
create mode 100644 Tests/XcodeGraphTests/Extensions/SettingsDictionary+ExtrasTests.swift
diff --git a/Sources/XcodeGraph/Extensions/SettingsDictionary+Extras.swift b/Sources/XcodeGraph/Extensions/SettingsDictionary+Extras.swift
new file mode 100644
index 00000000..ee3ebbbd
--- /dev/null
+++ b/Sources/XcodeGraph/Extensions/SettingsDictionary+Extras.swift
@@ -0,0 +1,40 @@
+import Foundation
+
+extension SettingsDictionary {
+ /// Overlays a SettingsDictionary by adding a `[sdk=*]` qualifier
+ /// e.g. for a multiplatform target
+ /// `LD_RUNPATH_SEARCH_PATHS = @executable_path/Frameworks`
+ /// `LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = @executable_path/../Frameworks`
+ public mutating func overlay(
+ with other: SettingsDictionary,
+ for platform: Platform
+ ) {
+ for (key, newValue) in other where self[key] != newValue {
+ let newKey = "\(key)[sdk=\(platform.xcodeSdkRoot)*]"
+ self[newKey] = newValue
+ if platform.hasSimulators, let simulatorSDK = platform.xcodeSimulatorSDK {
+ let newKey = "\(key)[sdk=\(simulatorSDK)*]"
+ self[newKey] = newValue
+ }
+ }
+ }
+
+ /// Combines two `SettingsDictionary`. Instead of overriding values for a duplicate key, it combines them.
+ public func combine(with settings: SettingsDictionary) -> SettingsDictionary {
+ merging(settings, uniquingKeysWith: { oldValue, newValue in
+ let newValues: [String]
+ switch newValue {
+ case let .string(value):
+ newValues = [value]
+ case let .array(values):
+ newValues = values
+ }
+ switch oldValue {
+ case let .array(values):
+ return .array(values + newValues)
+ case let .string(value):
+ return .array(value.split(separator: " ").map(String.init) + newValues)
+ }
+ })
+ }
+}
diff --git a/Sources/XcodeGraph/Models/Settings.swift b/Sources/XcodeGraph/Models/Settings.swift
index 48616f3c..d8f182c5 100644
--- a/Sources/XcodeGraph/Models/Settings.swift
+++ b/Sources/XcodeGraph/Models/Settings.swift
@@ -30,49 +30,6 @@ public enum SettingValue: ExpressibleByStringInterpolation, ExpressibleByStringL
}
}
-extension SettingsDictionary {
- /// Overlays a SettingsDictionary by adding a `[sdk=*]` qualifier
- /// e.g. for a multiplatform target
- /// `LD_RUNPATH_SEARCH_PATHS = @executable_path/Frameworks`
- /// `LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = @executable_path/../Frameworks`
- public mutating func overlay(
- with other: SettingsDictionary,
- for platform: Platform
- ) {
- for (key, newValue) in other {
- if self[key] == nil {
- self[key] = newValue
- } else if self[key] != newValue {
- let newKey = "\(key)[sdk=\(platform.xcodeSdkRoot)*]"
- self[newKey] = newValue
- if platform.hasSimulators, let simulatorSDK = platform.xcodeSimulatorSDK {
- let newKey = "\(key)[sdk=\(simulatorSDK)*]"
- self[newKey] = newValue
- }
- }
- }
- }
-
- /// Combines two `SettingsDictionary`. Instead of overriding values for a duplicate key, it combines them.
- public func combine(with settings: SettingsDictionary) -> SettingsDictionary {
- merging(settings, uniquingKeysWith: { oldValue, newValue in
- let newValues: [String]
- switch newValue {
- case let .string(value):
- newValues = [value]
- case let .array(values):
- newValues = values
- }
- switch oldValue {
- case let .array(values):
- return .array(values + newValues)
- case let .string(value):
- return .array(value.split(separator: " ").map(String.init) + newValues)
- }
- })
- }
-}
-
public struct Configuration: Equatable, Codable, Sendable {
// MARK: - Attributes
diff --git a/Tests/XcodeGraphTests/Extensions/SettingsDictionary+ExtrasTests.swift b/Tests/XcodeGraphTests/Extensions/SettingsDictionary+ExtrasTests.swift
new file mode 100644
index 00000000..32bae6a6
--- /dev/null
+++ b/Tests/XcodeGraphTests/Extensions/SettingsDictionary+ExtrasTests.swift
@@ -0,0 +1,29 @@
+import Foundation
+import XCTest
+
+@testable import XcodeGraph
+
+final class SettingsDictionaryExtrasTest: XCTestCase {
+ func testOverlay_addsPlatformSpecifierWhenSettingsDiffer() {
+ // Given
+ var settings: [String: SettingValue] = [
+ "A": "a value",
+ "B": "b value",
+ ]
+
+ // When
+ settings.overlay(with: [
+ "A": "overlayed value",
+ "B": "b value",
+ "C": "c value",
+ ], for: .macOS)
+
+ // Then
+ XCTAssertEqual(settings, [
+ "A[sdk=macosx*]": "overlayed value",
+ "A": "a value",
+ "B": "b value",
+ "C[sdk=macosx*]": "c value",
+ ])
+ }
+}
From 6dae7e2ce73b3335cb92d4c0829d4e0c35a72990 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Thu, 21 Nov 2024 12:56:10 +0000
Subject: [PATCH 090/914] [Release] XcodeGraph 0.19.4
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b71bb7c..75201ab2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.4] - 2024-11-21
+### Details
+#### Fix
+- Overlay platform-specific settings when the value does not exist by @fortmarek in [#79](https://github.com/tuist/XcodeGraph/pull/79)
+
## [0.19.3] - 2024-11-20
### Details
#### Fix
@@ -267,6 +272,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.4]: https://github.com/tuist/XcodeGraph/compare/0.19.3..0.19.4
[0.19.3]: https://github.com/tuist/XcodeGraph/compare/0.19.2..0.19.3
[0.19.2]: https://github.com/tuist/XcodeGraph/compare/0.19.1..0.19.2
[0.19.1]: https://github.com/tuist/XcodeGraph/compare/0.19.0..0.19.1
From 632584589455e0549afe2b05c441b2d26d5dc27f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 21 Nov 2024 21:56:22 +0000
Subject: [PATCH 091/914] chore(deps): update dependency tuist to v4.35.0 (#80)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 0192ed03..deeb3a69 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.34.3"
+tuist = "4.35.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 890ed26ccc35bf13bb770010b7af597036512ecf Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 21 Nov 2024 21:56:39 +0000
Subject: [PATCH 092/914] [Release] XcodeGraph 0.19.5
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75201ab2..fa92ccfe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.19.5] - 2024-11-21
+### Details
+#### Chore
+- Update dependency tuist to v4.35.0 by @renovate[bot] in [#80](https://github.com/tuist/XcodeGraph/pull/80)
+
## [0.19.4] - 2024-11-21
### Details
#### Fix
@@ -272,6 +277,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[0.19.5]: https://github.com/tuist/XcodeGraph/compare/0.19.4..0.19.5
[0.19.4]: https://github.com/tuist/XcodeGraph/compare/0.19.3..0.19.4
[0.19.3]: https://github.com/tuist/XcodeGraph/compare/0.19.2..0.19.3
[0.19.2]: https://github.com/tuist/XcodeGraph/compare/0.19.1..0.19.2
From 9e4966d43034773bb0b219bce97ede30e9f0036e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Pi=C3=B1era=20Buend=C3=ADa?=
<663605+pepicrft@users.noreply.github.com>
Date: Fri, 22 Nov 2024 18:39:31 +0100
Subject: [PATCH 093/914] feat!: major release 1.0 (#81)
* Get the release notes after bumping the version
* feat!: Major release 1.0
I removed the deprecated methods and attributes and cut a major release to symbolize
that the API is now mature and that coming changes are always backward-compatible.
* Fix linting issue
---
.github/workflows/release.yml | 18 +--
Sources/XcodeGraph/Models/Project.swift | 166 ------------------------
2 files changed, 9 insertions(+), 175 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6a896dcc..05121327 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -51,15 +51,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "NEXT_VERSION=$(git cliff --bumped-version)" >> "$GITHUB_OUTPUT"
- - name: Get release notes
- id: release-notes
- if: env.should-release == 'true'
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- echo "RELEASE_NOTES<> "$GITHUB_OUTPUT"
- git cliff --unreleased >> "$GITHUB_OUTPUT"
- echo "EOF" >> "$GITHUB_OUTPUT"
- name: Update CHANGELOG.md
if: env.should-release == 'true'
env:
@@ -74,6 +65,15 @@ jobs:
tagging_message: ${{ steps.next-version.outputs.NEXT_VERSION }}
skip_dirty_check: true
commit_message: "[Release] XcodeGraph ${{ steps.next-version.outputs.NEXT_VERSION }}"
+ - name: Get release notes
+ id: release-notes
+ if: env.should-release == 'true'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ echo "RELEASE_NOTES<> "$GITHUB_OUTPUT"
+ git cliff --latest >> "$GITHUB_OUTPUT"
+ echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
if: env.should-release == 'true'
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index db1dff06..85a975a9 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -74,81 +74,11 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
/// The version in which a check happened related to recommended settings after updating Xcode.
public var lastUpgradeCheck: Version?
- /// Indicates whether the project is imported through `Package.swift`.
- @available(*, deprecated, renamed: "type", message: "Use project type instead")
- public var isExternal: Bool
-
/// It represents the type of project.
public var type: ProjectType
// MARK: - Init
- /// Initializes the project with its attributes.
- ///
- /// - Parameters:
- /// - path: Path to the folder that contains the project manifest.
- /// - sourceRootPath: Path to the directory where the Xcode project will be generated.
- /// - xcodeProjPath: Path to the Xcode project that will be generated.
- /// - name: Project name.
- /// - organizationName: Organization name.
- /// - defaultKnownRegions: Default known regions.
- /// - developmentRegion: Development region.
- /// - options: Additional project options.
- /// - settings: The settings to apply at the project level
- /// - filesGroup: The root group to place project files within
- /// - targets: The project targets
- /// *(Those won't be included in any build phases)*
- /// - packages: Project swift packages.
- /// - schemes: Project schemes.
- /// - ideTemplateMacros: IDE template macros that represent content of IDETemplateMacros.plist.
- /// - additionalFiles: The additional files to include in the project
- /// - resourceSynthesizers: `ResourceSynthesizers` that will be applied on individual target's resources
- /// - lastUpgradeCheck: The version in which a check happened related to recommended settings after updating Xcode.
- /// - isExternal: Indicates whether the project is imported through `Package.swift`.
- @available(*, deprecated, message: "Use the constructor that takes type: insead of isExternal:")
- public init(
- path: AbsolutePath,
- sourceRootPath: AbsolutePath,
- xcodeProjPath: AbsolutePath,
- name: String,
- organizationName: String?,
- classPrefix: String?,
- defaultKnownRegions: [String]?,
- developmentRegion: String?,
- options: Options,
- settings: Settings,
- filesGroup: ProjectGroup,
- targets: [Target],
- packages: [Package],
- schemes: [Scheme],
- ideTemplateMacros: IDETemplateMacros?,
- additionalFiles: [FileElement],
- resourceSynthesizers: [ResourceSynthesizer],
- lastUpgradeCheck: Version?,
- isExternal: Bool
- ) {
- self.path = path
- self.sourceRootPath = sourceRootPath
- self.xcodeProjPath = xcodeProjPath
- self.name = name
- self.organizationName = organizationName
- self.classPrefix = classPrefix
- self.defaultKnownRegions = defaultKnownRegions
- self.developmentRegion = developmentRegion
- self.options = options
- self.targets = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
- self.packages = packages
- self.schemes = schemes
- self.settings = settings
- self.filesGroup = filesGroup
- self.ideTemplateMacros = ideTemplateMacros
- self.additionalFiles = additionalFiles
- self.resourceSynthesizers = resourceSynthesizers
- self.lastUpgradeCheck = lastUpgradeCheck
- self.isExternal = isExternal
- type = .external(hash: nil)
- }
-
/// Initializes the project with its attributes.
///
/// - Parameters:
@@ -210,10 +140,6 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
self.additionalFiles = additionalFiles
self.resourceSynthesizers = resourceSynthesizers
self.lastUpgradeCheck = lastUpgradeCheck
- isExternal = switch type {
- case .external: true
- case .local: false
- }
self.type = type
}
@@ -247,52 +173,6 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
#if DEBUG
extension Project {
- @available(*, deprecated, message: "Use test that takes a project type instead of isExternal attribute.")
- public static func test(
- path: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
- sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
- // swiftlint:disable:next force_try
- xcodeProjPath: AbsolutePath = try! AbsolutePath(validating: "/Project/Project.xcodeproj"),
- name: String = "Project",
- organizationName: String? = nil,
- classPrefix: String? = nil,
- defaultKnownRegions: [String]? = nil,
- developmentRegion: String? = nil,
- options: Options = .test(automaticSchemesOptions: .disabled),
- settings: Settings = Settings.test(),
- filesGroup: ProjectGroup = .group(name: "Project"),
- targets: [Target] = [Target.test()],
- packages: [Package] = [],
- schemes: [Scheme] = [],
- ideTemplateMacros: IDETemplateMacros? = nil,
- additionalFiles: [FileElement] = [],
- resourceSynthesizers: [ResourceSynthesizer] = [],
- lastUpgradeCheck: Version? = nil,
- isExternal: Bool
- ) -> Project {
- Project(
- path: path,
- sourceRootPath: sourceRootPath,
- xcodeProjPath: xcodeProjPath,
- name: name,
- organizationName: organizationName,
- classPrefix: classPrefix,
- defaultKnownRegions: defaultKnownRegions,
- developmentRegion: developmentRegion,
- options: options,
- settings: settings,
- filesGroup: filesGroup,
- targets: targets,
- packages: packages,
- schemes: schemes,
- ideTemplateMacros: ideTemplateMacros,
- additionalFiles: additionalFiles,
- resourceSynthesizers: resourceSynthesizers,
- lastUpgradeCheck: lastUpgradeCheck,
- isExternal: isExternal
- )
- }
-
public static func test(
path: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/Project"), // swiftlint:disable:this force_try
@@ -338,52 +218,6 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
)
}
- @available(*, deprecated, message: "Use empty that takes a project type instead of isExternal attribute.")
- public static func empty(
- path: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
- sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
- // swiftlint:disable:next force_try
- xcodeProjPath: AbsolutePath = try! AbsolutePath(validating: "/test/text.xcodeproj"),
- name: String = "Project",
- organizationName: String? = nil,
- classPrefix: String? = nil,
- defaultKnownRegions: [String]? = nil,
- developmentRegion: String? = nil,
- options: Options = .test(automaticSchemesOptions: .disabled),
- settings: Settings = .default,
- filesGroup: ProjectGroup = .group(name: "Project"),
- targets: [Target] = [],
- packages: [Package] = [],
- schemes: [Scheme] = [],
- ideTemplateMacros: IDETemplateMacros? = nil,
- additionalFiles: [FileElement] = [],
- resourceSynthesizers: [ResourceSynthesizer] = [],
- lastUpgradeCheck: Version? = nil,
- isExternal: Bool
- ) -> Project {
- Project(
- path: path,
- sourceRootPath: sourceRootPath,
- xcodeProjPath: xcodeProjPath,
- name: name,
- organizationName: organizationName,
- classPrefix: classPrefix,
- defaultKnownRegions: defaultKnownRegions,
- developmentRegion: developmentRegion,
- options: options,
- settings: settings,
- filesGroup: filesGroup,
- targets: targets,
- packages: packages,
- schemes: schemes,
- ideTemplateMacros: ideTemplateMacros,
- additionalFiles: additionalFiles,
- resourceSynthesizers: resourceSynthesizers,
- lastUpgradeCheck: lastUpgradeCheck,
- isExternal: isExternal
- )
- }
-
public static func empty(
path: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
sourceRootPath: AbsolutePath = try! AbsolutePath(validating: "/test/"), // swiftlint:disable:this force_try
From c59f0080233cc1e434a2445c1b414d55e0cf51ce Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Fri, 22 Nov 2024 17:40:39 +0000
Subject: [PATCH 094/914] [Release] XcodeGraph 1.0.0
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa92ccfe..5547b064 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.0] - 2024-11-22
+### Details
+#### Feat
+- Major release 1.0 by @pepicrft in [#81](https://github.com/tuist/XcodeGraph/pull/81)
+
## [0.19.5] - 2024-11-21
### Details
#### Chore
@@ -277,6 +282,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.0.0]: https://github.com/tuist/XcodeGraph/compare/0.19.5..1.0.0
[0.19.5]: https://github.com/tuist/XcodeGraph/compare/0.19.4..0.19.5
[0.19.4]: https://github.com/tuist/XcodeGraph/compare/0.19.3..0.19.4
[0.19.3]: https://github.com/tuist/XcodeGraph/compare/0.19.2..0.19.3
From 36ad1e9cc151689ae84bed7fe50413adbf66e9d5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 27 Nov 2024 19:56:56 +0000
Subject: [PATCH 095/914] chore(deps): update dependency tuist to v4.36.0 (#83)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index deeb3a69..d5a734d5 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.35.0"
+tuist = "4.36.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 95982f5b444bb442dae0e76de27cd6ed5ab5d4d8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 27 Nov 2024 19:57:14 +0000
Subject: [PATCH 096/914] [Release] XcodeGraph 1.0.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5547b064..ef2a1be7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.1] - 2024-11-27
+### Details
+#### Chore
+- Update dependency tuist to v4.36.0 by @renovate[bot] in [#83](https://github.com/tuist/XcodeGraph/pull/83)
+
## [1.0.0] - 2024-11-22
### Details
#### Feat
@@ -282,6 +287,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.0.1]: https://github.com/tuist/XcodeGraph/compare/1.0.0..1.0.1
[1.0.0]: https://github.com/tuist/XcodeGraph/compare/0.19.5..1.0.0
[0.19.5]: https://github.com/tuist/XcodeGraph/compare/0.19.4..0.19.5
[0.19.4]: https://github.com/tuist/XcodeGraph/compare/0.19.3..0.19.4
From 88ec3deabad2ccaddcf9c6d520f724504b11b763 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 12 Dec 2024 00:13:39 +0000
Subject: [PATCH 097/914] chore(deps): update dependency tuist to v4.37.0 (#85)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index d5a734d5..ca39cf17 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.36.0"
+tuist = "4.37.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From dbc198495714c6af0c60c1cf6c785b79704691f2 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Thu, 12 Dec 2024 00:13:55 +0000
Subject: [PATCH 098/914] [Release] XcodeGraph 1.0.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef2a1be7..f8f364e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.2] - 2024-12-12
+### Details
+#### Chore
+- Update dependency tuist to v4.37.0 by @renovate[bot] in [#85](https://github.com/tuist/XcodeGraph/pull/85)
+
## [1.0.1] - 2024-11-27
### Details
#### Chore
@@ -287,6 +292,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.0.2]: https://github.com/tuist/XcodeGraph/compare/1.0.1..1.0.2
[1.0.1]: https://github.com/tuist/XcodeGraph/compare/1.0.0..1.0.1
[1.0.0]: https://github.com/tuist/XcodeGraph/compare/0.19.5..1.0.0
[0.19.5]: https://github.com/tuist/XcodeGraph/compare/0.19.4..0.19.5
From 893961b198af7e064b07201d734452eb04fcd445 Mon Sep 17 00:00:00 2001
From: Xianhe
Date: Tue, 17 Dec 2024 00:26:14 +0800
Subject: [PATCH 099/914] feat: Add embeddable plugin determination (#86)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Marek Fořt
---
Sources/XcodeGraph/Models/Target.swift | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 2ddd943b..f7ac328d 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -259,6 +259,12 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
product == .xpc
}
+ /// Determines if the target is an embeddable plugin
+ /// i.e. a product that can be bundled with a host macOS application or a Mac Catalyst application
+ public func isEmbeddablePlugin() -> Bool {
+ supports(.macOS) && product == .bundle
+ }
+
/// Determines if the target is an embeddable system extension
/// i.e. a product that can be bundled with a host macOS application
public func isEmbeddableSystemExtension() -> Bool {
@@ -277,6 +283,12 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
supports(.macOS) && product == .app
}
+ /// Determines if the target is able to embed a plugin
+ /// i.e. a product that can be bundled with a macOS application or a Mac Catalyst application
+ public func canEmbedPlugins() -> Bool {
+ (supports(.macOS) || supportsCatalyst) && product == .app
+ }
+
/// Return the a set of PlatformFilters to control linking based on what platform is being compiled
/// This allows a target to link against a dependency conditionally when it is being compiled for a compatible platform
/// E.g. An app linking against CarPlay only when built for iOS.
From 4b3fd65736e68a6f333aa34a02f7753416120d80 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Mon, 16 Dec 2024 16:26:31 +0000
Subject: [PATCH 100/914] [Release] XcodeGraph 1.1.0
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8f364e2..87dbeaf6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.1.0] - 2024-12-16
+### Details
+#### Feat
+- Add embeddable plugin determination by @foyoodo in [#86](https://github.com/tuist/XcodeGraph/pull/86)
+
+## New Contributors
+* @foyoodo made their first contribution in [#86](https://github.com/tuist/XcodeGraph/pull/86)
## [1.0.2] - 2024-12-12
### Details
#### Chore
@@ -292,6 +299,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.1.0]: https://github.com/tuist/XcodeGraph/compare/1.0.2..1.1.0
[1.0.2]: https://github.com/tuist/XcodeGraph/compare/1.0.1..1.0.2
[1.0.1]: https://github.com/tuist/XcodeGraph/compare/1.0.0..1.0.1
[1.0.0]: https://github.com/tuist/XcodeGraph/compare/0.19.5..1.0.0
From 7c6584c3c615059df806c2c469e4f1f8baca2a4f Mon Sep 17 00:00:00 2001
From: Vladimir
Date: Tue, 17 Dec 2024 11:57:25 +0300
Subject: [PATCH 101/914] feat: supportsResources returns true if product is
staticFramework (#84)
Co-authored-by: Vladimir Kushelkov
---
Sources/XcodeGraph/Models/Target.swift | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index f7ac328d..941df996 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -128,12 +128,12 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
/// Returns whether a target is exclusive to a single platform
public func isExclusiveTo(_ platform: Platform) -> Bool {
- destinations.map(\.platform).allSatisfy { $0 == platform }
+ destinations.allSatisfy { $0.platform == platform }
}
/// Returns whether a target supports a platform
public func supports(_ platform: Platform) -> Bool {
- destinations.map(\.platform).contains(platform)
+ destinations.contains { $0.platform == platform }
}
/// List of platforms this target deploys to
@@ -209,6 +209,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
switch product {
case .app,
.framework,
+ .staticFramework,
.unitTests,
.uiTests,
.bundle,
@@ -227,7 +228,6 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
.macro,
.dynamicLibrary,
.staticLibrary,
- .staticFramework,
.xpc:
return false
}
@@ -239,10 +239,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
/// Returns true if the target is an AppClip
public var isAppClip: Bool {
- if case .appClip = product {
- return true
- }
- return false
+ product == .appClip
}
/// Determines if the target is an embeddable watch application
From af2e9fbe6edf508ee951543610af169b3233586c Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Tue, 17 Dec 2024 08:57:49 +0000
Subject: [PATCH 102/914] [Release] XcodeGraph 1.2.0
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87dbeaf6..c0460674 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.0] - 2024-12-17
+### Details
+#### Feat
+- SupportsResources returns true if product is staticFramework by @vldalx in [#84](https://github.com/tuist/XcodeGraph/pull/84)
+
+## New Contributors
+* @vldalx made their first contribution in [#84](https://github.com/tuist/XcodeGraph/pull/84)
## [1.1.0] - 2024-12-16
### Details
#### Feat
@@ -299,6 +306,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.2.0]: https://github.com/tuist/XcodeGraph/compare/1.1.0..1.2.0
[1.1.0]: https://github.com/tuist/XcodeGraph/compare/1.0.2..1.1.0
[1.0.2]: https://github.com/tuist/XcodeGraph/compare/1.0.1..1.0.2
[1.0.1]: https://github.com/tuist/XcodeGraph/compare/1.0.0..1.0.1
From 29616be6447395ef3311581e237664048096d555 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 17 Dec 2024 18:28:52 +0000
Subject: [PATCH 103/914] chore(deps): update dependency tuist to v4.38.0 (#88)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index ca39cf17..8f9990e4 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.37.0"
+tuist = "4.38.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 904a29a9a9a1ee442729f64b50848dc1dfb9731b Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Tue, 17 Dec 2024 18:29:08 +0000
Subject: [PATCH 104/914] [Release] XcodeGraph 1.2.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0460674..4d7199a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.1] - 2024-12-17
+### Details
+#### Chore
+- Update dependency tuist to v4.38.0 by @renovate[bot] in [#88](https://github.com/tuist/XcodeGraph/pull/88)
+
## [1.2.0] - 2024-12-17
### Details
#### Feat
@@ -306,6 +311,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.2.1]: https://github.com/tuist/XcodeGraph/compare/1.2.0..1.2.1
[1.2.0]: https://github.com/tuist/XcodeGraph/compare/1.1.0..1.2.0
[1.1.0]: https://github.com/tuist/XcodeGraph/compare/1.0.2..1.1.0
[1.0.2]: https://github.com/tuist/XcodeGraph/compare/1.0.1..1.0.2
From bbf1d36538255b1fd8881214c3bd004f35517fab Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 18 Dec 2024 15:11:14 +0000
Subject: [PATCH 105/914] chore(deps): update dependency tuist to v4.38.1 (#89)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 8f9990e4..073d2d6a 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.38.0"
+tuist = "4.38.1"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 0020a01f1045fbdcde0a50e963959a0a989ffd25 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 18 Dec 2024 15:11:32 +0000
Subject: [PATCH 106/914] [Release] XcodeGraph 1.2.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d7199a4..264909a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.2] - 2024-12-18
+### Details
+#### Chore
+- Update dependency tuist to v4.38.1 by @renovate[bot] in [#89](https://github.com/tuist/XcodeGraph/pull/89)
+
## [1.2.1] - 2024-12-17
### Details
#### Chore
@@ -311,6 +316,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.2.2]: https://github.com/tuist/XcodeGraph/compare/1.2.1..1.2.2
[1.2.1]: https://github.com/tuist/XcodeGraph/compare/1.2.0..1.2.1
[1.2.0]: https://github.com/tuist/XcodeGraph/compare/1.1.0..1.2.0
[1.1.0]: https://github.com/tuist/XcodeGraph/compare/1.0.2..1.1.0
From 93b57a6866be7b99e5a410f905f6b414255ca130 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 24 Dec 2024 22:39:28 +0000
Subject: [PATCH 107/914] chore(deps): update dependency tuist to v4.38.2 (#90)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 073d2d6a..eb0bd3c3 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.38.1"
+tuist = "4.38.2"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 83364ff902dc3974157f23f3a2b0228a0c811ba8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Tue, 24 Dec 2024 22:39:45 +0000
Subject: [PATCH 108/914] [Release] XcodeGraph 1.2.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 264909a8..213c2a5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.3] - 2024-12-24
+### Details
+#### Chore
+- Update dependency tuist to v4.38.2 by @renovate[bot] in [#90](https://github.com/tuist/XcodeGraph/pull/90)
+
## [1.2.2] - 2024-12-18
### Details
#### Chore
@@ -316,6 +321,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.2.3]: https://github.com/tuist/XcodeGraph/compare/1.2.2..1.2.3
[1.2.2]: https://github.com/tuist/XcodeGraph/compare/1.2.1..1.2.2
[1.2.1]: https://github.com/tuist/XcodeGraph/compare/1.2.0..1.2.1
[1.2.0]: https://github.com/tuist/XcodeGraph/compare/1.1.0..1.2.0
From 7824023ecae039d746d211f880cd27a818c5eb7a Mon Sep 17 00:00:00 2001
From: Jaewon-Yun
Date: Thu, 26 Dec 2024 17:56:50 +0900
Subject: [PATCH 109/914] Replace `isExternal` to distinguish a source of the
project (#22)
* Replace `isExternal` to `type`
* Revert "Replace `isExternal` to `type`"
This reverts commit a587120c4ecb292ffb4a2b033243df2f03edf0ae.
* Add `tags` to `Target` model
* Reconcile the need of specifying whether something is local or not with the new metadata attribute in targets
* Note the breaking change in BREAKME.md
* Fix typo and run tests
---------
Co-authored-by: Pedro
---
.github/workflows/XcodeGraph.yml | 44 +++++++++++++++++++
BREAKME.md | 6 ++-
.../Models/Metadata/TargetMetadata.swift | 29 ++++++++++--
Sources/XcodeGraph/Models/Project.swift | 2 +-
.../XcodeGraphTests/Models/TargetTests.swift | 18 +-------
5 files changed, 76 insertions(+), 23 deletions(-)
diff --git a/.github/workflows/XcodeGraph.yml b/.github/workflows/XcodeGraph.yml
index 2b422d93..576ef25f 100644
--- a/.github/workflows/XcodeGraph.yml
+++ b/.github/workflows/XcodeGraph.yml
@@ -22,6 +22,34 @@ env:
TUIST_CONFIG_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
jobs:
+ spm_test:
+ name: SPM Test
+ strategy:
+ matrix:
+ os:
+ - ubuntu-22.04
+ - macos-15
+ swift-version:
+ - "5.9"
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Swift
+ if: matrix.os == 'ubuntu-22.04'
+ uses: SwiftyLab/setup-swift@latest
+ with:
+ swift-version: ${{ matrix.swift-version }}
+ # DEBUG mode
+ - name: Build with debug mode.
+ id: debug_build
+ run: swift build --configuration debug
+ continue-on-error: true
+ - name: Retry build with debug mode if necessary
+ if: steps.debug_build.outcome == 'failure'
+ run: |
+ swift build --configuration debug
+ - name: Test
+ run: swift test
spm_build:
name: SPM Build
strategy:
@@ -57,6 +85,22 @@ jobs:
if: steps.release_build.outcome == 'failure'
run: |
swift build --configuration release
+ tuist_test:
+ name: Tuist Test
+ strategy:
+ matrix:
+ os:
+ - macos-15
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: jdx/mise-action@v2
+ with:
+ version: 2024.11.8
+ - name: Install dependencies
+ run: tuist install
+ - name: Test
+ run: tuist test
tuist_build:
name: Tuist Build
strategy:
diff --git a/BREAKME.md b/BREAKME.md
index e9314b34..e4245af6 100644
--- a/BREAKME.md
+++ b/BREAKME.md
@@ -3,4 +3,8 @@
This document lists breaking changes to introduce in the next major version of the project.
Every change must include what needs to be changed, and the rationale behind it.
-## Changes
\ No newline at end of file
+## Changes
+
+## Drop `TargetMetadata.init`
+
+We added the `TargetMetadata.metadata` API that gives us more flexibility and makes the instantiation of metadata more idiomatic. Therefore, we should remove `TargetMetadata.init`.
\ No newline at end of file
diff --git a/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
index e72de39d..d137a4d1 100644
--- a/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
@@ -1,21 +1,42 @@
/// The metadata associated with a target.
-public struct TargetMetadata: Codable, Equatable {
+public struct TargetMetadata: Codable, Equatable, Sendable {
+ /// Tags set by the user to group targets together.
+ /// Some Tuist features can leverage that information for doing things like filtering.
public var tags: Set
+ /// Projects can be external or not, which means they are declared or not using the Tuist Projects' DSL
+ /// and the targets of those projects can fall into two categories:
+ /// - Local: They've been linked from a local directory (e.g., a local package)
+ /// - Remote: They've been resolved and pulled by a package manager (e.g. SPM)
+ public var isLocal: Bool
+
+ @available(*, deprecated, renamed: "metadata(tags:)", message: "Use the static 'metadata' initializer instead")
public init(
tags: Set
) {
self.tags = tags
+ isLocal = true
+ }
+
+ init(tags: Set, isLocal: Bool) {
+ self.tags = tags
+ self.isLocal = isLocal
+ }
+
+ public static func metadata(tags: Set = Set(), isLocal: Bool = true) -> TargetMetadata {
+ self.init(tags: tags, isLocal: isLocal)
}
}
#if DEBUG
extension TargetMetadata {
public static func test(
- tags: Set = []
+ tags: Set = [],
+ isLocal: Bool = true
) -> TargetMetadata {
- TargetMetadata(
- tags: tags
+ TargetMetadata.metadata(
+ tags: tags,
+ isLocal: isLocal
)
}
}
diff --git a/Sources/XcodeGraph/Models/Project.swift b/Sources/XcodeGraph/Models/Project.swift
index 85a975a9..89a67d8f 100644
--- a/Sources/XcodeGraph/Models/Project.swift
+++ b/Sources/XcodeGraph/Models/Project.swift
@@ -12,7 +12,7 @@ public enum ProjectType: Hashable, Equatable, Codable, CustomStringConvertible,
public var description: String {
switch self {
case .local: "local project"
- case let .external(hash): "external project"
+ case .external: "external project"
}
}
}
diff --git a/Tests/XcodeGraphTests/Models/TargetTests.swift b/Tests/XcodeGraphTests/Models/TargetTests.swift
index fb6a9d2c..ebbe0927 100644
--- a/Tests/XcodeGraphTests/Models/TargetTests.swift
+++ b/Tests/XcodeGraphTests/Models/TargetTests.swift
@@ -16,23 +16,7 @@ final class TargetTests: XCTestCase {
XCTAssertEqual(
Target.validSourceExtensions,
[
- "m",
- "swift",
- "mm",
- "cpp",
- "c++",
- "cc",
- "c",
- "d",
- "s",
- "intentdefinition",
- "xcmappingmodel",
- "metal",
- "mlmodel",
- "docc",
- "playground",
- "rcproject",
- "mlpackage",
+ "m", "swift", "mm", "cpp", "c++", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel",
]
)
}
From c11bcd24f3d4bbaa8aca31d4769a69fd0c69a8b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Pi=C3=B1era=20Buend=C3=ADa?=
<663605+pepicrft@users.noreply.github.com>
Date: Thu, 26 Dec 2024 15:21:50 +0100
Subject: [PATCH 110/914] fix: replace `TargetMetadata.isLocal` with
`Target.type` enum (#91)
Replaces the `isLocal` boolean property with a new `TargetType` enum that better represents whether a target is local or remote. This change provides more explicit semantics around target types and removes the need for documentation comments explaining the boolean's meaning.
---
Package.swift | 7 +++++++
.../Models/Metadata/TargetMetadata.swift | 16 +++-------------
Sources/XcodeGraph/Models/Target.swift | 9 +++++++--
Sources/XcodeGraph/Models/TargetType.swift | 6 ++++++
4 files changed, 23 insertions(+), 15 deletions(-)
create mode 100644 Sources/XcodeGraph/Models/TargetType.swift
diff --git a/Package.swift b/Package.swift
index e63cef20..781feed7 100644
--- a/Package.swift
+++ b/Package.swift
@@ -13,6 +13,13 @@ var targets: [Target] = [
.enableExperimentalFeature("StrictConcurrency"),
]
),
+ .testTarget(
+ name: "XcodeGraphTests",
+ dependencies: [.target(name: "XcodeGraph")],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
+ ]
+ ),
]
let package = Package(
diff --git a/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
index d137a4d1..2f1d51eb 100644
--- a/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/TargetMetadata.swift
@@ -4,23 +4,15 @@ public struct TargetMetadata: Codable, Equatable, Sendable {
/// Some Tuist features can leverage that information for doing things like filtering.
public var tags: Set
- /// Projects can be external or not, which means they are declared or not using the Tuist Projects' DSL
- /// and the targets of those projects can fall into two categories:
- /// - Local: They've been linked from a local directory (e.g., a local package)
- /// - Remote: They've been resolved and pulled by a package manager (e.g. SPM)
- public var isLocal: Bool
-
@available(*, deprecated, renamed: "metadata(tags:)", message: "Use the static 'metadata' initializer instead")
public init(
tags: Set
) {
self.tags = tags
- isLocal = true
}
- init(tags: Set, isLocal: Bool) {
+ init(tags: Set, isLocal _: Bool) {
self.tags = tags
- self.isLocal = isLocal
}
public static func metadata(tags: Set = Set(), isLocal: Bool = true) -> TargetMetadata {
@@ -31,12 +23,10 @@ public struct TargetMetadata: Codable, Equatable, Sendable {
#if DEBUG
extension TargetMetadata {
public static func test(
- tags: Set = [],
- isLocal: Bool = true
+ tags: Set = []
) -> TargetMetadata {
TargetMetadata.metadata(
- tags: tags,
- isLocal: isLocal
+ tags: tags
)
}
}
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 941df996..58844188 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -50,6 +50,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
public let mergeable: Bool
public let onDemandResourcesTags: OnDemandResourcesTags?
public let metadata: TargetMetadata
+ public let type: TargetType
// MARK: - Init
@@ -81,7 +82,8 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
mergedBinaryType: MergedBinaryType = .disabled,
mergeable: Bool = false,
onDemandResourcesTags: OnDemandResourcesTags? = nil,
- metadata: TargetMetadata = .init(tags: [])
+ metadata: TargetMetadata = .metadata(tags: []),
+ type: TargetType = .local
) {
self.name = name
self.product = product
@@ -111,6 +113,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
self.mergeable = mergeable
self.onDemandResourcesTags = onDemandResourcesTags
self.metadata = metadata
+ self.type = type
}
/// Given a target name, it obtains the product name by turning "-" characters into "_" and "/" into "_"
@@ -312,7 +315,8 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
lhs.dependencies == rhs.dependencies &&
lhs.mergedBinaryType == rhs.mergedBinaryType &&
lhs.mergeable == rhs.mergeable &&
- lhs.environmentVariables == rhs.environmentVariables
+ lhs.environmentVariables == rhs.environmentVariables &&
+ lhs.type == rhs.type
}
public func hash(into hasher: inout Hasher) {
@@ -322,6 +326,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
hasher.combine(bundleId)
hasher.combine(productName)
hasher.combine(environmentVariables)
+ hasher.combine(type)
}
/// Returns a new copy of the target with the given InfoPlist set.
diff --git a/Sources/XcodeGraph/Models/TargetType.swift b/Sources/XcodeGraph/Models/TargetType.swift
new file mode 100644
index 00000000..7c818350
--- /dev/null
+++ b/Sources/XcodeGraph/Models/TargetType.swift
@@ -0,0 +1,6 @@
+public enum TargetType: Codable, Hashable, Equatable, Sendable {
+ /// A target is local when it hasn't been resolved and pulled by a package manager (e.g., SPM).
+ case local
+ /// A target is remote, when it has been resolved and pulled by a package manager (e.g., SwiftPM).
+ case remote
+}
From d689d7dc436437ed3af3202b6d13284a5ab8298a Mon Sep 17 00:00:00 2001
From: pepicrft
Date: Thu, 26 Dec 2024 14:22:10 +0000
Subject: [PATCH 111/914] [Release] XcodeGraph 1.2.4
---
CHANGELOG.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 213c2a5a..24381ec8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.4] - 2024-12-26
+### Details
+#### Fix
+- Replace `TargetMetadata.isLocal` with `Target.type` enum by @pepicrft in [#91](https://github.com/tuist/XcodeGraph/pull/91)
+
+## New Contributors
+* @woin2ee made their first contribution in [#22](https://github.com/tuist/XcodeGraph/pull/22)
## [1.2.3] - 2024-12-24
### Details
#### Chore
@@ -321,6 +328,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.2.4]: https://github.com/tuist/XcodeGraph/compare/1.2.3..1.2.4
[1.2.3]: https://github.com/tuist/XcodeGraph/compare/1.2.2..1.2.3
[1.2.2]: https://github.com/tuist/XcodeGraph/compare/1.2.1..1.2.2
[1.2.1]: https://github.com/tuist/XcodeGraph/compare/1.2.0..1.2.1
From 6479b3f032bd8060fda7632d62b4e145b0c9607a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Fri, 3 Jan 2025 09:59:49 +0100
Subject: [PATCH 112/914] feat: add TestableTarget.parallelization property
(#92)
---
.../XcodeGraph/Models/TestableTarget.swift | 40 +++++++++++++++++--
.../Models/TestableTargetTests.swift | 18 ++++++++-
2 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/Sources/XcodeGraph/Models/TestableTarget.swift b/Sources/XcodeGraph/Models/TestableTarget.swift
index 7d800202..aa01d488 100644
--- a/Sources/XcodeGraph/Models/TestableTarget.swift
+++ b/Sources/XcodeGraph/Models/TestableTarget.swift
@@ -3,27 +3,61 @@ import Path
/// Testable target describe target and tests information.
public struct TestableTarget: Equatable, Hashable, Codable, Sendable {
+ /// With the introduction of Swift Testing and Xcode 16, you can now choose to run your tests
+ /// in parallel across either the full suite of tests in a target with `.all`, just those created
+ /// under Swift Testing with `.swiftTestingOnly`, or run them serially with the `.none` option.
+ public enum Parallelization: Equatable, Hashable, Codable, Sendable {
+ case none, swiftTestingOnly, all
+ }
+
/// The target name and its project path.
public let target: TargetReference
/// Skip test target from TestAction.
public let isSkipped: Bool
+
/// Execute tests in parallel.
- public let isParallelizable: Bool
+ @available(
+ *,
+ deprecated,
+ renamed: "parallelization",
+ message: "isParallelizable was deprecated. Use the paralellization property instead."
+ )
+ public var isParallelizable: Bool {
+ parallelization == .none
+ }
+
+ public let parallelization: Parallelization
+
/// Execute tests in random order.
public let isRandomExecutionOrdering: Bool
/// A simulated location used when testing this test target.
public let simulatedLocation: SimulatedLocation?
+ @available(*, deprecated, renamed: "init(target:skipped:parallelization:randomExecutionOrdering:simulatedLocation:)")
+ public init(
+ target: TargetReference,
+ skipped: Bool = false,
+ parallelizable: Bool,
+ randomExecutionOrdering: Bool = false,
+ simulatedLocation: SimulatedLocation? = nil
+ ) {
+ self.target = target
+ isSkipped = skipped
+ parallelization = parallelizable ? .all : .none
+ isRandomExecutionOrdering = randomExecutionOrdering
+ self.simulatedLocation = simulatedLocation
+ }
+
public init(
target: TargetReference,
skipped: Bool = false,
- parallelizable: Bool = false,
+ parallelization: Parallelization = .none,
randomExecutionOrdering: Bool = false,
simulatedLocation: SimulatedLocation? = nil
) {
self.target = target
isSkipped = skipped
- isParallelizable = parallelizable
+ self.parallelization = parallelization
isRandomExecutionOrdering = randomExecutionOrdering
self.simulatedLocation = simulatedLocation
}
diff --git a/Tests/XcodeGraphTests/Models/TestableTargetTests.swift b/Tests/XcodeGraphTests/Models/TestableTargetTests.swift
index 2332fb21..ea780400 100644
--- a/Tests/XcodeGraphTests/Models/TestableTargetTests.swift
+++ b/Tests/XcodeGraphTests/Models/TestableTargetTests.swift
@@ -5,7 +5,7 @@ import XCTest
@testable import XcodeGraph
final class TestableTargetTests: XCTestCase {
- func test_codable() {
+ func test_codable_with_deprecated_parallelizable() {
// Given
let subject = TestableTarget(
target: .init(
@@ -20,4 +20,20 @@ final class TestableTargetTests: XCTestCase {
// Then
XCTAssertCodable(subject)
}
+
+ func test_codable() {
+ // Given
+ let subject = TestableTarget(
+ target: .init(
+ projectPath: try! AbsolutePath(validating: "/path/to/project"),
+ name: "name"
+ ),
+ skipped: true,
+ parallelization: .all,
+ randomExecutionOrdering: true
+ )
+
+ // Then
+ XCTAssertCodable(subject)
+ }
}
From 0b0b0fcad094702f2df5c143d862fa2ef6bcf3a4 Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Fri, 3 Jan 2025 09:00:10 +0000
Subject: [PATCH 113/914] [Release] XcodeGraph 1.3.0
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24381ec8..1145be5d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.0] - 2025-01-03
+### Details
+#### Feat
+- Add TestableTarget.parallelization property by @fortmarek in [#92](https://github.com/tuist/XcodeGraph/pull/92)
+
## [1.2.4] - 2024-12-26
### Details
#### Fix
@@ -328,6 +333,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.3.0]: https://github.com/tuist/XcodeGraph/compare/1.2.4..1.3.0
[1.2.4]: https://github.com/tuist/XcodeGraph/compare/1.2.3..1.2.4
[1.2.3]: https://github.com/tuist/XcodeGraph/compare/1.2.2..1.2.3
[1.2.2]: https://github.com/tuist/XcodeGraph/compare/1.2.1..1.2.2
From 952751a8a39b83aea8678aad6cc45fd91828e06f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 8 Jan 2025 22:56:17 +0000
Subject: [PATCH 114/914] chore(deps): update dependency tuist to v4.39.0 (#93)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index eb0bd3c3..558fa2ed 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.38.2"
+tuist = "4.39.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 4f5f9c08379d7534621d6d2a19888986bce79a37 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Wed, 8 Jan 2025 22:56:34 +0000
Subject: [PATCH 115/914] [Release] XcodeGraph 1.3.1
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1145be5d..6354f7de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.1] - 2025-01-08
+### Details
+#### Chore
+- Update dependency tuist to v4.39.0 by @renovate[bot] in [#93](https://github.com/tuist/XcodeGraph/pull/93)
+
## [1.3.0] - 2025-01-03
### Details
#### Feat
@@ -333,6 +338,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.3.1]: https://github.com/tuist/XcodeGraph/compare/1.3.0..1.3.1
[1.3.0]: https://github.com/tuist/XcodeGraph/compare/1.2.4..1.3.0
[1.2.4]: https://github.com/tuist/XcodeGraph/compare/1.2.3..1.2.4
[1.2.3]: https://github.com/tuist/XcodeGraph/compare/1.2.2..1.2.3
From fc10c6fca3af12900d04ff10683b329ff1223454 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Fo=C5=99t?=
Date: Fri, 10 Jan 2025 09:41:56 +0100
Subject: [PATCH 116/914] fix: revert staticFramework supporting resources
(#94)
---
Sources/XcodeGraph/Models/Target.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift
index 58844188..bd86c180 100644
--- a/Sources/XcodeGraph/Models/Target.swift
+++ b/Sources/XcodeGraph/Models/Target.swift
@@ -212,7 +212,6 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
switch product {
case .app,
.framework,
- .staticFramework,
.unitTests,
.uiTests,
.bundle,
@@ -231,6 +230,7 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable {
.macro,
.dynamicLibrary,
.staticLibrary,
+ .staticFramework,
.xpc:
return false
}
From c533cf2543dc41428f0118d4f0df5c890307918a Mon Sep 17 00:00:00 2001
From: fortmarek
Date: Fri, 10 Jan 2025 08:42:11 +0000
Subject: [PATCH 117/914] [Release] XcodeGraph 1.3.2
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6354f7de..6f61b303 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.2] - 2025-01-10
+### Details
+#### Fix
+- Revert staticFramework supporting resources by @fortmarek in [#94](https://github.com/tuist/XcodeGraph/pull/94)
+
## [1.3.1] - 2025-01-08
### Details
#### Chore
@@ -338,6 +343,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.3.2]: https://github.com/tuist/XcodeGraph/compare/1.3.1..1.3.2
[1.3.1]: https://github.com/tuist/XcodeGraph/compare/1.3.0..1.3.1
[1.3.0]: https://github.com/tuist/XcodeGraph/compare/1.2.4..1.3.0
[1.2.4]: https://github.com/tuist/XcodeGraph/compare/1.2.3..1.2.4
From 20ca790450a728f3d31b2c73e97e9a1d1ac79951 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 10 Jan 2025 18:23:47 +0000
Subject: [PATCH 118/914] chore(deps): update dependency tuist to v4.39.1 (#95)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 558fa2ed..78329322 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.39.0"
+tuist = "4.39.1"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From 6dd713a53fa1a51c2e9c41720ab84ff932e6d6b3 Mon Sep 17 00:00:00 2001
From: "renovate[bot]"
Date: Fri, 10 Jan 2025 18:24:05 +0000
Subject: [PATCH 119/914] [Release] XcodeGraph 1.3.3
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f61b303..4327e1c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.3] - 2025-01-10
+### Details
+#### Chore
+- Update dependency tuist to v4.39.1 by @renovate[bot] in [#95](https://github.com/tuist/XcodeGraph/pull/95)
+
## [1.3.2] - 2025-01-10
### Details
#### Fix
@@ -343,6 +348,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.3.3]: https://github.com/tuist/XcodeGraph/compare/1.3.2..1.3.3
[1.3.2]: https://github.com/tuist/XcodeGraph/compare/1.3.1..1.3.2
[1.3.1]: https://github.com/tuist/XcodeGraph/compare/1.3.0..1.3.1
[1.3.0]: https://github.com/tuist/XcodeGraph/compare/1.2.4..1.3.0
From 892f93e9cef7062d92520c06d632898d6fc9ac10 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 22 Jan 2025 21:17:10 +0000
Subject: [PATCH 120/914] chore(deps): update dependency tuist to v4.40.0 (#96)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.mise.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mise.toml b/.mise.toml
index 78329322..6cd94164 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -1,5 +1,5 @@
[tools]
-tuist = "4.39.1"
+tuist = "4.40.0"
swiftlint = "0.54.0"
swiftformat = "0.53.3"
"git-cliff" = "2.6.1"
From eb6a05e508127e123a452898c7574883f003b685 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 22 Jan 2025 21:17:26 +0000
Subject: [PATCH 121/914] [Release] XcodeGraph 1.3.4
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4327e1c8..d9d241de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.4] - 2025-01-22
+### Details
+#### Chore
+- Update dependency tuist to v4.40.0 by @renovate[bot] in [#96](https://github.com/tuist/XcodeGraph/pull/96)
+
## [1.3.3] - 2025-01-10
### Details
#### Chore
@@ -348,6 +353,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Contributors
* @darrarski made their first contribution
+[1.3.4]: https://github.com/tuist/XcodeGraph/compare/1.3.3..1.3.4
[1.3.3]: https://github.com/tuist/XcodeGraph/compare/1.3.2..1.3.3
[1.3.2]: https://github.com/tuist/XcodeGraph/compare/1.3.1..1.3.2
[1.3.1]: https://github.com/tuist/XcodeGraph/compare/1.3.0..1.3.1
From 527101c47bf341007fdd36e21a58f54b5939f9b5 Mon Sep 17 00:00:00 2001
From: Andy Kolean
Date: Wed, 5 Feb 2025 03:27:49 -0500
Subject: [PATCH 122/914] feat: introduce XcodeProjMapper to map XcodeProj to
XcodeGraph (#87)
* [XcodeProjToGraph] add XcodeProjToGraph and tests
* lint files
* [ProjectDescriptionHelpers] add fixtures to resources
* [WorkspaceMapperTests] add unit tests
* [Integration Tests] remove
* [access modifiers] add missing access modifiers
* [swift tools version] 5.9
* Address code review feedback: refine mappers, clean up test data, and improve structure
* [XcodeProj] mainPBXProject extension
* refactor: add accidental public removal back
* feat: Strengthen error handling for unknown objects
* feat: Introduce PathDependencyMapper
* refactor: Remove ProjectParser & unify usage via XcodeGraphMapper
* chore: Bump XcodeProj version 8.26.0
* feat: Add command runner
* feat: MetadataProviders module
* feat: MetadataProvidersTests
* refactor: Test cleanup
* lint
* [Fixtures] add test fixtures for metadata module
* [Error] remove fatal error
* [Path] for unwrap proj and workspace path
* [Tuist] manifest update
* [Tests] fix tests
* [Lint] lint files
* [MockProvider] remove
* [metadata] integrate into mappers
* [Phases] fix access modifiers
* [Tests] fix flaky tests
* [Lint] lint files
* [Tuist] fix manifest
* [Doc] update readme
* [plis] remove dead code
* [Version] Update swift version to 5.10
* [Lint] lint errors
* Fix build on Linux
* Remove Project.swift
* Remove cache workflow
* Skip testing XcodeMetadataTests
* Update FileSystem
---------
Co-authored-by: fortmarek
---
.gitattributes | 1 +
.github/workflows/XcodeGraph.yml | 41 +-
.github/workflows/cache.yml | 38 --
.gitignore | 1 +
.swiftformat | 2 +-
.swiftlint.yml | 9 +-
Package.resolved | 156 ++++-
Package.swift | 57 +-
Project.swift | 107 ---
README.md | 25 +-
.../DependenciesGraph/DependenciesGraph.swift | 4 +-
Sources/XcodeGraph/Graph/Graph.swift | 2 +-
.../XcodeGraph/Graph/GraphDependency.swift | 6 +-
Sources/XcodeGraph/Graph/GraphEdge.swift | 2 +-
.../Models/BinaryArchitecture.swift | 4 +-
Sources/XcodeGraph/Models/Platform.swift | 8 +-
.../XcodeGraph/Models/PlatformFilter.swift | 2 +-
Sources/XcodeGraph/Models/Plist.swift | 20 +-
.../Models/ResourceSynthesizer.swift | 2 +-
Sources/XcodeGraph/Models/SDKSource.swift | 2 +-
.../XcodeGraph/Models/TestableTarget.swift | 2 +-
.../Models/XCFrameworkInfoPlist.swift | 10 +-
.../BinaryArchitecture+Extension.swift | 87 +++
.../Extensions/FileHandle+ReadHelpers.swift | 28 +
.../Extensions/MachOByteSwapExtensions.swift | 69 ++
.../Sequence+ExecutionContext.swift | 63 ++
.../LoggerServiceContextKey.swift | 16 +
.../Providers/FrameworkMetadataProvider.swift | 104 +++
.../Providers/LibraryMetadataProvider.swift | 77 +++
.../PrecompiledMetadataProvider.swift | 241 +++++++
.../SystemFrameworkMetadataProvider.swift | 96 +++
.../XCFrameworkMetadataProvider.swift | 196 ++++++
.../Documentation.docc/XcodeProjMapper.md | 26 +
.../Extensions/PBXProject+Extensions.swift | 11 +
.../Extensions/Package+Extensions.swift | 13 +
.../Extensions/Platform+Extensions.swift | 28 +
.../PlatformFilter+Extensions.swift | 25 +
.../TargetDependency+Extensions.swift | 25 +
.../Extensions/XCWorkspace+Extensions.swift | 28 +
.../XCWorkspaceDataFileRef+Extensions.swift | 38 ++
.../Mappers/Graph/XcodeGraphMapper.swift | 286 ++++++++
.../Mappers/Packages/XCPackageMapper.swift | 78 +++
.../Mappers/Phases/BuildPhaseConstants.swift | 46 ++
.../Phases/PBXCopyFilesBuildPhaseMapper.swift | 80 +++
.../PBXCoreDataModelsBuildPhaseMapper.swift | 51 ++
.../PBXFrameworksBuildPhaseMapper.swift | 67 ++
.../Phases/PBXHeadersBuildPhaseMapper.swift | 82 +++
.../Phases/PBXResourcesBuildPhaseMapper.swift | 86 +++
.../Phases/PBXScriptsBuildPhaseMapper.swift | 116 ++++
.../Phases/PBXSourcesBuildPhaseMapper.swift | 71 ++
.../Mappers/Project/PBXProjectMapper.swift | 157 +++++
.../Mappers/Project/ProjectAttribute.swift | 8 +
.../Project/XcodeProj+Extensions.swift | 23 +
.../SchemeDiagnosticsOptions+XCScheme.swift | 25 +
.../Mappers/Schemes/XCSchemeMapper.swift | 224 +++++++
.../Mappers/Settings/BuildSettings.swift | 100 +++
.../XCConfigurationList+Helpers.swift | 41 ++
.../Settings/XCConfigurationMapper.swift | 128 ++++
.../Mappers/Targets/PBXBuildRuleMapper.swift | 67 ++
.../Targets/PBXTarget+BuildHeaders.swift | 8 +
.../Targets/PBXTarget+BuildSettings.swift | 88 +++
.../Targets/PBXTarget+GraphMapping.swift | 61 ++
.../Targets/PBXTarget+PlatformInference.swift | 90 +++
...BXTargetDependency+PlatformCondition.swift | 19 +
.../Targets/PBXTargetDependencyMapper.swift | 181 +++++
.../Mappers/Targets/PBXTargetMapper.swift | 336 ++++++++++
.../TargetDependency+GraphMapping.swift | 273 ++++++++
.../Mappers/Workspace/XCWorkspaceMapper.swift | 130 ++++
.../Utilities/ConfigurationMatcher.swift | 54 ++
.../DeveloperDirectoryProvider.swift | 32 +
.../Utilities/Optional+Throwing.swift | 13 +
.../Utilities/PathDependencyMapper.swift | 71 ++
.../MyFramework.xcframework/Info.plist | 39 ++
.../Headers/MyFramework-Swift.h | 204 ++++++
.../MyFramework.framework/Info.plist | Bin 0 -> 851 bytes
.../arm64-apple-ios.swiftdoc | Bin 0 -> 376 bytes
.../arm64-apple-ios.swiftinterface | 10 +
.../MyFramework.swiftmodule/arm64.swiftdoc | Bin 0 -> 376 bytes
.../arm64.swiftinterface | 10 +
.../Modules/module.modulemap | 4 +
.../MyFramework.framework/MyFramework | Bin 0 -> 99932 bytes
.../Headers/MyFramework-Swift.h | 204 ++++++
.../MyFramework.framework/Info.plist | Bin 0 -> 831 bytes
.../x86_64-apple-ios-simulator.swiftdoc | Bin 0 -> 388 bytes
.../x86_64-apple-ios-simulator.swiftinterface | 10 +
.../MyFramework.swiftmodule/x86_64.swiftdoc | Bin 0 -> 388 bytes
.../x86_64.swiftinterface | 10 +
.../Modules/module.modulemap | 4 +
.../MyFramework.framework/MyFramework | Bin 0 -> 32304 bytes
.../_CodeSignature/CodeResources | 190 ++++++
.../Info.plist | 39 ++
.../Headers/MyFramework-Swift.h | 204 ++++++
.../Info.plist | Bin 0 -> 851 bytes
.../arm64-apple-ios.swiftdoc | Bin 0 -> 376 bytes
.../arm64-apple-ios.swiftinterface | 10 +
.../MyFramework.swiftmodule/arm64.swiftdoc | Bin 0 -> 376 bytes
.../arm64.swiftinterface | 10 +
.../Modules/module.modulemap | 4 +
.../MyFrameworkMissingArch | Bin 0 -> 99932 bytes
.../Info.plist | 47 ++
.../Headers/MyMergeableFramework-Swift.h | 311 +++++++++
.../MyMergeableFramework.framework/Info.plist | Bin 0 -> 788 bytes
.../arm64-apple-ios.abi.json | 9 +
.../arm64-apple-ios.private.swiftinterface | 8 +
.../arm64-apple-ios.swiftdoc | Bin 0 -> 404 bytes
.../arm64-apple-ios.swiftinterface | 8 +
.../arm64-apple-ios.swiftmodule | Bin 0 -> 20676 bytes
.../Modules/module.modulemap | 4 +
.../MyMergeableFramework | Bin 0 -> 91336 bytes
.../Headers/MyMergeableFramework-Swift.h | 618 ++++++++++++++++++
.../MyMergeableFramework.framework/Info.plist | Bin 0 -> 768 bytes
.../x86_64-apple-ios-simulator.abi.json | 9 +
...apple-ios-simulator.private.swiftinterface | 8 +
.../x86_64-apple-ios-simulator.swiftdoc | Bin 0 -> 416 bytes
.../x86_64-apple-ios-simulator.swiftinterface | 8 +
.../x86_64-apple-ios-simulator.swiftmodule | Bin 0 -> 20852 bytes
.../Modules/module.modulemap | 4 +
.../MyMergeableFramework | Bin 0 -> 208800 bytes
.../_CodeSignature/CodeResources | 234 +++++++
.../MyStaticLibrary.xcframework/Info.plist | 39 ++
.../ios-arm64/libMyStaticLibrary.a | Bin 0 -> 26096 bytes
.../ios-x86_64-simulator/libMyStaticLibrary.a | Bin 0 -> 9960 bytes
Tests/Fixtures/libStaticLibrary.a | Bin 0 -> 7536 bytes
.../xpm.framework.dSYM/Contents/Info.plist | 20 +
.../Contents/Resources/DWARF/xpm | Bin 0 -> 9344 bytes
Tests/Fixtures/xpm.framework/Headers/xpm.h | 19 +
Tests/Fixtures/xpm.framework/Info.plist | Bin 0 -> 735 bytes
.../xpm.framework/Modules/module.modulemap | 6 +
.../xpm.framework/PrivateHeaders/private.h | 0
Tests/Fixtures/xpm.framework/xpm | Bin 0 -> 82572 bytes
.../Extensions/XCTestCase+Extras.swift | 4 +-
.../Models/TestableTargetTests.swift | 2 +-
.../AssertionsTesting.swift | 30 +
.../FrameworkMetadataProviderTests.swift | 39 ++
.../LibraryMetadataProviderTests.swift | 39 ++
.../PrecompiledMetadataProviderTests.swift | 96 +++
...SystemFrameworkMetadataProviderTests.swift | 108 +++
.../XCFrameworkMetadataProviderTests.swift | 251 +++++++
.../MapperTests/Graph/GraphMapperTests.swift | 216 ++++++
.../Package/XCPackageMapperTests.swift | 185 ++++++
.../PBXCopyFilesBuildPhaseMapperTests.swift | 61 ++
...XCoreDataModelsBuildPhaseMapperTests.swift | 57 ++
.../PBXFrameworksBuildPhaseMapperTests.swift | 43 ++
.../PBXHeadersBuildPhaseMapperTests.swift | 71 ++
.../PBXResourcesBuildPhaseMapperTests.swift | 87 +++
.../PBXScriptsBuildPhaseMapperTests.swift | 73 +++
.../PBXSourcesBuildPhaseMapperTests.swift | 139 ++++
.../Project/PBXProjectMapperTests.swift | 154 +++++
.../Schemes/XCSchemeMapperTests.swift | 280 ++++++++
.../Settings/BuildSettingsTests.swift | 104 +++
.../Settings/ConfigurationMatcherTests.swift | 78 +++
.../Settings/XCConfigurationMapperTests.swift | 152 +++++
.../Target/PBXBuildRuleMapperTests.swift | 147 +++++
.../Target/PBXTargetDependencyMapper.swift | 354 ++++++++++
.../Target/PBXTargetMapperTests.swift | 355 ++++++++++
.../TargetDependencyExtensionsTests.swift | 267 ++++++++
.../Workspace/XCWorkspaceMapperTests.swift | 214 ++++++
.../Mocks/AssertionsTesting.swift | 32 +
.../Mocks/MockDefaults.swift | 22 +
Tests/XcodeProjMapperTests/TestData/.swift | 1 +
.../TestData/PBXBuildRule+TestData.swift | 32 +
.../TestData/PBXFileReference+TestData.swift | 21 +
.../TestData/PBXGroup+TestData.swift | 17 +
.../TestData/PBXNativeTarget+TestData.swift | 36 +
.../TestData/PBXProj+TestData.swift | 61 ++
.../TestData/PBXProject+TestData.swift | 114 ++++
.../PBXShellScriptBuildPhase+TestData.swift | 35 +
.../PBXTargetDependency+TestData.swift | 19 +
.../TestData/PBXVariantGroup+TestData.swift | 17 +
.../XCBuildConfiguration+TestData.swift | 25 +
.../XCConfigurationList+TestData.swift | 15 +
.../TestData/XCScheme+TestData.swift | 29 +
.../XCSchemeTestableReference+TestData.swift | 28 +
.../TestData/XCUserData+TestData.swift | 27 +
.../TestData/XCVersionGroup+TestData.swift | 34 +
.../TestData/XCWorkspace+TestData.swift | 22 +
.../XCWorkspaceDataElement+TestData.swift | 7 +
.../XCWorkspaceDataGroup+TestData.swift | 9 +
.../TestData/XcodeProj+TestData.swift | 58 ++
Tuist.swift | 2 +-
Tuist/ProjectDescriptionHelpers/Module.swift | 34 +-
181 files changed, 11145 insertions(+), 226 deletions(-)
create mode 100644 .gitattributes
delete mode 100644 .github/workflows/cache.yml
delete mode 100644 Project.swift
create mode 100644 Sources/XcodeMetadata/Extensions/BinaryArchitecture+Extension.swift
create mode 100644 Sources/XcodeMetadata/Extensions/FileHandle+ReadHelpers.swift
create mode 100644 Sources/XcodeMetadata/Extensions/MachOByteSwapExtensions.swift
create mode 100644 Sources/XcodeMetadata/Extensions/Sequence+ExecutionContext.swift
create mode 100644 Sources/XcodeMetadata/LoggerServiceContextKey.swift
create mode 100644 Sources/XcodeMetadata/Providers/FrameworkMetadataProvider.swift
create mode 100644 Sources/XcodeMetadata/Providers/LibraryMetadataProvider.swift
create mode 100644 Sources/XcodeMetadata/Providers/PrecompiledMetadataProvider.swift
create mode 100644 Sources/XcodeMetadata/Providers/SystemFrameworkMetadataProvider.swift
create mode 100644 Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
create mode 100644 Sources/XcodeProjMapper/Documentation.docc/XcodeProjMapper.md
create mode 100644 Sources/XcodeProjMapper/Extensions/PBXProject+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/Package+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/Platform+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/PlatformFilter+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/TargetDependency+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/XCWorkspace+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Extensions/XCWorkspaceDataFileRef+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Graph/XcodeGraphMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Packages/XCPackageMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/BuildPhaseConstants.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXCoreDataModelsBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXResourcesBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXScriptsBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Project/PBXProjectMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Project/ProjectAttribute.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Project/XcodeProj+Extensions.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Schemes/SchemeDiagnosticsOptions+XCScheme.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Schemes/XCSchemeMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Settings/BuildSettings.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationList+Helpers.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXBuildRuleMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildHeaders.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildSettings.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+GraphMapping.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+PlatformInference.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependency+PlatformCondition.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/PBXTargetMapper.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
create mode 100644 Sources/XcodeProjMapper/Mappers/Workspace/XCWorkspaceMapper.swift
create mode 100644 Sources/XcodeProjMapper/Utilities/ConfigurationMatcher.swift
create mode 100644 Sources/XcodeProjMapper/Utilities/DeveloperDirectoryProvider.swift
create mode 100644 Sources/XcodeProjMapper/Utilities/Optional+Throwing.swift
create mode 100644 Sources/XcodeProjMapper/Utilities/PathDependencyMapper.swift
create mode 100644 Tests/Fixtures/MyFramework.xcframework/Info.plist
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Headers/MyFramework-Swift.h
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Info.plist
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftdoc
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftinterface
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64.swiftdoc
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64.swiftinterface
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/module.modulemap
create mode 100755 Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/MyFramework
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Headers/MyFramework-Swift.h
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Info.plist
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64.swiftdoc
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64.swiftinterface
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/module.modulemap
create mode 100755 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/MyFramework
create mode 100644 Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/_CodeSignature/CodeResources
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/Info.plist
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Headers/MyFramework-Swift.h
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Info.plist
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftdoc
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftinterface
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Modules/MyFramework.swiftmodule/arm64.swiftdoc
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Modules/MyFramework.swiftmodule/arm64.swiftinterface
create mode 100644 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Modules/module.modulemap
create mode 100755 Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/MyFrameworkMissingArch
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/Info.plist
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Headers/MyMergeableFramework-Swift.h
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Info.plist
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/arm64-apple-ios.abi.json
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/arm64-apple-ios.private.swiftinterface
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/arm64-apple-ios.swiftdoc
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/arm64-apple-ios.swiftinterface
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/arm64-apple-ios.swiftmodule
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/Modules/module.modulemap
create mode 100755 Tests/Fixtures/MyMergeableFramework.xcframework/ios-arm64/MyMergeableFramework.framework/MyMergeableFramework
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Headers/MyMergeableFramework-Swift.h
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Info.plist
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/MyMergeableFramework.swiftmodule/x86_64-apple-ios-simulator.swiftmodule
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/Modules/module.modulemap
create mode 100755 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/MyMergeableFramework
create mode 100644 Tests/Fixtures/MyMergeableFramework.xcframework/ios-x86_64-simulator/MyMergeableFramework.framework/_CodeSignature/CodeResources
create mode 100644 Tests/Fixtures/MyStaticLibrary.xcframework/Info.plist
create mode 100644 Tests/Fixtures/MyStaticLibrary.xcframework/ios-arm64/libMyStaticLibrary.a
create mode 100644 Tests/Fixtures/MyStaticLibrary.xcframework/ios-x86_64-simulator/libMyStaticLibrary.a
create mode 100644 Tests/Fixtures/libStaticLibrary.a
create mode 100644 Tests/Fixtures/xpm.framework.dSYM/Contents/Info.plist
create mode 100644 Tests/Fixtures/xpm.framework.dSYM/Contents/Resources/DWARF/xpm
create mode 100644 Tests/Fixtures/xpm.framework/Headers/xpm.h
create mode 100644 Tests/Fixtures/xpm.framework/Info.plist
create mode 100644 Tests/Fixtures/xpm.framework/Modules/module.modulemap
create mode 100644 Tests/Fixtures/xpm.framework/PrivateHeaders/private.h
create mode 100755 Tests/Fixtures/xpm.framework/xpm
create mode 100644 Tests/XcodeMetadataTests/AssertionsTesting.swift
create mode 100644 Tests/XcodeMetadataTests/FrameworkMetadataProviderTests.swift
create mode 100644 Tests/XcodeMetadataTests/LibraryMetadataProviderTests.swift
create mode 100644 Tests/XcodeMetadataTests/PrecompiledMetadataProviderTests.swift
create mode 100644 Tests/XcodeMetadataTests/SystemFrameworkMetadataProviderTests.swift
create mode 100644 Tests/XcodeMetadataTests/XCFrameworkMetadataProviderTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Graph/GraphMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Package/XCPackageMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXCopyFilesBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXCoreDataModelsBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXFrameworksBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXHeadersBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXResourcesBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXScriptsBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Phases/PBXSourcesBuildPhaseMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Project/PBXProjectMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Schemes/XCSchemeMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Settings/BuildSettingsTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Settings/ConfigurationMatcherTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Settings/XCConfigurationMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Target/PBXBuildRuleMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Target/PBXTargetDependencyMapper.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Target/PBXTargetMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Target/TargetDependencyExtensionsTests.swift
create mode 100644 Tests/XcodeProjMapperTests/MapperTests/Workspace/XCWorkspaceMapperTests.swift
create mode 100644 Tests/XcodeProjMapperTests/Mocks/AssertionsTesting.swift
create mode 100644 Tests/XcodeProjMapperTests/Mocks/MockDefaults.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXBuildRule+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXFileReference+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXGroup+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXNativeTarget+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXProj+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXProject+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXShellScriptBuildPhase+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXTargetDependency+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/PBXVariantGroup+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCBuildConfiguration+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCConfigurationList+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCScheme+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCSchemeTestableReference+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCUserData+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCVersionGroup+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCWorkspace+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCWorkspaceDataElement+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XCWorkspaceDataGroup+TestData.swift
create mode 100644 Tests/XcodeProjMapperTests/TestData/XcodeProj+TestData.swift
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..1ed45f5b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+fixtures/* binary
diff --git a/.github/workflows/XcodeGraph.yml b/.github/workflows/XcodeGraph.yml
index 576ef25f..2c3b8f06 100644
--- a/.github/workflows/XcodeGraph.yml
+++ b/.github/workflows/XcodeGraph.yml
@@ -30,7 +30,7 @@ jobs:
- ubuntu-22.04
- macos-15
swift-version:
- - "5.9"
+ - "6.0.3"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -48,7 +48,12 @@ jobs:
if: steps.debug_build.outcome == 'failure'
run: |
swift build --configuration debug
+ - name: Test on Linux
+ if: matrix.os == 'ubuntu-22.04'
+ # These momdules rely on some Xcode utilities like xcode-select
+ run: swift test --skip XcodeProjMapperTests --skip XcodeMetadataTests
- name: Test
+ if: matrix.os != 'ubuntu-22.04'
run: swift test
spm_build:
name: SPM Build
@@ -58,7 +63,7 @@ jobs:
- ubuntu-22.04
- macos-15
swift-version:
- - "5.9"
+ - "6.0.3"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -85,38 +90,6 @@ jobs:
if: steps.release_build.outcome == 'failure'
run: |
swift build --configuration release
- tuist_test:
- name: Tuist Test
- strategy:
- matrix:
- os:
- - macos-15
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v4
- - uses: jdx/mise-action@v2
- with:
- version: 2024.11.8
- - name: Install dependencies
- run: tuist install
- - name: Test
- run: tuist test
- tuist_build:
- name: Tuist Build
- strategy:
- matrix:
- os:
- - macos-15
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v4
- - uses: jdx/mise-action@v2
- with:
- version: 2024.11.8
- - name: Install dependencies
- run: tuist install
- - name: Build
- run: tuist build
lint:
name: Lint
strategy:
diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml
deleted file mode 100644
index 00dd8252..00000000
--- a/.github/workflows/cache.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: Cache
-on:
- push:
- branches:
- - main
- paths:
- - "**/*.swift"
- - ".github/workflows/*.yml"
- pull_request:
- paths:
- - "**/*.swift"
- - ".github/workflows/*.yml"
-
-concurrency:
- group: Cache-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-env:
- MISE_EXPERIMENTAL: 1
- TUIST_CONFIG_TOKEN: ${{ secrets.TUIST_CONFIG_CLOUD_TOKEN }}
-
-jobs:
- warm:
- name: Warm
- strategy:
- matrix:
- os:
- - macos-15
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v4
- - uses: jdx/mise-action@v2
- with:
- version: 2024.11.8
- - name: Install dependencies
- run: tuist install
- - name: Build
- run: tuist cache
diff --git a/.gitignore b/.gitignore
index 6f032e45..3083e85c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,6 +102,7 @@ Tuist/Dependencies/graph.json
# VSCode Settings
.vscode/launch.json
+.vscode
# Release artifacts
.bundle
\ No newline at end of file
diff --git a/.swiftformat b/.swiftformat
index 6ef4b1cc..4fa95466 100644
--- a/.swiftformat
+++ b/.swiftformat
@@ -7,7 +7,7 @@
--disable hoistAwait
--disable hoistTry
--disable redundantReturn
---swiftversion 5.9
+--swiftversion 5.10
--minversion 0.53.0
# format options
diff --git a/.swiftlint.yml b/.swiftlint.yml
index 42dbd48f..80604831 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -8,6 +8,7 @@ disabled_rules:
- function_parameter_count
- opening_brace
- line_length
+ - large_tuple
identifier_name:
min_length:
error: 1
@@ -21,4 +22,10 @@ inclusive_language:
type_name:
min_length:
error: 1
- warning: 1
\ No newline at end of file
+ warning: 1
+custom_rules:
+ error_must_conform_to_localizederror:
+ name: "Error must conform to LocalizedError"
+ regex: '(struct|class|enum)\s+\w+Error:\s*Error\s*(?!,\s*LocalizedError)'
+ message: "Errors must conform to LocalizedError to provide user-friendly descriptions."
+ severity: error
diff --git a/Package.resolved b/Package.resolved
index c1487b6c..89c23aa7 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -1,5 +1,15 @@
{
+ "originHash" : "e0b3d78a18232f4e8d0bd5c09f120c7920018f6a53c16bc16ff38be99be54dbe",
"pins" : [
+ {
+ "identity" : "aexml",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/tadija/AEXML.git",
+ "state" : {
+ "revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3",
+ "version" : "4.6.1"
+ }
+ },
{
"identity" : "anycodable",
"kind" : "remoteSourceControl",
@@ -9,6 +19,42 @@
"version" : "0.6.7"
}
},
+ {
+ "identity" : "command",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/tuist/Command.git",
+ "state" : {
+ "revision" : "437e0c0ca18d1a16194c55b4690971b5bfb1f185",
+ "version" : "0.12.0"
+ }
+ },
+ {
+ "identity" : "filesystem",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/tuist/FileSystem.git",
+ "state" : {
+ "revision" : "267329f17e523575162f0887d20f2b78f609585a",
+ "version" : "0.7.6"
+ }
+ },
+ {
+ "identity" : "machokit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/p-x9/MachOKit",
+ "state" : {
+ "revision" : "518e8e1aca7ee64b87b08ecec5f7cad2a63b8efd",
+ "version" : "0.28.0"
+ }
+ },
+ {
+ "identity" : "mockable",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Kolos65/Mockable.git",
+ "state" : {
+ "revision" : "e1b311b01c11415099341eee49769185e965ac4c",
+ "version" : "0.2.0"
+ }
+ },
{
"identity" : "path",
"kind" : "remoteSourceControl",
@@ -17,7 +63,115 @@
"revision" : "7c74ac435e03a927c3a73134c48b61e60221abcb",
"version" : "0.3.8"
}
+ },
+ {
+ "identity" : "pathkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kylef/PathKit.git",
+ "state" : {
+ "revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
+ "version" : "1.0.1"
+ }
+ },
+ {
+ "identity" : "spectre",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kylef/Spectre.git",
+ "state" : {
+ "revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
+ "version" : "0.10.1"
+ }
+ },
+ {
+ "identity" : "swift-atomics",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-atomics.git",
+ "state" : {
+ "revision" : "cd142fd2f64be2100422d658e7411e39489da985",
+ "version" : "1.2.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections.git",
+ "state" : {
+ "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
+ "version" : "1.1.4"
+ }
+ },
+ {
+ "identity" : "swift-log",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-log",
+ "state" : {
+ "revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91",
+ "version" : "1.6.2"
+ }
+ },
+ {
+ "identity" : "swift-nio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio",
+ "state" : {
+ "revision" : "dff45738d84a53dbc8ee899c306b3a7227f54f89",
+ "version" : "2.80.0"
+ }
+ },
+ {
+ "identity" : "swift-service-context",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-service-context",
+ "state" : {
+ "revision" : "0c62c5b4601d6c125050b5c3a97f20cce881d32b",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/swiftlang/swift-syntax.git",
+ "state" : {
+ "revision" : "0687f71944021d616d34d922343dcef086855920",
+ "version" : "600.0.1"
+ }
+ },
+ {
+ "identity" : "swift-system",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-system.git",
+ "state" : {
+ "revision" : "c8a44d836fe7913603e246acab7c528c2e780168",
+ "version" : "1.4.0"
+ }
+ },
+ {
+ "identity" : "xcodeproj",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/tuist/XcodeProj",
+ "state" : {
+ "revision" : "647cba2719e85748ec82d0720ee7afe5b7a6421e",
+ "version" : "8.26.3"
+ }
+ },
+ {
+ "identity" : "xctest-dynamic-overlay",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
+ "state" : {
+ "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1",
+ "version" : "1.4.3"
+ }
+ },
+ {
+ "identity" : "zipfoundation",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/tuist/ZIPFoundation",
+ "state" : {
+ "revision" : "e9b1917bd4d7d050e0ff4ec157b5d6e253c84385",
+ "version" : "0.9.20"
+ }
}
],
- "version" : 2
+ "version" : 3
}
diff --git a/Package.swift b/Package.swift
index 781feed7..2bc8b861 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,8 +1,7 @@
-// swift-tools-version:5.9
-
+// swift-tools-version:5.10
import PackageDescription
-var targets: [Target] = [
+let targets: [Target] = [
.target(
name: "XcodeGraph",
dependencies: [
@@ -13,6 +12,39 @@ var targets: [Target] = [
.enableExperimentalFeature("StrictConcurrency"),
]
),
+ .target(
+ name: "XcodeMetadata",
+ dependencies: [
+ .product(name: "ServiceContextModule", package: "swift-service-context"),
+ .product(name: "FileSystem", package: "FileSystem"),
+ .product(name: "Mockable", package: "Mockable"),
+ .product(name: "MachOKitC", package: "MachOKit"),
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
+ .define("MOCKING", .when(configuration: .debug)),
+ ]
+ ),
+ .testTarget(
+ name: "XcodeMetadataTests",
+ dependencies: ["XcodeMetadata", "XcodeGraph"],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
+ ]
+ ),
+ .target(
+ name: "XcodeProjMapper",
+ dependencies: [
+ "XcodeGraph",
+ "XcodeMetadata",
+ .product(name: "Command", package: "Command"),
+ .product(name: "Path", package: "Path"),
+ .product(name: "XcodeProj", package: "XcodeProj"),
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
+ ]
+ ),
.testTarget(
name: "XcodeGraphTests",
dependencies: [.target(name: "XcodeGraph")],
@@ -20,20 +52,37 @@ var targets: [Target] = [
.enableExperimentalFeature("StrictConcurrency"),
]
),
+ .testTarget(
+ name: "XcodeProjMapperTests",
+ dependencies: [
+ "XcodeProjMapper",
+ .product(name: "FileSystem", package: "FileSystem"),
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency"),
+ ]
+ ),
]
let package = Package(
name: "XcodeGraph",
- platforms: [.macOS(.v12)],
+ platforms: [.macOS(.v13)],
products: [
.library(
name: "XcodeGraph",
targets: ["XcodeGraph"]
),
+ .library(name: "XcodeProjMapper", targets: ["XcodeProjMapper"]),
],
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", from: "8.26.0"),
+ .package(url: "https://github.com/tuist/Command.git", from: "0.11.0"),
+ .package(url: "https://github.com/tuist/FileSystem.git", .upToNextMajor(from: "0.6.17")),
+ .package(url: "https://github.com/apple/swift-service-context", .upToNextMajor(from: "1.0.0")),
+ .package(url: "https://github.com/Kolos65/Mockable.git", .upToNextMajor(from: "0.0.11")),
+ .package(url: "https://github.com/p-x9/MachOKit", .upToNextMajor(from: "0.28.0")),
],
targets: targets
)
diff --git a/Project.swift b/Project.swift
deleted file mode 100644
index 4b4cd374..00000000
--- a/Project.swift
+++ /dev/null
@@ -1,107 +0,0 @@
-import ProjectDescription
-import ProjectDescriptionHelpers
-
-let baseSettings: SettingsDictionary = [
- "SWIFT_STRICT_CONCURRENCY": "complete",
-]
-
-func debugSettings() -> SettingsDictionary {
- var settings = baseSettings
- settings["ENABLE_TESTABILITY"] = "YES"
- return settings
-}
-
-func releaseSettings() -> SettingsDictionary {
- baseSettings
-}
-
-func schemes() -> [Scheme] {
- var schemes: [Scheme] = [
- .scheme(
- name: "XcodeGraph-Workspace",
- buildAction: .buildAction(targets: Module.allCases.flatMap(\.targets).map(\.name).sorted().map { .target($0) }),
- testAction: .targets(
- Module.allCases.flatMap(\.testTargets).map { .testableTarget(target: .target($0.name)) }
- ),
- runAction: .runAction(
- arguments: .arguments(
- environmentVariables: [
- "TUIST_CONFIG_SRCROOT": "$(SRCROOT)",
- "TUIST_FRAMEWORK_SEARCH_PATHS": "$(FRAMEWORK_SEARCH_PATHS)",
- ]
- )
- )
- ),
- .scheme(
- name: "TuistAcceptanceTests",
- buildAction: .buildAction(
- targets: Module.allCases.flatMap(\.acceptanceTestTargets).map(\.name).sorted()
- .map { .target($0) }
- ),
- testAction: .targets(
- Module.allCases.flatMap(\.acceptanceTestTargets).map { .testableTarget(target: .target($0.name)) }
- ),
- runAction: .runAction(
- arguments: .arguments(
- environmentVariables: [
- "TUIST_CONFIG_SRCROOT": "$(SRCROOT)",
- "TUIST_FRAMEWORK_SEARCH_PATHS": "$(FRAMEWORK_SEARCH_PATHS)",
- ]
- )
- )
- ),
- ]
- schemes.append(contentsOf: Module.allCases.filter(\.isRunnable).map {
- .scheme(
- name: $0.targetName,
- buildAction: .buildAction(targets: [.target($0.targetName)]),
- runAction: .runAction(
- executable: .target($0.targetName),
- arguments: .arguments(
- environmentVariables: [
- "TUIST_CONFIG_SRCROOT": "$(SRCROOT)",
- "TUIST_FRAMEWORK_SEARCH_PATHS": "$(FRAMEWORK_SEARCH_PATHS)",
- ]
- )
- )
- )
- })
-
- schemes.append(contentsOf: Module.allCases.compactMap(\.acceptanceTestsTargetName).map {
- .scheme(
- name: $0,
- hidden: true,
- buildAction: .buildAction(targets: [.target($0)]),
- testAction: .targets([.testableTarget(target: .target($0))]),
- runAction: .runAction(
- arguments: .arguments(
- environmentVariables: [
- "TUIST_CONFIG_SRCROOT": "$(SRCROOT)",
- "TUIST_FRAMEWORK_SEARCH_PATHS": "$(FRAMEWORK_SEARCH_PATHS)",
- ]
- )
- )
- )
- })
-
- return schemes
-}
-
-let project = Project(
- name: "XcodeGraph",
- options: .options(
- automaticSchemesOptions: .disabled,
- textSettings: .textSettings(usesTabs: false, indentWidth: 4, tabWidth: 4)
- ),
- settings: .settings(
- configurations: [
- .debug(name: "Debug", settings: debugSettings(), xcconfig: nil),
- .release(name: "Release", settings: releaseSettings(), xcconfig: nil),
- ]
- ),
- targets: Module.allCases.flatMap(\.targets),
- schemes: schemes(),
- additionalFiles: [
- "README.md",
- ]
-)
diff --git a/README.md b/README.md
index c36d4eb0..1ee6be33 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,29 @@ let package = Package(
],
)
```
+
+## XcodeGraphMapper
+
+XcodeGraphMapper parses `.xcworkspace` or `.xcodeproj` files using `XcodeProj` and constructs a `XcodeGraph.Graph` representing their projects, targets, and dependencies:
+
+### Usage
+
+```swift
+import XcodeGraphMapper
+let mapper: XcodeGraphMapping = XcodeGraphMapper()
+let path = try AbsolutePath(validating: "/path/to/MyProjectOrWorkspace")
+let graph = try await mapper.map(at: path)
+// You now have a Graph containing projects, targets, packages, and dependencies.*
+// Example: print all target names across all projects*
+for project in graph.projects.values {
+ for (targetName, _) in project.targets {
+ print("Found target: \(targetName)")
+ }
+}
+```
+
+Once you have the Graph, you can explore or transform it as needed—printing targets, analyzing dependencies, generating reports, or integrating into other build tools.
+
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -45,4 +68,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
\ No newline at end of file
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
diff --git a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
index 62972a92..639bd16b 100644
--- a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
+++ b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
@@ -22,7 +22,7 @@ public struct DependenciesGraph: Equatable, Codable, Sendable {
#if DEBUG
extension DependenciesGraph {
- public static func test(
+ static func test(
externalDependencies: [String: [TargetDependency]] = [:],
externalProjects: [AbsolutePath: Project] = [:]
) -> Self {
@@ -43,7 +43,7 @@ public struct DependenciesGraph: Equatable, Codable, Sendable {
)
}
- public static func test(
+ static func test(
packageFolder: AbsolutePath
) -> Self {
let externalDependencies = [
diff --git a/Sources/XcodeGraph/Graph/Graph.swift b/Sources/XcodeGraph/Graph/Graph.swift
index 8cdf72d7..5f749c82 100644
--- a/Sources/XcodeGraph/Graph/Graph.swift
+++ b/Sources/XcodeGraph/Graph/Graph.swift
@@ -68,7 +68,7 @@ extension [GraphEdge: PlatformCondition] {
#if DEBUG
extension Graph {
- public static func test(
+ static func test(
name: String = "graph",
path: AbsolutePath = .root,
workspace: Workspace = .test(),
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 005cc1e8..37988426 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -1,8 +1,8 @@
import Foundation
import Path
-public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Codable {
- public struct XCFramework: Hashable, CustomStringConvertible, Comparable, Codable {
+public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Codable, Sendable {
+ public struct XCFramework: Hashable, CustomStringConvertible, Comparable, Codable, Sendable {
public let path: AbsolutePath
public let infoPlist: XCFrameworkInfoPlist
public let linking: BinaryLinking
@@ -39,7 +39,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
}
}
- public enum PackageProductType: String, Hashable, CustomStringConvertible, Comparable, Codable {
+ public enum PackageProductType: String, Hashable, CustomStringConvertible, Comparable, Codable, Sendable {
public var description: String {
rawValue
}
diff --git a/Sources/XcodeGraph/Graph/GraphEdge.swift b/Sources/XcodeGraph/Graph/GraphEdge.swift
index da6a3975..2ad7e166 100644
--- a/Sources/XcodeGraph/Graph/GraphEdge.swift
+++ b/Sources/XcodeGraph/Graph/GraphEdge.swift
@@ -2,7 +2,7 @@ import Foundation
/// A directed edge linking representing a dependent relationship
/// e.g. `from` (MainApp) depends on `to` (UIKit)
-public struct GraphEdge: Hashable, Codable {
+public struct GraphEdge: Hashable, Codable, Sendable {
public let from: GraphDependency
public let to: GraphDependency
public init(from: GraphDependency, to: GraphDependency) {
diff --git a/Sources/XcodeGraph/Models/BinaryArchitecture.swift b/Sources/XcodeGraph/Models/BinaryArchitecture.swift
index ce2321a3..c731c5fe 100644
--- a/Sources/XcodeGraph/Models/BinaryArchitecture.swift
+++ b/Sources/XcodeGraph/Models/BinaryArchitecture.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum BinaryArchitecture: String, Codable {
+public enum BinaryArchitecture: String, CaseIterable, Codable, Sendable {
case x8664 = "x86_64"
case i386
case armv7
@@ -11,7 +11,7 @@ public enum BinaryArchitecture: String, Codable {
case arm64e
}
-public enum BinaryLinking: String, Hashable, Codable {
+public enum BinaryLinking: String, Hashable, Codable, Sendable {
case `static`, dynamic
}
diff --git a/Sources/XcodeGraph/Models/Platform.swift b/Sources/XcodeGraph/Models/Platform.swift
index 7bc512f6..b0102d21 100644
--- a/Sources/XcodeGraph/Models/Platform.swift
+++ b/Sources/XcodeGraph/Models/Platform.swift
@@ -1,13 +1,17 @@
import Foundation
-public struct UnsupportedPlatformError: Error, CustomStringConvertible, Equatable {
+public struct UnsupportedPlatformError: LocalizedError, CustomStringConvertible, Equatable {
let input: String
public var description: String {
"Specified platform \(input) does not map to any of these supported platforms: \(Platform.allCases.map(\.caseValue).joined(separator: ", ")) "
}
+
+ public var errorDescription: String? {
+ description
+ }
}
-public enum Platform: String, CaseIterable, Codable, Comparable {
+public enum Platform: String, CaseIterable, Codable, Comparable, Sendable {
case iOS = "ios"
case macOS = "macos"
case tvOS = "tvos"
diff --git a/Sources/XcodeGraph/Models/PlatformFilter.swift b/Sources/XcodeGraph/Models/PlatformFilter.swift
index a7bff439..419e126d 100644
--- a/Sources/XcodeGraph/Models/PlatformFilter.swift
+++ b/Sources/XcodeGraph/Models/PlatformFilter.swift
@@ -3,7 +3,7 @@ import Foundation
/// Convenience typealias to be used to ensure unique filters are applied
public typealias PlatformFilters = Set
-extension PlatformFilters: Comparable {
+extension PlatformFilters: @retroactive Comparable {
public static func < (lhs: Set, rhs: Set) -> Bool {
lhs.map(\.xcodeprojValue).sorted().joined() < rhs.map(\.xcodeprojValue).sorted().joined()
}
diff --git a/Sources/XcodeGraph/Models/Plist.swift b/Sources/XcodeGraph/Models/Plist.swift
index 5f1d743b..30e95550 100644
--- a/Sources/XcodeGraph/Models/Plist.swift
+++ b/Sources/XcodeGraph/Models/Plist.swift
@@ -94,24 +94,24 @@ extension Dictionary where Value == Plist.Value {
public enum InfoPlist: Equatable, Codable, Sendable {
// Path to a user defined info.plist file (already exists on disk).
- case file(path: AbsolutePath)
+ case file(path: AbsolutePath, configuration: BuildConfiguration? = nil)
// Path to a generated info.plist file (may not exist on disk at the time of project generation).
// Data of the generated file
- case generatedFile(path: AbsolutePath, data: Data)
+ case generatedFile(path: AbsolutePath, data: Data, configuration: BuildConfiguration? = nil)
// User defined dictionary of keys/values for an info.plist file.
- case dictionary([String: Plist.Value])
+ case dictionary([String: Plist.Value], configuration: BuildConfiguration? = nil)
// User defined dictionary of keys/values for an info.plist file extending the default set of keys/values
// for the target type.
- case extendingDefault(with: [String: Plist.Value])
+ case extendingDefault(with: [String: Plist.Value], configuration: BuildConfiguration? = nil)
// MARK: - Public
public var path: AbsolutePath? {
switch self {
- case let .file(path), let .generatedFile(path: path, data: _):
+ case let .file(path, _), let .generatedFile(path: path, data: _, configuration: _):
return path
default:
return nil
@@ -131,23 +131,23 @@ extension InfoPlist: ExpressibleByStringLiteral {
public enum Entitlements: Equatable, Codable, Sendable {
// Path to a user defined .entitlements file (already exists on disk).
- case file(path: AbsolutePath)
+ case file(path: AbsolutePath, configuration: BuildConfiguration? = nil)
// Path to a generated .entitlements file (may not exist on disk at the time of project generation).
// Data of the generated file
- case generatedFile(path: AbsolutePath, data: Data)
+ case generatedFile(path: AbsolutePath, data: Data, configuration: BuildConfiguration? = nil)
// User defined dictionary of keys/values for an .entitlements file.
- case dictionary([String: Plist.Value])
+ case dictionary([String: Plist.Value], configuration: BuildConfiguration? = nil)
// A user defined xcconfig variable map to .entitlements file
- case variable(String)
+ case variable(String, configuration: BuildConfiguration? = nil)
// MARK: - Public
public var path: AbsolutePath? {
switch self {
- case let .file(path), let .generatedFile(path: path, data: _):
+ case let .file(path, _), let .generatedFile(path: path, data: _, _):
return path
default:
return nil
diff --git a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
index 9b8880e2..23748225 100644
--- a/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
+++ b/Sources/XcodeGraph/Models/ResourceSynthesizer.swift
@@ -13,7 +13,7 @@ public struct ResourceSynthesizer: Equatable, Hashable, Codable, Sendable {
case defaultTemplate(String)
}
- public enum Parser: String, Equatable, Hashable, Codable, Sendable {
+ public enum Parser: String, CaseIterable, Equatable, Hashable, Codable, Sendable {
case strings
case stringsCatalog
case assets
diff --git a/Sources/XcodeGraph/Models/SDKSource.swift b/Sources/XcodeGraph/Models/SDKSource.swift
index 2c52a66e..b0d12fce 100644
--- a/Sources/XcodeGraph/Models/SDKSource.swift
+++ b/Sources/XcodeGraph/Models/SDKSource.swift
@@ -1,6 +1,6 @@
import Foundation
-public enum SDKSource: String, Equatable, Codable {
+public enum SDKSource: String, Equatable, Codable, Sendable {
case developer // Platforms/iPhoneOS.platform/Developer/Library
case system // Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library
diff --git a/Sources/XcodeGraph/Models/TestableTarget.swift b/Sources/XcodeGraph/Models/TestableTarget.swift
index aa01d488..dfb21aa6 100644
--- a/Sources/XcodeGraph/Models/TestableTarget.swift
+++ b/Sources/XcodeGraph/Models/TestableTarget.swift
@@ -76,7 +76,7 @@ public struct TestableTarget: Equatable, Hashable, Codable, Sendable {
TestableTarget(
target: target,
skipped: skipped,
- parallelizable: parallelizable,
+ parallelization: parallelizable ? .all : .none,
randomExecutionOrdering: randomExecutionOrdering,
simulatedLocation: simulatedLocation
)
diff --git a/Sources/XcodeGraph/Models/XCFrameworkInfoPlist.swift b/Sources/XcodeGraph/Models/XCFrameworkInfoPlist.swift
index dadd42f3..5d0377b2 100644
--- a/Sources/XcodeGraph/Models/XCFrameworkInfoPlist.swift
+++ b/Sources/XcodeGraph/Models/XCFrameworkInfoPlist.swift
@@ -2,14 +2,14 @@ import Foundation
import Path
/// It represents th Info.plist contained in an .xcframework bundle.
-public struct XCFrameworkInfoPlist: Codable, Hashable, Equatable {
+public struct XCFrameworkInfoPlist: Codable, Hashable, Equatable, Sendable {
private enum CodingKeys: String, CodingKey {
case libraries = "AvailableLibraries"
}
/// It represents a library inside an .xcframework
- public struct Library: Codable, Hashable, Equatable {
- public enum Platform: String, CaseIterable, Codable {
+ public struct Library: Codable, Hashable, Equatable, Sendable {
+ public enum Platform: String, CaseIterable, Codable, Sendable {
case iOS = "ios"
case macOS = "macos"
case tvOS = "tvos"
@@ -78,6 +78,10 @@ public struct XCFrameworkInfoPlist: Codable, Hashable, Equatable {
/// List of libraries that are part of the .xcframework.
public let libraries: [Library]
+
+ public init(libraries: [Library]) {
+ self.libraries = libraries
+ }
}
#if DEBUG
diff --git a/Sources/XcodeMetadata/Extensions/BinaryArchitecture+Extension.swift b/Sources/XcodeMetadata/Extensions/BinaryArchitecture+Extension.swift
new file mode 100644
index 00000000..513f75c3
--- /dev/null
+++ b/Sources/XcodeMetadata/Extensions/BinaryArchitecture+Extension.swift
@@ -0,0 +1,87 @@
+import Foundation
+#if canImport(MachO)
+ import MachO
+#else
+ import MachOKitC
+#endif
+import XcodeGraph
+
+extension BinaryArchitecture {
+ /// An array of `(cpu_type_t, cpu_subtype_t)` pairs representing this architecture.
+ private var pairs: [(cpu_type_t, cpu_subtype_t)] {
+ switch self {
+ case .x8664:
+ return [
+ (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL),
+ (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H),
+ ]
+ case .i386:
+ return [
+ (CPU_TYPE_X86, CPU_SUBTYPE_X86_ALL),
+ ]
+ case .armv7:
+ return [
+ (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7),
+ ]
+ case .armv7s:
+ return [
+ (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S),
+ ]
+ case .armv7k:
+ return [
+ (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K),
+ ]
+ case .arm64:
+ return [
+ (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL),
+ ]
+ case .arm64e:
+ return [
+ (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E),
+ ]
+ case .arm6432:
+ return [
+ (CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_ALL),
+ (CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8),
+ ]
+ }
+ }
+
+ /// Builds a single dictionary mapping `(cputype, cpusubtype)` to `BinaryArchitecture`.
+ ///
+ /// This is computed once by enumerating all enum cases and collecting their pairs.
+ private static let architectureMap: [CPUIdentifier: BinaryArchitecture] = {
+ var map = [CPUIdentifier: BinaryArchitecture]()
+ for arch in Self.allCases {
+ for (cputype, subtype) in arch.pairs {
+ map[CPUIdentifier(cputype: cputype, cpusubtype: subtype)] = arch
+ }
+ }
+ return map
+ }()
+
+ /// Initializes a `BinaryArchitecture` from `(cputype, cpusubtype)`.
+ ///
+ /// If not found in `architectureMap`, returns `nil`.
+ public init?(cputype: cpu_type_t, subtype: cpu_subtype_t) {
+ let key = CPUIdentifier(cputype: cputype, cpusubtype: subtype)
+ // CPU_SUBTYPE_ANY is not available in MachOKitC
+ #if canImport(MachO)
+ let fallbackKey = CPUIdentifier(cputype: cputype, cpusubtype: CPU_SUBTYPE_ANY)
+ guard let architecture = Self.architectureMap[key] ?? Self.architectureMap[fallbackKey] else {
+ return nil
+ }
+ #else
+ guard let architecture = Self.architectureMap[key] else {
+ return nil
+ }
+ #endif
+ self = architecture
+ }
+}
+
+/// A small Hashable struct to store `(cputype, cpusubtype)` pairs as dictionary keys.
+private struct CPUIdentifier: Hashable, Sendable {
+ let cputype: cpu_type_t
+ let cpusubtype: cpu_subtype_t
+}
diff --git a/Sources/XcodeMetadata/Extensions/FileHandle+ReadHelpers.swift b/Sources/XcodeMetadata/Extensions/FileHandle+ReadHelpers.swift
new file mode 100644
index 00000000..d44a6ed9
--- /dev/null
+++ b/Sources/XcodeMetadata/Extensions/FileHandle+ReadHelpers.swift
@@ -0,0 +1,28 @@
+import Foundation
+
+extension FileHandle {
+ /// Returns the current offset in the file.
+ var currentOffset: UInt64 {
+ offsetInFile
+ }
+
+ /// Seeks to a specific file offset.
+ func seek(to offset: UInt64) {
+ seek(toFileOffset: offset)
+ }
+
+ /// Reads a value of type `T` from the file handle.
+ /// - Returns: The value `T` loaded from the next `MemoryLayout.size` bytes.
+ func read() -> T {
+ let data = readData(ofLength: MemoryLayout.size)
+ return data.withUnsafeBytes { $0.load(as: T.self) }
+ }
+
+ /// Reads a string of a specified length using ASCII encoding.
+ /// - Parameter length: The number of bytes to read.
+ /// - Returns: A `String` if decoding succeeds, otherwise `nil`.
+ func readString(ofLength length: Int) -> String? {
+ let data = readData(ofLength: length)
+ return String(data: data, encoding: .ascii)
+ }
+}
diff --git a/Sources/XcodeMetadata/Extensions/MachOByteSwapExtensions.swift b/Sources/XcodeMetadata/Extensions/MachOByteSwapExtensions.swift
new file mode 100644
index 00000000..17f632c9
--- /dev/null
+++ b/Sources/XcodeMetadata/Extensions/MachOByteSwapExtensions.swift
@@ -0,0 +1,69 @@
+import Foundation
+#if canImport(MachO)
+ import MachO
+#else
+ import MachOKitC
+#endif
+
+extension fat_header {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ magic = magic.byteSwapped
+ nfat_arch = nfat_arch.byteSwapped
+ }
+}
+
+extension fat_arch {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ cputype = cputype.byteSwapped
+ cpusubtype = cpusubtype.byteSwapped
+ offset = offset.byteSwapped
+ size = size.byteSwapped
+ align = align.byteSwapped
+ }
+}
+
+extension mach_header {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ magic = magic.byteSwapped
+ cputype = cputype.byteSwapped
+ cpusubtype = cpusubtype.byteSwapped
+ filetype = filetype.byteSwapped
+ ncmds = ncmds.byteSwapped
+ sizeofcmds = sizeofcmds.byteSwapped
+ flags = flags.byteSwapped
+ }
+}
+
+extension mach_header_64 {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ magic = magic.byteSwapped
+ cputype = cputype.byteSwapped
+ cpusubtype = cpusubtype.byteSwapped
+ filetype = filetype.byteSwapped
+ ncmds = ncmds.byteSwapped
+ sizeofcmds = sizeofcmds.byteSwapped
+ flags = flags.byteSwapped
+ reserved = reserved.byteSwapped
+ }
+}
+
+extension load_command {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ cmd = cmd.byteSwapped
+ cmdsize = cmdsize.byteSwapped
+ }
+}
+
+extension uuid_command {
+ mutating func swapIfNeeded(_ shouldSwap: Bool) {
+ guard shouldSwap else { return }
+ cmd = cmd.byteSwapped
+ cmdsize = cmdsize.byteSwapped
+ // The uuid field is 16 raw bytes; no integer fields to swap for endianness.
+ }
+}
diff --git a/Sources/XcodeMetadata/Extensions/Sequence+ExecutionContext.swift b/Sources/XcodeMetadata/Extensions/Sequence+ExecutionContext.swift
new file mode 100644
index 00000000..f8cb22b9
--- /dev/null
+++ b/Sources/XcodeMetadata/Extensions/Sequence+ExecutionContext.swift
@@ -0,0 +1,63 @@
+import Foundation
+
+extension Sequence where Element: Sendable {
+ /// Taken from: https://github.com/JohnSundell/CollectionConcurrencyKit/blob/b4f23e24b5a1bff301efc5e70871083ca029ff95/Sources/CollectionConcurrencyKit.swift
+ /// Transform the sequence into an array of new values using
+ /// an async closure that returns optional values. Only the
+ /// non-`nil` return values will be included in the new array.
+ ///
+ /// The closure calls will be performed in order, by waiting for
+ /// each call to complete before proceeding with the next one. If
+ /// any of the closure calls throw an error, then the iteration
+ /// will be terminated and the error rethrown.
+ ///
+ /// - parameter transform: The transform to run on each element.
+ /// - returns: The transformed values as an array. The order of
+ /// the transformed values will match the original sequence,
+ /// except for the values that were transformed into `nil`.
+ /// - throws: Rethrows any error thrown by the passed closure.
+ public func serialCompactMap(
+ _ transform: (Element) async throws -> T?
+ ) async rethrows -> [T] {
+ var values = [T]()
+
+ for element in self {
+ guard let value = try await transform(element) else {
+ continue
+ }
+
+ values.append(value)
+ }
+
+ return values
+ }
+
+ /// Filter (with execution context)
+ ///
+ /// - Parameters:
+ /// - context: The execution context to perform the `perform` operation with
+ /// - perform: The perform closure to call on each element in the array
+ func concurrentFilter(_ filter: @Sendable @escaping (Element) async throws -> Bool) async rethrows -> [Element] {
+ return try await concurrentCompactMap {
+ try await filter($0) ? $0 : nil
+ }
+ }
+
+ /// Async concurrent compact map
+ ///
+ /// - Parameters:
+ /// - transform: The transformation closure to apply to the array
+ func concurrentCompactMap(_ transform: @Sendable @escaping (Element) async throws -> B?) async rethrows
+ -> [B]
+ {
+ let tasks = map { element in
+ Task {
+ try await transform(element)
+ }
+ }
+
+ return try await tasks.serialCompactMap { task in
+ try await task.value
+ }
+ }
+}
diff --git a/Sources/XcodeMetadata/LoggerServiceContextKey.swift b/Sources/XcodeMetadata/LoggerServiceContextKey.swift
new file mode 100644
index 00000000..6d17f7f3
--- /dev/null
+++ b/Sources/XcodeMetadata/LoggerServiceContextKey.swift
@@ -0,0 +1,16 @@
+import Logging
+import ServiceContextModule
+
+private enum LoggerServiceContextKey: ServiceContextKey {
+ typealias Value = Logger
+}
+
+extension ServiceContext {
+ var logger: Logger? {
+ get {
+ self[LoggerServiceContextKey.self]
+ } set {
+ self[LoggerServiceContextKey.self] = newValue
+ }
+ }
+}
diff --git a/Sources/XcodeMetadata/Providers/FrameworkMetadataProvider.swift b/Sources/XcodeMetadata/Providers/FrameworkMetadataProvider.swift
new file mode 100644
index 00000000..acba747d
--- /dev/null
+++ b/Sources/XcodeMetadata/Providers/FrameworkMetadataProvider.swift
@@ -0,0 +1,104 @@
+@preconcurrency import FileSystem
+import Foundation
+import Path
+import XcodeGraph
+
+// MARK: - Provider Errors
+
+enum FrameworkMetadataProviderError: LocalizedError, Equatable {
+ case frameworkNotFound(AbsolutePath)
+
+ // MARK: - FatalError
+
+ var errorDescription: String? {
+ switch self {
+ case let .frameworkNotFound(path):
+ return "Couldn't find framework at \(path.pathString)"
+ }
+ }
+}
+
+// MARK: - Provider
+
+public protocol FrameworkMetadataProviding: PrecompiledMetadataProviding {
+ /// Loads all the metadata associated with a framework at the specified path
+ /// - Note: This performs various shell calls and disk operations
+ func loadMetadata(at path: AbsolutePath, status: LinkingStatus) async throws -> FrameworkMetadata
+
+ /// Given the path to a framework, it returns the path to its dSYMs if they exist
+ /// in the same framework directory.
+ /// - Parameter frameworkPath: Path to the .framework directory.
+ func dsymPath(frameworkPath: AbsolutePath) async throws -> AbsolutePath?
+
+ /// Given the path to a framework, it returns the list of .bcsymbolmap files that
+ /// are associated to the framework and that are present in the same directory.
+ /// - Parameter frameworkPath: Path to the .framework directory.
+ func bcsymbolmapPaths(frameworkPath: AbsolutePath) async throws -> [AbsolutePath]
+
+ /// Returns the product for the framework at the given path.
+ /// - Parameter frameworkPath: Path to the .framework directory.
+ func product(frameworkPath: AbsolutePath) throws -> Product
+}
+
+// MARK: - Default Implementation
+
+public final class FrameworkMetadataProvider: PrecompiledMetadataProvider, FrameworkMetadataProviding {
+ private let fileSystem: FileSysteming
+
+ public init(
+ fileSystem: FileSysteming = FileSystem()
+ ) {
+ self.fileSystem = fileSystem
+ super.init()
+ }
+
+ public func loadMetadata(at path: AbsolutePath, status: LinkingStatus) async throws -> FrameworkMetadata {
+ guard try await fileSystem.exists(path) else {
+ throw FrameworkMetadataProviderError.frameworkNotFound(path)
+ }
+ let binaryPath = binaryPath(frameworkPath: path)
+ let dsymPath = try await dsymPath(frameworkPath: path)
+ let bcsymbolmapPaths = try await bcsymbolmapPaths(frameworkPath: path)
+ let linking = try linking(binaryPath: binaryPath)
+ let architectures = try architectures(binaryPath: binaryPath)
+ return FrameworkMetadata(
+ path: path,
+ binaryPath: binaryPath,
+ dsymPath: dsymPath,
+ bcsymbolmapPaths: bcsymbolmapPaths,
+ linking: linking,
+ architectures: architectures,
+ status: status
+ )
+ }
+
+ public func dsymPath(frameworkPath: AbsolutePath) async throws -> AbsolutePath? {
+ let path = try AbsolutePath(validating: "\(frameworkPath.pathString).dSYM")
+ if try await fileSystem.exists(path) { return path }
+ return nil
+ }
+
+ public func bcsymbolmapPaths(frameworkPath: AbsolutePath) async throws -> [AbsolutePath] {
+ let binaryPath = binaryPath(frameworkPath: frameworkPath)
+ let uuids = try uuids(binaryPath: binaryPath)
+ let fileSystem = fileSystem
+ return try await uuids
+ .map { frameworkPath.parentDirectory.appending(component: "\($0).bcsymbolmap") }
+ .concurrentFilter { try await fileSystem.exists($0) }
+ .sorted()
+ }
+
+ public func product(frameworkPath: AbsolutePath) throws -> Product {
+ let binaryPath = binaryPath(frameworkPath: frameworkPath)
+ switch try linking(binaryPath: binaryPath) {
+ case .dynamic:
+ return .framework
+ case .static:
+ return .staticFramework
+ }
+ }
+
+ private func binaryPath(frameworkPath: AbsolutePath) -> AbsolutePath {
+ frameworkPath.appending(component: frameworkPath.basenameWithoutExt)
+ }
+}
diff --git a/Sources/XcodeMetadata/Providers/LibraryMetadataProvider.swift b/Sources/XcodeMetadata/Providers/LibraryMetadataProvider.swift
new file mode 100644
index 00000000..984e42ee
--- /dev/null
+++ b/Sources/XcodeMetadata/Providers/LibraryMetadataProvider.swift
@@ -0,0 +1,77 @@
+import FileSystem
+import Foundation
+import Path
+import XcodeGraph
+
+// MARK: - Provider Errors
+
+enum LibraryMetadataProviderError: LocalizedError, Equatable {
+ case libraryNotFound(AbsolutePath)
+ case publicHeadersNotFound(libraryPath: AbsolutePath, headersPath: AbsolutePath)
+ case swiftModuleMapNotFound(libraryPath: AbsolutePath, moduleMapPath: AbsolutePath)
+
+ // MARK: - FatalError
+
+ var errorDescription: String? {
+ switch self {
+ case let .libraryNotFound(path):
+ return "Couldn't find library at \(path.pathString)"
+ case let .publicHeadersNotFound(libraryPath: libraryPath, headersPath: headersPath):
+ return "Couldn't find the public headers at \(headersPath.pathString) for library \(libraryPath.pathString)"
+ case let .swiftModuleMapNotFound(libraryPath: libraryPath, moduleMapPath: moduleMapPath):
+ return "Couldn't find the public headers at \(moduleMapPath.pathString) for library \(libraryPath.pathString)"
+ }
+ }
+}
+
+// MARK: - Provider
+
+public protocol LibraryMetadataProviding: PrecompiledMetadataProviding {
+ /// Loads all the metadata associated with a library (.a / .dylib) at the specified path
+ /// - Note: This performs various shell calls and disk operations
+ func loadMetadata(
+ at path: AbsolutePath,
+ publicHeaders: AbsolutePath,
+ swiftModuleMap: AbsolutePath?
+ ) async throws -> LibraryMetadata
+}
+
+// MARK: - Default Implementation
+
+public final class LibraryMetadataProvider: PrecompiledMetadataProvider, LibraryMetadataProviding {
+ private let fileSystem: FileSysteming
+
+ public init(
+ fileSystem: FileSysteming = FileSystem()
+ ) {
+ self.fileSystem = fileSystem
+ }
+
+ public func loadMetadata(
+ at path: AbsolutePath,
+ publicHeaders: AbsolutePath,
+ swiftModuleMap: AbsolutePath?
+ ) async throws -> LibraryMetadata {
+ guard try await fileSystem.exists(path) else {
+ throw LibraryMetadataProviderError.libraryNotFound(path)
+ }
+ guard try await fileSystem.exists(publicHeaders) else {
+ throw LibraryMetadataProviderError.publicHeadersNotFound(libraryPath: path, headersPath: publicHeaders)
+ }
+ if let swiftModuleMap {
+ guard try await fileSystem.exists(swiftModuleMap) else {
+ throw LibraryMetadataProviderError.swiftModuleMapNotFound(libraryPath: path, moduleMapPath: swiftModuleMap)
+ }
+ }
+
+ let architectures = try architectures(binaryPath: path)
+ let linking = try linking(binaryPath: path)
+ return LibraryMetadata(
+ path: path,
+ publicHeaders: publicHeaders,
+ swiftModuleMap: swiftModuleMap,
+ architectures: architectures,
+ linking: linking
+ )
+ }
+}
diff --git a/Sources/XcodeMetadata/Providers/PrecompiledMetadataProvider.swift b/Sources/XcodeMetadata/Providers/PrecompiledMetadataProvider.swift
new file mode 100644
index 00000000..c5418478
--- /dev/null
+++ b/Sources/XcodeMetadata/Providers/PrecompiledMetadataProvider.swift
@@ -0,0 +1,241 @@
+import Foundation
+#if canImport(MachO)
+ import MachO
+#else
+ import MachOKitC
+#endif
+import Path
+import XcodeGraph
+
+// swiftlint:disable identifier_name
+private let CPU_SUBTYPE_MASK = Int32(bitPattern: 0xFF00_0000)
+
+// MARK: - Errors
+
+enum PrecompiledMetadataProviderError: LocalizedError, Equatable {
+ case architecturesNotFound(AbsolutePath)
+ case metadataNotFound(AbsolutePath)
+
+ // MARK: - FatalError
+
+ var errorDescription: String? {
+ switch self {
+ case let .architecturesNotFound(path):
+ return "Couldn't find architectures for binary at path \(path.pathString)"
+ case let .metadataNotFound(path):
+ return "Couldn't find metadata for binary at path \(path.pathString)"
+ }
+ }
+}
+
+// MARK: - Protocol
+
+public protocol PrecompiledMetadataProviding {
+ /// Returns the supported architectures of the binary at the given path.
+ func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture]
+
+ /// Returns how other binaries should link the binary at the given path (.dynamic or .static).
+ func linking(binaryPath: AbsolutePath) throws -> BinaryLinking
+
+ /// Uses 'dwarfdump' logic to find UUIDs for each arch (helps match .bcsymbolmap files).
+ func uuids(binaryPath: AbsolutePath) throws -> Set
+}
+
+// MARK: - PrecompiledMetadataProvider
+
+/// Reads Mach-O metadata (arches, linking type, UUIDs) without calling deprecated swap_* APIs.
+public class PrecompiledMetadataProvider: PrecompiledMetadataProviding {
+ // A local struct for arch/linking/UUID data
+ typealias Metadata = (BinaryArchitecture, BinaryLinking, UUID?)
+
+ public func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture] {
+ let metadata = try readMetadatas(binaryPath: binaryPath)
+ return metadata.map(\.0)
+ }
+
+ public func linking(binaryPath: AbsolutePath) throws -> BinaryLinking {
+ let metadata = try readMetadatas(binaryPath: binaryPath)
+ // If *any* arch is dynamic, the overall binary is dynamic.
+ return metadata.contains(where: { $0.1 == .dynamic }) ? .dynamic : .static
+ }
+
+ public func uuids(binaryPath: AbsolutePath) throws -> Set {
+ let metadata = try readMetadatas(binaryPath: binaryPath)
+ return Set(metadata.compactMap(\.2))
+ }
+
+ // MARK: - Internal
+
+ private let sizeOfArchiveHeader: UInt64 = 60
+ private let archiveHeaderSizeOffset: UInt64 = 56
+ private let archiveFormatMagic = "!\n"
+ private let archiveExtendedFormat = "#1/"
+
+ /// Reads all arch/linking/UUID info for the given binary (whether fat or thin).
+ func readMetadatas(binaryPath: AbsolutePath) throws -> [Metadata] {
+ guard let binary = FileHandle(forReadingAtPath: binaryPath.pathString) else {
+ throw PrecompiledMetadataProviderError.metadataNotFound(binaryPath)
+ }
+ defer { binary.closeFile() }
+
+ // Peek at magic
+ let magic: UInt32 = binary.read()
+ // Reset to start
+ binary.seek(to: 0)
+
+ if isFat(magic) {
+ return try readMetadatasFromFatHeader(binary: binary, magic: magic, binaryPath: binaryPath)
+ } else if let singleMetadata = try readMetadataFromMachHeaderIfAvailable(binary: binary) {
+ return [singleMetadata]
+ } else {
+ throw PrecompiledMetadataProviderError.metadataNotFound(binaryPath)
+ }
+ }
+
+ private func readMetadatasFromFatHeader(
+ binary: FileHandle,
+ magic: UInt32,
+ binaryPath: AbsolutePath
+ ) throws -> [Metadata] {
+ var header: fat_header = binary.read()
+ header.swapIfNeeded(shouldSwap(magic))
+
+ return try (0 ..< header.nfat_arch).map { _ in
+ var fatArch: fat_arch = binary.read()
+ fatArch.swapIfNeeded(shouldSwap(magic))
+
+ let savedOffset = binary.currentOffset
+ // Jump to that arch offset
+ binary.seek(to: UInt64(fatArch.offset))
+
+ // Attempt to parse Mach-O data
+ let maybeMetadata = try readMetadataFromMachHeaderIfAvailable(binary: binary)
+ // Restore offset
+ binary.seek(to: savedOffset)
+
+ if let metadata = maybeMetadata {
+ return metadata
+ } else {
+ let maskedSubtype = fatArch.cpusubtype & ~CPU_SUBTYPE_MASK // 0x00000002
+
+ // If we cannot parse Mach-O, fallback to static if cputype is known
+ guard let arch = readBinaryArchitecture(
+ cputype: fatArch.cputype,
+ cpusubtype: maskedSubtype
+ ) else {
+ throw PrecompiledMetadataProviderError.architecturesNotFound(binaryPath)
+ }
+ return (arch, .static, nil)
+ }
+ }
+ }
+
+ private func readMetadataFromMachHeaderIfAvailable(binary: FileHandle) throws -> Metadata? {
+ readArchiveFormatIfAvailable(binary)
+
+ let currentOffset = binary.currentOffset
+ let magic: UInt32 = binary.read()
+ binary.seek(to: currentOffset)
+
+ guard isMagic(magic) else {
+ return nil
+ }
+
+ let (cputype, cpusubtype, filetype, ncmds) = try readMachHeader(binary: binary, magic: magic)
+ guard let arch = readBinaryArchitecture(cputype: cputype, cpusubtype: cpusubtype) else {
+ return nil
+ }
+
+ var foundUUID: UUID?
+ for _ in 0 ..< ncmds {
+ let cmdStart = binary.currentOffset
+ var loadCmd: load_command = binary.read()
+ loadCmd.swapIfNeeded(shouldSwap(magic))
+
+ guard loadCmd.cmd == LC_UUID else {
+ binary.seek(to: cmdStart + UInt64(loadCmd.cmdsize))
+ continue
+ }
+
+ // re-read the entire uuid_command
+ binary.seek(to: cmdStart)
+ var uuidCmd: uuid_command = binary.read()
+ uuidCmd.swapIfNeeded(shouldSwap(magic))
+
+ foundUUID = UUID(uuid: uuidCmd.uuid)
+ break
+ }
+
+ let linking: BinaryLinking = (filetype == MH_DYLIB) ? .dynamic : .static
+ return (arch, linking, foundUUID)
+ }
+
+ private func readMachHeader(
+ binary: FileHandle,
+ magic: UInt32
+ ) throws -> (cpu_type_t, cpu_subtype_t, UInt32, UInt32) {
+ if is64(magic) {
+ var header64: mach_header_64 = binary.read()
+ header64.swapIfNeeded(shouldSwap(magic))
+ return (header64.cputype, header64.cpusubtype, header64.filetype, header64.ncmds)
+ } else {
+ var header32: mach_header = binary.read()
+ header32.swapIfNeeded(shouldSwap(magic))
+ return (header32.cputype, header32.cpusubtype, header32.filetype, header32.ncmds)
+ }
+ }
+
+ private func readArchiveFormatIfAvailable(_ binary: FileHandle) {
+ let currentOffset = binary.currentOffset
+ let data = binary.readData(ofLength: 8)
+ binary.seek(to: currentOffset)
+
+ guard let magicStr = String(data: data, encoding: .ascii),
+ magicStr == archiveFormatMagic
+ else { return }
+
+ binary.seek(to: currentOffset + archiveHeaderSizeOffset)
+ guard let sizeString = binary.readString(ofLength: 10) else { return }
+
+ let size = strtoul(sizeString, nil, 10)
+ // skip the archive header
+ binary.seek(to: 8 + sizeOfArchiveHeader + UInt64(size))
+
+ guard let name = binary.readString(ofLength: 16) else { return }
+ binary.seek(to: binary.currentOffset - 16)
+
+ if name.hasPrefix(archiveExtendedFormat) {
+ let nameSize = strtoul(String(name.dropFirst(3)), nil, 10)
+ binary.seek(to: binary.currentOffset + sizeOfArchiveHeader + UInt64(nameSize))
+ } else {
+ binary.seek(to: binary.currentOffset + sizeOfArchiveHeader)
+ }
+ }
+
+ // MARK: - Architecture Mapping
+
+ private func readBinaryArchitecture(cputype: cpu_type_t, cpusubtype: cpu_subtype_t) -> BinaryArchitecture? {
+ BinaryArchitecture(cputype: cputype, subtype: cpusubtype)
+ }
+
+ // MARK: - Helpers
+
+ private func isMagic(_ magic: UInt32) -> Bool {
+ [MH_MAGIC, MH_MAGIC_64, MH_CIGAM, MH_CIGAM_64, FAT_MAGIC, FAT_CIGAM].contains(magic)
+ }
+
+ private func isFat(_ magic: UInt32) -> Bool {
+ [FAT_MAGIC, FAT_CIGAM].contains(magic)
+ }
+
+ private func is64(_ magic: UInt32) -> Bool {
+ [MH_MAGIC_64, MH_CIGAM_64].contains(magic)
+ }
+
+ /// If magic is CIGAM or FAT_CIGAM, it's big-endian -> we need to byte-swap
+ private func shouldSwap(_ magic: UInt32) -> Bool {
+ [MH_CIGAM, MH_CIGAM_64, FAT_CIGAM].contains(magic)
+ }
+}
+
+// swiftlint:enable identifier_name
diff --git a/Sources/XcodeMetadata/Providers/SystemFrameworkMetadataProvider.swift b/Sources/XcodeMetadata/Providers/SystemFrameworkMetadataProvider.swift
new file mode 100644
index 00000000..f69144df
--- /dev/null
+++ b/Sources/XcodeMetadata/Providers/SystemFrameworkMetadataProvider.swift
@@ -0,0 +1,96 @@
+import Foundation
+import Path
+import XcodeGraph
+
+// MARK: - Provider Errors
+
+public enum SystemFrameworkMetadataProviderError: LocalizedError, Equatable {
+ case unsupportedSDK(name: String)
+
+ public var errorDescription: String? {
+ switch self {
+ case let .unsupportedSDK(sdk):
+ let supportedTypes = SDKType.supportedTypesDescription
+ return "The SDK type of \(sdk) is not currently supported - only \(supportedTypes) are supported."
+ }
+ }
+}
+
+// MARK: - Provider
+
+public protocol SystemFrameworkMetadataProviding {
+ func loadMetadata(sdkName: String, status: LinkingStatus, platform: Platform, source: SDKSource) throws
+ -> SystemFrameworkMetadata
+}
+
+extension SystemFrameworkMetadataProviding {
+ func loadXCTestMetadata(platform: Platform) throws -> SystemFrameworkMetadata {
+ try loadMetadata(sdkName: "XCTest.framework", status: .required, platform: platform, source: .developer)
+ }
+}
+
+// MARK: - Default Implementation
+
+public final class SystemFrameworkMetadataProvider: SystemFrameworkMetadataProviding {
+ public init() {}
+
+ public func loadMetadata(
+ sdkName: String,
+ status: LinkingStatus,
+ platform: Platform,
+ source: SDKSource
+ ) throws -> SystemFrameworkMetadata {
+ let sdkNamePath = try AbsolutePath(validating: "/\(sdkName)")
+ guard let sdkExtension = sdkNamePath.extension
+ else { throw SystemFrameworkMetadataProviderError.unsupportedSDK(name: sdkName) }
+
+ let sdkType: SDKType
+ switch sdkExtension {
+ case "framework":
+ sdkType = .framework
+ case "tbd":
+ if sdkName.starts(with: "libswift") {
+ sdkType = .swiftLibrary
+ } else {
+ sdkType = .library
+ }
+ default:
+ throw SystemFrameworkMetadataProviderError.unsupportedSDK(name: sdkName)
+ }
+
+ let path = try sdkPath(name: sdkName, platform: platform, type: sdkType, source: source)
+ return SystemFrameworkMetadata(
+ name: sdkName,
+ path: path,
+ status: status,
+ source: source
+ )
+ }
+
+ private func sdkPath(name: String, platform: Platform, type: SDKType, source: SDKSource) throws -> AbsolutePath {
+ switch source {
+ case .developer:
+ let xcodeDeveloperSdkRootPath = platform.xcodeDeveloperSdkRootPath
+ let sdkRootPath = try AbsolutePath(validating: "/\(xcodeDeveloperSdkRootPath)")
+ return sdkRootPath
+ .appending(try RelativePath(validating: "Frameworks"))
+ .appending(component: name)
+
+ case .system:
+ let sdkRootPath = try AbsolutePath(validating: "/\(platform.xcodeSdkRootPath)")
+ switch type {
+ case .framework:
+ return sdkRootPath
+ .appending(try RelativePath(validating: "System/Library/Frameworks"))
+ .appending(component: name)
+ case .library:
+ return sdkRootPath
+ .appending(try RelativePath(validating: "usr/lib"))
+ .appending(component: name)
+ case .swiftLibrary:
+ return sdkRootPath
+ .appending(components: "usr", "lib", "swift", name)
+ }
+ }
+ }
+}
diff --git a/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift b/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
new file mode 100644
index 00000000..8cc8149e
--- /dev/null
+++ b/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
@@ -0,0 +1,196 @@
+import FileSystem
+import Foundation
+import Mockable
+import Path
+import ServiceContextModule
+import XcodeGraph
+
+// MARK: - Provider Errors
+
+enum XCFrameworkMetadataProviderError: LocalizedError, Equatable {
+ case xcframeworkNotFound(AbsolutePath)
+ case missingRequiredFile(AbsolutePath)
+ case supportedArchitectureReferencesNotFound(AbsolutePath)
+ case fileTypeNotRecognised(file: RelativePath, frameworkName: String)
+
+ // MARK: - FatalError
+
+ var errorDescription: String? {
+ switch self {
+ case let .xcframeworkNotFound(path):
+ return "Couldn't find xcframework at \(path.pathString)"
+ case let .missingRequiredFile(path):
+ return
+ "The .xcframework at path \(path.pathString) doesn't contain an Info.plist. It's possible that the .xcframework was not generated properly or that got corrupted. Please, double check with the author of the framework."
+ case let .supportedArchitectureReferencesNotFound(path):
+ return
+ "Couldn't find any supported architecture references at \(path.pathString). It's possible that the .xcframework was not generated properly or that it got corrupted. Please, double check with the author of the framework."
+ case let .fileTypeNotRecognised(file, frameworkName):
+ return
+ "The extension of the file `\(file)`, which was found while parsing the xcframework `\(frameworkName)`, is not supported."
+ }
+ }
+}
+
+// MARK: - Provider
+
+@Mockable
+public protocol XCFrameworkMetadataProviding: PrecompiledMetadataProviding {
+ /// It returns the supported architectures of the binary at the given path.
+ /// - Parameter binaryPath: Binary path.
+ func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture]
+
+ /// Return how other binaries should link the binary at the given path.
+ /// - Parameter binaryPath: Path to the binary.
+ func linking(binaryPath: AbsolutePath) throws -> BinaryLinking
+
+ /// It uses 'dwarfdump' to dump the UUIDs of each architecture.
+ /// The UUIDs allows us to know which .bcsymbolmap files belong to this binary.
+ /// - Parameter binaryPath: Path to the binary.
+ func uuids(binaryPath: AbsolutePath) throws -> Set
+
+ /// Loads all the metadata associated with an XCFramework at the specified path
+ /// - Note: This performs various shell calls and disk operations
+ func loadMetadata(at path: AbsolutePath, status: LinkingStatus) async throws
+ -> XCFrameworkMetadata
+
+ /// Returns the info.plist of the xcframework at the given path.
+ /// - Parameter xcframeworkPath: Path to the xcframework.
+ func infoPlist(xcframeworkPath: AbsolutePath) async throws -> XCFrameworkInfoPlist
+}
+
+// MARK: - Default Implementation
+
+public final class XCFrameworkMetadataProvider: PrecompiledMetadataProvider,
+ XCFrameworkMetadataProviding
+{
+ private let fileSystem: FileSysteming
+
+ public init(
+ fileSystem: FileSysteming = FileSystem()
+ ) {
+ self.fileSystem = fileSystem
+ super.init()
+ }
+
+ public func loadMetadata(
+ at path: AbsolutePath,
+ status: LinkingStatus
+ ) async throws -> XCFrameworkMetadata {
+ guard try await fileSystem.exists(path) else {
+ throw XCFrameworkMetadataProviderError.xcframeworkNotFound(path)
+ }
+ let infoPlist = try await infoPlist(xcframeworkPath: path)
+ let linking = try await linking(
+ xcframeworkPath: path,
+ libraries: infoPlist.libraries
+ )
+ return XCFrameworkMetadata(
+ path: path,
+ infoPlist: infoPlist,
+ linking: linking,
+ mergeable: infoPlist.libraries.allSatisfy(\.mergeable),
+ status: status,
+ macroPath: try await macroPath(xcframeworkPath: path),
+ swiftModules: try await fileSystem.glob(directory: path, include: ["**/*.swiftmodule"]).collect().sorted(),
+ moduleMaps: try await fileSystem.glob(directory: path, include: ["**/*.modulemap"]).collect().sorted()
+ )
+ }
+
+ /**
+ An XCFramework that contains static frameworks that represent macros, those are frameworks with a Macros directory in them.
+ We assume that the Swift Macros, which are command line executables, are fat binaries for both architectures supported by macOS:
+ x86_64 and arm64.
+ */
+ public func macroPath(xcframeworkPath: AbsolutePath) async throws -> AbsolutePath? {
+ guard let frameworkPath = try await fileSystem.glob(directory: xcframeworkPath, include: ["*/*.framework"])
+ .collect()
+ .sorted()
+ .first
+ else { return nil }
+ guard let macroPath = try await fileSystem.glob(directory: frameworkPath, include: ["Macros/*"]).collect().first else {
+ return nil
+ }
+ return try AbsolutePath(validating: macroPath.pathString)
+ }
+
+ public func infoPlist(xcframeworkPath: AbsolutePath) async throws -> XCFrameworkInfoPlist {
+ let infoPlist = xcframeworkPath.appending(component: "Info.plist")
+ guard try await fileSystem.exists(infoPlist) else {
+ throw XCFrameworkMetadataProviderError.missingRequiredFile(infoPlist)
+ }
+
+ return try await fileSystem.readPlistFile(at: infoPlist)
+ }
+
+ private func linking(xcframeworkPath: AbsolutePath, libraries: [XCFrameworkInfoPlist.Library])
+ async throws -> BinaryLinking
+ {
+ let archs: [BinaryArchitecture] = [.arm64, .x8664]
+
+ for library in libraries {
+ let hasValidArchitectures = !library.architectures.filter(archs.contains).isEmpty
+ guard hasValidArchitectures,
+ let linking = try? await linking(for: library, xcframeworkPath: xcframeworkPath)
+ else {
+ continue
+ }
+
+ return linking
+ }
+
+ throw XCFrameworkMetadataProviderError.supportedArchitectureReferencesNotFound(
+ xcframeworkPath
+ )
+ }
+
+ private func linking(
+ for library: XCFrameworkInfoPlist.Library,
+ xcframeworkPath: AbsolutePath
+ ) async throws -> BinaryLinking {
+ let (binaryPath, linking): (AbsolutePath, BinaryLinking?)
+
+ switch library.path.extension {
+ case "framework":
+ binaryPath = try AbsolutePath(
+ validating: library.identifier, relativeTo: xcframeworkPath
+ )
+ .appending(try RelativePath(validating: library.path.pathString))
+ .appending(component: library.path.basenameWithoutExt)
+ linking = try? self.linking(binaryPath: binaryPath)
+ case "a":
+ binaryPath = try AbsolutePath(
+ validating: library.identifier, relativeTo: xcframeworkPath
+ )
+ .appending(try RelativePath(validating: library.path.pathString))
+ linking = .static
+ case "dylib":
+ binaryPath = try AbsolutePath(
+ validating: library.identifier, relativeTo: xcframeworkPath
+ )
+ .appending(try RelativePath(validating: library.path.pathString))
+ linking = .dynamic
+ default:
+ throw XCFrameworkMetadataProviderError.fileTypeNotRecognised(
+ file: library.path,
+ frameworkName: xcframeworkPath.basename
+ )
+ }
+
+ guard try await fileSystem.exists(binaryPath), let linking else {
+ // The missing slice relative to the XCFramework folder. e.g ios-x86_64-simulator/Alamofire.framework/Alamofire
+ let relativeArchitectureBinaryPath = binaryPath.components.suffix(3).joined(
+ separator: "/"
+ )
+ ServiceContext.current?.logger?
+ .warning(
+ "\(xcframeworkPath.basename) is missing architecture \(relativeArchitectureBinaryPath) defined in the Info.plist"
+ )
+ throw XCFrameworkMetadataProviderError.supportedArchitectureReferencesNotFound(
+ binaryPath
+ )
+ }
+
+ return linking
+ }
+}
diff --git a/Sources/XcodeProjMapper/Documentation.docc/XcodeProjMapper.md b/Sources/XcodeProjMapper/Documentation.docc/XcodeProjMapper.md
new file mode 100644
index 00000000..0091e6da
--- /dev/null
+++ b/Sources/XcodeProjMapper/Documentation.docc/XcodeProjMapper.md
@@ -0,0 +1,26 @@
+# ``XcodeProjMapper``
+
+@Metadata {
+ @DisplayName("Xcode Project Mapper")
+ @TitleHeading("Documentation Portal")
+ @PageColor(purple)
+}
+
+A tool that maps Xcode projects (`.xcodeproj` and `.xcworkspace`) into a structured, analyzable graph of projects, targets, dependencies, and build settings. This enables downstream tasks such as code generation, dependency analysis, and integration with custom tooling pipelines.
+
+## Overview
+
+``XcodeProjMapper`` leverages ``XcodeProj`` to parse and navigate Xcode project files, then translates them into a domain-specific graph model (``XcodeGraph/Graph``). This model captures all essential components—projects, targets, packages, dependencies, build settings, schemes, and more—providing a high-level, language-agnostic structure for further processing.
+
+By using this graph-based representation, you can easily analyze project configurations, visualize complex dependency graphs, or integrate advanced workflows into your build pipelines. For example, teams can leverage ``XcodeProjMapper`` to:
+
+- Generate code based on discovered resources and targets.
+- Validate project configurations and detect missing bundle identifiers or invalid references.
+- Explore dependencies between multiple projects and packages within a workspace.
+- Automate repetitive tasks like scheme generation, resource synthesis, or compliance checks.
+
+## Topics
+
+### XcodeGraphMapper
+
+- ``XcodeGraphMapper``
diff --git a/Sources/XcodeProjMapper/Extensions/PBXProject+Extensions.swift b/Sources/XcodeProjMapper/Extensions/PBXProject+Extensions.swift
new file mode 100644
index 00000000..c6cbe15e
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/PBXProject+Extensions.swift
@@ -0,0 +1,11 @@
+import XcodeProj
+
+extension PBXProject {
+ /// Retrieves the value of a specific project attribute.
+ ///
+ /// - 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
+ }
+}
diff --git a/Sources/XcodeProjMapper/Extensions/Package+Extensions.swift b/Sources/XcodeProjMapper/Extensions/Package+Extensions.swift
new file mode 100644
index 00000000..e06e2354
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/Package+Extensions.swift
@@ -0,0 +1,13 @@
+import XcodeGraph
+
+extension Package {
+ /// Returns a URL or identifier for the package based on whether it's remote or local.
+ var url: String {
+ switch self {
+ case let .remote(url, _):
+ return url
+ case let .local(path):
+ return path.pathString
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Extensions/Platform+Extensions.swift b/Sources/XcodeProjMapper/Extensions/Platform+Extensions.swift
new file mode 100644
index 00000000..14ef58c3
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/Platform+Extensions.swift
@@ -0,0 +1,28 @@
+import XcodeGraph
+
+extension Platform {
+ /// Initializes a `Platform` instance from an SDK root string (e.g., "iphoneos", "macosx").
+ /// Returns `nil` if no matching platform is found.
+ init?(sdkroot: String) {
+ guard let platform = Platform.allCases.first(where: { $0.xcodeSdkRoot == sdkroot }) else {
+ return nil
+ }
+ self = platform
+ }
+
+ /// Returns a set of `Destination` values supported by this platform.
+ var destinations: Destinations {
+ switch self {
+ case .iOS:
+ return [.iPad, .iPhone, .macCatalyst, .macWithiPadDesign, .appleVisionWithiPadDesign]
+ case .macOS:
+ return [.mac]
+ case .tvOS:
+ return [.appleTv]
+ case .watchOS:
+ return [.appleWatch]
+ case .visionOS:
+ return [.appleVision]
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Extensions/PlatformFilter+Extensions.swift b/Sources/XcodeProjMapper/Extensions/PlatformFilter+Extensions.swift
new file mode 100644
index 00000000..d3906440
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/PlatformFilter+Extensions.swift
@@ -0,0 +1,25 @@
+import XcodeGraph
+
+extension PlatformFilter {
+ /// Initializes a `PlatformFilter` from a string that matches Xcodeproj values.
+ init?(rawValue: String) {
+ switch rawValue {
+ case PlatformFilter.ios.xcodeprojValue:
+ self = .ios
+ case PlatformFilter.macos.xcodeprojValue:
+ self = .macos
+ case PlatformFilter.tvos.xcodeprojValue:
+ self = .tvos
+ case PlatformFilter.catalyst.xcodeprojValue:
+ self = .catalyst
+ case PlatformFilter.driverkit.xcodeprojValue:
+ self = .driverkit
+ case PlatformFilter.watchos.xcodeprojValue:
+ self = .watchos
+ case PlatformFilter.visionos.xcodeprojValue:
+ self = .visionos
+ default:
+ return nil
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Extensions/TargetDependency+Extensions.swift b/Sources/XcodeProjMapper/Extensions/TargetDependency+Extensions.swift
new file mode 100644
index 00000000..c92c1fce
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/TargetDependency+Extensions.swift
@@ -0,0 +1,25 @@
+import XcodeGraph
+
+extension TargetDependency {
+ /// Extracts the name of the dependency for relevant cases, such as target, project, SDK, package, and libraries.
+ var name: String {
+ switch self {
+ case let .target(name, _, _):
+ return name
+ case let .project(target, _, _, _):
+ return target
+ case let .sdk(name, _, _):
+ return name
+ case let .package(product, _, _):
+ return product
+ case let .framework(path, _, _):
+ return path.basenameWithoutExt
+ case let .xcframework(path, _, _):
+ return path.basenameWithoutExt
+ case let .library(path, _, _, _):
+ return path.basenameWithoutExt
+ case .xctest:
+ return "xctest"
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Extensions/XCWorkspace+Extensions.swift b/Sources/XcodeProjMapper/Extensions/XCWorkspace+Extensions.swift
new file mode 100644
index 00000000..e5811c85
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/XCWorkspace+Extensions.swift
@@ -0,0 +1,28 @@
+import Foundation
+import Path
+import XcodeProj
+
+// swiftlint:disable force_try
+extension XCWorkspace {
+ /// A computed property that either returns the workspace’s `path`
+ public var workspacePath: AbsolutePath {
+ try! AbsolutePath(validating: path!.string)
+ }
+}
+
+extension XcodeProj {
+ /// A computed property that either returns the project’s `path`
+ public var projectPath: AbsolutePath {
+ try! AbsolutePath(validating: path!.string)
+ }
+
+ public var srcPath: AbsolutePath {
+ projectPath.parentDirectory
+ }
+
+ public var srcPathString: String {
+ srcPath.pathString
+ }
+}
+
+// swiftlint:enable force_try
diff --git a/Sources/XcodeProjMapper/Extensions/XCWorkspaceDataFileRef+Extensions.swift b/Sources/XcodeProjMapper/Extensions/XCWorkspaceDataFileRef+Extensions.swift
new file mode 100644
index 00000000..a525e9cc
--- /dev/null
+++ b/Sources/XcodeProjMapper/Extensions/XCWorkspaceDataFileRef+Extensions.swift
@@ -0,0 +1,38 @@
+import Path
+import XcodeProj
+
+extension XCWorkspaceDataFileRef {
+ /// Resolves the absolute path referenced by this `XCWorkspaceDataFileRef`.
+ ///
+ /// - Parameter srcPath: The workspace source root path.
+ /// - Returns: The resolved `AbsolutePath` of this file reference.
+ func path(
+ srcPath: AbsolutePath,
+ developerDirectoryProvider: DeveloperDirectoryProviding = DeveloperDirectoryProvider()
+ ) async throws -> AbsolutePath {
+ switch location {
+ case let .absolute(path):
+ return try AbsolutePath(validating: path)
+ case let .container(subPath):
+ let relativePath = try RelativePath(validating: subPath)
+ return srcPath.appending(relativePath)
+ case let .developer(subPath):
+ return try AbsolutePath(
+ validating: subPath,
+ relativeTo: try await developerDirectoryProvider.developerDirectory()
+ )
+ case let .group(subPath):
+ // Group paths are relative to the workspace file itself
+ let relativePath = try RelativePath(validating: subPath)
+ return srcPath.appending(relativePath)
+ case let .current(subPath):
+ // Current paths are relative to the current directory
+ let relativePath = try RelativePath(validating: subPath)
+ return srcPath.appending(relativePath)
+ case let .other(type, subPath):
+ // Other path types: prefix with the type and append subpath
+ let relativePath = try RelativePath(validating: "\(type)/\(subPath)")
+ return srcPath.appending(relativePath)
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Graph/XcodeGraphMapper.swift b/Sources/XcodeProjMapper/Mappers/Graph/XcodeGraphMapper.swift
new file mode 100644
index 00000000..8dc56b3b
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Graph/XcodeGraphMapper.swift
@@ -0,0 +1,286 @@
+import FileSystem
+import Foundation
+import Path
+import PathKit
+import XcodeGraph
+import XcodeProj
+
+/// A protocol defining how to map a given file path to a `Graph`.
+public protocol XcodeGraphMapping {
+ /// Builds a `Graph` from the specified path.
+ /// - Parameter path: The absolute path to a `.xcworkspace`, `.xcodeproj`, or directory containing them.
+ /// - Returns: A `Graph` representing the projects, targets, and dependencies found at `pathString`.
+ /// - Throws: If the path doesn't exist or no projects are found.
+ func map(at path: AbsolutePath) async throws -> Graph
+}
+
+/// An error type for `XcodeGraphMapper` when the path is invalid or no projects are found.
+public enum XcodeGraphMapperError: LocalizedError {
+ case pathNotFound(String)
+ case noProjectsFound(String)
+
+ public var errorDescription: String? {
+ switch self {
+ case let .pathNotFound(path):
+ return "The specified path does not exist: \(path)"
+ case let .noProjectsFound(path):
+ return "No `.xcworkspace` or `.xcodeproj` was found at: \(path)"
+ }
+ }
+}
+
+/// Specifies whether we’re mapping a single `.xcodeproj` or an `.xcworkspace`.
+enum XcodeMapperGraphType {
+ case workspace(XCWorkspace)
+ case project(XcodeProj)
+}
+
+/// A unified entry point that locates `.xcworkspace` or `.xcodeproj` files—even within directories—and
+/// constructs a comprehensive `Graph` of projects, targets, and dependencies.
+///
+/// Specifically, this mapper:
+/// 1. Detects whether the input path is a single project, a workspace, or a directory.
+/// 2. Enumerates all discovered targets and dependencies to assemble the final `Graph`.
+///
+/// This replaces old parsers/providers with a single approach. For example:
+/// ```swift
+/// let mapper: XcodeGraphMapping = XcodeGraphMapper()
+/// let graph = try await mapper.map(at: "/path/to/MyApp")
+/// ```
+public struct XcodeGraphMapper: XcodeGraphMapping {
+ private let fileSystem: FileSysteming
+
+ // MARK: - Initialization
+
+ public init(fileSystem: FileSysteming = FileSystem()) {
+ self.fileSystem = fileSystem
+ }
+
+ // MARK: - Public API
+
+ public func map(at path: AbsolutePath) async throws -> Graph {
+ guard try await fileSystem.exists(path) else {
+ throw XcodeGraphMapperError.pathNotFound(path.pathString)
+ }
+
+ let graphType = try await determineGraphType(at: path)
+ return try await buildGraph(from: graphType)
+ }
+
+ // MARK: - Determine Graph Type
+
+ private func determineGraphType(at path: AbsolutePath) async throws -> XcodeMapperGraphType {
+ // Try a direct match for .xcworkspace / .xcodeproj
+ if let directType = try detectDirectGraphType(at: path) {
+ return directType
+ }
+ // Otherwise look inside the directory
+ return try await detectGraphTypeInDirectory(at: path)
+ }
+
+ private func detectDirectGraphType(at path: AbsolutePath) throws -> XcodeMapperGraphType? {
+ guard let ext = path.extension?.lowercased() else {
+ return nil
+ }
+
+ switch ext {
+ case "xcworkspace":
+ let xcworkspace = try XCWorkspace(path: Path(path.pathString))
+ return .workspace(xcworkspace)
+ case "xcodeproj":
+ let xcodeProj = try XcodeProj(pathString: path.pathString)
+ return .project(xcodeProj)
+ default:
+ return nil
+ }
+ }
+
+ private func detectGraphTypeInDirectory(at path: AbsolutePath) async throws -> XcodeMapperGraphType {
+ let patterns = ["**/*.xcworkspace", "**/*.xcodeproj"]
+ let contents = try fileSystem.glob(directory: path, include: patterns)
+
+ if let workspacePath = try await contents.first(where: { $0.extension?.lowercased() == "xcworkspace" }) {
+ let xcworkspace = try XCWorkspace(path: Path(workspacePath.pathString))
+ return .workspace(xcworkspace)
+ }
+
+ if let projectPath = try await contents.first(where: { $0.extension?.lowercased() == "xcodeproj" }) {
+ let xcodeProj = try XcodeProj(pathString: projectPath.pathString)
+ return .project(xcodeProj)
+ }
+
+ throw XcodeGraphMapperError.noProjectsFound(path.pathString)
+ }
+
+ // MARK: - Build Graph
+
+ func buildGraph(from graphType: XcodeMapperGraphType) async throws -> Graph {
+ let projectPaths = try await identifyProjectPaths(from: graphType)
+ let workspace = assembleWorkspace(graphType: graphType, projectPaths: projectPaths)
+ let projects = try await loadProjects(projectPaths)
+ let packages = extractPackages(from: projects)
+ let (dependencies, dependencyConditions) = try await resolveDependencies(for: projects)
+
+ return assembleFinalGraph(
+ workspace: workspace,
+ projects: projects,
+ packages: packages,
+ dependencies: dependencies,
+ dependencyConditions: dependencyConditions
+ )
+ }
+
+ private func identifyProjectPaths(from graphType: XcodeMapperGraphType) async throws -> [AbsolutePath] {
+ switch graphType {
+ case let .workspace(xcworkspace):
+ return try await extractProjectPaths(
+ from: xcworkspace.data.children,
+ srcPath: xcworkspace.workspacePath.parentDirectory
+ )
+ case let .project(xcodeProj):
+ return [xcodeProj.projectPath]
+ }
+ }
+
+ private func assembleWorkspace(
+ graphType: XcodeMapperGraphType,
+ projectPaths: [AbsolutePath]
+ ) -> Workspace {
+ let workspacePath: AbsolutePath
+ let name: String
+
+ switch graphType {
+ case let .workspace(xcworkspace):
+ workspacePath = xcworkspace.workspacePath
+ name = workspacePath.basenameWithoutExt
+ case let .project(xcodeProj):
+ workspacePath = xcodeProj.projectPath.parentDirectory
+ name = "Workspace"
+ }
+
+ return Workspace(
+ path: workspacePath,
+ xcWorkspacePath: workspacePath,
+ name: name,
+ projects: projectPaths
+ )
+ }
+
+ private func loadProjects(_ projectPaths: [AbsolutePath]) async throws -> [AbsolutePath: Project] {
+ var projects = [AbsolutePath: Project]()
+
+ for path in projectPaths {
+ let xcodeProj = try XcodeProj(pathString: path.pathString)
+ let projectMapper = PBXProjectMapper()
+ let project = try await projectMapper.map(xcodeProj: xcodeProj)
+ projects[path] = project
+ }
+
+ return projects
+ }
+
+ private func extractPackages(
+ from projects: [AbsolutePath: Project]
+ ) -> [AbsolutePath: [String: Package]] {
+ projects.compactMapValues { project in
+ guard !project.packages.isEmpty else { return nil }
+ return Dictionary(
+ uniqueKeysWithValues: project.packages.map { ($0.url, $0) }
+ )
+ }
+ }
+
+ private func resolveDependencies(
+ for projects: [AbsolutePath: Project]
+ ) async throws -> ([GraphDependency: Set], [GraphEdge: PlatformCondition]) {
+ let allTargetsMap = Dictionary(
+ projects.values.flatMap(\.targets),
+ uniquingKeysWith: { existing, _ in existing }
+ )
+ return try await buildDependencies(for: projects, using: allTargetsMap)
+ }
+
+ private func buildDependencies(
+ for projects: [AbsolutePath: Project],
+ using allTargetsMap: [String: Target]
+ ) async throws -> ([GraphDependency: Set], [GraphEdge: PlatformCondition]) {
+ var dependencies = [GraphDependency: Set]()
+ var dependencyConditions = [GraphEdge: PlatformCondition]()
+
+ for (path, project) in projects {
+ for (name, target) in project.targets {
+ let sourceDependency = GraphDependency.target(name: name, path: path.parentDirectory)
+
+ // Build edges for each target dependency
+ let edgesAndDeps = try await target.dependencies.serialCompactMap { (dep: TargetDependency) async throws -> (
+ GraphEdge,
+ PlatformCondition?,
+ GraphDependency
+ ) in
+ let graphDep = try await dep.graphDependency(
+ sourceDirectory: path.parentDirectory,
+ allTargetsMap: allTargetsMap,
+ target: target
+ )
+ return (GraphEdge(from: sourceDependency, to: graphDep), dep.condition, graphDep)
+ }
+
+ // Update conditions dictionary
+ for (edge, condition, _) in edgesAndDeps {
+ if let condition {
+ dependencyConditions[edge] = condition
+ }
+ }
+
+ // Update dependencies dictionary
+ let targetDeps = edgesAndDeps.map(\.2)
+ if !targetDeps.isEmpty {
+ dependencies[sourceDependency] = Set(targetDeps)
+ }
+ }
+ }
+ return (dependencies, dependencyConditions)
+ }
+
+ private func assembleFinalGraph(
+ workspace: Workspace,
+ projects: [AbsolutePath: Project],
+ packages: [AbsolutePath: [String: Package]],
+ dependencies: [GraphDependency: Set],
+ dependencyConditions: [GraphEdge: PlatformCondition]
+ ) -> Graph {
+ Graph(
+ name: workspace.name,
+ path: workspace.path,
+ workspace: workspace,
+ projects: projects,
+ packages: packages,
+ dependencies: dependencies,
+ dependencyConditions: dependencyConditions
+ )
+ }
+
+ // MARK: - Project Path Extraction
+
+ private func extractProjectPaths(
+ from elements: [XCWorkspaceDataElement],
+ srcPath: AbsolutePath
+ ) async throws -> [AbsolutePath] {
+ var paths: [AbsolutePath] = []
+
+ for element in elements {
+ switch element {
+ case let .file(ref):
+ let refPath = try await ref.path(srcPath: srcPath)
+ if refPath.extension == "xcodeproj" {
+ paths.append(refPath)
+ }
+ case let .group(group):
+ let nestedPaths = try await extractProjectPaths(from: group.children, srcPath: srcPath)
+ paths.append(contentsOf: nestedPaths)
+ }
+ }
+
+ return paths
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Packages/XCPackageMapper.swift b/Sources/XcodeProjMapper/Mappers/Packages/XCPackageMapper.swift
new file mode 100644
index 00000000..a3ddc4de
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Packages/XCPackageMapper.swift
@@ -0,0 +1,78 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// Defines errors that may occur when mapping package references.
+enum PackageMappingError: Error, LocalizedError, Equatable {
+ case missingRepositoryURL(packageName: String)
+
+ var errorDescription: String? {
+ switch self {
+ case let .missingRepositoryURL(packageName):
+ return "The repository URL is missing for the package: \(packageName)."
+ }
+ }
+}
+
+/// A protocol defining how to map remote and local Swift package references into `Package` models.
+protocol PackageMapping {
+ /// Maps a remote Swift package reference to a `Package`.
+ ///
+ /// - Parameter package: The remote package reference.
+ /// - Returns: A `Package` representing the remote package.
+ /// - Throws: `PackageMappingError.missingRepositoryURL` if the package has no repository URL.
+ func map(package: XCRemoteSwiftPackageReference) throws -> Package
+
+ /// Maps a local Swift package reference to a `Package`.
+ ///
+ /// - Parameters:
+ /// - package: The local Swift package reference.
+ /// - sourceDirectory: The project’s source directory used to resolve relative paths.
+ /// - Returns: A `Package` representing the local package.
+ /// - Throws: If the provided path is invalid and cannot be resolved.
+ func map(package: XCLocalSwiftPackageReference, sourceDirectory: AbsolutePath) throws -> Package
+}
+
+/// A mapper that converts remote and local Swift package references into `Package` domain models.
+struct XCPackageMapper: PackageMapping {
+ func map(package: XCRemoteSwiftPackageReference) throws -> Package {
+ guard let repositoryURL = package.repositoryURL else {
+ let name = package.name ?? "Unknown Package"
+ throw PackageMappingError.missingRepositoryURL(packageName: name)
+ }
+ let requirement = mapRequirement(package: package)
+ return .remote(url: repositoryURL, requirement: requirement)
+ }
+
+ func map(package: XCLocalSwiftPackageReference, sourceDirectory: AbsolutePath) throws -> Package {
+ let relativePath = try RelativePath(validating: package.relativePath)
+ let path = sourceDirectory.appending(relativePath)
+ return .local(path: path)
+ }
+
+ // MARK: - Private Helpers
+
+ /// Determines the version requirement for a remote Swift package.
+ private func mapRequirement(package: XCRemoteSwiftPackageReference) -> Requirement {
+ guard let versionRequirement = package.versionRequirement else {
+ // Default to an all-zero version if none is specified
+ return .upToNextMajor("0.0.0")
+ }
+
+ switch versionRequirement {
+ case let .upToNextMajorVersion(version):
+ return .upToNextMajor(version)
+ case let .upToNextMinorVersion(version):
+ return .upToNextMinor(version)
+ case let .exact(version):
+ return .exact(version)
+ case let .range(lowerBound, upperBound):
+ return .range(from: lowerBound, to: upperBound)
+ case let .branch(branch):
+ return .branch(branch)
+ case let .revision(revision):
+ return .revision(revision)
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/BuildPhaseConstants.swift b/Sources/XcodeProjMapper/Mappers/Phases/BuildPhaseConstants.swift
new file mode 100644
index 00000000..e51d16d0
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/BuildPhaseConstants.swift
@@ -0,0 +1,46 @@
+import Foundation
+import Path
+
+/// Constants related to various build phases and their default values.
+enum BuildPhaseConstants {
+ /// The default name for a run script build phase if none is provided.
+ static let defaultScriptName = "Run Script"
+ /// The default shell path used by run script build phases.
+ static let defaultShellPath = "/bin/sh"
+ /// A placeholder name used when a shell script build phase has no name.
+ static let unnamedScriptPhase = "Unnamed Shell Script Phase"
+ /// The default name for a copy files build phase if none is provided.
+ static let copyFilesDefault = "Copy Files"
+}
+
+/// Attributes indicating header visibility within a build target.
+enum HeaderAttribute: String {
+ /// Indicates that a header is and can be exposed outside the module.
+ case `public` = "Public"
+ /// Indicates that a header is private and not exposed outside the module.
+ case `private` = "Private"
+}
+
+/// Attributes related to code generation behavior for source files.
+enum CodeGenAttribute: String {
+ /// Indicates that code generation is enabled publicly.
+ case `public` = "codegen"
+ /// Indicates that code generation is restricted to private scopes.
+ case `private` = "private_codegen"
+ /// Indicates that code generation is scoped to the project only.
+ case project = "project_codegen"
+ /// Indicates that code generation is disabled for the file.
+ case disabled = "no_codegen"
+}
+
+/// Commonly referenced directory names within a project.
+enum DirectoryName {
+ /// The directory used to store headers.
+ static let headers = "Headers"
+}
+
+/// Attributes that can be assigned to build files in certain build phases.
+enum BuildFileAttribute: String {
+ /// Indicates that the file should be code signed on copy during a copy files build phase.
+ case codeSignOnCopy = "CodeSignOnCopy"
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift
new file mode 100644
index 00000000..1c01c216
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXCopyFilesBuildPhaseMapper.swift
@@ -0,0 +1,80 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping `PBXCopyFilesBuildPhase` objects to `CopyFilesAction` models.
+protocol PBXCopyFilesBuildPhaseMapping {
+ /// Maps the provided copy files phases to an array of `CopyFilesAction`.
+ /// - Parameters:
+ /// - copyFilesPhases: The build phases to map.
+ /// - xcodeProj: The `XcodeProj` containing project configuration and file references.
+ /// - Returns: An array of mapped `CopyFilesAction`s.
+ /// - Throws: If any file paths are invalid or cannot be resolved.
+ func map(_ copyFilesPhases: [PBXCopyFilesBuildPhase], xcodeProj: XcodeProj) throws -> [CopyFilesAction]
+}
+
+/// A mapper that converts `PBXCopyFilesBuildPhase` objects into `CopyFilesAction` domain models.
+struct PBXCopyFilesBuildPhaseMapper: PBXCopyFilesBuildPhaseMapping {
+ /// Maps the provided copy files phases to sorted `CopyFilesAction` models.
+ func map(_ copyFilesPhases: [PBXCopyFilesBuildPhase], xcodeProj: XcodeProj) throws -> [CopyFilesAction] {
+ try copyFilesPhases
+ .compactMap { try mapCopyFilesPhase($0, xcodeProj: xcodeProj) }
+ .sorted { $0.name < $1.name }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Converts a single `PBXCopyFilesBuildPhase` to a `CopyFilesAction`.
+ /// - Parameters:
+ /// - phase: The `PBXCopyFilesBuildPhase` to convert.
+ /// - xcodeProj: The `XcodeProj` for path resolution.
+ /// - Returns: A `CopyFilesAction` if the phase could be mapped, otherwise `nil`.
+ /// - Throws: If file paths are invalid or unresolved.
+ private func mapCopyFilesPhase(
+ _ phase: PBXCopyFilesBuildPhase,
+ xcodeProj: XcodeProj
+ ) throws -> CopyFilesAction? {
+ let files = try (phase.files ?? [])
+ .compactMap { buildFile -> CopyFileElement? in
+ guard let fileRef = buildFile.file,
+ let pathString = try fileRef.fullPath(sourceRoot: xcodeProj.srcPathString)
+ else {
+ return nil
+ }
+
+ let absolutePath = try AbsolutePath(validating: pathString)
+ let attributes = buildFile.settings?.stringArray(for: .attributes)
+ let codeSignOnCopy = attributes?.contains(BuildFileAttribute.codeSignOnCopy.rawValue) ?? false
+
+ return .file(path: absolutePath, condition: nil, codeSignOnCopy: codeSignOnCopy)
+ }
+ .sorted { $0.path < $1.path }
+
+ return CopyFilesAction(
+ name: phase.name ?? BuildPhaseConstants.copyFilesDefault,
+ destination: mapDstSubfolderSpec(phase.dstSubfolderSpec),
+ subpath: (phase.dstPath?.isEmpty == true) ? nil : phase.dstPath,
+ files: files
+ )
+ }
+
+ /// Maps a `PBXCopyFilesBuildPhase.SubFolder` to a `CopyFilesAction.Destination`.
+ private func mapDstSubfolderSpec(
+ _ subfolderSpec: PBXCopyFilesBuildPhase.SubFolder?
+ ) -> CopyFilesAction.Destination {
+ switch subfolderSpec {
+ case .absolutePath: return .absolutePath
+ case .productsDirectory: return .productsDirectory
+ case .wrapper: return .wrapper
+ case .executables: return .executables
+ case .resources: return .resources
+ case .javaResources: return .javaResources
+ case .frameworks: return .frameworks
+ case .sharedFrameworks: return .sharedFrameworks
+ case .sharedSupport: return .sharedSupport
+ case .plugins: return .plugins
+ default: return .productsDirectory
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXCoreDataModelsBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXCoreDataModelsBuildPhaseMapper.swift
new file mode 100644
index 00000000..ac789586
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXCoreDataModelsBuildPhaseMapper.swift
@@ -0,0 +1,51 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping a set of resource files into `CoreDataModel` objects.
+protocol PBXCoreDataModelsBuildPhaseMapping {
+ /// Maps the provided resource files into an array of `CoreDataModel`.
+ /// - Parameters:
+ /// - resourceFiles: The build files that might contain Core Data models.
+ /// - xcodeProj: The `XcodeProj` for resolving file paths.
+ /// - Returns: An array of `CoreDataModel` objects.
+ /// - Throws: If any paths are invalid.
+ func map(_ resourceFiles: [PBXBuildFile], xcodeProj: XcodeProj) throws -> [CoreDataModel]
+}
+
+/// Maps `PBXBuildFile` objects to `CoreDataModel` domain models if they represent `.xcdatamodeld` files.
+struct PBXCoreDataModelsBuildPhaseMapper: PBXCoreDataModelsBuildPhaseMapping {
+ func map(_ resourceFiles: [PBXBuildFile], xcodeProj: XcodeProj) throws -> [CoreDataModel] {
+ try resourceFiles.compactMap { try mapCoreDataModel($0, xcodeProj: xcodeProj) }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Converts a single `PBXBuildFile` into a `CoreDataModel` if it references a `.xcdatamodeld` version group.
+ private func mapCoreDataModel(_ buildFile: PBXBuildFile, xcodeProj: XcodeProj) throws -> CoreDataModel? {
+ guard let versionGroup = buildFile.file as? XCVersionGroup,
+ versionGroup.path?.hasSuffix(FileExtension.coreData.rawValue) == true,
+ let modelPathString = try versionGroup.fullPath(sourceRoot: xcodeProj.srcPathString)
+ else {
+ return nil
+ }
+
+ let modelPath = try AbsolutePath(validating: modelPathString)
+
+ // Gather all child .xcdatamodel versions
+ let versionPaths = versionGroup.children.compactMap(\.path)
+ let resolvedVersions = try versionPaths.map {
+ try AbsolutePath(validating: $0, relativeTo: modelPath)
+ }
+
+ // Current version defaults to the first if not explicitly set
+ let currentVersion = versionGroup.currentVersion?.path ?? resolvedVersions.first?.pathString ?? ""
+
+ return CoreDataModel(
+ path: modelPath,
+ versions: resolvedVersions,
+ currentVersion: currentVersion
+ )
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
new file mode 100644
index 00000000..6bd5b64b
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
@@ -0,0 +1,67 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping a `PBXFrameworksBuildPhase` into associated `TargetDependency`s.
+protocol PBXFrameworksBuildPhaseMapping {
+ /// Maps the given frameworks build phase to a list of `TargetDependency` instances.
+ ///
+ /// - Parameters:
+ /// - frameworksBuildPhase: The `PBXFrameworksBuildPhase` to map.
+ /// - xcodeProj: The `XcodeProj` for path resolution.
+ /// - Returns: An array of `TargetDependency` objects representing the frameworks.
+ /// - Throws: If any file paths or references cannot be resolved.
+ func map(
+ _ frameworksBuildPhase: PBXFrameworksBuildPhase,
+ xcodeProj: XcodeProj
+ ) throws -> [TargetDependency]
+}
+
+/// The default mapper that converts `PBXFrameworksBuildPhase` files into `TargetDependency` models.
+struct PBXFrameworksBuildPhaseMapper: PBXFrameworksBuildPhaseMapping {
+ private let pathMapper: PathDependencyMapping
+
+ init(pathMapper: PathDependencyMapping = PathDependencyMapper()) {
+ self.pathMapper = pathMapper
+ }
+
+ func map(
+ _ frameworksBuildPhase: PBXFrameworksBuildPhase,
+ xcodeProj: XcodeProj
+ ) throws -> [TargetDependency] {
+ let files = frameworksBuildPhase.files ?? []
+ return try files.map { try mapFrameworkDependency($0, xcodeProj: xcodeProj) }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Maps a single PBXBuildFile from the frameworks build phase to a `TargetDependency`.
+ private func mapFrameworkDependency(
+ _ buildFile: PBXBuildFile,
+ xcodeProj: XcodeProj
+ ) throws -> TargetDependency {
+ let fileRef = try buildFile.file.throwing(PBXFrameworksBuildPhaseMappingError.missingFileReference)
+ let filePathString = try fileRef.fullPath(sourceRoot: xcodeProj.srcPathString)
+ .throwing(PBXFrameworksBuildPhaseMappingError.missingFilePath(name: fileRef.name))
+
+ let absolutePath = try AbsolutePath(validating: filePathString)
+ return try pathMapper.map(path: absolutePath, condition: nil)
+ }
+}
+
+/// Errors that may occur when mapping framework build phase files.
+enum PBXFrameworksBuildPhaseMappingError: Error, LocalizedError {
+ case missingFileReference
+ case missingFilePath(name: String?)
+
+ var errorDescription: String? {
+ switch self {
+ case .missingFileReference:
+ return "Missing `PBXBuildFile.file` reference."
+ case let .missingFilePath(name):
+ let fileName = name ?? "Unknown"
+ return "Missing or invalid file path for `PBXBuildFile`: \(fileName)."
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift
new file mode 100644
index 00000000..379b0515
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXHeadersBuildPhaseMapper.swift
@@ -0,0 +1,82 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping a PBXHeadersBuildPhase into a Headers model.
+protocol PBXHeadersBuildPhaseMapping {
+ /// Converts the given headers build phase into a `Headers` object.
+ /// - Parameters:
+ /// - headersBuildPhase: The build phase containing header files.
+ /// - xcodeProj: The `XcodeProj` used for resolving file paths.
+ /// - Returns: A `Headers` object, or `nil` if there are no valid header files.
+ /// - Throws: If any file paths cannot be resolved.
+ func map(_ headersBuildPhase: PBXHeadersBuildPhase, xcodeProj: XcodeProj) throws -> Headers?
+}
+
+/// Maps a `PBXHeadersBuildPhase` to a `Headers` domain model.
+struct PBXHeadersBuildPhaseMapper: PBXHeadersBuildPhaseMapping {
+ func map(_ headersBuildPhase: PBXHeadersBuildPhase, xcodeProj: XcodeProj) throws -> Headers? {
+ // Gather all valid HeaderInfo objects
+ let headerInfos = try (headersBuildPhase.files ?? []).compactMap {
+ try mapHeaderFile($0, xcodeProj: xcodeProj)
+ }
+
+ guard !headerInfos.isEmpty else {
+ return nil
+ }
+
+ let publicHeaders = headerInfos
+ .filter { $0.visibility == .public }
+ .map(\.path)
+ let privateHeaders = headerInfos
+ .filter { $0.visibility == .private }
+ .map(\.path)
+ let projectHeaders = headerInfos
+ .filter { $0.visibility == .project }
+ .map(\.path)
+
+ return Headers(
+ public: publicHeaders,
+ private: privateHeaders,
+ project: projectHeaders
+ )
+ }
+
+ // MARK: - Private Helpers
+
+ /// Converts a single `PBXBuildFile` into a `HeaderInfo` if it's a valid header reference.
+ private func mapHeaderFile(_ buildFile: PBXBuildFile, xcodeProj: XcodeProj) throws -> HeaderInfo? {
+ guard let pbxElement = buildFile.file,
+ let pathString = try pbxElement.fullPath(sourceRoot: xcodeProj.srcPathString)
+ else {
+ return nil
+ }
+
+ let absolutePath = try AbsolutePath(validating: pathString)
+ let attributes = buildFile.settings?.stringArray(for: .attributes)
+
+ let visibility: HeaderInfo.HeaderVisibility
+ if attributes?.contains(HeaderAttribute.public.rawValue) == true {
+ visibility = .public
+ } else if attributes?.contains(HeaderAttribute.private.rawValue) == true {
+ visibility = .private
+ } else {
+ visibility = .project
+ }
+
+ return HeaderInfo(path: absolutePath, visibility: visibility)
+ }
+}
+
+/// Internal struct used to capture a single header file’s path and visibility.
+private struct HeaderInfo {
+ let path: AbsolutePath
+ let visibility: HeaderVisibility
+
+ enum HeaderVisibility {
+ case `public`
+ case `private`
+ case project
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXResourcesBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXResourcesBuildPhaseMapper.swift
new file mode 100644
index 00000000..db89ce32
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXResourcesBuildPhaseMapper.swift
@@ -0,0 +1,86 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping a PBXResourcesBuildPhase into an array of ResourceFileElement.
+protocol PBXResourcesBuildPhaseMapping {
+ /// Converts the given resources build phase to a list of `ResourceFileElement` models.
+ /// - Parameters:
+ /// - resourcesBuildPhase: The build phase that may contain resource files and variant groups.
+ /// - xcodeProj: The `XcodeProj` used for path resolution.
+ /// - Returns: An array of `ResourceFileElement`s, representing file paths or grouped variants.
+ /// - Throws: If any file references are missing or paths cannot be resolved.
+ func map(_ resourcesBuildPhase: PBXResourcesBuildPhase, xcodeProj: XcodeProj) throws -> [ResourceFileElement]
+}
+
+/// A mapper that converts a `PBXResourcesBuildPhase` into a list of `ResourceFileElement`.
+struct PBXResourcesBuildPhaseMapper: PBXResourcesBuildPhaseMapping {
+ func map(
+ _ resourcesBuildPhase: PBXResourcesBuildPhase,
+ xcodeProj: XcodeProj
+ ) throws -> [ResourceFileElement] {
+ let files = resourcesBuildPhase.files ?? []
+ let elements = try files.flatMap { buildFile in
+ try mapResourceElement(buildFile, xcodeProj: xcodeProj)
+ }
+ return elements.sorted { $0.path < $1.path }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Maps a single `PBXBuildFile` to one or more `ResourceFileElement`s.
+ private func mapResourceElement(
+ _ buildFile: PBXBuildFile,
+ xcodeProj: XcodeProj
+ ) throws -> [ResourceFileElement] {
+ let fileElement = try buildFile.file
+ .throwing(PBXResourcesMappingError.missingFileReference)
+
+ // If it's a PBXVariantGroup, map each child within that group.
+ if let variantGroup = fileElement as? PBXVariantGroup {
+ return try mapVariantGroup(variantGroup, xcodeProj: xcodeProj)
+ } else {
+ // Otherwise, it's a straightforward file or reference.
+ return try mapFileElement(fileElement, xcodeProj: xcodeProj)
+ }
+ }
+
+ /// Maps a simple (non-variant) file element to a list (usually a single entry) of `ResourceFileElement`.
+ private func mapFileElement(
+ _ fileElement: PBXFileElement,
+ xcodeProj: XcodeProj
+ ) throws -> [ResourceFileElement] {
+ let pathString = try fileElement
+ .fullPath(sourceRoot: xcodeProj.srcPathString)
+ .throwing(PBXResourcesMappingError.missingFullPath(fileElement.name ?? "Unknown"))
+
+ let absolutePath = try AbsolutePath(validating: pathString)
+ return [.file(path: absolutePath)]
+ }
+
+ /// Maps a PBXVariantGroup by expanding each child into a `ResourceFileElement`.
+ private func mapVariantGroup(
+ _ variantGroup: PBXVariantGroup,
+ xcodeProj: XcodeProj
+ ) throws -> [ResourceFileElement] {
+ try variantGroup.children.flatMap { child in
+ try mapFileElement(child, xcodeProj: xcodeProj)
+ }
+ }
+}
+
+/// Example error types for resource mapping.
+enum PBXResourcesMappingError: LocalizedError {
+ case missingFileReference
+ case missingFullPath(String)
+
+ var errorDescription: String? {
+ switch self {
+ case .missingFileReference:
+ return "Missing file reference for resource."
+ case let .missingFullPath(name):
+ return "No valid path for resource file element: \(name)."
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXScriptsBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXScriptsBuildPhaseMapper.swift
new file mode 100644
index 00000000..672ae6ba
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXScriptsBuildPhaseMapper.swift
@@ -0,0 +1,116 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping PBXShellScriptBuildPhases into domain models.
+protocol PBXScriptsBuildPhaseMapping {
+ /// Maps the given script phases into `TargetScript` models.
+ ///
+ /// - Parameters:
+ /// - scriptPhases: The shell script build phases to convert.
+ /// - buildPhases: The full list of a target's `PBXBuildPhase`s, used to determine script order.
+ /// - Returns: An array of `TargetScript` models representing each shell script build phase.
+ /// - Throws: If any file paths cannot be validated.
+ func map(
+ _ scriptPhases: [PBXShellScriptBuildPhase],
+ buildPhases: [PBXBuildPhase]
+ ) throws -> [TargetScript]
+
+ /// Maps shell script build phases into a simpler, “raw” representation (`RawScriptBuildPhase`).
+ ///
+ /// - Parameter scriptPhases: The shell script build phases to map.
+ /// - Returns: A list of `RawScriptBuildPhase` models for each script.
+ func mapRawScriptBuildPhases(_ scriptPhases: [PBXShellScriptBuildPhase]) -> [RawScriptBuildPhase]
+}
+
+/// Maps `PBXShellScriptBuildPhase` instances into `TargetScript` and `RawScriptBuildPhase` models.
+struct PBXScriptsBuildPhaseMapper: PBXScriptsBuildPhaseMapping {
+ func map(
+ _ scriptPhases: [PBXShellScriptBuildPhase],
+ buildPhases: [PBXBuildPhase]
+ ) throws -> [TargetScript] {
+ try scriptPhases.compactMap {
+ try mapScriptPhase($0, buildPhases: buildPhases)
+ }
+ }
+
+ func mapRawScriptBuildPhases(_ scriptPhases: [PBXShellScriptBuildPhase]) -> [RawScriptBuildPhase] {
+ scriptPhases.map { mapShellScriptBuildPhase($0) }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Converts a single `PBXShellScriptBuildPhase` to a `TargetScript`, if valid.
+ private func mapScriptPhase(
+ _ scriptPhase: PBXShellScriptBuildPhase,
+ buildPhases: [PBXBuildPhase]
+ ) throws -> TargetScript? {
+ guard let shellScript = scriptPhase.shellScript else {
+ return nil
+ }
+
+ let inputFileListPaths = try scriptPhase.inputFileListPaths?.compactMap {
+ try AbsolutePath(validating: $0)
+ } ?? []
+
+ let outputFileListPaths = try scriptPhase.outputFileListPaths?.compactMap {
+ try AbsolutePath(validating: $0)
+ } ?? []
+
+ let dependencyFile = try scriptPhase.dependencyFile.map {
+ try AbsolutePath(validating: $0)
+ }
+
+ return TargetScript(
+ name: scriptPhase.name ?? BuildPhaseConstants.defaultScriptName,
+ order: determineScriptOrder(buildPhases: buildPhases, scriptPhase: scriptPhase),
+ script: .embedded(shellScript),
+ inputPaths: scriptPhase.inputPaths,
+ inputFileListPaths: inputFileListPaths,
+ outputPaths: scriptPhase.outputPaths,
+ outputFileListPaths: outputFileListPaths,
+ showEnvVarsInLog: scriptPhase.showEnvVarsInLog,
+ basedOnDependencyAnalysis: scriptPhase.alwaysOutOfDate ? false : nil,
+ runForInstallBuildsOnly: scriptPhase.runOnlyForDeploymentPostprocessing,
+ shellPath: scriptPhase.shellPath ?? BuildPhaseConstants.defaultShellPath,
+ dependencyFile: dependencyFile
+ )
+ }
+
+ /// Converts a single `PBXShellScriptBuildPhase` into a simpler `RawScriptBuildPhase`.
+ private func mapShellScriptBuildPhase(
+ _ buildPhase: PBXShellScriptBuildPhase
+ ) -> RawScriptBuildPhase {
+ let name = buildPhase.name() ?? BuildPhaseConstants.unnamedScriptPhase
+ let shellPath = buildPhase.shellPath ?? BuildPhaseConstants.defaultShellPath
+ let script = buildPhase.shellScript ?? ""
+ let showEnvVarsInLog = buildPhase.showEnvVarsInLog
+
+ return RawScriptBuildPhase(
+ name: name,
+ script: script,
+ showEnvVarsInLog: showEnvVarsInLog,
+ hashable: false,
+ shellPath: shellPath
+ )
+ }
+
+ /// Determines the order of the script relative to other build phases (pre or post sources).
+ private func determineScriptOrder(
+ buildPhases: [PBXBuildPhase],
+ scriptPhase: PBXShellScriptBuildPhase
+ ) -> TargetScript.Order {
+ guard let scriptIndex = buildPhases.firstIndex(of: scriptPhase) else {
+ return .pre
+ }
+
+ // If we have a Sources phase, check whether this script is above or below it.
+ if let sourcesIndex = buildPhases.firstIndex(where: { $0.buildPhase == .sources }) {
+ return scriptIndex > sourcesIndex ? .post : .pre
+ }
+
+ // Fallback: if it's at index 0, consider it pre; otherwise post.
+ return scriptIndex == 0 ? .pre : .post
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift b/Sources/XcodeProjMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift
new file mode 100644
index 00000000..dc0e5745
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Phases/PBXSourcesBuildPhaseMapper.swift
@@ -0,0 +1,71 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol for mapping a PBXSourcesBuildPhase into an array of SourceFile models.
+protocol PBXSourcesBuildPhaseMapping {
+ /// Converts the given sources build phase into a list of `SourceFile`s.
+ /// - Parameters:
+ /// - sourcesBuildPhase: The build phase that may contain source files.
+ /// - xcodeProj: The `XcodeProj` used for path resolution.
+ /// - Returns: A sorted list of `SourceFile`s by their path.
+ /// - Throws: If file paths are invalid or unavailable.
+ func map(_ sourcesBuildPhase: PBXSourcesBuildPhase, xcodeProj: XcodeProj) throws -> [SourceFile]
+}
+
+/// The default mapper that converts a `PBXSourcesBuildPhase` to an array of `SourceFile`s.
+struct PBXSourcesBuildPhaseMapper: PBXSourcesBuildPhaseMapping {
+ func map(
+ _ sourcesBuildPhase: PBXSourcesBuildPhase,
+ xcodeProj: XcodeProj
+ ) throws -> [SourceFile] {
+ let files = sourcesBuildPhase.files ?? []
+ return try files
+ .compactMap { try mapSourceFile($0, xcodeProj: xcodeProj) }
+ .sorted { $0.path < $1.path }
+ }
+
+ // MARK: - Private Helpers
+
+ /// Maps a single `PBXBuildFile` into a `SourceFile` if valid.
+ private func mapSourceFile(
+ _ buildFile: PBXBuildFile,
+ xcodeProj: XcodeProj
+ ) throws -> SourceFile? {
+ guard let fileRef = buildFile.file,
+ let pathString = try fileRef.fullPath(sourceRoot: xcodeProj.srcPathString)
+ else {
+ return nil
+ }
+
+ let path = try AbsolutePath(validating: pathString)
+ let settings = buildFile.settings ?? [:]
+ let compilerFlags: String? = settings.string(for: .compilerFlags)
+ let attributes: [String]? = settings.stringArray(for: .attributes)
+
+ return SourceFile(
+ path: path,
+ compilerFlags: compilerFlags,
+ codeGen: mapCodeGenAttribute(attributes)
+ )
+ }
+
+ /// Translates file attributes into a `FileCodeGen`, if specified.
+ private func mapCodeGenAttribute(_ attributes: [String]?) -> FileCodeGen? {
+ guard let attributes else { return nil }
+
+ switch true {
+ case attributes.contains(FileCodeGen.public.rawValue):
+ return .public
+ case attributes.contains(FileCodeGen.private.rawValue):
+ return .private
+ case attributes.contains(FileCodeGen.project.rawValue):
+ return .project
+ case attributes.contains(FileCodeGen.disabled.rawValue):
+ return .disabled
+ default:
+ return nil
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Project/PBXProjectMapper.swift b/Sources/XcodeProjMapper/Mappers/Project/PBXProjectMapper.swift
new file mode 100644
index 00000000..e9687749
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Project/PBXProjectMapper.swift
@@ -0,0 +1,157 @@
+import Foundation
+import Path
+import PathKit
+import XcodeGraph
+import XcodeMetadata
+@preconcurrency import XcodeProj
+
+// swiftlint:disable function_body_length
+
+/// A protocol for mapping an Xcode project (`.xcodeproj`) into a `Project` domain model.
+protocol PBXProjectMapping {
+ /// Maps the given `XcodeProj` into a `Project` model.
+ ///
+ /// - Parameter xcodeProj: The Xcode project to be transformed.
+ /// - Returns: A fully constructed `Project` model.
+ /// - Throws: If reading or transforming project data fails.
+ func map(xcodeProj: XcodeProj) async throws -> Project
+}
+
+/// A mapper that transforms a `.xcodeproj` into a `Project` domain model.
+///
+/// This process involves:
+/// - Mapping project-level settings.
+/// - Converting `PBXTarget`s into `Target` models.
+/// - Resolving both remote and local Swift packages.
+/// - Identifying and integrating user and shared schemes.
+/// - Providing resource synthesizers for code generation.
+struct PBXProjectMapper: PBXProjectMapping {
+ /// Maps the given Xcode project into a `Project` model.
+ ///
+ /// - Parameter xcodeProj: The Xcode project reference containing `.pbxproj` data.
+ /// - Returns: A fully constructed `Project` model.
+ /// - Throws: If reading or transforming project data fails.
+ func map(xcodeProj: XcodeProj) async throws -> Project {
+ let settingsMapper = XCConfigurationMapper()
+ let pbxProject = try xcodeProj.mainPBXProject()
+ let xcodeProjPath = xcodeProj.projectPath
+ let sourceDirectory = xcodeProjPath.parentDirectory
+
+ // Map the project-wide build settings
+ let settings = try settingsMapper.map(
+ xcodeProj: xcodeProj,
+ configurationList: pbxProject.buildConfigurationList
+ )
+
+ // Map PBXTargets to domain Targets
+ let targetMapper = PBXTargetMapper()
+ let targets = try await pbxProject.targets.serialCompactMap {
+ try await targetMapper.map(pbxTarget: $0, xcodeProj: xcodeProj)
+ }
+ .sorted()
+
+ // Map remote and local packages
+ let packageMapper = XCPackageMapper()
+ let remotePackages = try pbxProject.remotePackages.compactMap {
+ try packageMapper.map(package: $0)
+ }
+ let localPackages = try pbxProject.localPackages.compactMap {
+ try packageMapper.map(package: $0, sourceDirectory: sourceDirectory)
+ }
+
+ // Create a files group for the main group
+ let filesGroup = ProjectGroup.group(name: pbxProject.mainGroup?.name ?? "Project")
+
+ // Map user and shared schemes
+ let schemeMapper = XCSchemeMapper()
+ let graphType: XcodeMapperGraphType = .project(xcodeProj)
+ let userSchemes = try xcodeProj.userData.flatMap(\.schemes).map {
+ try schemeMapper.map($0, shared: false, graphType: graphType)
+ }
+ let sharedSchemes = try xcodeProj.sharedData?.schemes.map {
+ try schemeMapper.map($0, shared: true, graphType: graphType)
+ } ?? []
+ let schemes = userSchemes + sharedSchemes
+
+ // Other project-level metadata
+ let lastUpgradeCheck = pbxProject.attribute(for: .lastUpgradeCheck).flatMap { Version(string: $0) }
+ let defaultKnownRegions = pbxProject.knownRegions.isEmpty ? nil : pbxProject.knownRegions
+
+ // Construct the final `Project`
+ return Project(
+ path: sourceDirectory,
+ sourceRootPath: sourceDirectory,
+ xcodeProjPath: xcodeProjPath,
+ name: pbxProject.name,
+ organizationName: pbxProject.attribute(for: .organization),
+ classPrefix: pbxProject.attribute(for: .classPrefix),
+ defaultKnownRegions: defaultKnownRegions,
+ developmentRegion: pbxProject.developmentRegion,
+ options: .init(
+ automaticSchemesOptions: .disabled,
+ disableBundleAccessors: false,
+ disableShowEnvironmentVarsInScriptPhases: false,
+ disableSynthesizedResourceAccessors: false,
+ textSettings: .init(
+ usesTabs: nil,
+ indentWidth: nil,
+ tabWidth: nil,
+ wrapsLines: nil
+ )
+ ),
+ settings: settings,
+ filesGroup: filesGroup,
+ targets: targets,
+ packages: remotePackages + localPackages,
+ schemes: schemes,
+ ideTemplateMacros: nil,
+ additionalFiles: [],
+ resourceSynthesizers: mapResourceSynthesizers(),
+ lastUpgradeCheck: lastUpgradeCheck,
+ type: .local
+ )
+ }
+
+ /// Returns a set of default resource synthesizers for common resource types.
+ private func mapResourceSynthesizers() -> [ResourceSynthesizer] {
+ ResourceSynthesizer.Parser.allCases.map { parser in
+ let (exts, template) = parser.resourceTypes()
+ return ResourceSynthesizer(
+ parser: parser,
+ parserOptions: [:],
+ extensions: Set(exts),
+ template: .defaultTemplate(template)
+ )
+ }
+ }
+}
+
+// MARK: - ResourceSynthesizer.Parser Helpers
+
+extension ResourceSynthesizer.Parser {
+ /// Defines resource extensions and default templates for each parser type.
+ fileprivate func resourceTypes() -> (exts: [String], template: String) {
+ switch self {
+ case .strings, .stringsCatalog:
+ return (["strings", "stringsdict"], "Strings")
+ case .assets:
+ return (["xcassets"], "Assets")
+ case .plists:
+ return (["plist"], "Plists")
+ case .fonts:
+ return (["ttf", "otf", "ttc"], "Fonts")
+ case .coreData:
+ return (["xcdatamodeld"], "CoreData")
+ case .interfaceBuilder:
+ return (["xib", "storyboard"], "InterfaceBuilder")
+ case .json:
+ return (["json"], "JSON")
+ case .yaml:
+ return (["yaml", "yml"], "YAML")
+ case .files:
+ return (["txt", "md"], "Files")
+ }
+ }
+}
+
+// swiftlint:enable function_body_length
diff --git a/Sources/XcodeProjMapper/Mappers/Project/ProjectAttribute.swift b/Sources/XcodeProjMapper/Mappers/Project/ProjectAttribute.swift
new file mode 100644
index 00000000..ae33a42f
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Project/ProjectAttribute.swift
@@ -0,0 +1,8 @@
+import Foundation
+
+/// Attributes for project settings that can be retrieved from a `PBXProject`.
+enum ProjectAttribute: String {
+ case classPrefix = "CLASSPREFIX"
+ case organization = "ORGANIZATIONNAME"
+ case lastUpgradeCheck = "LastUpgradeCheck"
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Project/XcodeProj+Extensions.swift b/Sources/XcodeProjMapper/Mappers/Project/XcodeProj+Extensions.swift
new file mode 100644
index 00000000..b90eb076
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Project/XcodeProj+Extensions.swift
@@ -0,0 +1,23 @@
+import Foundation
+import XcodeProj
+
+/// Errors that may occur while accessing main `PBXProject` information.
+enum XcodeProjError: LocalizedError, Equatable {
+ case noProjectsFound
+
+ var errorDescription: String? {
+ switch self {
+ case .noProjectsFound:
+ return "No `PBXProject` was found in the `.xcodeproj`"
+ }
+ }
+}
+
+extension XcodeProj {
+ func mainPBXProject() throws -> PBXProject {
+ guard let pbxProject = pbxproj.projects.first else {
+ throw XcodeProjError.noProjectsFound
+ }
+ return pbxProject
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Schemes/SchemeDiagnosticsOptions+XCScheme.swift b/Sources/XcodeProjMapper/Mappers/Schemes/SchemeDiagnosticsOptions+XCScheme.swift
new file mode 100644
index 00000000..ebbf3f2c
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Schemes/SchemeDiagnosticsOptions+XCScheme.swift
@@ -0,0 +1,25 @@
+import XcodeGraph
+import XcodeProj
+
+extension SchemeDiagnosticsOptions {
+ /// Creates a SchemeDiagnosticsOptions from a LaunchAction.
+ init(action: XCScheme.LaunchAction) {
+ self = SchemeDiagnosticsOptions(
+ addressSanitizerEnabled: action.enableAddressSanitizer,
+ detectStackUseAfterReturnEnabled: action.enableASanStackUseAfterReturn,
+ threadSanitizerEnabled: action.enableThreadSanitizer,
+ mainThreadCheckerEnabled: !action.disableMainThreadChecker,
+ performanceAntipatternCheckerEnabled: !action.disablePerformanceAntipatternChecker
+ )
+ }
+
+ /// Creates a SchemeDiagnosticsOptions from a TestAction.
+ init(action: XCScheme.TestAction) {
+ self = SchemeDiagnosticsOptions(
+ addressSanitizerEnabled: action.enableAddressSanitizer,
+ detectStackUseAfterReturnEnabled: action.enableASanStackUseAfterReturn,
+ threadSanitizerEnabled: action.enableThreadSanitizer,
+ mainThreadCheckerEnabled: !action.disableMainThreadChecker
+ )
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Schemes/XCSchemeMapper.swift b/Sources/XcodeProjMapper/Mappers/Schemes/XCSchemeMapper.swift
new file mode 100644
index 00000000..eeda5bdb
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Schemes/XCSchemeMapper.swift
@@ -0,0 +1,224 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol defining how to map a single `XCScheme` object (and its actions) into a domain `Scheme` model.
+///
+/// Conforming types translate a raw `XCScheme` instance, including its build, test, run, archive, profile,
+/// and analyze actions, into a `Scheme` model ready for analysis, code generation, or tooling integration.
+protocol SchemeMapping {
+ /// Maps a single `XCScheme` into a `Scheme` model.
+ ///
+ /// - Parameters:
+ /// - xcscheme: The `XCScheme` to map.
+ /// - shared: Indicates whether the scheme is shared.
+ /// - graphType: Specifies if we’re dealing with a workspace or project for path resolution.
+ /// - Returns: A `Scheme` model corresponding to the given `XCScheme`.
+ /// - Throws: If any of the scheme's actions (build, test, run, etc.) cannot be resolved.
+ func map(
+ _ xcscheme: XCScheme,
+ shared: Bool,
+ graphType: XcodeMapperGraphType
+ ) throws -> Scheme
+}
+
+/// A mapper responsible for converting an `XCScheme` object into a `Scheme` model.
+///
+/// `XCSchemeMapper` resolves references to targets, environment variables, and all scheme actions.
+/// The resulting `Scheme` models enable analysis, code generation, or integration with custom tooling.
+struct XCSchemeMapper: SchemeMapping {
+ // MARK: - Public API
+
+ func map(
+ _ xcscheme: XCScheme,
+ shared: Bool,
+ graphType: XcodeMapperGraphType
+ ) throws -> Scheme {
+ Scheme(
+ name: xcscheme.name,
+ shared: shared,
+ hidden: false,
+ buildAction: try mapBuildAction(action: xcscheme.buildAction, graphType: graphType),
+ testAction: try mapTestAction(action: xcscheme.testAction, graphType: graphType),
+ runAction: try mapRunAction(action: xcscheme.launchAction, graphType: graphType),
+ archiveAction: try mapArchiveAction(action: xcscheme.archiveAction),
+ profileAction: try mapProfileAction(action: xcscheme.profileAction, graphType: graphType),
+ analyzeAction: try mapAnalyzeAction(action: xcscheme.analyzeAction)
+ )
+ }
+
+ // MARK: - Action Mappings
+
+ /// Maps the optional build action into a domain `BuildAction`, or returns `nil` if not present.
+ private func mapBuildAction(
+ action: XCScheme.BuildAction?,
+ graphType: XcodeMapperGraphType
+ ) throws -> BuildAction? {
+ guard let action else { return nil }
+
+ let targets = try action.buildActionEntries.compactMap {
+ try mapTargetReference(buildableReference: $0.buildableReference, graphType: graphType)
+ }
+
+ return BuildAction(
+ targets: targets,
+ preActions: [],
+ postActions: [],
+ runPostActionsOnFailure: action.runPostActionsOnFailure ?? false,
+ findImplicitDependencies: action.buildImplicitDependencies
+ )
+ }
+
+ /// Maps the optional test action into a domain `TestAction`, or returns `nil` if not present.
+ private func mapTestAction(
+ action: XCScheme.TestAction?,
+ graphType: XcodeMapperGraphType
+ ) throws -> TestAction? {
+ guard let action else { return nil }
+
+ let testTargets = try action.testables.compactMap { testable in
+ let targetRef = try mapTargetReference(
+ buildableReference: testable.buildableReference,
+ graphType: graphType
+ )
+ return TestableTarget(target: targetRef, skipped: testable.skipped)
+ }
+
+ let arguments = mapArguments(
+ environmentVariables: action.environmentVariables,
+ commandlineArguments: action.commandlineArguments
+ )
+ let diagnosticsOptions = SchemeDiagnosticsOptions(action: action)
+
+ return TestAction(
+ targets: testTargets,
+ arguments: arguments,
+ configurationName: action.buildConfiguration,
+ attachDebugger: true,
+ coverage: action.codeCoverageEnabled,
+ codeCoverageTargets: [],
+ expandVariableFromTarget: nil,
+ preActions: [],
+ postActions: [],
+ diagnosticsOptions: diagnosticsOptions,
+ language: action.language,
+ region: action.region
+ )
+ }
+
+ /// Maps the optional run (launch) action into a domain `RunAction`, or returns `nil` if not present.
+ private func mapRunAction(
+ action: XCScheme.LaunchAction?,
+ graphType: XcodeMapperGraphType
+ ) throws -> RunAction? {
+ guard let action else { return nil }
+
+ let executable: TargetReference? = try {
+ if let buildableRef = action.runnable?.buildableReference {
+ return try mapTargetReference(buildableReference: buildableRef, graphType: graphType)
+ }
+ return nil
+ }()
+
+ let arguments = mapArguments(
+ environmentVariables: action.environmentVariables,
+ commandlineArguments: action.commandlineArguments
+ )
+ let diagnosticsOptions = SchemeDiagnosticsOptions(action: action)
+ // If no debugger is explicitly chosen, Xcode uses the default lldb (true).
+ let attachDebugger = action.selectedDebuggerIdentifier.isEmpty
+
+ return RunAction(
+ configurationName: action.buildConfiguration,
+ attachDebugger: attachDebugger,
+ customLLDBInitFile: nil,
+ preActions: [],
+ postActions: [],
+ executable: executable,
+ filePath: nil,
+ arguments: arguments,
+ options: RunActionOptions(),
+ diagnosticsOptions: diagnosticsOptions
+ )
+ }
+
+ /// Maps the optional archive action into a domain `ArchiveAction`, or returns `nil` if not present.
+ private func mapArchiveAction(
+ action: XCScheme.ArchiveAction?
+ ) throws -> ArchiveAction? {
+ guard let action else { return nil }
+ return ArchiveAction(
+ configurationName: action.buildConfiguration,
+ revealArchiveInOrganizer: action.revealArchiveInOrganizer
+ )
+ }
+
+ /// Maps the optional profile action into a domain `ProfileAction`, or returns `nil` if not present.
+ func mapProfileAction(
+ action: XCScheme.ProfileAction?,
+ graphType: XcodeMapperGraphType
+ ) throws -> ProfileAction? {
+ guard let action else { return nil }
+
+ let executable: TargetReference? = try {
+ if let buildableRef = action.buildableProductRunnable?.buildableReference {
+ return try mapTargetReference(buildableReference: buildableRef, graphType: graphType)
+ }
+ return nil
+ }()
+
+ return ProfileAction(
+ configurationName: action.buildConfiguration,
+ executable: executable
+ )
+ }
+
+ /// Maps the optional analyze action into a domain `AnalyzeAction`, or returns `nil` if not present.
+ private func mapAnalyzeAction(
+ action: XCScheme.AnalyzeAction?
+ ) throws -> AnalyzeAction? {
+ guard let action else { return nil }
+ return AnalyzeAction(configurationName: action.buildConfiguration)
+ }
+
+ // MARK: - Helper Methods
+
+ /// Converts a buildable reference within a scheme to a `TargetReference`.
+ private func mapTargetReference(
+ buildableReference: XCScheme.BuildableReference,
+ graphType: XcodeMapperGraphType
+ ) throws -> TargetReference {
+ let targetName = buildableReference.blueprintName
+ let container = buildableReference.referencedContainer
+
+ let projectPath: AbsolutePath
+ switch graphType {
+ case let .workspace(xcworkspace):
+ // Container is relative to the workspace’s parent directory
+ let relativeContainerPath = container.replacingOccurrences(of: "container:", with: "")
+ let relPath = try RelativePath(validating: relativeContainerPath)
+ projectPath = xcworkspace.workspacePath.parentDirectory.appending(relPath)
+ case let .project(xcodeProj):
+ projectPath = xcodeProj.projectPath
+ }
+
+ return TargetReference(projectPath: projectPath, name: targetName)
+ }
+
+ /// Converts environment variables and command-line arguments into a unified `Arguments` model.
+ private func mapArguments(
+ environmentVariables: [XCScheme.EnvironmentVariable]?,
+ commandlineArguments: XCScheme.CommandLineArguments?
+ ) -> Arguments {
+ let envVars = environmentVariables?.reduce(into: [String: EnvironmentVariable]()) { dict, variable in
+ dict[variable.variable] = EnvironmentVariable(value: variable.value, isEnabled: variable.enabled)
+ } ?? [:]
+
+ let launchArgs = commandlineArguments?.arguments.map {
+ LaunchArgument(name: $0.name, isEnabled: $0.enabled)
+ } ?? []
+
+ return Arguments(environmentVariables: envVars, launchArguments: launchArgs)
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Settings/BuildSettings.swift b/Sources/XcodeProjMapper/Mappers/Settings/BuildSettings.swift
new file mode 100644
index 00000000..e5652b26
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Settings/BuildSettings.swift
@@ -0,0 +1,100 @@
+import Foundation
+
+/// 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"
+ case iPhoneOSDeploymentTarget = "IPHONEOS_DEPLOYMENT_TARGET"
+ case macOSDeploymentTarget = "MACOSX_DEPLOYMENT_TARGET"
+ case watchOSDeploymentTarget = "WATCHOS_DEPLOYMENT_TARGET"
+ case tvOSDeploymentTarget = "TVOS_DEPLOYMENT_TARGET"
+ 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)
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationList+Helpers.swift b/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationList+Helpers.swift
new file mode 100644
index 00000000..8c429113
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationList+Helpers.swift
@@ -0,0 +1,41 @@
+import XcodeGraph
+import XcodeProj
+
+extension XCConfigurationList {
+ /// Retrieves a build setting value from the first configuration in which it is found.
+ ///
+ /// - Parameter key: The `BuildSettingKey` to look up.
+ /// - Returns: The value as a `String` if found, otherwise `nil`.
+ func stringSettings(for key: BuildSettingKey) -> [BuildConfiguration: String] {
+ let configurationMatcher = ConfigurationMatcher()
+ var results = [BuildConfiguration: String]()
+ for config in buildConfigurations {
+ if let value = config.buildSettings.string(for: key) {
+ let variant = configurationMatcher.variant(for: config.name)
+ let buildConfig = BuildConfiguration(name: config.name, variant: variant)
+ results[buildConfig] = value
+ }
+ }
+ return results
+ }
+
+ /// Retrieves all deployment target values from all configurations and aggregates them.
+ ///
+ /// - Parameters:
+ /// - keys: A list of keys to search (e.g., `.iPhoneOSDeploymentTarget`, `.macOSDeploymentTarget`)
+ /// - Returns: A dictionary mapping `BuildSettingKey` to the found value.
+ func allDeploymentTargets(keys: [BuildSettingKey]) -> [BuildSettingKey: String] {
+ var results = [BuildSettingKey: String]()
+
+ for key in keys {
+ for config in buildConfigurations {
+ if let value = config.buildSettings.string(for: key) {
+ results[key] = value
+ break // Once found, move to the next key
+ }
+ }
+ }
+
+ return results
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationMapper.swift b/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationMapper.swift
new file mode 100644
index 00000000..5a5b683c
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Settings/XCConfigurationMapper.swift
@@ -0,0 +1,128 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol that defines how to map an Xcode project's `XCConfigurationList` into a domain-specific `Settings` model.
+///
+/// Conforming types provide an asynchronous mapping function that takes a `projectProvider` and an optional
+/// `XCConfigurationList`, then returns a `Settings` model. If no configuration list is provided, they return default settings.
+protocol SettingsMapping: Sendable {
+ /// Maps a given `XCConfigurationList` into a `Settings` model.
+ ///
+ /// This operation extracts build configurations and their associated build settings, translating them into a
+ /// domain-specific `Settings` representation. If the provided configuration list is `nil`, default settings are returned.
+ ///
+ /// - Parameters:
+ /// - projectProvider: A provider for project-related paths and files, used to resolve paths like `.xcconfig` files.
+ /// - configurationList: The `XCConfigurationList` from which to derive settings. If `nil`, defaults are returned.
+ /// - Returns: A `Settings` model derived from the configuration list, or default settings if none are found.
+ /// - Throws: If any build settings cannot be properly mapped into a `Settings` model.
+ func map(
+ xcodeProj: XcodeProj,
+ configurationList: XCConfigurationList?
+ ) throws -> Settings
+}
+
+/// A mapper responsible for converting an Xcode project's configuration list into a `Settings` domain model.
+///
+/// `SettingsMapper` reads through the project's `XCConfigurationList`, extracting each build configuration along with
+/// its raw build settings. It then translates these settings into a structured `Settings` model, associating them with
+/// corresponding `BuildConfiguration` variants (e.g., debug or release). Additionally, it attempts to resolve any
+/// `.xcconfig` references into absolute paths. If no configuration list is provided, `SettingsMapper` returns default settings.
+///
+/// Typical usage:
+/// ```swift
+/// let mapper = SettingsMapper()
+/// let settings = try mapper.map(xcodeProj: provider.xcodeProj, configurationList: configurationList)
+/// ```
+final class XCConfigurationMapper: SettingsMapping {
+ func map(
+ xcodeProj: XcodeProj,
+ configurationList: XCConfigurationList?
+ ) throws -> Settings {
+ guard let configurationList else {
+ return Settings.default
+ }
+
+ var configurations: [BuildConfiguration: Configuration?] = [:]
+ for buildConfig in configurationList.buildConfigurations {
+ let buildSettings = buildConfig.buildSettings
+ let settingsDict = try mapBuildSettings(buildSettings)
+
+ var xcconfigAbsolutePath: AbsolutePath?
+ if let baseConfigRef = buildConfig.baseConfiguration,
+ let xcconfigPath = try baseConfigRef.fullPath(
+ sourceRoot: xcodeProj.srcPathString
+ )
+ {
+ xcconfigAbsolutePath = try AbsolutePath(validating: xcconfigPath)
+ }
+
+ let variant = variant(for: buildConfig.name)
+ let buildConfiguration = BuildConfiguration(name: buildConfig.name, variant: variant)
+ configurations[buildConfiguration] = Configuration(
+ settings: settingsDict,
+ xcconfig: xcconfigAbsolutePath
+ )
+ }
+
+ return Settings(
+ base: [:],
+ baseDebug: [:],
+ configurations: configurations,
+ defaultSettings: .recommended
+ )
+ }
+
+ /// Converts a dictionary of raw build settings (`[String: Any]`) into a structured `SettingsDictionary`.
+ ///
+ /// Each raw setting value is mapped to a `SettingValue`. Strings and arrays of strings are preserved as-is;
+ /// other types are converted into strings as a fallback. This ensures that all settings are represented in a
+ /// uniform and easily processed manner.
+ ///
+ /// - 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 {
+ var settingsDict = SettingsDictionary()
+ for (key, value) in buildSettings {
+ settingsDict[key] = try mapSettingValue(value)
+ }
+ 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.
+ ///
+ /// - Parameter name: The name of the build configuration (e.g., "Debug", "Release", "Development").
+ /// - Returns: The corresponding `BuildConfiguration.Variant` inferred from the name.
+ private func variant(
+ for name: String,
+ configurationMatcher: ConfigurationMatching = ConfigurationMatcher()
+ ) -> BuildConfiguration.Variant {
+ configurationMatcher.variant(for: name)
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXBuildRuleMapper.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXBuildRuleMapper.swift
new file mode 100644
index 00000000..2d559c61
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXBuildRuleMapper.swift
@@ -0,0 +1,67 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol defining how to map a single `PBXBuildRule` instance into a `BuildRule` domain model.
+///
+/// Conforming types transform an individual build rule defined in an Xcode project into a
+/// structured `BuildRule` model, enabling further analysis or code generation steps to operate on
+/// well-defined representations of build rules.
+protocol BuildRuleMapping {
+ /// Maps a single `PBXBuildRule` into a `BuildRule` model.
+ ///
+ /// - Parameter buildRule: The `PBXBuildRule` to map.
+ /// - Returns: A `BuildRule` model if the compiler spec and file type are recognized; otherwise, `nil`.
+ /// - Throws: If resolving or mapping the build rule fails.
+ func map(_ buildRule: PBXBuildRule) throws -> BuildRule?
+}
+
+/// A mapper that converts a `PBXBuildRule` object into a `BuildRule` domain model.
+///
+/// `BuildRuleMapper` extracts known compiler specs and file types from the provided build rule.
+/// If the compiler spec or file type is unknown, the build rule is ignored (returning `nil`).
+struct PBXBuildRuleMapper: BuildRuleMapping {
+ func map(_ buildRule: PBXBuildRule) throws -> BuildRule? {
+ let compilerSpec = try mapCompilerSpec(buildRule.compilerSpec)
+ let fileType = try mapFileType(buildRule.fileType)
+
+ return BuildRule(
+ compilerSpec: compilerSpec,
+ fileType: fileType,
+ filePatterns: buildRule.filePatterns,
+ name: buildRule.name,
+ outputFiles: buildRule.outputFiles,
+ inputFiles: buildRule.inputFiles,
+ outputFilesCompilerFlags: buildRule.outputFilesCompilerFlags,
+ script: buildRule.script,
+ runOncePerArchitecture: buildRule.runOncePerArchitecture
+ )
+ }
+
+ // MARK: - Private Helpers
+
+ private func mapCompilerSpec(_ compilerSpec: String) throws -> BuildRule.CompilerSpec {
+ try BuildRule.CompilerSpec(rawValue: compilerSpec)
+ .throwing(PBXBuildRuleMappingError.unknownCompilerSpec(compilerSpec))
+ }
+
+ private func mapFileType(_ fileType: String) throws -> BuildRule.FileType {
+ try BuildRule.FileType(rawValue: fileType)
+ .throwing(PBXBuildRuleMappingError.unknownFileType(fileType))
+ }
+}
+
+enum PBXBuildRuleMappingError: Error, LocalizedError, Equatable {
+ case unknownFileType(String)
+ case unknownCompilerSpec(String)
+
+ var errorDescription: String? {
+ switch self {
+ case let .unknownFileType(fileType):
+ return "Unknown file type: \(fileType)"
+ case let .unknownCompilerSpec(compilerSpec):
+ return "Unknown compiler spec: \(compilerSpec)"
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildHeaders.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildHeaders.swift
new file mode 100644
index 00000000..3708f285
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildHeaders.swift
@@ -0,0 +1,8 @@
+import XcodeProj
+
+extension PBXTarget {
+ /// Returns the headers build phase, if any.
+ func headersBuildPhase() throws -> PBXHeadersBuildPhase? {
+ buildPhases.compactMap { $0 as? PBXHeadersBuildPhase }.first
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildSettings.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildSettings.swift
new file mode 100644
index 00000000..88115db3
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+BuildSettings.swift
@@ -0,0 +1,88 @@
+import Foundation
+import Path
+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`.
+ func infoPlistPath() -> [BuildConfiguration: String] {
+ buildConfigurationList?.stringSettings(for: .infoPlistFile) ?? [:]
+ }
+
+ /// Retrieves the path to the entitlements file from the target's build settings.
+ ///
+ /// - Returns: The `CODE_SIGN_ENTITLEMENTS` value if present, otherwise `nil`.
+ func entitlementsPath() -> [BuildConfiguration: String] {
+ buildConfigurationList?.stringSettings(for: .codeSignEntitlements) ?? [:]
+ }
+
+ func defaultBuildConfiguration(
+ configurationMatcher: ConfigurationMatching = ConfigurationMatcher()
+ ) -> BuildConfiguration? {
+ guard let defaultName = buildConfigurationList?.defaultConfigurationName else { return nil }
+ let variant = configurationMatcher.variant(for: defaultName)
+ return BuildConfiguration(name: defaultName, variant: variant)
+ }
+
+ /// Retrieves deployment target versions for various platforms supported by this target.
+ ///
+ /// Checks build configurations for:
+ /// - `IPHONEOS_DEPLOYMENT_TARGET`
+ /// - `MACOSX_DEPLOYMENT_TARGET`
+ /// - `WATCHOS_DEPLOYMENT_TARGET`
+ /// - `TVOS_DEPLOYMENT_TARGET`
+ /// - `VISIONOS_DEPLOYMENT_TARGET`
+ ///
+ /// - Returns: A `DeploymentTargets` instance containing any discovered versions.
+ func deploymentTargets() -> DeploymentTargets {
+ guard let configList = buildConfigurationList else {
+ return DeploymentTargets(iOS: nil, macOS: nil, watchOS: nil, tvOS: nil, visionOS: nil)
+ }
+
+ let keys: [BuildSettingKey] = [
+ .iPhoneOSDeploymentTarget,
+ .macOSDeploymentTarget,
+ .watchOSDeploymentTarget,
+ .tvOSDeploymentTarget,
+ .visionOSDeploymentTarget,
+ ]
+
+ let targets = configList.allDeploymentTargets(keys: keys)
+ return DeploymentTargets(
+ iOS: targets[.iPhoneOSDeploymentTarget],
+ macOS: targets[.macOSDeploymentTarget],
+ watchOS: targets[.watchOSDeploymentTarget],
+ tvOS: targets[.tvOSDeploymentTarget],
+ visionOS: targets[.visionOSDeploymentTarget]
+ )
+ }
+
+ /// 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] {
+ buildConfigurationList?.buildConfigurations.first(where: { $0.name == "Debug" })?.buildSettings
+ ?? [:]
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+GraphMapping.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+GraphMapping.swift
new file mode 100644
index 00000000..98d22726
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+GraphMapping.swift
@@ -0,0 +1,61 @@
+import Foundation
+import Path
+import XcodeGraph
+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) {
+ return bundleId
+ }
+ throw PBXTargetMappingError.missingBundleIdentifier(targetName: name)
+ }
+
+ /// Returns an array of all `PBXCopyFilesBuildPhase` instances for this target.
+ func copyFilesBuildPhases() -> [PBXCopyFilesBuildPhase] {
+ 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)
+ 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/XcodeProjMapper/Mappers/Targets/PBXTarget+PlatformInference.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+PlatformInference.swift
new file mode 100644
index 00000000..e67e33fd
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTarget+PlatformInference.swift
@@ -0,0 +1,90 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+enum PlatformInferenceError: LocalizedError, Equatable {
+ case noPlatformInferred(String)
+
+ var errorDescription: String? {
+ switch self {
+ case let .noPlatformInferred(name):
+ return "No platform could be inferred from target '\(name)'."
+ }
+ }
+}
+
+extension PBXTarget {
+ /// Determines the set of `Destinations` supported by this target.
+ ///
+ /// Attempts to identify platforms from:
+ /// 1. `SDKROOT` (if present)
+ /// 2. Deployment targets
+ /// 3. Product type as a final fallback
+ ///
+ /// Supports multi-platform scenarios by unioning destinations from all inferred platforms.
+ ///
+ /// - Returns: A `Destinations` set representing all supported destinations.
+ /// - Throws: If retrieving deployment targets fails.
+ func platform() throws -> Destinations {
+ if let sdkNames = buildConfigurationList?.stringSettings(for: .sdkroot),
+ let sdkName = sdkNames.values.first,
+ let root = Platform(sdkroot: sdkName)
+ {
+ return root.destinations
+ } else {
+ return try inferPlatformFromTarget()
+ }
+ }
+
+ /// Infers the platform from deployment targets if `SDKROOT` is not set or recognized.
+ ///
+ /// Aggregates all platforms indicated by the deployment targets. If none are found,
+ /// picks a default based on product type:
+ /// - iOS for most apps, clips, and app extensions.
+ /// - macOS for frameworks, libraries, command line tools, macros, xpc, system extensions.
+ /// - tvOS for tvTopShelfExtension.
+ /// - iOS otherwise.
+ ///
+ /// - Returns: A `Destinations` set representing all inferred destinations.
+ /// - Throws: If retrieving deployment targets fails.
+ private func inferPlatformFromTarget() throws -> Destinations {
+ let deploymentTargets = deploymentTargets()
+ var result = Destinations()
+
+ if deploymentTargets.iOS != nil {
+ result.formUnion(Platform.iOS.destinations)
+ }
+ if deploymentTargets.macOS != nil {
+ result.formUnion(Platform.macOS.destinations)
+ }
+ if deploymentTargets.watchOS != nil {
+ result.formUnion(Platform.watchOS.destinations)
+ }
+ if deploymentTargets.tvOS != nil {
+ result.formUnion(Platform.tvOS.destinations)
+ }
+ if deploymentTargets.visionOS != nil {
+ result.formUnion(Platform.visionOS.destinations)
+ }
+
+ guard result.isEmpty else { return result }
+
+ let productType = productType?.mapProductType()
+ let product = try productType.throwing(PlatformInferenceError.noPlatformInferred(name))
+
+ switch product {
+ case .app, .stickerPackExtension, .appClip, .appExtension:
+ return Platform.iOS.destinations
+ case .framework, .staticLibrary, .dynamicLibrary, .commandLineTool, .macro, .xpc,
+ .systemExtension:
+ return Platform.macOS.destinations
+ case .tvTopShelfExtension:
+ return Platform.tvOS.destinations
+ case .watch2App, .watch2Extension:
+ return Platform.watchOS.destinations
+ default:
+ return Platform.iOS.destinations
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependency+PlatformCondition.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependency+PlatformCondition.swift
new file mode 100644
index 00000000..84bdfaaa
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependency+PlatformCondition.swift
@@ -0,0 +1,19 @@
+import XcodeGraph
+import XcodeProj
+
+/// A mapper for platform-related conditions, extracting platform filters from `PBXTargetDependency`.
+extension PBXTargetDependency {
+ /// Maps the platform filters on a given `PBXTargetDependency` into a `PlatformCondition`.
+ ///
+ /// Returns `nil` if no filters apply, meaning the dependency isn't restricted by platform and
+ /// should be considered available on all platforms.
+ func platformCondition() -> PlatformCondition? {
+ var filters = Set(platformFilters ?? [])
+ if let singleFilter = platformFilter {
+ filters.insert(singleFilter)
+ }
+
+ let platformFilters = Set(filters.compactMap { PlatformFilter(rawValue: $0) })
+ return PlatformCondition.when(platformFilters)
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependencyMapper.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
new file mode 100644
index 00000000..f06e4eb1
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
@@ -0,0 +1,181 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// A protocol defining how to map a single `PBXTargetDependency` into a `TargetDependency` model.
+///
+/// Conforming types handle all known dependency types—direct targets, package products,
+/// proxy references (which may point to other targets or external projects), and file-based dependencies.
+protocol PBXTargetDependencyMapping {
+ /// Maps a single `PBXTargetDependency` into a `TargetDependency` model.
+ ///
+ /// - Parameters:
+ /// - dependency: The `PBXTargetDependency` to map.
+ /// - xcodeProj: Provides the `.xcodeproj` data and source directory paths.
+ /// - Returns: A `TargetDependency` if the dependency can be resolved.
+ /// - Throws: If the dependency references invalid paths or targets that cannot be resolved.
+ func map(_ dependency: PBXTargetDependency, xcodeProj: XcodeProj) throws -> TargetDependency
+}
+
+/// A unified mapper that handles all types of `PBXTargetDependency` instances.
+///
+/// `PBXTargetDependencyMapper` checks if the dependency references a direct target, a package product,
+/// or a proxy. For proxy dependencies, it may resolve references to another target, a project,
+/// or file-based dependencies (frameworks, libraries, etc.). If a dependency cannot be resolved
+/// to a known domain model, it throws an error.
+struct PBXTargetDependencyMapper: PBXTargetDependencyMapping {
+ private let pathMapper: PathDependencyMapping
+
+ init(pathMapper: PathDependencyMapping = PathDependencyMapper()) {
+ self.pathMapper = pathMapper
+ }
+
+ func map(_ dependency: PBXTargetDependency, xcodeProj: XcodeProj) throws -> TargetDependency {
+ let condition = dependency.platformCondition()
+
+ // 1. Direct target dependency
+ if let target = dependency.target {
+ return .target(name: target.name, status: .required, condition: condition)
+ }
+
+ // 2. Package product dependency
+ if let product = dependency.product {
+ return .package(
+ product: product.productName,
+ type: .runtime,
+ condition: condition
+ )
+ }
+
+ // 3. Proxy dependency
+ if let targetProxy = dependency.targetProxy {
+ switch targetProxy.proxyType {
+ case .nativeTarget:
+ return try mapNativeTargetProxy(targetProxy, condition: condition, xcodeProj: xcodeProj)
+ case .reference:
+ return try mapReferenceProxy(targetProxy, condition: condition, xcodeProj: xcodeProj)
+ case .other, .none:
+ throw TargetDependencyMappingError.unsupportedProxyType(dependency.name)
+ }
+ }
+
+ // If none of the above matched, it's an unknown dependency type.
+ throw TargetDependencyMappingError.unknownDependencyType(
+ name: dependency.name ?? "Unknown dependency name"
+ )
+ }
+
+ // MARK: - Private Helpers
+
+ private func mapNativeTargetProxy(
+ _ targetProxy: PBXContainerItemProxy,
+ condition: PlatformCondition?,
+ xcodeProj: XcodeProj
+ ) throws -> TargetDependency {
+ let remoteInfo = try targetProxy.remoteInfo.throwing(
+ TargetDependencyMappingError.missingRemoteInfoInNativeProxy
+ )
+
+ switch targetProxy.containerPortal {
+ case .project:
+ // Direct reference to another target in the same project.
+ return .target(name: remoteInfo, status: .required, condition: condition)
+ case let .fileReference(fileReference):
+ let projectRelativePath = try fileReference.path
+ .throwing(TargetDependencyMappingError.missingFileReference(fileReference.name ?? ""))
+
+ let path = xcodeProj.srcPath.appending(component: projectRelativePath)
+ // Reference to a target in another project.
+ return .project(target: remoteInfo, path: path, status: .required, condition: condition)
+ case let .unknownObject(object):
+ throw TargetDependencyMappingError.unknownObject(object.debugDescription)
+ }
+ }
+
+ private func mapReferenceProxy(
+ _ targetProxy: PBXContainerItemProxy,
+ condition: PlatformCondition?,
+ xcodeProj: XcodeProj
+ ) throws -> TargetDependency {
+ let remoteGlobalID = try targetProxy.remoteGlobalID.throwing(
+ TargetDependencyMappingError.missingRemoteGlobalIDInReferenceProxy
+ )
+
+ switch remoteGlobalID {
+ case let .object(object):
+ // File-based dependency
+ if let fileRef = object as? PBXFileReference {
+ return try mapFileDependency(
+ pathString: fileRef.path,
+ condition: condition,
+ xcodeProj: xcodeProj
+ )
+ } else if let refProxy = object as? PBXReferenceProxy {
+ return try mapFileDependency(
+ pathString: refProxy.path,
+ condition: condition,
+ xcodeProj: xcodeProj
+ )
+ }
+ throw TargetDependencyMappingError.unknownObject("\(object)")
+
+ case .string:
+ // If remoteGlobalID is just a string, we can’t map a file or target from it.
+ throw TargetDependencyMappingError.unknownDependencyType(
+ name: "remoteGlobalID is a string, cannot map a known target or file reference."
+ )
+ }
+ }
+
+ /// Maps file-based dependencies (e.g., frameworks, libraries) into `TargetDependency` models.
+ /// - Parameters:
+ /// - pathString: The path string for the file-based dependency (relative or absolute).
+ /// - condition: An optional platform condition.
+ /// - xcodeProj: The Xcode project reference for resolving the directory structure.
+ /// - Returns: A `TargetDependency` reflecting the file’s extension (framework, library, etc.).
+ /// - Throws: If the path is missing or invalid.
+ private func mapFileDependency(
+ pathString: String?,
+ condition: PlatformCondition?,
+ xcodeProj: XcodeProj
+ ) throws -> TargetDependency {
+ let pathString = try pathString.throwing(
+ TargetDependencyMappingError.missingFileReference("Path string is nil in file dependency.")
+ )
+ let path = xcodeProj.srcPath.appending(try RelativePath(validating: pathString))
+ return try pathMapper.map(path: path, condition: condition)
+ }
+}
+
+// MARK: - Errors
+
+/// Errors that may occur when mapping `PBXTargetDependency` instances.
+enum TargetDependencyMappingError: LocalizedError, Equatable {
+ case targetNotFound(targetName: String, path: AbsolutePath)
+ case unknownDependencyType(name: String)
+ case missingFileReference(String)
+ case unknownObject(String)
+ case missingRemoteInfoInNativeProxy
+ case missingRemoteGlobalIDInReferenceProxy
+ case unsupportedProxyType(String?)
+
+ var errorDescription: String? {
+ switch self {
+ case let .targetNotFound(targetName, path):
+ return "The target '\(targetName)' could not be found in the project at: \(path.pathString)."
+ case let .unknownDependencyType(name):
+ return "An unknown dependency type '\(name)' was encountered."
+ case let .missingFileReference(description):
+ return "File reference path is missing in target dependency: \(description)."
+ case let .unknownObject(description):
+ return "Encountered an unknown PBXObject in target dependency: \(description)."
+ case .missingRemoteInfoInNativeProxy:
+ return "A native target proxy is missing `remoteInfo` in target dependency."
+ case .missingRemoteGlobalIDInReferenceProxy:
+ return "A reference proxy is missing `remoteGlobalID` in target dependency."
+ case let .unsupportedProxyType(name):
+ return "Encountered an unsupported PBXProxyType in dependency: \(name ?? "Unknown")."
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetMapper.swift b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetMapper.swift
new file mode 100644
index 00000000..432d0c65
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/PBXTargetMapper.swift
@@ -0,0 +1,336 @@
+import FileSystem
+import Foundation
+import Path
+import XcodeGraph
+import XcodeProj
+
+/// Errors that may occur while mapping a `PBXTarget` into a domain-level `Target`.
+enum PBXTargetMappingError: LocalizedError, Equatable {
+ case noProjectsFound(path: String)
+ case missingFilesGroup(targetName: String)
+ case invalidPlist(path: String)
+ case missingBundleIdentifier(targetName: String)
+
+ var errorDescription: String? {
+ switch self {
+ case let .noProjectsFound(path):
+ return "No project was found at: \(path)."
+ case let .missingFilesGroup(targetName):
+ return "The files group is missing for the target '\(targetName)'."
+ case let .invalidPlist(path):
+ return "Failed to read a valid plist dictionary from file at: \(path)."
+ case let .missingBundleIdentifier(targetName):
+ return "The bundle identifier is missing for the target '\(targetName)'."
+ }
+ }
+}
+
+/// A protocol defining how to map a `PBXTarget` into a domain-level `Target` model.
+///
+/// Conforming types transform raw `PBXTarget` instances—including their build phases,
+/// settings, and dependencies—into fully realized `Target` models suitable for analysis,
+/// code generation, or tooling integration.
+protocol PBXTargetMapping {
+ /// Maps a given `PBXTarget` into a `Target` model.
+ ///
+ /// This involves:
+ /// - Extracting platform, product, and deployment information.
+ /// - Mapping build phases (sources, resources, headers, scripts, copy files, frameworks, etc.).
+ /// - Resolving dependencies (project-based, frameworks, libraries, packages, SDKs).
+ /// - Reading settings, launch arguments, and metadata.
+ ///
+ /// - Parameters:
+ /// - pbxTarget: The `PBXTarget` to map.
+ /// - xcodeProj: Provides access to `.xcodeproj` data and the source directory for path resolution.
+ /// - Returns: A fully mapped `Target` model.
+ /// - Throws: `PBXTargetMappingError` if required data (like a bundle identifier) is missing,
+ /// or if necessary files/groups cannot be found.
+ func map(pbxTarget: PBXTarget, xcodeProj: XcodeProj) async throws -> Target
+}
+
+// swiftlint:disable function_body_length
+/// A mapper that converts a `PBXTarget` into a domain `Target` model.
+///
+/// `PBXTargetMapper` orchestrates various specialized mappers (e.g., sources, resources, headers)
+/// and dependency resolvers to produce a comprehensive `Target` suitable for downstream tasks.
+struct PBXTargetMapper: PBXTargetMapping {
+ private let settingsMapper: SettingsMapping
+ private let sourcesMapper: PBXSourcesBuildPhaseMapping
+ private let resourcesMapper: PBXResourcesBuildPhaseMapping
+ private let headersMapper: PBXHeadersBuildPhaseMapping
+ private let scriptsMapper: PBXScriptsBuildPhaseMapping
+ private let copyFilesMapper: PBXCopyFilesBuildPhaseMapping
+ private let coreDataModelsMapper: PBXCoreDataModelsBuildPhaseMapping
+ private let frameworksMapper: PBXFrameworksBuildPhaseMapping
+ private let dependencyMapper: PBXTargetDependencyMapping
+ private let buildRuleMapper: BuildRuleMapping
+
+ init(
+ settingsMapper: SettingsMapping = XCConfigurationMapper(),
+ sourcesMapper: PBXSourcesBuildPhaseMapping = PBXSourcesBuildPhaseMapper(),
+ resourcesMapper: PBXResourcesBuildPhaseMapping = PBXResourcesBuildPhaseMapper(),
+ headersMapper: PBXHeadersBuildPhaseMapping = PBXHeadersBuildPhaseMapper(),
+ scriptsMapper: PBXScriptsBuildPhaseMapping = PBXScriptsBuildPhaseMapper(),
+ copyFilesMapper: PBXCopyFilesBuildPhaseMapping = PBXCopyFilesBuildPhaseMapper(),
+ coreDataModelsMapper: PBXCoreDataModelsBuildPhaseMapping = PBXCoreDataModelsBuildPhaseMapper(),
+ frameworksMapper: PBXFrameworksBuildPhaseMapping = PBXFrameworksBuildPhaseMapper(),
+ dependencyMapper: PBXTargetDependencyMapping = PBXTargetDependencyMapper(),
+ buildRuleMapper: BuildRuleMapping = PBXBuildRuleMapper()
+ ) {
+ self.settingsMapper = settingsMapper
+ self.sourcesMapper = sourcesMapper
+ self.resourcesMapper = resourcesMapper
+ self.headersMapper = headersMapper
+ self.scriptsMapper = scriptsMapper
+ self.copyFilesMapper = copyFilesMapper
+ self.coreDataModelsMapper = coreDataModelsMapper
+ self.frameworksMapper = frameworksMapper
+ self.dependencyMapper = dependencyMapper
+ self.buildRuleMapper = buildRuleMapper
+ }
+
+ func map(pbxTarget: PBXTarget, xcodeProj: XcodeProj) async throws -> Target {
+ let platform = try pbxTarget.platform()
+ let deploymentTargets = pbxTarget.deploymentTargets()
+ let productType = pbxTarget.productType?.mapProductType()
+ let product = try productType.throwing(PlatformInferenceError.noPlatformInferred(pbxTarget.name))
+
+ // Project settings and configurations
+ let settings = try settingsMapper.map(
+ xcodeProj: xcodeProj,
+ configurationList: pbxTarget.buildConfigurationList
+ )
+
+ // Build Phases
+ let sources = try pbxTarget.sourcesBuildPhase().map {
+ try sourcesMapper.map($0, xcodeProj: xcodeProj)
+ } ?? []
+
+ let resources = try pbxTarget.resourcesBuildPhase().map {
+ try resourcesMapper.map($0, xcodeProj: xcodeProj)
+ } ?? []
+
+ let headers = try pbxTarget.headersBuildPhase().map {
+ try headersMapper.map($0, xcodeProj: xcodeProj)
+ } ?? nil
+
+ let runScriptPhases = pbxTarget.runScriptBuildPhases()
+ let scripts = try scriptsMapper.map(runScriptPhases, buildPhases: pbxTarget.buildPhases)
+ let rawScriptBuildPhases = scriptsMapper.mapRawScriptBuildPhases(runScriptPhases)
+
+ let copyFilesPhases = pbxTarget.copyFilesBuildPhases()
+ let copyFiles = try copyFilesMapper.map(copyFilesPhases, xcodeProj: xcodeProj)
+
+ // Core Data models
+ let resourceFiles = try pbxTarget.resourcesBuildPhase()?.files ?? []
+ let coreDataModels = try coreDataModelsMapper.map(resourceFiles, xcodeProj: xcodeProj)
+
+ // Frameworks & libraries
+ let frameworksPhase = try pbxTarget.frameworksBuildPhase()
+ let frameworks = try frameworksPhase.map {
+ try frameworksMapper.map($0, xcodeProj: xcodeProj)
+ } ?? []
+
+ // Additional files (not in build phases)
+ let additionalFiles = try mapAdditionalFiles(from: pbxTarget, xcodeProj: xcodeProj)
+
+ // Resource elements
+ let resourceFileElements = ResourceFileElements(resources)
+
+ // 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)
+
+ // Swift Playgrounds
+ 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 targetDependencies = try pbxTarget.dependencies.compactMap {
+ try dependencyMapper.map($0, xcodeProj: xcodeProj)
+ }
+ let allDependencies = (targetDependencies + frameworks).sorted { $0.name < $1.name }
+
+ // Construct final Target
+ return Target(
+ name: pbxTarget.name,
+ destinations: platform,
+ product: product,
+ productName: pbxTarget.productName ?? pbxTarget.name,
+ bundleId: try pbxTarget.bundleIdentifier(),
+ deploymentTargets: deploymentTargets,
+ infoPlist: try await extractInfoPlist(from: pbxTarget, xcodeProj: xcodeProj),
+ entitlements: try extractEntitlements(from: pbxTarget, xcodeProj: xcodeProj),
+ settings: settings,
+ sources: sources,
+ resources: resourceFileElements,
+ copyFiles: copyFiles,
+ 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
+ )
+ }
+
+ // MARK: - Private helpers
+
+ /// Identifies files not included in any build phase, returning them as `FileElement` models.
+ private func mapAdditionalFiles(from pbxTarget: PBXTarget, xcodeProj: XcodeProj) throws -> [FileElement] {
+ guard let pbxProject = xcodeProj.pbxproj.projects.first,
+ let mainGroup = pbxProject.mainGroup
+ else {
+ throw PBXTargetMappingError.noProjectsFound(path: xcodeProj.projectPath.pathString)
+ }
+
+ let allFiles = try collectAllFiles(from: mainGroup, xcodeProj: xcodeProj)
+ let filesInBuildPhases = try filesReferencedByBuildPhases(pbxTarget: pbxTarget, xcodeProj: xcodeProj)
+ let additionalFiles = allFiles.subtracting(filesInBuildPhases).sorted()
+ return additionalFiles.map { FileElement.file(path: $0) }
+ }
+
+ /// Extracts the main files group for the target.
+ private func extractFilesGroup(from target: PBXTarget, xcodeProj: XcodeProj) throws -> ProjectGroup {
+ guard let pbxProject = xcodeProj.pbxproj.projects.first,
+ let mainGroup = pbxProject.mainGroup
+ else {
+ throw PBXTargetMappingError.missingFilesGroup(targetName: target.name)
+ }
+ return ProjectGroup.group(name: mainGroup.name ?? "MainGroup")
+ }
+
+ /// Extracts and parses the project's Info.plist as a dictionary, or returns an empty dictionary if none is found.
+ private func extractInfoPlist(from target: PBXTarget, xcodeProj: XcodeProj) async throws -> InfoPlist {
+ if let (config, plistPath) = target.infoPlistPath().first {
+ let path = xcodeProj.srcPath.appending(try RelativePath(validating: plistPath))
+ let plistDictionary = try await readPlistAsDictionary(at: path)
+ return .dictionary(plistDictionary, configuration: config)
+ }
+ return .dictionary([:])
+ }
+
+ /// Extracts the target's entitlements file, if present.
+ private func extractEntitlements(from target: PBXTarget, xcodeProj: XcodeProj) throws -> Entitlements? {
+ let entitlementsMap = target.entitlementsPath()
+ guard let configuration = target.defaultBuildConfiguration() ?? entitlementsMap.keys.first else { return nil }
+ guard let entitlementsPath = entitlementsMap[configuration] else { return nil }
+
+ let path = xcodeProj.srcPath.appending(try RelativePath(validating: entitlementsPath))
+ return Entitlements.file(path: path, configuration: configuration)
+ }
+
+ /// Recursively collects all files from a given `PBXGroup`.
+ private func collectAllFiles(from group: PBXGroup, xcodeProj: XcodeProj) throws -> Set {
+ var files = Set()
+ for child in group.children {
+ if let file = child as? PBXFileReference,
+ let pathString = try file.fullPath(sourceRoot: xcodeProj.srcPathString)
+ {
+ let path = try AbsolutePath(validating: pathString)
+ files.insert(path)
+ } else if let subgroup = child as? PBXGroup {
+ files.formUnion(try collectAllFiles(from: subgroup, xcodeProj: xcodeProj))
+ }
+ }
+ return files
+ }
+
+ /// Identifies all files referenced by any build phase in the target.
+ private func filesReferencedByBuildPhases(
+ pbxTarget: PBXTarget,
+ xcodeProj: XcodeProj
+ ) throws -> Set {
+ let filePaths = try pbxTarget.buildPhases
+ .compactMap(\.files)
+ .flatMap { $0 }
+ .compactMap { buildFile -> AbsolutePath? in
+ guard let fileRef = buildFile.file,
+ let filePath = try fileRef.fullPath(sourceRoot: xcodeProj.srcPathString)
+ else {
+ return nil
+ }
+ return try AbsolutePath(validating: filePath)
+ }
+ return Set(filePaths)
+ }
+
+ /// Extracts playground files from the target's sources.
+ private func extractPlaygrounds(from pbxTarget: PBXTarget, xcodeProj: XcodeProj) throws -> [AbsolutePath] {
+ let sources = try pbxTarget.sourcesBuildPhase().map {
+ try sourcesMapper.map($0, xcodeProj: xcodeProj)
+ } ?? []
+ return sources.filter { $0.path.fileExtension == .playground }.map(\.path)
+ }
+
+ /// Reads and parses a plist file into a `[String: Plist.Value]` dictionary.
+ private func readPlistAsDictionary(
+ at path: AbsolutePath,
+ fileSystem: FileSysteming = FileSystem()
+ ) async throws -> [String: Plist.Value] {
+ var format = PropertyListSerialization.PropertyListFormat.xml
+
+ let data = try await fileSystem.readFile(at: path)
+
+ guard let plist = try? PropertyListSerialization.propertyList(
+ from: data,
+ options: .mutableContainersAndLeaves,
+ format: &format
+ ) as? [String: Any] else {
+ throw PBXTargetMappingError.invalidPlist(path: path.pathString)
+ }
+
+ return try plist.reduce(into: [String: Plist.Value]()) { result, item in
+ result[item.key] = try convertToPlistValue(item.value)
+ }
+ }
+
+ /// Converts a raw plist value into a `Plist.Value`.
+ private func convertToPlistValue(_ value: Any) throws -> Plist.Value {
+ switch value {
+ case let stringValue as String:
+ return .string(stringValue)
+ case let intValue as Int:
+ return .integer(intValue)
+ case let doubleValue as Double:
+ return .real(doubleValue)
+ case let boolValue as Bool:
+ return .boolean(boolValue)
+ case let arrayValue as [Any]:
+ let converted = try arrayValue.map { try convertToPlistValue($0) }
+ return .array(converted)
+ case let dictValue as [String: Any]:
+ let converted = try dictValue.reduce(into: [String: Plist.Value]()) { dictResult, entry in
+ dictResult[entry.key] = try convertToPlistValue(entry.value)
+ }
+ return .dictionary(converted)
+ default:
+ // If unrecognized, store its string description
+ return .string(String(describing: value))
+ }
+ }
+}
+
+// swiftlint:enable function_body_length
diff --git a/Sources/XcodeProjMapper/Mappers/Targets/TargetDependency+GraphMapping.swift b/Sources/XcodeProjMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
new file mode 100644
index 00000000..b0d65102
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
@@ -0,0 +1,273 @@
+import Foundation
+import Path
+import XcodeGraph
+import XcodeMetadata
+import XcodeProj
+
+// swiftlint:disable function_body_length
+extension TargetDependency {
+ /// Maps this `TargetDependency` to a `GraphDependency` by resolving paths, product types,
+ /// and linking details. Project-based dependencies are resolved using `allTargetsMap`.
+ ///
+ /// - Parameters:
+ /// - sourceDirectory: The root directory for resolving relative paths.
+ /// - allTargetsMap: A map of target names to `Target` models for resolving project-based dependencies.
+ /// - target: The target of this dependency.
+ /// - xcframeworkMetadataProvider: Provides metadata (linking, architectures, etc.) for `.xcframework` dependencies.
+ /// - libraryMetadataProvider: Provides metadata for libraries.
+ /// - frameworkMetadataProvider: Provides metadata for frameworks.
+ /// - systemFrameworkMetadataProvider: Provides metadata for system frameworks.
+ /// - developerDirectoryProvider: Provides xcode developer directory.
+ /// - Returns: A corresponding `GraphDependency` model for this dependency.
+ /// - Throws: `TargetDependencyMappingError` if a referenced target is not found or if the dependency type is unknown.
+ func graphDependency(
+ sourceDirectory: AbsolutePath,
+ allTargetsMap: [String: Target],
+ target: Target,
+ xcframeworkMetadataProvider: XCFrameworkMetadataProviding = XCFrameworkMetadataProvider(),
+ libraryMetadataProvider: LibraryMetadataProviding = LibraryMetadataProvider(),
+ frameworkMetadataProvider: FrameworkMetadataProviding = FrameworkMetadataProvider(),
+ systemFrameworkMetadataProvider: SystemFrameworkMetadataProviding = SystemFrameworkMetadataProvider(),
+ developerDirectoryProvider: DeveloperDirectoryProviding = DeveloperDirectoryProvider()
+ ) async throws -> GraphDependency {
+ switch self {
+ // MARK: - Simple Cases
+
+ case let .target(name, status, _):
+ return .target(name: name, path: sourceDirectory, status: status)
+
+ case let .project(targetName, projectPath, status, _):
+ return try mapProjectGraphDependency(
+ projectPath: projectPath,
+ targetName: targetName,
+ status: status,
+ allTargetsMap: allTargetsMap
+ )
+
+ // MARK: - Precompiled Binary Cases
+
+ case let .framework(path, status, _):
+ let metadata = try await frameworkMetadataProvider.loadMetadata(at: path, status: status)
+ return .framework(
+ path: path,
+ binaryPath: metadata.binaryPath,
+ dsymPath: metadata.dsymPath,
+ bcsymbolmapPaths: metadata.bcsymbolmapPaths,
+ linking: metadata.linking,
+ architectures: metadata.architectures,
+ status: status
+ )
+
+ case let .xcframework(path, status, _):
+ let metadata = try await xcframeworkMetadataProvider.loadMetadata(at: path, status: status)
+ return .xcframework(
+ .init(
+ path: path,
+ infoPlist: metadata.infoPlist,
+ linking: metadata.linking,
+ mergeable: metadata.mergeable,
+ status: status,
+ macroPath: metadata.macroPath,
+ swiftModules: metadata.swiftModules,
+ moduleMaps: metadata.moduleMaps
+ )
+ )
+
+ case let .library(path, publicHeaders, swiftModuleMap, _):
+ let metadata = try await libraryMetadataProvider.loadMetadata(
+ at: path,
+ publicHeaders: publicHeaders,
+ swiftModuleMap: swiftModuleMap
+ )
+ return .library(
+ path: path,
+ publicHeaders: publicHeaders,
+ linking: metadata.linking,
+ architectures: metadata.architectures,
+ swiftModuleMap: swiftModuleMap
+ )
+
+ // MARK: - Package & SDK
+
+ case let .package(product, type, _):
+ return .packageProduct(
+ path: sourceDirectory,
+ product: product,
+ type: type.graphPackageType
+ )
+
+ case let .sdk(name, status, _):
+ return .sdk(
+ name: name,
+ path: sourceDirectory,
+ status: status,
+ source: .developer
+ )
+
+ // MARK: - XCTest (System Provided)
+
+ case .xctest:
+ let frameworkData = try systemFrameworkMetadataProvider.loadMetadata(
+ sdkName: "XCTest.framework",
+ status: .required,
+ platform: target.legacyPlatform,
+ source: .developer
+ )
+ let developerDirectory = try await developerDirectoryProvider.developerDirectory()
+ let path = try AbsolutePath(
+ validating: developerDirectory.pathString + frameworkData
+ .path.pathString
+ )
+
+ let metadata = try await frameworkMetadataProvider.loadMetadata(at: path, status: .required)
+ return .framework(
+ path: path,
+ binaryPath: metadata.binaryPath,
+ dsymPath: metadata.dsymPath,
+ bcsymbolmapPaths: metadata.bcsymbolmapPaths,
+ linking: metadata.linking,
+ architectures: metadata.architectures,
+ status: .required
+ )
+ }
+ }
+
+ /// Resolves a project-based target dependency into a `GraphDependency`.
+ ///
+ /// - Parameters:
+ /// - projectPath: The absolute path of the `.xcodeproj` directory.
+ /// - targetName: The name of the target within that project.
+ /// - status: The linking status of the dependency.
+ /// - allTargetsMap: A dictionary of target names to `Target` models for resolution.
+ /// - Returns: A `GraphDependency` representing the resolved dependency.
+ /// - Throws: `TargetDependencyMappingError.targetNotFound` if `targetName` isn't in `allTargetsMap`,
+ /// `TargetDependencyMappingError.unknownDependencyType` if the product type can't be mapped.
+ private func mapProjectGraphDependency(
+ projectPath: AbsolutePath,
+ targetName: String,
+ status: LinkingStatus,
+ allTargetsMap: [String: Target]
+ ) throws -> GraphDependency {
+ guard let target = allTargetsMap[targetName] else {
+ throw TargetDependencyMappingError.targetNotFound(
+ targetName: targetName,
+ path: projectPath
+ )
+ }
+
+ let product = target.product
+ switch product {
+ case .framework, .staticFramework:
+ let linking: BinaryLinking = (product == .staticFramework) ? .static : .dynamic
+ return .framework(
+ path: projectPath,
+ binaryPath: projectPath.appending(component: "\(targetName).framework"),
+ dsymPath: nil,
+ bcsymbolmapPaths: [],
+ linking: linking,
+ architectures: [],
+ status: status
+ )
+
+ case .staticLibrary, .dynamicLibrary:
+ let linking: BinaryLinking = (product == .staticLibrary) ? .static : .dynamic
+ let libName = (linking == .static) ? "lib\(targetName).a" : "lib\(targetName).dylib"
+ let publicHeadersPath = projectPath.appending(component: "include")
+ return .library(
+ path: projectPath.appending(component: libName),
+ publicHeaders: publicHeadersPath,
+ linking: linking,
+ architectures: [],
+ swiftModuleMap: nil
+ )
+
+ case .bundle:
+ return .bundle(path: projectPath.appending(component: "\(targetName).bundle"))
+
+ case .app, .commandLineTool:
+ return .target(name: targetName, path: projectPath, status: status)
+
+ default:
+ throw TargetDependencyMappingError.unknownDependencyType(name: product.description)
+ }
+ }
+}
+
+/// Resolves the system-provided `XCTest.framework` path, falling back to a standard location
+/// inside the selected Xcode’s SharedFrameworks directory.
+///
+/// - Parameter developerDirectoryProvider: Provides the current Xcode Developer directory (via `xcode-select -p`).
+/// - Returns: The absolute path to `XCTest.framework`.
+/// - Throws: If the developer directory cannot be retrieved or validated.
+private func xctestFrameworkPath(
+ developerDirectoryProvider: DeveloperDirectoryProviding = DeveloperDirectoryProvider()
+) async throws -> AbsolutePath {
+ let devDir = try await developerDirectoryProvider.developerDirectory()
+ // Typically: /Applications/Xcode.app/Contents/Developer
+ // Move one directory up (/Contents) and then into SharedFrameworks/XCTest.framework
+ return devDir.parentDirectory.appending(components: ["SharedFrameworks", "XCTest.framework"])
+}
+
+extension TargetDependency.PackageType {
+ /// Translates `TargetDependency.PackageType` into `GraphDependency.PackageProductType`.
+ var graphPackageType: GraphDependency.PackageProductType {
+ switch self {
+ case .runtime:
+ return .runtime
+ case .runtimeEmbedded:
+ return .runtimeEmbedded
+ case .plugin:
+ return .plugin
+ case .macro:
+ return .macro
+ }
+ }
+}
+
+extension PBXProductType {
+ /// Maps `PBXProductType` to a `Product`, or returns `nil` if unsupported.
+ func mapProductType() -> Product? {
+ switch self {
+ case .application, .messagesApplication, .onDemandInstallCapableApplication:
+ return .app
+ case .framework, .xcFramework:
+ return .framework
+ case .staticFramework:
+ return .staticFramework
+ case .dynamicLibrary:
+ return .dynamicLibrary
+ case .staticLibrary, .metalLibrary:
+ return .staticLibrary
+ case .bundle, .ocUnitTestBundle:
+ return .bundle
+ case .unitTestBundle:
+ return .unitTests
+ case .uiTestBundle:
+ return .uiTests
+ case .appExtension:
+ return .appExtension
+ case .extensionKitExtension, .xcodeExtension:
+ return .extensionKitExtension
+ case .commandLineTool:
+ return .commandLineTool
+ case .messagesExtension:
+ return .messagesExtension
+ case .stickerPack:
+ return .stickerPackExtension
+ case .xpcService:
+ return .xpc
+ case .watchApp, .watch2App, .watch2AppContainer:
+ return .watch2App
+ case .watchExtension, .watch2Extension:
+ return .watch2Extension
+ case .tvExtension:
+ return .tvTopShelfExtension
+ case .systemExtension:
+ return .systemExtension
+ case .instrumentsPackage, .intentsServiceExtension, .driverExtension, .none:
+ return nil
+ }
+ }
+}
+
+// swiftlint:enable function_body_length
diff --git a/Sources/XcodeProjMapper/Mappers/Workspace/XCWorkspaceMapper.swift b/Sources/XcodeProjMapper/Mappers/Workspace/XCWorkspaceMapper.swift
new file mode 100644
index 00000000..db6e15be
--- /dev/null
+++ b/Sources/XcodeProjMapper/Mappers/Workspace/XCWorkspaceMapper.swift
@@ -0,0 +1,130 @@
+import Foundation
+import Path
+import PathKit
+import XcodeGraph
+import XcodeProj
+
+/// A protocol defining how to map an `.xcworkspace` into a `Workspace` model.
+///
+/// Conforming types extract project references, shared schemes, and any other relevant data
+/// to produce a high-level `Workspace` domain model.
+protocol WorkspaceMapping {
+ /// Maps the `.xcworkspace` into a `Workspace` domain model.
+ ///
+ /// This includes:
+ /// - Identifying `.xcodeproj` references in the workspace data structure.
+ /// - Mapping any shared schemes present in `xcshareddata/xcschemes`.
+ ///
+ /// - Parameter xcworkspace: The `XCWorkspace` to be mapped.
+ /// - Returns: A fully constructed `Workspace` representing the workspace’s structure.
+ /// - Throws: If reading projects or schemes fails.
+ func map(xcworkspace: XCWorkspace) async throws -> Workspace
+}
+
+/// A mapper that converts an `.xcworkspace` into a `Workspace` model.
+///
+/// `XCWorkspaceMapper`:
+/// - Locates all referenced Xcode projects (`.xcodeproj`) in the workspace data,
+/// - Maps shared schemes (if any),
+/// - Produces a `Workspace` model suitable for analysis or code generation.
+struct XCWorkspaceMapper: WorkspaceMapping {
+ private let schemeMapper: SchemeMapping
+
+ /// Creates a new mapper that uses the provided `SchemeMapping` instance for scheme parsing.
+ /// - Parameter schemeMapper: Defaults to `XCSchemeMapper()` for scheme resolution.
+ init(schemeMapper: SchemeMapping = XCSchemeMapper()) {
+ self.schemeMapper = schemeMapper
+ }
+
+ func map(xcworkspace: XCWorkspace) async throws -> Workspace {
+ let xcWorkspacePath = xcworkspace.workspacePath
+ let srcPath = xcWorkspacePath.parentDirectory
+
+ let projectPaths = try await extractProjectPaths(
+ from: xcworkspace.data.children,
+ srcPath: srcPath
+ )
+
+ let workspaceName = xcWorkspacePath.basenameWithoutExt
+ let schemes = try mapSchemes(from: xcworkspace)
+
+ let generationOptions = Workspace.GenerationOptions(
+ enableAutomaticXcodeSchemes: nil,
+ autogeneratedWorkspaceSchemes: .disabled,
+ lastXcodeUpgradeCheck: nil,
+ renderMarkdownReadme: false
+ )
+
+ return Workspace(
+ path: srcPath,
+ xcWorkspacePath: xcWorkspacePath,
+ name: workspaceName,
+ projects: projectPaths,
+ schemes: schemes,
+ generationOptions: generationOptions,
+ ideTemplateMacros: nil,
+ additionalFiles: []
+ )
+ }
+
+ // MARK: - Private Helpers
+
+ /// Recursively identifies all `.xcodeproj` files within the workspace structure.
+ ///
+ /// - Parameters:
+ /// - elements: The workspace elements (files/groups).
+ /// - srcPath: The workspace’s root directory for resolving relative references.
+ /// - Returns: An array of absolute paths to `.xcodeproj` directories found in the workspace.
+ private func extractProjectPaths(
+ from elements: [XCWorkspaceDataElement],
+ srcPath: AbsolutePath
+ ) async throws -> [AbsolutePath] {
+ var paths: [AbsolutePath] = []
+
+ for element in elements {
+ switch element {
+ case let .file(ref):
+ let refPath = try await ref.path(srcPath: srcPath)
+ if refPath.fileExtension == .xcodeproj {
+ paths.append(refPath)
+ }
+ case let .group(group):
+ // For each group, create a nested source path and recurse.
+ let nestedSrcPath = srcPath.appending(component: group.location.path)
+ let groupPaths = try await extractProjectPaths(
+ from: group.children,
+ srcPath: nestedSrcPath
+ )
+ paths.append(contentsOf: groupPaths)
+ }
+ }
+
+ return paths
+ }
+
+ /// Maps any shared schemes defined within the `.xcworkspace`.
+ ///
+ /// Schemes are typically located under `xcshareddata/xcschemes`. If found,
+ /// this method parses them and maps them into domain `Scheme` models.
+ ///
+ /// - Parameter xcworkspace: The workspace whose schemes should be mapped.
+ /// - Returns: An array of `Scheme` instances for all shared schemes in the workspace.
+ private func mapSchemes(from xcworkspace: XCWorkspace) throws -> [Scheme] {
+ let srcPath = xcworkspace.workspacePath.parentDirectory
+ let sharedDataPath = Path(srcPath.pathString) + "xcshareddata/xcschemes"
+
+ guard sharedDataPath.exists else {
+ return []
+ }
+
+ let schemePaths = try sharedDataPath.children().filter { $0.extension == "xcscheme" }
+ return try schemePaths.map { schemePath in
+ let xcscheme = try XCScheme(path: schemePath)
+ return try schemeMapper.map(
+ xcscheme,
+ shared: true,
+ graphType: .workspace(xcworkspace)
+ )
+ }
+ }
+}
diff --git a/Sources/XcodeProjMapper/Utilities/ConfigurationMatcher.swift b/Sources/XcodeProjMapper/Utilities/ConfigurationMatcher.swift
new file mode 100644
index 00000000..ff325dfa
--- /dev/null
+++ b/Sources/XcodeProjMapper/Utilities/ConfigurationMatcher.swift
@@ -0,0 +1,54 @@
+import Foundation
+import XcodeGraph
+
+/// A protocol defining methods for determining the variant of a build configuration
+/// and validating configuration names.
+protocol ConfigurationMatching {
+ /// Returns the build configuration variant for a given configuration name.
+ ///
+ /// This method checks for keywords that identify known variants and defaults to `.debug` if none match.
+ ///
+ /// - Parameter name: The name of the build configuration.
+ /// - Returns: The determined `BuildConfiguration.Variant` for the given name.
+ func variant(for name: String) -> BuildConfiguration.Variant
+
+ /// Validates that a configuration name is non-empty and contains no whitespace.
+ ///
+ /// - Parameter name: The configuration name to validate.
+ /// - Returns: `true` if the name is valid; `false` otherwise.
+ func validateConfigurationName(_ name: String) -> Bool
+}
+
+/// A concrete implementation of `ConfigurationMatching` that uses predefined keyword patterns
+/// to determine configuration variants.
+struct ConfigurationMatcher: ConfigurationMatching {
+ /// Represents a pattern mapping a set of keywords to a configuration variant.
+ struct Pattern {
+ let keywords: Set
+ let variant: BuildConfiguration.Variant
+ }
+
+ /// Common patterns for identifying build configuration variants.
+ let patterns: [Pattern]
+
+ /// Initializes a new `ConfigurationMatcher` with default patterns.
+ ///
+ /// - Parameter patterns: An optional array of `Pattern` to override defaults.
+ init(patterns: [Pattern]? = nil) {
+ self.patterns = patterns ?? [
+ Pattern(keywords: ["debug", "development", "dev"], variant: .debug),
+ Pattern(keywords: ["release", "prod", "production"], variant: .release),
+ ]
+ }
+
+ func variant(for name: String) -> BuildConfiguration.Variant {
+ let lowercased = name.lowercased()
+ return patterns.first { pattern in
+ pattern.keywords.contains(where: { lowercased.contains($0) })
+ }?.variant ?? .debug
+ }
+
+ func validateConfigurationName(_ name: String) -> Bool {
+ !name.isEmpty && name.rangeOfCharacter(from: .whitespaces) == nil
+ }
+}
diff --git a/Sources/XcodeProjMapper/Utilities/DeveloperDirectoryProvider.swift b/Sources/XcodeProjMapper/Utilities/DeveloperDirectoryProvider.swift
new file mode 100644
index 00000000..64e22fa6
--- /dev/null
+++ b/Sources/XcodeProjMapper/Utilities/DeveloperDirectoryProvider.swift
@@ -0,0 +1,32 @@
+import Command
+import Foundation
+import Path
+
+/// A protocol that obtains the current developer directory (via `xcode-select -p`) asynchronously.
+protocol DeveloperDirectoryProviding {
+ /// Returns the absolute path to the currently selected Xcode’s Developer directory.
+ /// - Throws: If `xcode-select -p` fails or if the output is invalid.
+ func developerDirectory() async throws -> AbsolutePath
+}
+
+/// Default implementation of `DeveloperDirectoryProviding` that uses `CommandRunner`.
+struct DeveloperDirectoryProvider: DeveloperDirectoryProviding {
+ private let commandRunner: CommandRunning
+
+ /// Creates a new provider that uses the given `CommandRunning` instance.
+ /// - Parameter commandRunner: By default, uses `CommandRunner()`.
+ init(commandRunner: CommandRunning = CommandRunner()) {
+ self.commandRunner = commandRunner
+ }
+
+ /// Uses `xcode-select -p` to get the path to the currently selected Xcode’s Developer folder.
+ /// - Throws: If `xcode-select -p` fails or if the path output is invalid.
+ /// - Returns: A valid `AbsolutePath` pointing to the developer directory.
+ func developerDirectory() async throws -> AbsolutePath {
+ let stream = commandRunner.run(arguments: ["xcode-select", "-p"])
+ let rawPath = try await stream.concatenatedString()
+ .trimmingCharacters(in: .whitespacesAndNewlines)
+
+ return try AbsolutePath(validating: rawPath)
+ }
+}
diff --git a/Sources/XcodeProjMapper/Utilities/Optional+Throwing.swift b/Sources/XcodeProjMapper/Utilities/Optional+Throwing.swift
new file mode 100644
index 00000000..564ae36b
--- /dev/null
+++ b/Sources/XcodeProjMapper/Utilities/Optional+Throwing.swift
@@ -0,0 +1,13 @@
+import Foundation
+
+extension Optional {
+ /// Unwraps the optional value or throws the provided error if `nil`.
+ ///
+ /// - Parameter error: An autoclosure that generates the error to throw if the optional is `nil`.
+ /// - Returns: The unwrapped value of the optional.
+ /// - Throws: The provided error if the optional is `nil`.
+ func throwing(_ error: @autoclosure () -> Error) throws -> Wrapped {
+ guard let value = self else { throw error() }
+ return value
+ }
+}
diff --git a/Sources/XcodeProjMapper/Utilities/PathDependencyMapper.swift b/Sources/XcodeProjMapper/Utilities/PathDependencyMapper.swift
new file mode 100644
index 00000000..417c1f6d
--- /dev/null
+++ b/Sources/XcodeProjMapper/Utilities/PathDependencyMapper.swift
@@ -0,0 +1,71 @@
+import Foundation
+import Path
+import XcodeGraph
+
+/// A protocol defining how to map a file path into a `TargetDependency` based on its extension.
+protocol PathDependencyMapping {
+ /// Maps the given path to a `TargetDependency`.
+ ///
+ /// - Parameters:
+ /// - path: The file path to map.
+ /// - condition: An optional platform condition (e.g., iOS only).
+ /// - Returns: The corresponding `TargetDependency`, if the path extension is recognized.
+ /// - Throws: `PathDependencyError.invalidExtension` if the file extension is not supported.
+ func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency
+}
+
+/// A mapper that converts file paths (like `.framework`, `.xcframework`, or libraries) to `TargetDependency` models.
+struct PathDependencyMapper: PathDependencyMapping {
+ func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency {
+ let status: LinkingStatus = .required
+
+ switch path.fileExtension {
+ case .framework:
+ return .framework(path: path, status: status, condition: condition)
+ case .xcframework:
+ return .xcframework(path: path, status: status, condition: condition)
+ case .dynamicLibrary, .textBasedDynamicLibrary, .staticLibrary:
+ return .library(
+ path: path,
+ publicHeaders: path.parentDirectory, // heuristics; can be overridden if needed
+ swiftModuleMap: nil,
+ condition: condition
+ )
+ case .xcodeproj, .xcworkspace, .coreData, .playground, .none:
+ throw PathDependencyError.invalidExtension(path: path.pathString)
+ }
+ }
+}
+
+/// Errors that may occur when mapping paths to `TargetDependency`.
+enum PathDependencyError: Error, LocalizedError {
+ case invalidExtension(path: String)
+
+ var errorDescription: String? {
+ switch self {
+ case let .invalidExtension(path):
+ return "Encountered an invalid or unsupported file extension: \(path)"
+ }
+ }
+}
+
+/// Common file extensions encountered in Xcode projects and their associated artifacts.
+enum FileExtension: String {
+ case xcodeproj
+ case xcworkspace
+ case framework
+ case xcframework
+ case staticLibrary = "a"
+ case dynamicLibrary = "dylib"
+ case textBasedDynamicLibrary = "tbd"
+ case coreData = "xcdatamodeld"
+ case playground
+}
+
+/// A convenience extension to retrieve a file's `FileExtension` from `AbsolutePath`.
+extension AbsolutePath {
+ var fileExtension: FileExtension? {
+ guard let ext = `extension`?.lowercased() else { return nil }
+ return FileExtension(rawValue: ext)
+ }
+}
diff --git a/Tests/Fixtures/MyFramework.xcframework/Info.plist b/Tests/Fixtures/MyFramework.xcframework/Info.plist
new file mode 100644
index 00000000..51e4cc34
--- /dev/null
+++ b/Tests/Fixtures/MyFramework.xcframework/Info.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ AvailableLibraries
+
+
+ LibraryIdentifier
+ ios-x86_64-simulator
+ LibraryPath
+ MyFramework.framework
+ SupportedArchitectures
+
+ x86_64
+
+ SupportedPlatform
+ ios
+ SupportedPlatformVariant
+ simulator
+
+
+ LibraryIdentifier
+ ios-arm64
+ LibraryPath
+ MyFramework.framework
+ SupportedArchitectures
+
+ arm64
+
+ SupportedPlatform
+ ios
+
+
+ CFBundlePackageType
+ XFWK
+ XCFrameworkFormatVersion
+ 1.0
+
+
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Headers/MyFramework-Swift.h b/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Headers/MyFramework-Swift.h
new file mode 100644
index 00000000..1b9cd391
--- /dev/null
+++ b/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Headers/MyFramework-Swift.h
@@ -0,0 +1,204 @@
+// Generated by Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+
+#if !defined(__has_include)
+# define __has_include(x) 0
+#endif
+#if !defined(__has_attribute)
+# define __has_attribute(x) 0
+#endif
+#if !defined(__has_feature)
+# define __has_feature(x) 0
+#endif
+#if !defined(__has_warning)
+# define __has_warning(x) 0
+#endif
+
+#if __has_include()
+# include
+#endif
+
+#pragma clang diagnostic ignored "-Wauto-import"
+#include
+#include
+#include
+#include
+
+#if !defined(SWIFT_TYPEDEFS)
+# define SWIFT_TYPEDEFS 1
+# if __has_include()
+# include
+# elif !defined(__cplusplus)
+typedef uint_least16_t char16_t;
+typedef uint_least32_t char32_t;
+# endif
+typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
+typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
+typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
+typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
+typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
+typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
+typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
+typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
+typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
+typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
+typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
+typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
+#endif
+
+#if !defined(SWIFT_PASTE)
+# define SWIFT_PASTE_HELPER(x, y) x##y
+# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
+#endif
+#if !defined(SWIFT_METATYPE)
+# define SWIFT_METATYPE(X) Class
+#endif
+#if !defined(SWIFT_CLASS_PROPERTY)
+# if __has_feature(objc_class_property)
+# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
+# else
+# define SWIFT_CLASS_PROPERTY(...)
+# endif
+#endif
+
+#if __has_attribute(objc_runtime_name)
+# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
+#else
+# define SWIFT_RUNTIME_NAME(X)
+#endif
+#if __has_attribute(swift_name)
+# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
+#else
+# define SWIFT_COMPILE_NAME(X)
+#endif
+#if __has_attribute(objc_method_family)
+# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
+#else
+# define SWIFT_METHOD_FAMILY(X)
+#endif
+#if __has_attribute(noescape)
+# define SWIFT_NOESCAPE __attribute__((noescape))
+#else
+# define SWIFT_NOESCAPE
+#endif
+#if __has_attribute(warn_unused_result)
+# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define SWIFT_WARN_UNUSED_RESULT
+#endif
+#if __has_attribute(noreturn)
+# define SWIFT_NORETURN __attribute__((noreturn))
+#else
+# define SWIFT_NORETURN
+#endif
+#if !defined(SWIFT_CLASS_EXTRA)
+# define SWIFT_CLASS_EXTRA
+#endif
+#if !defined(SWIFT_PROTOCOL_EXTRA)
+# define SWIFT_PROTOCOL_EXTRA
+#endif
+#if !defined(SWIFT_ENUM_EXTRA)
+# define SWIFT_ENUM_EXTRA
+#endif
+#if !defined(SWIFT_CLASS)
+# if __has_attribute(objc_subclassing_restricted)
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# else
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# endif
+#endif
+#if !defined(SWIFT_RESILIENT_CLASS)
+# if __has_attribute(objc_class_stub)
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# else
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# endif
+#endif
+
+#if !defined(SWIFT_PROTOCOL)
+# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+#endif
+
+#if !defined(SWIFT_EXTENSION)
+# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
+#endif
+
+#if !defined(OBJC_DESIGNATED_INITIALIZER)
+# if __has_attribute(objc_designated_initializer)
+# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+# else
+# define OBJC_DESIGNATED_INITIALIZER
+# endif
+#endif
+#if !defined(SWIFT_ENUM_ATTR)
+# if defined(__has_attribute) && __has_attribute(enum_extensibility)
+# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
+# else
+# define SWIFT_ENUM_ATTR(_extensibility)
+# endif
+#endif
+#if !defined(SWIFT_ENUM)
+# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# if __has_feature(generalized_swift_name)
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# else
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
+# endif
+#endif
+#if !defined(SWIFT_UNAVAILABLE)
+# define SWIFT_UNAVAILABLE __attribute__((unavailable))
+#endif
+#if !defined(SWIFT_UNAVAILABLE_MSG)
+# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
+#endif
+#if !defined(SWIFT_AVAILABILITY)
+# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
+#endif
+#if !defined(SWIFT_WEAK_IMPORT)
+# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
+#endif
+#if !defined(SWIFT_DEPRECATED)
+# define SWIFT_DEPRECATED __attribute__((deprecated))
+#endif
+#if !defined(SWIFT_DEPRECATED_MSG)
+# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
+#endif
+#if __has_feature(attribute_diagnose_if_objc)
+# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
+#else
+# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
+#endif
+#if !defined(IBSegueAction)
+# define IBSegueAction
+#endif
+#if __has_feature(modules)
+#if __has_warning("-Watimport-in-framework-header")
+#pragma clang diagnostic ignored "-Watimport-in-framework-header"
+#endif
+#endif
+
+#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
+#pragma clang diagnostic ignored "-Wduplicate-method-arg"
+#if __has_warning("-Wpragma-clang-attribute")
+# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
+#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnullability"
+
+#if __has_attribute(external_source_symbol)
+# pragma push_macro("any")
+# undef any
+# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="MyFramework",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
+# pragma pop_macro("any")
+#endif
+
+#if __has_attribute(external_source_symbol)
+# pragma clang attribute pop
+#endif
+#pragma clang diagnostic pop
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Info.plist b/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..40a2eeab2d94414984240f7d3383fb17e8719d6f
GIT binary patch
literal 851
zcmY*X%Wu;#7`M%9Jm)=G$2PW7-Yg_+blgzaP^hMErO6(!X_1?Ftu-eOd301raNx>~
zKLCvzXKtK0f)GMn;K-F565J7P*J#C&?Ck?=)l24hmZ6eJ$C%W
z$y2?!uYX{0X!!I@YFJhEuqi(TSXC~;aN%L~65uv$IZTrSEXGn*EO%XrhmW6Q+~eH62RI(tH%_@v4>
zix>@8v|D#Ib*4hS^-{AC$QE`)ux;T%CX^_azkt$38(Bp)3#HWsLsQh&@{S^Otg+1#83l0LC?_#^a*`NU(i?3UI^};vdRAz`u-2kCjhGe
literal 0
HcmV?d00001
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftdoc b/Tests/Fixtures/MyFramework.xcframework/ios-arm64/MyFramework.framework/Modules/MyFramework.swiftmodule/arm64-apple-ios.swiftdoc
new file mode 100644
index 0000000000000000000000000000000000000000..00046fd4e53abb74496a52d33451b65a549699de
GIT binary patch
literal 376
zcmaDfX9YVW2Lpp90|Ns)qlJ#c+7Dby0=U-%aP4>CT5rI$Ux91Q2d>2%xY!*xUFbQa
zy@N^j#3Ahshx9v`wD%m+SaZl(Xp58P1t*OOP8vYc#=uE?50mBX(b9}sYS(^`FRSadWH%b#UP=a#JqG}Lqh`tJp(->a|1m?V})d7
zF=Jyrb4`XApgF#kZbgZ?spa`a*$nf5{KTSMGZWoJkmCT5rI$Ux91Q2d>2%xY!*xUFbQa
zy@N^j#3Ahshx9v`wD%m+SaZl(Xp58P1t*OOP8vYc#=uE?50mBX(b9}sYS(^`FRSadWH%b#UP=a#JqG}Lqh`tJp(->a|1m?V})d7
zF=Jyrb4`XApgF#kZbgZ?spa`a*$nf5{KTSMGZWoJkm$WmoKC;cw(9FoppwyeXwYbg7O!K|w>{(a=?Y`gleSYun_q;PO
zXXd)*nrp84&&N4u&e=JiUcLCI1R)B66M`QNUgnNa6_N=sZyfj>K0+Ez+Pw5M%o*gE
z;b}KuV-KoWr_mJaON%)Ig2DQjHI6hHl!YCpd5);#C_s!WsnHY_ugWOuXARb8dCZZd
zles!z=6gSxZMp-+>e&ok(az!|KskJDGm$z$489~xp`W?@0mK!peE%US18Waso@
znyWg;xjE2iGK-3hxdqEP_F#R=^^Sx|Tso%N2IT0QYy}?FcXe)d@pMgb8B|!GJy_pT
zPTvSFKc?AE!qJ0+etn=T4=f$5Ps8cMeF8ft?VyGp`}Jk#>hrSlvj*$S;`I4(Sus7h
zj;>n!_2K$Lw;8OD;q*B%!}RU-ttwcZTacy6Eyyl}f(GlWKJVdR|^#v4x(|N!5-T!1Fz96FLag(OJg?So7v5{*BE-wyjj=0JZu(WGE
zMZkBCJuITpIB;_|hNGU&%m}G$j`%)~g=trP9J@czXtH$0x_)wqeODkqc20emzP-MT
zqJi1i+3#2t?4hZ}5qGu&rd;(6o?GyEs0XjnBqc3P#?ns5NixIJSSR*a3XchmCSz4W
zR-PWBth<%I=2)l1U`w2QM~Ll81P^)^EV^f2`~t@)g>mG_w+s&;>;};|)m5`6&{z*1
zHx9l-LS;1QigUt~%My&beEsS|<0E0&&X_9~vaxO=l%&KlD36AKhR_P3)7gOT1ER@9
zp%f?r_kCQZyn;fbAgryVP$%#`Af5sqJ3L2Yk7aQ`lz_jLON9I*he`AW9xi9X#&pHn
zf6iL;^6Do_nPb~(B7sMPcZ@-df+vFF9eqK7cvB#b=P{=Xg$UMzRGi0gaM-4F@Hmat
zi3kID!B{fFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z5CjMU1Ob8o
zL4Y7Y5FiK;1PB5I0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z
z5CjMU1Ob8oL4Y7Y5FiK;1PB5I0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq
z06~BtKoB4Z5CjMU1Ob8oL4Y7Y5FiK;1PB5I0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx
z2oMDR-ym>Fq0KzR9Or}o0HL}XUhJ+M
z9@=Gt`1Q5wU6=2T+XeYI+xVK3H`LUzU+?Du5A>`R?79y8(MhUZGeDP()AcPMMPgmM
zA^!=f>i5qVQM$zOvT?9&+aTZ8C9$B(XZJI#
zu2H*tApY;z_%ol{{R53rI=p!uKo0RQ^hSg2d;PK?(ognR~LkLA!*UV1k|Z=(<%o38+Lnnw;m{RDe!-WGU4NuY&$vaP6htXkSCAZ6JNii*mLC4&I>jU_KZdTs9rTOV=W_mjc_djn~&6gmy%mt-+4+?*ZLn#egl>*S73*%HC*0
z$F8r1epG=r55_X=FKnt|ZNlvZZQh31{^Tqhw?QD}$8C$tbF~Ynjpx#wxL_Ih_Nh<#
z2nX(v*QbW3`W;OLk0zYNabAS#cNG_>rmKFZA3vH{m3Xr?|wGE^(Pl
zyw7_;zSAY%;WI$G9acB;cwa!M-U(g5CjMU1Ob8oL4Y7Y5FiK;1PB5I0fGQQfFM8+
zAP5iy2m%BFf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z5CjMU1Ob8oL4Y7Y5FiK;1PB5I
z0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq!2fLo#N6-xeYlY3!cZ=Z=E8U`
zOyR<1TxbG=@LmPZeG8oSDc3t=|6WLg7;I61ayY&Ba{1G^5btq-_mE(?kcQCJ-KxAA
zo2%4+9#?Vsr*Zo&^lNb07onf$5_yx$hxc&6js`CRUjrW3qlT*|L|KP<*afe3gn3-J
zp9`;Xp(B+X2?7KGf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z5CjMU1Ob8oL4Y7Y5FiK;
z1PB5I0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z5CjMU1Ob8o
zL4Y7Y5FiK;1PB5I0fGQQ;C~tdr8;9E@?(E`4^`tYcGS;(8H7~nsXa>{DEul~w(h4l
zWS19xQkVIqRQ^lXye3=8X16W8$2>}FGYWR{gP&@jdi9&98-ph_fBVWu6%9Rp-$<6<
zwJGV$T~AF}gYvT;ez?6fa#PUG(y3XiR*zi&-n;RiT*?2ldd}kK4{oT*eP(CbiMa)L
zKG-_%+T!-3J|ic+wl;0%Now31HHoY3o6(elO`pFRxZsVi{(dlZ`e%Q>KY}o#2>0|Ln{`_w1Z{PguC+*fJx8&}gx%R?6VgG!``-G;jurcP`n9afOZ`^8#
zIPv$$7w5nIgx@CB*0Y`7kA8UF;CJQSS1$$d-D0*B+p
z?+-rM{m$o>)Q7W|2hZv(do_REi;q72iq`jfm!Gu#l>AV_p@O=vW@jg_JRS9#Leh0Q^{uVtC+*<@r$={Jb$(3+ZFk>A
zZ~1oOqP6nZb5@OwzvJnN^Y1SxEY-d0op$%(-97V#S-TGKH!8NypB%X9n+1ux%-+*a
z$fKp^B@doW|Mj%&y-hiv?JIrYIo-Zbx4wU5xAxVNGkv|8qvEY)jvGM%LufLvJYSnMQOkEG{8+Yi-maMDC
zif2{#T#?+nG}Rp3d1cSjbN
z(_U;Vv>OB`etYbzh2E$B>6^Ue;a9Jh9DTU7RJd@>lj_;hJ<`J8&(wL{MSUtxIk{t1
z(8X;xLax3$>5rFZzw*uJo2#$-&i>);LlOGQ6EDs^eP`Fz){U3G_2`~)_^u~cCtsNr
z@M7})UlvX@&i2=@e7Z-JGiUkt3->5LrN8qyybbNQ3(jo}d2Yk3Awm~emZ?+$+(ke
zY-2s}pz{HLZ3dRTaU)J`J2M>^c*BSci8A=9ZLhri+ZXE}{%r5PfBbgw=ewuIQ;}|v
z7AQd|WCTJUyg-CEnivCKWD43GYZojK$|7fNp(-Vl+{EJo1RFCLdLA!si$|PbWTa2D
zSbmRtWO+@Zp*=7t-rHO&i`~M@vWuI2Xmvsj{V^3;UMZiVm|}@|v}xI9+HMEEi{)S+
zgudE|SFnvHgL!ohzsO=X|CG(|
zY@pw%2ES7%;7p+ZX;Z-A*nm@3zpg<4Qw^RVaJs>>tHH0e#SfJSw8jP;ZSd=D@H|8X
z{6NXgkIF?QrJ~ge5o0wQEaoDGXbqIBFdMAqH5Rk6UZnMGZSXs9@;s@9QYLhp0?t6(
z=GWce393%D1oSaL`X7c;uuMQ_px;^Gs3sh0@bA)kUe@_trn(q0WkDXD`H4a*k(rdp
z&r-(;RJ_pG@hab}9c+Cmi+FXZd4^I{CN`HS%rhAC>O|3MtJ!EV&rpa;63r#3H#Xph
z20y07|4f5tM{GcAga6?M&+Z=o<1PNZ4Sr`EJP(`vk2e4s{%2bJPci<71N}NEx)ubCv1rXsQJKPAhECFchjjs9{Gr%@
zOH@E_53WcD6>y4%Qv5GaUO=91@Pkqya-Q;Ujg>+KYI+I~fQrXXU_2{FqJpND=s%$;
z*P~O&GDB8_LVat_;HlN-`87e%dwOqqY6wSo%Ym3=ynq^l^Vy*OU>D@
zp1qGhx2|H&-q=}9>e+kwDz8f^UgwbH$3AJkR(eS$y_h1+M_xZ!q(7>>&e2}^DbfoT
z>BUYV2>jS7ypSU8(MnOQR}by=j}+lak3zN?lWv%eyyj<4!eN88(G)$!*-
zxpnI3PW4>7dR7O&N!XJj{n;jbL@B*w@#<4~{RDBXw69YLsxE51N`dT_o`X`bj90Hr
zYJ`Z@>$^^AUX$>uQhF8TML&xY%cr3V&&nWxBCw1{Q%C7UzOqZDLY6#w2TGqI+k!e<
zgjw#4n-$IMQjI!+f$(AkDU~t%)xPx=b1Ky_b!uN*TTEj`Os5)<@vY;>G^%6JW!meP
z6sfIS`ctRyJnhxfDLvOI?CX<$-z~kIBE8fpJZF=B-zm)pfo^HOO?plyJ&$CUWztIq
z={c2GzEawk;$@3I+ZNr_7PC)13kl{{R?OM1p4AEEs%KU5=bosTQ`a^JJtC8yQ+h$A
zzN5WdNdl{
zE%dFq2OZV&O>VYvFx=4s824i#996B7PnJ*-Niz4$asxf8JgeNI7*(0^j8eHNDpvf$
zh`7!qnRiaPC4N+8{Ij&rXO#>bKNmDKO%{A82vklMw?yj1(wSI$T7S<3m0j5o>BmUJ}yTHI`VTewv{+rpn!
zR}oX&;(x7GPN^hOvkvxod{28FWTfA#q-Rs4KPrVKeI7g0({pl^b}zviC7zg7ajfIb
z%c6M6S%`OE{kL==g=l_Q2kb@^J1!gVT9gQ`GcnP+DNALQ#sH(!ya|p2jt5TBE)gO$
zEpqG69tSPbOMM=_7U@})^jxF1WTSqd_yr8*KRctY9ROVt@WK@V&%PXRp
z3XQ6KLrc=9Ln;&N*Vz)@ZPUK;kFo0l{m=G{?F{s{oBR&9_#dLBKXi{hBt{QD^ZoBt
znnY3A6}RFW
zr*)EVY3Vvj6gC`mWqls$iDiNH6Rqu*4nHbOz~46UM7t$9Fh!D{AvXV4t;L#J{d}eT
z+mN{05x;k>NNCYEG~iv7m}((X1-Eohe)S(_<^qElRa^+8b(<)d5-CQ=(-WiUe`HuY
z{jc%dQ6#;gu4XddduK?vckhOb!_1CR^OopU5sCEfb`|~F5^M0Xalx{sBh%9pUlS`r
z+o#W$)lrjLHXeA~qE+eUsTNq9n;Byrg@VCZ9vJ6+*jxNJvEu&58J`VpDVb<>pGTrH
zLcx2EdVBH0z%xk_iTHJKL`3lYm4BD-xKnWWITQ8eQ-Lk!bzS0A8n#!+MY3QmH
zYgLMtue?&FDHW|uF|WK*wGz&<^`bTDJXuaHm}jbsaiAx@!^!Ifet4Y8Lw28
zSw&@uqSEw#31QsrO>y6+^2qD+IH>gc%_2Ro@>eiaJ$9Wg;KxA!4pRX1`i?+=6
zU?_OnQ~`&3{C+UOSumgrN#JT@9gL4aKYP#E^QM5af&SgPD&K)BQ*wxRw9Rd-fTzvMtIgR`|dvY*DOT=yuidl&I
zfdw?`&1G;^1n_G4a0)NlDJp?eyLMQ7jy3UPy3{fIV&VKZ7xcqiUs-XSA6I$S5*ReE
zTDD16?N*{UIo#8*TI$K%{c)x?kV(2hR@lOBTc
zh|2vf51`LgQn>C^0V%t{^tOIny(+N=2pG&-QSkVqGxoyu6H5zX=(o^&E&lA(4EnpM
z7U9pHmzADzY%=vOUlCXFATy$euSnoO1g6x)%cEqcGH7REvf61@`oxDSRvZaptO2?4
zy55ZoKqp}oHHp*cUqLf*YFV>D_*e&)PpjIc{XzQZ{pu$0eF}YRs8iv7b
zgdH~KYuV2pDK7i+sb7hRY^B7E0|*e(+UpY9qoE|6gLRT;PKl#LG;((`n84JKxxGlB
z!rSk$PMNji<@x?M8}nV(F`kSw1%v>Z7nOH22x`DNdE|#apG#$Ej*|h%y!m)H7z3@O
zVwqZd*8TN!Al{!z^31Z0J49kM(U}R5DP3%IwO5(-X84hSn~hN>^za3U>u%VX4IN?Y
z5YGx)9DD4^BQHL%9GWS~sTZjyFZ*X7G*W-Hwf3|^{|PFT;$lqJqM!L0UpraNB
zF+3lu=dzPW?T;U@9XKFAaA0TV*inB>WCBC)SYo#wZ!-Y^cPUGmB`Dn_38lVC7{^=9
zhk3W7^Y;C=M%|GJSf*QP
zowKk(ad=0f-FE3v1u`#m7{R2VBy92oKG^&;GXmQzu_B-GlSWvR?6%1HmFV#SY4n^!
zIvk|SpS59WJC;^VA7M?g+uk%+4k)g3h|a~LFI>T*$P+}P)gRj~y;?OOSLBdefaRJM
z9w4X0a1VQiZzbm$V;w$m8&v`9rK8s8H8RLJI}1O%POWZUvmgE(Ov
z`g^Izju7)m#8V_vyw-);*0*i6$F^NHp-W4Cm3-^*iMq0}O12PinnwdjYM%rXz|3~n
z{hk2Q1ztD?awa27K#5}WgI_;9aiMO-`6W
z{-C*ebXW6Kanq4^n@4vyU(ap6{&Dl@zUJpEn&-XOw5q82VY+FwscF)NrXw#kKfk&8
zZyTHShnrR%x8AY;&A0#l?)x7cY-#)OqmMs1^69b9K7ZqjFTeVFf!5ji#8vD-7c1Os
zldHMD%GAk!5c4A#=;23A((2i&K%dF%a31v)|0?qKyC(;bS1pR)$&=7;jAX_m)C~7-
zc;C<{G2_?XKx>OXEWKna-B_^-
zU%2eEtLN78=XR9V!s^aixMWE*8(^8oz%MB^8));mj*<9=zDMa*p!E8#5AIo|QL=Xb
zGl6(%r&Sk_t=xd`Jc%UDwSDo&LMH>GfsDNzw;%Hdv*bdjkDZyMtV6~=H
zR9e3{=`t)uK;D2A0f%*dy@7a{XB`F0V195t+NTqyRaHcHw8b>3=K>&b)p%ANQv+mM
z?T7AAQ}b%D2iABLB4fR2*>^JO1y~h;MGj?93*3akon2Rt{~0hKaa>GO#oWCWzLjm!
zwXhb_HfLYUEPET&kv+YZ(TF{783Ph&b&X`)2xDX-9b~HAy!lwXRi?BrgeCB$
z8lkrHUg#U2oGr`mUb(3;*t1P3;m^D0gks^jIif8|wJk~sa8PReJ@it&u_(8&;NFZC
z`pn|o68%*5aI6LSy5)L$K|yxmU!!QO`yDYog{D@jBz`+=8ju
zMqR#sb)oT*VKWafv8n;esUx?@pexSIQ5WWC0-uwqt1|%
zn^}bQIO9|E*cR#sV%k6@BV8&SIm3lN-6bCB5|46;M`)n`z48ehz$wd7Q>(ZCEXEt0nJeuc_5i!jl*L)jS1
z6G57FZ;M3j7n+N+B>73w&?Y|Ok49%fQ+Y556w#t$Wm}5Kr~;ydB@7OtlqD<7KPk&-s4%vD?F4ZvWNm2Dk18
zv1p|nuelW=v>DEw_#V>ZaY|eX;Vz)@dZ359Q32;DSSK;mtLMV0rU}+$+se=T9cu7|
z2LNyiJs;@rnETN&xR{3}KP2jy1M9=v;e^DWYlUT7b+n}o_ZS_G;L;t|c?7ZSbwB21
zrSQ?eln8l2eByk&WwB5oz&D5eb5GIQ9?J=#77ce85N60303Pu#-EUHIwso$%HdgykOxI2NZk2?7S-6!OdyI|
zk+_D!ct0f2LaiK5Ms)Z1ee{hwfTdF)GQ(+6D};eT8O50+1mLP@h$
zOON$#JkY&y1fSuJiPRyPw|cS!E|ml?|7$F+;B`3a^$`kW^Fr2#Jb5_`HXuWr4(08o1Li4psL|3iN@%O0j3!#%U0y1v
z%&GmblIqmd#!kDsBOLxGOOU(74lG@5<+N+xhX2{hveJR2{K_J+)yBrEV4wT9JWVpN
zHb__nrdvJ!5z!&{LER#l?>PY~!dV9^+n2#~c5H+WtpnTk19cE@`T*CVo2^4Ri>tE^
zs-bn@l?mb(kAapoM@WzNZ9I6z;~=z(LK}!1JHq`l@up7r2UfLb4HL+)omD=@(ODno
z30j!3b~Wy-hpe>qZp1J!C2-gSh@8{Wu!%p!o!H~$7Tjm!N4%q*)ELEA#T1EeP!rky
zi_>0fY#V(T8QkLh#fXpC#Q;>c>>@@6#<72PbizyFcm$`5@KD`@hpOVqAm}-f&Y>Cy
zL$zT}d5;qMsrDU{tlX2A;j|0S5DurK5rSKSe!E=fnp@d1tKW_wq1zZ}JL%q$Y(rL$
zF4w9&-U^hB5;BYAXrTRWE-#HOPtDG4cp1M~d4h6pP>!>rCf?MpfIg8WmU>D(;)E)9
z+oxOJ%lK;B`=S#&r{=QHB;A|q$3wMQobbX7#;F4ts^a|Joj4UcFL5PkC!cW_t^zS<
z|CT*gZ^46EW%6fwsYt|hSCYR5@o5!PJ;A*$2
zpoN7~IuPZMjot>brQMsXI<6J$bT;I8VWRj5Zli`ysp{|Jy}vNvsjc1C1oLJNb=P(%
zCNWS4awGX?VOFrYT@jB_guyJ~CoKUBX3*tk1k?+W@?$Fi(Q%R=9i!h1kN&lId^Aas
zOa|lCF}82URRQ%JC2-)1&o4u8m3@Uz8R$g7b_-v?omOP7H$X
z8Ww~4d?gQ~moHuz1lY^~4;{>57Ysabf!z4@s74krnRPlEp{Wpajsr2o@i<6%_S{>I
zuXnw3?ni3+ihG{L4^&@_3w=d+17CAE&RNCvJu=S&Fs{W4A3Qg;!jw5MH>Ct54xXFh
z$^?Q8Jg(PAR3As4o$wDv!XQR?5L?^i8*`b2oJwI1fZHaFH{hwYsiQV_rB4GaI7V=F
zLHHF6*6C=(t{{T#<_O+_rj76K^LQW6Gb$f=&WO(hwQ`?%;&LkdLIT)QT(9ecxwIb|
zP9J@fzR953i3P?kD@An<0=9pTfDrfZPUzpU{=y;sds_5P7FPd5@@u^COB2KUGH|ml
z){1Vn=5m4(PVctaNM#=9-r0rcgZ6F&^TUOf>E-2MpyM<)oLFFfR>~L@A`f$`YWzmrqH6IR7om7lDd5M5YuTyR
zfoFA#3SaVLr=y3{Ax9x}n47urAD|;vIxZr5FX)&fwzPHP6A3$>{!+(y6ru=o+tBzM
z*5NfSLirBpxJxX}thkj9DcEy!UHs4o@v?Hsn5WApL}sL9>BPcyG9O=jA;H=p9H_@i
zs0UoaqDZN1Vx`|zNmt-$Ccq+9cwb8)usF
z&B#*z1XHxhyFMtyiK`xFq~Cz>=GQgh8!gOF7C5et<2gD7LhNv;!XZpE^B<8N}|Qy{+q`1r+#XMm5-?^usG
z6rml!$4?cSfj`p82l>66e5tp&GGwO_zdu;10U-VxQuIDQ5Ep9UK#K)9vBA*P!h-Ycj4o5%^ZKqApTb155}_+
zc%O3eGhO5>LBBg(Y+#(@f$zi_%O?UK+q1+)9v(=dG>)%v;bT3UfRC@s^MDUg)_%ND
z(Fg<{7azOy+jh{?%JI(+;&*d=cxc<-Z?XKJz{lf)hPZQFVE#BL6yrV{;-k27Z4h@l
zXTN$H+7HV)-TyBCI_SYTIh|9_t@!DHWw=uqLZ|$#_|{vGW7X3N
zU{O0YwZxFTlwyH>OGbO`6=X6uV{^Yvlyn8H{b
zW>DrvvyUe<8T#e91+kf#`l2F^Va&~1u6IDZL|?4SEf^>yN;8~Ho?cg^9}sf2b-Ifs
zGc@_Syu8B9Y@=S!Rv@#`SQwU@6=u{IXBHNO6&7T=@L9VYrRfW@90szv`fCDgwjM(p
zA~vopGh02BpR98zWX*8YaHtTQIddpKJ8KwTyh9;8y23SMpGGYxz)ylQ^+TnTS2?8f
z3JV{(&%g>N>x**=hf(YtEX&hY4AtXIEV^A{@^Fa`VA57N>{zZZUQm!*oU6-&2Vz-(
zhnltN)&(w4TY2lmqT3}V50}VUGmOqf5fNd^Fs06zKV#ZdoxzZ&pPE}(6cL4){d0%w
zBRZD{O`2PFcxUGw=+>x4_=_rbkkf+T{PYcy(SpCD*zF;_JVZRF3=jyHl8&`l+&5pzAn&L8e*00YFrJb)1
z&^Nt1(613L7F??a1M_A*pd%07Hu!5j__Ta;*6dt;URHkAt+L(>8lD{3?9c=*RVcbu
z?tu!pOe+z1UU7YcA``#(ke6FjJp3CJj!6SwA+YDnd^nH8O9;AQ+F)R$t-MVtOzyWy
zh1q?WROj?QOwuCm0@d~Ra00mY#`i^W|H1F!?uYx4f9v;fGRN=XDrL^!!^Pd^_i%Bp
zzlUqLgRL8A$8|OQW@QBXo0W<9H!Bml->lTYZ&oUkWT8cZ#8&j740*=7`+Nz%Uy%qJ
zVU_K_=e_fkPryr1^}>-%YG(Dr}zep)>}le#A;WJH^?!1W!p|NL#P{>d{6pE$VF
zlj{pyQA$lLzMiCE0xeihmpEQ_qaD|qw1VYMjfhafg=Sb(R9JKf3KAzqmRl7;VtZsc
zqXa6doQYN^Q_i4_$|sb#%2JJ)aO_O?-CeGL~;|Kf3JD4+$e)*v^t&$4{R;e0JHH?%v~P
zPM=Spmgw-yEDM6}EPBazQqzCW`zb4FM|JS}zjKKNujAwO4YgZpGf+*{pWmtf6u7lup3cMZ)b(o9oa#@w&UUf``cMc1d59Kdfm7k$E_TOvY?w(|5
zW`|PE&;*CpTb!WTT95-lfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq06~BtKoB4Z
z5CjMU1Ob8oL4Y7Y5FiK;1PB5I0fGQQfFM8+AP5iy2m%BFf&f8)AV3fx2oMAa0t5kq
z06~BtKoB4Z5CjMU1Ob8oL4Y7Y5FiNr4@Y2Rpx7W=JQ1a<4a!v58QT!OB-S8@Kbhl)
z^WG|Mmmrr-_0VFYh4+qSlq#z%4}YF#RfYyWE0d)+!X}^=9~Erwg7@!3#wMOc^_LqQ
zAx7+!`inh{5VH?&27n!9!9VB6&WNBu$97yUJ6RzMGK%{XT=uiW39^BNp&NK%0r@Qi
z@WyvIo$gN`Zl5bGq3D@kIfqWgu0?1}}+h|<6|zPzD(Pa);deFe}n
zJmkjixr6RaM!IZNPC57XK`&5?(1$$4Q=|Jc(W3%%?Kg@_L+2>urb8vGP(4+EexVA{
zR$e|j$P=KN-zg+Ob~qwl7OJJ<(H7W6w*D&S?q!c%a72RTY3OaLC=Go`C8wd!DcA`M
zy$hwRKnJOn=nu$p7K;J>DbOZR+{e-10Br{P8b|*G^d?;JAGnB`>-e~zJ+`yQ4)%D0
zJ$Azp@f_PLpuh1P+bN*8AomqEednJzw}s)sktyfHaUUF6$NWMiE=x?ABdDcRDSp8c
za4bIyGzJJ5)VGe4!?ch=OV>N-2993F(XRt7h5aRH#*w}s)36`z6`O;`+h=&eLBYx6
zEmARkh0`AmG-SbIta}iSt~BGI9h-|leCwceysf9J{JVqb6NBg<2hr}3*)_lKAbR#7
zI@LitckN)e;K1A5V)XDf8+gl9ylu1$Jl+-A0N?B%@pIjxk&c*W!OY$NXD+
zronclh4@Qkt~1>*Qwzs(om8BxVWyK|lh}!bOBr?w;nD^s4~}uGX@g51I$;c%p#_f26G(~0k8HIVdnXoMxIM@NlWlDkVT3rNEQ;`EKcGEF^5n#s_WiaNJ
L=!*5YB@y~x1#rG2
literal 0
HcmV?d00001
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Headers/MyFramework-Swift.h b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Headers/MyFramework-Swift.h
new file mode 100644
index 00000000..1b9cd391
--- /dev/null
+++ b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Headers/MyFramework-Swift.h
@@ -0,0 +1,204 @@
+// Generated by Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+
+#if !defined(__has_include)
+# define __has_include(x) 0
+#endif
+#if !defined(__has_attribute)
+# define __has_attribute(x) 0
+#endif
+#if !defined(__has_feature)
+# define __has_feature(x) 0
+#endif
+#if !defined(__has_warning)
+# define __has_warning(x) 0
+#endif
+
+#if __has_include()
+# include
+#endif
+
+#pragma clang diagnostic ignored "-Wauto-import"
+#include
+#include
+#include
+#include
+
+#if !defined(SWIFT_TYPEDEFS)
+# define SWIFT_TYPEDEFS 1
+# if __has_include()
+# include
+# elif !defined(__cplusplus)
+typedef uint_least16_t char16_t;
+typedef uint_least32_t char32_t;
+# endif
+typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
+typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
+typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
+typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
+typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
+typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
+typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
+typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
+typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
+typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
+typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
+typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
+#endif
+
+#if !defined(SWIFT_PASTE)
+# define SWIFT_PASTE_HELPER(x, y) x##y
+# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
+#endif
+#if !defined(SWIFT_METATYPE)
+# define SWIFT_METATYPE(X) Class
+#endif
+#if !defined(SWIFT_CLASS_PROPERTY)
+# if __has_feature(objc_class_property)
+# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
+# else
+# define SWIFT_CLASS_PROPERTY(...)
+# endif
+#endif
+
+#if __has_attribute(objc_runtime_name)
+# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
+#else
+# define SWIFT_RUNTIME_NAME(X)
+#endif
+#if __has_attribute(swift_name)
+# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
+#else
+# define SWIFT_COMPILE_NAME(X)
+#endif
+#if __has_attribute(objc_method_family)
+# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
+#else
+# define SWIFT_METHOD_FAMILY(X)
+#endif
+#if __has_attribute(noescape)
+# define SWIFT_NOESCAPE __attribute__((noescape))
+#else
+# define SWIFT_NOESCAPE
+#endif
+#if __has_attribute(warn_unused_result)
+# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define SWIFT_WARN_UNUSED_RESULT
+#endif
+#if __has_attribute(noreturn)
+# define SWIFT_NORETURN __attribute__((noreturn))
+#else
+# define SWIFT_NORETURN
+#endif
+#if !defined(SWIFT_CLASS_EXTRA)
+# define SWIFT_CLASS_EXTRA
+#endif
+#if !defined(SWIFT_PROTOCOL_EXTRA)
+# define SWIFT_PROTOCOL_EXTRA
+#endif
+#if !defined(SWIFT_ENUM_EXTRA)
+# define SWIFT_ENUM_EXTRA
+#endif
+#if !defined(SWIFT_CLASS)
+# if __has_attribute(objc_subclassing_restricted)
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# else
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# endif
+#endif
+#if !defined(SWIFT_RESILIENT_CLASS)
+# if __has_attribute(objc_class_stub)
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# else
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# endif
+#endif
+
+#if !defined(SWIFT_PROTOCOL)
+# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+#endif
+
+#if !defined(SWIFT_EXTENSION)
+# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
+#endif
+
+#if !defined(OBJC_DESIGNATED_INITIALIZER)
+# if __has_attribute(objc_designated_initializer)
+# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+# else
+# define OBJC_DESIGNATED_INITIALIZER
+# endif
+#endif
+#if !defined(SWIFT_ENUM_ATTR)
+# if defined(__has_attribute) && __has_attribute(enum_extensibility)
+# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
+# else
+# define SWIFT_ENUM_ATTR(_extensibility)
+# endif
+#endif
+#if !defined(SWIFT_ENUM)
+# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# if __has_feature(generalized_swift_name)
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# else
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
+# endif
+#endif
+#if !defined(SWIFT_UNAVAILABLE)
+# define SWIFT_UNAVAILABLE __attribute__((unavailable))
+#endif
+#if !defined(SWIFT_UNAVAILABLE_MSG)
+# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
+#endif
+#if !defined(SWIFT_AVAILABILITY)
+# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
+#endif
+#if !defined(SWIFT_WEAK_IMPORT)
+# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
+#endif
+#if !defined(SWIFT_DEPRECATED)
+# define SWIFT_DEPRECATED __attribute__((deprecated))
+#endif
+#if !defined(SWIFT_DEPRECATED_MSG)
+# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
+#endif
+#if __has_feature(attribute_diagnose_if_objc)
+# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
+#else
+# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
+#endif
+#if !defined(IBSegueAction)
+# define IBSegueAction
+#endif
+#if __has_feature(modules)
+#if __has_warning("-Watimport-in-framework-header")
+#pragma clang diagnostic ignored "-Watimport-in-framework-header"
+#endif
+#endif
+
+#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
+#pragma clang diagnostic ignored "-Wduplicate-method-arg"
+#if __has_warning("-Wpragma-clang-attribute")
+# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
+#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnullability"
+
+#if __has_attribute(external_source_symbol)
+# pragma push_macro("any")
+# undef any
+# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="MyFramework",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
+# pragma pop_macro("any")
+#endif
+
+#if __has_attribute(external_source_symbol)
+# pragma clang attribute pop
+#endif
+#pragma clang diagnostic pop
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Info.plist b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..b1987383943d1ed98be0b3309c897cf9669d6d7a
GIT binary patch
literal 831
zcmZ8f%T5$Q6unix@CC(79*Pd&o21h%f-ZCc(~b}v7^fRhB(z0$F;k(stJ4p368Qml
zet-+zxpe2sg^3|`})re3=Rzso;^2mp)fi&K6>%e$<59nw3Sa^aP!Tjv2=vRaix
zhKi-xH1Gs7*$xvj^jVNpS&hrUAw7BT-gD-r3EdQ|4V?EBm2YttIbpL9M-+hM{URuOeu&B_ls}
zNLQ}RL~Ev9lzzxXb7x(qol{#U-Z-fDchh|U9Ox}`p~Ze|+A3`lS55noD?PTJ#mqiZ
z<^lIpf62a$YP!0c|KKKS8-}j08ph1c8#^%vSYajRkw15@aZ8a>e7Ub{{Z!O#Iyu%M=R0k7aa
ze1z}t3w5Fbnm~8a1N0PaqZI9;m*_S6fcDT=^bP$$ztJD`7psSl`|$w2gNt|>ui+4<
Wco%=dpYa~FK8*GwOk^<&_{%?~Ncsf;
literal 0
HcmV?d00001
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc
new file mode 100644
index 0000000000000000000000000000000000000000..49cbc2c07e13e7f5583f8a3bc7d06170392fb89a
GIT binary patch
literal 388
zcmaDfX9YVW2Lpp90|Ns)qlJ#c+7Dby0=U-%aP4>CT5rI$Ux91Q2d>2%xY!*xUFbQa
zy@N^j#3Ahshx9v`wD%m+SaZl(Xp58P1t*OOP8vYc#=uE?50mB3tG%(OJ&@(bO&@(hvNJbVj
zHr6xOWQYNp<6G%gl$e`Zo?n#B@D0eXurP}^Gto^18Lyj}UuCT5rI$Ux91Q2d>2%xY!*xUFbQa
zy@N^j#3Ahshx9v`wD%m+SaZl(Xp58P1t*OOP8vYc#=uE?50mB3tG%(OJ&@(bO&@(hvNJbVj
zHr6xOWQYNp<6G%gl$e`Zo?n#B@D0eXurP}^Gto^18Lyj}Uu`?=%T9cLXZN&E;;B9A56uyFDOM
z^ccY5=FH4l$5Gol#@68vacss?Cpap!(W0%LB5fT;J2ssv27lDlfvTunKi}QAcbiKB
zw*AYQ_n7zN`{RAy_kH&5TW<6Ak0(DqbrxgN6vo&z{HQX7a@1Us3`vW
zb$-&6B>Tj28Pt@|;bukA)XiGaw-oOXcl^?3_>imzV0M|x(q@xtMM-L@j$}bqiud}t
zhRMM_NR|rTjFoddnlmK>Pw7@8aW$dKW9R!7$7?5NB$9ckhGJZN!B{j|oN#G;?dKWY
zUNtI4@|gK@y;&(rFsUWN(Jrni#dFLw42!s(WGmj7SmA^DZVGp5*C<*qCaiXr;Q44r9
zUo4wayfnu%IV1V>c&X^7a5SWZqn$AfREoEQMVXU3ee(H`U~
zlC5}LU8oc#6wm?%aa??#qdz4RkL1(ibtH?svAOPa2)!6sXtd4iN|F^%GR%dN?gtOD
zqBMA$ZmD18HSPw+9NeXu*Iafq88YEmv1r>S99o}--tcfH;sLF@rm^?NM4(6A6ieJ$
z-D$S0T6ELXrHrNZp-{-s$f)~ui%}eaJc{N(En~|u1g$aso>n)X^{c>apbTTsGGDa+%tIuEO
zd@8eD0gatZJki+ekO@n_KW1*jR&bS6ggNw2KBv3WBpAlMhowX;8=R`K=
zk+(JAmbCQKAA2;a-f+hfs4H#9i@D6OE+}1sO!nq$85_QdG$Q>!46N~F-gCA3Jlj8#y_w&7vI{(YTkM{`
z)REf5wXcl~V`+P`Zo4PDrNWz;=FQwwK5~R~+h3N~yY6Tk%IW>rx>{X+9it(;vLV}5
z?#a&QIIljBo0tLWA#`iVCMy~;TkH*)AnNx=`bd}gY~ijwTFj@4vZe9(HH82
zl)Z&^8%)J`^~GS&G-3|@CdMcZ?akC%hK7X8IYOjaFI{2&MDNH?005`44o?00==LqJ
z7a0J@ZO1gIjDRmJL#%DbQcx6^h6TT#wdLE_&0JvO)nnB<+uQQP`t|c>b-CM=DU5u3
zJeOZoeLiFPMGn1OqL<6`@>;!Iu9qwH@;a0aeV3d-`hhoj^RfZiK^*9nI(S-Sqf9
z_E%~xq1It)`Ka|OwUX3&iCPa)YcI8)rq)x`dV^a1XrV4VW6X=pFE5$cNASV3Fg=QT
z7`gIPKL2OkVmyQBztHr2L~Q!+NP2c+GiQjI4Sto+({ptcH7+=-`Z}K{zjoB93eQ;F
zMvQRG|7$p_E?eB(vihbLSK|sxOtK8+&Gl}-Re5!-`RjFbC1Fxh{F=MA*3xr~z@qri
z1iYlhG<~1XkUu4Xw0Npej1<$m(EWq-$fTp+F{H0VrbIQ#NYX-Plj!&Gbr2|Dikpu&R0l|P^KrkQ}5DW+g1OtKr!GK^uFz`PzFl%9X+(b@CW9UIi3#?s}cS7W{3bz09s98A!Y
zl*{q?CKv|PkMb#jNF)~IpgEQ(FBm%1u5i>945~Px3WL39NQ6UOs?nuIji`a7N)8p1
zIB0=zlllQ-
zyxPN}bAeK<=pFc-Q*RE*BWp_48@dqzt7*^5%6pS>={8(Z0K;BR1q(p~Hm
z_~&p`72-jN$07C`;+qh^AT&dK58^3^2j&4ceF=)aRJ=gN%T&Bh#SxsEwE2wBA%1FW
zO7(PL?LC1wN6FehA4N_^k*-5ASVv3c(1p%+r_*6iOO9q)mhE=_!X<>4PV=Xx*$96h
zlO}munwj4}Ji+DL`Fp5mAfJbKK{D_E0?9a-OXs60(fOpAC}i$GpUa~hpU$+R3%Tj(
zHk4L5ZOGOiUTSTKRRHz``%}nfDUI<4pU=1UuRUTL@QKHxE-a!L~7>
zN#8KIWx}n3+lre6S4_4&aP#<$Vcbrv@DE-T`W^?{EOrBBBi|MHcM0L33@=5I$89A=
z2OMFue6WEJXYkzsZTj&I3Dq&9Y5jJ`m58_>Bm+aI9KO;hU0b%i3Z~pB}on`pF|tJF1rcI(fK%
z>!xkK^$X(ru5>?Pn;Sp6UpWn_m|z=KhV;&A-(_L+@D|l`Qh*F{*L>L
z`}QpLv_A6KK-JYl8!x@#b!sI$%8c-ynnyH`9l=Ty_dJwLqfsqg=8_M!_~hEDw8u?OG%#Wm&8
z74L1j_pQ4={?npmD_8Yxe$lt!?s=|fj;?=W|A$Ym7|5U4_5N@6{VJ_p97?fSYYsja
zx?|;6%|Cne?PuR^e0cvYZ9``rUHtp|`|A84lW5gn51)KzIrA
literal 0
HcmV?d00001
diff --git a/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/_CodeSignature/CodeResources b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/_CodeSignature/CodeResources
new file mode 100644
index 00000000..450c6351
--- /dev/null
+++ b/Tests/Fixtures/MyFramework.xcframework/ios-x86_64-simulator/MyFramework.framework/_CodeSignature/CodeResources
@@ -0,0 +1,190 @@
+
+
+
+
+ files
+
+ Headers/MyFramework-Swift.h
+
+ EOml7gTGbnkeX3Xpxg7tlYPRKtU=
+
+ Info.plist
+
+ F+EfNZCopyyBoAd9dBDmL0zDb4s=
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc
+
+ 7bg9TJQa4x30QgKxioKlaO1K3NM=
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface
+
+ IpEcA6BZsvJ6HhGVH4IfKVk3Efc=
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftmodule
+
+ xX8pm1gJtDr/i8kuOa8qHfr4+XU=
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftdoc
+
+ 7bg9TJQa4x30QgKxioKlaO1K3NM=
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftinterface
+
+ IpEcA6BZsvJ6HhGVH4IfKVk3Efc=
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftmodule
+
+ xX8pm1gJtDr/i8kuOa8qHfr4+XU=
+
+ Modules/module.modulemap
+
+ QNWjsEW54sEWY7N9pv5xhf1yaNA=
+
+
+ files2
+
+ Headers/MyFramework-Swift.h
+
+ hash2
+
+ 2diZoQecvqo1AfgzNjWxZhINTQrVumTXsf8L3xYG8SY=
+
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc
+
+ hash2
+
+ UNdAHDwb9sXOwAf4sScUFrgtpLKuRR1AkmGUmuSQLmA=
+
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface
+
+ hash2
+
+ VHwpf/TdwuHZvE7vkuo7VCgvDnYAmf1/y3nOP7tHkrM=
+
+
+ Modules/MyFramework.swiftmodule/x86_64-apple-ios-simulator.swiftmodule
+
+ hash2
+
+ Elp5fP58+yjQtEigQ0mx4tCrNq8ylzTJnzvR6NyStbA=
+
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftdoc
+
+ hash2
+
+ UNdAHDwb9sXOwAf4sScUFrgtpLKuRR1AkmGUmuSQLmA=
+
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftinterface
+
+ hash2
+
+ VHwpf/TdwuHZvE7vkuo7VCgvDnYAmf1/y3nOP7tHkrM=
+
+
+ Modules/MyFramework.swiftmodule/x86_64.swiftmodule
+
+ hash2
+
+ Elp5fP58+yjQtEigQ0mx4tCrNq8ylzTJnzvR6NyStbA=
+
+
+ Modules/module.modulemap
+
+ hash2
+
+ Ze/OfIIWDLWEMkV5nVXLs+Mzzp3k2eZtpQBPldwh0k4=
+
+
+
+ rules
+
+ ^.*
+
+ ^.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^.*
+
+ ^.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Base\.lproj/
+
+ weight
+ 1010
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Fixtures/MyFrameworkMissingArch.xcframework/Info.plist b/Tests/Fixtures/MyFrameworkMissingArch.xcframework/Info.plist
new file mode 100644
index 00000000..3d5676bd
--- /dev/null
+++ b/Tests/Fixtures/MyFrameworkMissingArch.xcframework/Info.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ AvailableLibraries
+
+
+ LibraryIdentifier
+ ios-x86_64-simulator
+ LibraryPath
+ MyFrameworkMissingArch.framework
+ SupportedArchitectures
+
+ x86_64
+
+ SupportedPlatform
+ ios
+ SupportedPlatformVariant
+ simulator
+
+
+ LibraryIdentifier
+ ios-arm64
+ LibraryPath
+ MyFrameworkMissingArch.framework
+ SupportedArchitectures
+
+ arm64
+
+ SupportedPlatform
+ ios
+
+
+ CFBundlePackageType
+ XFWK
+ XCFrameworkFormatVersion
+ 1.0
+
+
diff --git a/Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Headers/MyFramework-Swift.h b/Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Headers/MyFramework-Swift.h
new file mode 100644
index 00000000..1b9cd391
--- /dev/null
+++ b/Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Headers/MyFramework-Swift.h
@@ -0,0 +1,204 @@
+// Generated by Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+
+#if !defined(__has_include)
+# define __has_include(x) 0
+#endif
+#if !defined(__has_attribute)
+# define __has_attribute(x) 0
+#endif
+#if !defined(__has_feature)
+# define __has_feature(x) 0
+#endif
+#if !defined(__has_warning)
+# define __has_warning(x) 0
+#endif
+
+#if __has_include()
+# include
+#endif
+
+#pragma clang diagnostic ignored "-Wauto-import"
+#include
+#include
+#include
+#include
+
+#if !defined(SWIFT_TYPEDEFS)
+# define SWIFT_TYPEDEFS 1
+# if __has_include()
+# include
+# elif !defined(__cplusplus)
+typedef uint_least16_t char16_t;
+typedef uint_least32_t char32_t;
+# endif
+typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
+typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
+typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
+typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
+typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
+typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
+typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
+typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
+typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
+typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
+typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
+typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
+#endif
+
+#if !defined(SWIFT_PASTE)
+# define SWIFT_PASTE_HELPER(x, y) x##y
+# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
+#endif
+#if !defined(SWIFT_METATYPE)
+# define SWIFT_METATYPE(X) Class
+#endif
+#if !defined(SWIFT_CLASS_PROPERTY)
+# if __has_feature(objc_class_property)
+# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
+# else
+# define SWIFT_CLASS_PROPERTY(...)
+# endif
+#endif
+
+#if __has_attribute(objc_runtime_name)
+# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
+#else
+# define SWIFT_RUNTIME_NAME(X)
+#endif
+#if __has_attribute(swift_name)
+# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
+#else
+# define SWIFT_COMPILE_NAME(X)
+#endif
+#if __has_attribute(objc_method_family)
+# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
+#else
+# define SWIFT_METHOD_FAMILY(X)
+#endif
+#if __has_attribute(noescape)
+# define SWIFT_NOESCAPE __attribute__((noescape))
+#else
+# define SWIFT_NOESCAPE
+#endif
+#if __has_attribute(warn_unused_result)
+# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define SWIFT_WARN_UNUSED_RESULT
+#endif
+#if __has_attribute(noreturn)
+# define SWIFT_NORETURN __attribute__((noreturn))
+#else
+# define SWIFT_NORETURN
+#endif
+#if !defined(SWIFT_CLASS_EXTRA)
+# define SWIFT_CLASS_EXTRA
+#endif
+#if !defined(SWIFT_PROTOCOL_EXTRA)
+# define SWIFT_PROTOCOL_EXTRA
+#endif
+#if !defined(SWIFT_ENUM_EXTRA)
+# define SWIFT_ENUM_EXTRA
+#endif
+#if !defined(SWIFT_CLASS)
+# if __has_attribute(objc_subclassing_restricted)
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# else
+# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
+# endif
+#endif
+#if !defined(SWIFT_RESILIENT_CLASS)
+# if __has_attribute(objc_class_stub)
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# else
+# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
+# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
+# endif
+#endif
+
+#if !defined(SWIFT_PROTOCOL)
+# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
+#endif
+
+#if !defined(SWIFT_EXTENSION)
+# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
+#endif
+
+#if !defined(OBJC_DESIGNATED_INITIALIZER)
+# if __has_attribute(objc_designated_initializer)
+# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+# else
+# define OBJC_DESIGNATED_INITIALIZER
+# endif
+#endif
+#if !defined(SWIFT_ENUM_ATTR)
+# if defined(__has_attribute) && __has_attribute(enum_extensibility)
+# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
+# else
+# define SWIFT_ENUM_ATTR(_extensibility)
+# endif
+#endif
+#if !defined(SWIFT_ENUM)
+# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# if __has_feature(generalized_swift_name)
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
+# else
+# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
+# endif
+#endif
+#if !defined(SWIFT_UNAVAILABLE)
+# define SWIFT_UNAVAILABLE __attribute__((unavailable))
+#endif
+#if !defined(SWIFT_UNAVAILABLE_MSG)
+# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
+#endif
+#if !defined(SWIFT_AVAILABILITY)
+# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
+#endif
+#if !defined(SWIFT_WEAK_IMPORT)
+# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
+#endif
+#if !defined(SWIFT_DEPRECATED)
+# define SWIFT_DEPRECATED __attribute__((deprecated))
+#endif
+#if !defined(SWIFT_DEPRECATED_MSG)
+# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
+#endif
+#if __has_feature(attribute_diagnose_if_objc)
+# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
+#else
+# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
+#endif
+#if !defined(IBSegueAction)
+# define IBSegueAction
+#endif
+#if __has_feature(modules)
+#if __has_warning("-Watimport-in-framework-header")
+#pragma clang diagnostic ignored "-Watimport-in-framework-header"
+#endif
+#endif
+
+#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
+#pragma clang diagnostic ignored "-Wduplicate-method-arg"
+#if __has_warning("-Wpragma-clang-attribute")
+# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
+#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnullability"
+
+#if __has_attribute(external_source_symbol)
+# pragma push_macro("any")
+# undef any
+# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="MyFramework",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
+# pragma pop_macro("any")
+#endif
+
+#if __has_attribute(external_source_symbol)
+# pragma clang attribute pop
+#endif
+#pragma clang diagnostic pop
diff --git a/Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Info.plist b/Tests/Fixtures/MyFrameworkMissingArch.xcframework/ios-arm64/MyFrameworkMissingArch.framework/Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..40a2eeab2d94414984240f7d3383fb17e8719d6f
GIT binary patch
literal 851
zcmY*X%Wu;#7`M%9Jm)=G$2PW7-Yg_+blgzaP^hMErO6(!X_1?Ftu-eOd301raNx>~
zKLCvzXKtK0f)GMn;K-F565J7P*J#C&?Ck?=)l24hmZ6eJ$C%W
z$y2?!uYX{0X!!I@YFJhEuqi(TSXC~;aN%L~65uv$IZTrSEXGn*EO%XrhmW6Q+~eH62RI(tH%_@v4>
zix>@8v|D#Ib*4hS^-{AC$QE`)ux;T%CX^_azkt$38(Bp)3#HWsLsQh