diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 000000000..e940ed957 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,440 @@ +{ + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "contributors": [ + { + "login": "jcolicchio", + "name": "Joseph Colicchio", + "avatar_url": "https://avatars3.githubusercontent.com/u/2837288?v=4", + "profile": "https://joecolicch.io", + "contributions": [ + "ideas" + ] + }, + { + "login": "deatondg", + "name": "deatondg", + "avatar_url": "https://avatars0.githubusercontent.com/u/3221590?v=4", + "profile": "https://github.com/deatondg", + "contributions": [ + "ideas" + ] + }, + { + "login": "dflems", + "name": "Dan Fleming", + "avatar_url": "https://avatars3.githubusercontent.com/u/925850?v=4", + "profile": "https://github.com/dflems", + "contributions": [ + "code" + ] + }, + { + "login": "sascha", + "name": "Sascha Schwabbauer", + "avatar_url": "https://avatars3.githubusercontent.com/u/895505?v=4", + "profile": "https://twitter.com/_SaschaS", + "contributions": [ + "ideas" + ] + }, + { + "login": "marciniwanicki", + "name": "Marcin Iwanicki", + "avatar_url": "https://avatars3.githubusercontent.com/u/946649?v=4", + "profile": "https://github.com/marciniwanicki", + "contributions": [ + "maintenance" + ] + }, + { + "login": "adamkhazi", + "name": "Adam Khazi", + "avatar_url": "https://avatars2.githubusercontent.com/u/9820670?v=4", + "profile": "https://github.com/adamkhazi", + "contributions": [ + "maintenance" + ] + }, + { + "login": "elliottwilliams", + "name": "Elliott Williams", + "avatar_url": "https://avatars3.githubusercontent.com/u/910198?v=4", + "profile": "https://github.com/elliottwilliams", + "contributions": [ + "code" + ] + }, + { + "login": "muukii", + "name": "Muukii", + "avatar_url": "https://avatars.githubusercontent.com/u/1888355?v=4", + "profile": "http://muukii.app", + "contributions": [ + "content" + ] + }, + { + "login": "nnsnodnb", + "name": "Yuya Oka", + "avatar_url": "https://avatars.githubusercontent.com/u/9856514?v=4", + "profile": "https://nnsnodnb.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "keith", + "name": "Keith Smiley", + "avatar_url": "https://avatars.githubusercontent.com/u/283886?v=4", + "profile": "https://smileykeith.com", + "contributions": [ + "content" + ] + }, + { + "login": "ileitch", + "name": "Ian Leitch", + "avatar_url": "https://avatars.githubusercontent.com/u/48235?v=4", + "profile": "https://github.com/ileitch", + "contributions": [ + "code" + ] + }, + { + "login": "subdan", + "name": "Daniil Subbotin", + "avatar_url": "https://avatars.githubusercontent.com/u/410293?v=4", + "profile": "https://github.com/subdan", + "contributions": [ + "code" + ] + }, + { + "login": "flowbe", + "name": "Florentin Bekier", + "avatar_url": "https://avatars.githubusercontent.com/u/8288625?v=4", + "profile": "https://www.florentin.tech", + "contributions": [ + "code" + ] + }, + { + "login": "CognitiveDisson", + "name": "Vadim Smal", + "avatar_url": "https://avatars.githubusercontent.com/u/10621118?v=4", + "profile": "https://github.com/CognitiveDisson", + "contributions": [ + "bug" + ] + }, + { + "login": "freddi-kit", + "name": "freddi(Yuki Aki)", + "avatar_url": "https://avatars.githubusercontent.com/u/13707872?v=4", + "profile": "http://freddi.dev", + "contributions": [ + "code" + ] + }, + { + "login": "KrisRJack", + "name": "Kristopher Jackson", + "avatar_url": "https://avatars.githubusercontent.com/u/35638500?v=4", + "profile": "http://KrisRJack.com", + "contributions": [ + "code" + ] + }, + { + "login": "Jake-Prickett", + "name": "Jake Prickett", + "avatar_url": "https://avatars.githubusercontent.com/u/26095410?v=4", + "profile": "https://github.com/Jake-Prickett", + "contributions": [ + "code" + ] + }, + { + "login": "jakeatoms", + "name": "Jake Adams", + "avatar_url": "https://avatars.githubusercontent.com/u/3605966?v=4", + "profile": "http://www.jakeadams.co", + "contributions": [ + "code" + ] + }, + { + "login": "mtj0928", + "name": "matsuji", + "avatar_url": "https://avatars.githubusercontent.com/u/12427733?v=4", + "profile": "https://github.com/mtj0928", + "contributions": [ + "code" + ] + }, + { + "login": "Bogdan-Belogurov", + "name": "Bogdan Belogurov", + "avatar_url": "https://avatars.githubusercontent.com/u/39379705?v=4", + "profile": "https://github.com/Bogdan-Belogurov", + "contributions": [ + "code" + ] + }, + { + "login": "cgrindel", + "name": "Chuck Grindel", + "avatar_url": "https://avatars.githubusercontent.com/u/159968?v=4", + "profile": "https://chuckgrindel.com/", + "contributions": [ + "code" + ] + }, + { + "login": "michaelmcguire", + "name": "Michael McGuire", + "avatar_url": "https://avatars.githubusercontent.com/u/429790?v=4", + "profile": "https://twitter.com/MonocularVision", + "contributions": [ + "code" + ] + }, + { + "login": "CrazyFanFan", + "name": "C-凡", + "avatar_url": "https://avatars.githubusercontent.com/u/15794964?v=4", + "profile": "https://github.com/CrazyFanFan", + "contributions": [ + "code" + ] + }, + { + "login": "maxwellE", + "name": "Maxwell Elliott", + "avatar_url": "https://avatars.githubusercontent.com/u/566328?v=4", + "profile": "http://www.tinder.com", + "contributions": [ + "code" + ] + }, + { + "login": "brentleyjones", + "name": "Brentley Jones", + "avatar_url": "https://avatars.githubusercontent.com/u/158658?v=4", + "profile": "https://brentleyjones.com", + "contributions": [ + "code" + ] + }, + { + "login": "teameh", + "name": "Teameh", + "avatar_url": "https://avatars.githubusercontent.com/u/1330668?v=4", + "profile": "https://www.linkedin.com/in/tiemevanveen", + "contributions": [ + "code" + ] + }, + { + "login": "technocidal", + "name": "Johannes Ebeling", + "avatar_url": "https://avatars.githubusercontent.com/u/14994778?v=4", + "profile": "https://technocidal.com", + "contributions": [ + "code" + ] + }, + { + "login": "baekteun", + "name": "baegteun", + "avatar_url": "https://avatars.githubusercontent.com/u/74440939?v=4", + "profile": "https://baegteun.com", + "contributions": [ + "doc" + ] + }, + { + "login": "AlexKobachiJP", + "name": "Alex Kovács", + "avatar_url": "https://avatars.githubusercontent.com/u/103150233?v=4", + "profile": "https://kobachi.jp", + "contributions": [ + "doc" + ] + }, + { + "login": "zenangst", + "name": "Christoffer Winterkvist", + "avatar_url": "https://avatars.githubusercontent.com/u/57446?v=4", + "profile": "http://zenangst.com", + "contributions": [ + "code" + ] + }, + { + "login": "timothycosta", + "name": "Timothy Costa", + "avatar_url": "https://avatars.githubusercontent.com/u/948806?v=4", + "profile": "http://www.timothycosta.com", + "contributions": [ + "code" + ] + }, + { + "login": "Mstrodl", + "name": "Mary ", + "avatar_url": "https://avatars.githubusercontent.com/u/6877780?v=4", + "profile": "https://coolmathgames.tech", + "contributions": [ + "code" + ] + }, + { + "login": "Ibrahimhass", + "name": "Md. Ibrahim Hassan", + "avatar_url": "https://avatars.githubusercontent.com/u/16992520?v=4", + "profile": "https://github.com/Ibrahimhass", + "contributions": [ + "code" + ] + }, + { + "login": "tatagrigory", + "name": "tatagrigory", + "avatar_url": "https://avatars.githubusercontent.com/u/5187973?v=4", + "profile": "https://github.com/tatagrigory", + "contributions": [ + "code" + ] + }, + { + "login": "art-divin", + "name": "Ruslan Alikhamov", + "avatar_url": "https://avatars.githubusercontent.com/u/1614869?v=4", + "profile": "https://github.com/art-divin", + "contributions": [ + "code" + ] + }, + { + "login": "ladislas", + "name": "Ladislas de Toldi", + "avatar_url": "https://avatars.githubusercontent.com/u/2206544?v=4", + "profile": "https://ladislas.detoldi.me", + "contributions": [ + "code" + ] + }, + { + "login": "mattmassicotte", + "name": "Matt Massicotte", + "avatar_url": "https://avatars.githubusercontent.com/u/85322?v=4", + "profile": "https://www.massicotte.org", + "contributions": [ + "code" + ] + }, + { + "login": "VorkhlikArtem", + "name": "Артем Ворхлик", + "avatar_url": "https://avatars.githubusercontent.com/u/115653999?v=4", + "profile": "https://github.com/VorkhlikArtem", + "contributions": [ + "code" + ] + }, + { + "login": "woin2ee", + "name": "Jaewon-Yun", + "avatar_url": "https://avatars.githubusercontent.com/u/81426024?v=4", + "profile": "https://github.com/woin2ee", + "contributions": [ + "code" + ] + }, + { + "login": "mikeger", + "name": "Mike Gerasymenko", + "avatar_url": "https://avatars.githubusercontent.com/u/715129?v=4", + "profile": "https://gera.cx", + "contributions": [ + "code" + ] + }, + { + "login": "filipracki", + "name": "Filip Racki", + "avatar_url": "https://avatars.githubusercontent.com/u/27164368?v=4", + "profile": "https://github.com/filipracki", + "contributions": [ + "code" + ] + }, + { + "login": "kelvinharron", + "name": "Kelvin Harron", + "avatar_url": "https://avatars.githubusercontent.com/u/16445381?v=4", + "profile": "https://github.com/kelvinharron", + "contributions": [ + "code" + ] + }, + { + "login": "georgenavarro", + "name": "George Navarro", + "avatar_url": "https://avatars.githubusercontent.com/u/2748028?v=4", + "profile": "https://github.com/georgenavarro", + "contributions": [ + "code" + ] + }, + { + "login": "Speakus", + "name": "Maxim", + "avatar_url": "https://avatars.githubusercontent.com/u/849294?v=4", + "profile": "https://github.com/Speakus", + "contributions": [ + "code" + ] + }, + { + "login": "bryansum", + "name": "Bryan Summersett", + "avatar_url": "https://avatars.githubusercontent.com/u/49369?v=4", + "profile": "https://github.com/bryansum", + "contributions": [ + "code" + ] + }, + { + "login": "mikhailmulyar", + "name": "Mikhail", + "avatar_url": "https://avatars.githubusercontent.com/u/2234720?v=4", + "profile": "https://github.com/mikhailmulyar", + "contributions": [ + "code" + ] + }, + { + "login": "michaelversus", + "name": "Michael", + "avatar_url": "https://avatars.githubusercontent.com/u/10232018?v=4", + "profile": "https://github.com/michaelversus", + "contributions": [ + "code" + ] + } + ], + "contributorsPerLine": 7, + "projectName": "XcodeProj", + "projectOwner": "tuist", + "repoType": "github", + "repoHost": "https://github.com", + "skipCi": true, + "commitConvention": "angular", + "commitType": "docs" +} diff --git a/.github/.commitlint.rules.js b/.github/.commitlint.rules.js new file mode 100644 index 000000000..ee3d4d402 --- /dev/null +++ b/.github/.commitlint.rules.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + 'header-max-length': [2, 'always', 200], + } +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..fae50dada --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +open_collective: tuistapp +github: tuist diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 3d2e47940..000000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,24 +0,0 @@ -# https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname -name: Checks - -on: [pull_request] - -jobs: - swiftlint: - name: Swiftlint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: GitHub Action for SwiftLint - uses: pepibumur/action-swiftlint@0d4afd006bb24e4525b5afcefd4ab5e2537193ac - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - changelog: - name: Changelog - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Changelog Reminder - uses: peterjgrainger/action-changelog-reminder@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/conventional-pr.yml b/.github/workflows/conventional-pr.yml new file mode 100644 index 000000000..ba43b8f6f --- /dev/null +++ b/.github/workflows/conventional-pr.yml @@ -0,0 +1,21 @@ +name: conventional-pr +on: + pull_request: + branches: + - main + - master + types: + - opened + - edited + - synchronize +jobs: + lint-pr: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: CondeNast/conventional-pull-request-action@v0.2.0 + with: + commitTitleMatch: false + commitlintRulesPath: ".github/.commitlint.rules.js" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml deleted file mode 100644 index 65ebe2dab..000000000 --- a/.github/workflows/package.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname -name: Package - -on: [push] - -jobs: - build: - name: Build - runs-on: macOS-latest - steps: - - uses: actions/checkout@v1 - - name: Build - run: swift build -c release - - test: - name: Test - runs-on: macOS-latest - steps: - - uses: actions/checkout@v1 - - name: Generate project - run: swift package generate-xcodeproj - - name: Run tests - run: xcodebuild test -project XcodeProj.xcodeproj -scheme XcodeProj-Package -enableCodeCoverage YES - - name: Send test coverage report - run: bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..d6845f8d1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,90 @@ +name: Release + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + version: + description: "The version to release" + type: string + +permissions: + contents: write + pull-requests: read + statuses: write + packages: write + +jobs: + release: + name: Release + runs-on: "ubuntu-latest" + timeout-minutes: 15 + if: "!startsWith(github.event.head_commit.message, '[Release]')" + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + - uses: jdx/mise-action@v3 + with: + experimental: true + - name: Check if there are releasable changes + id: is-releasable + run: | + # Run git cliff and save the output + bumped_output=$(git cliff 8.22.0.. --bump) + echo "Bumped output:" + echo "${bumped_output}" + + # Read the content of CHANGELOG.md + changelog_content=$(cat CHANGELOG.md) + echo "CHANGELOG.md content:" + echo "${changelog_content}" + + # Compare the outputs and set the result + if [ "${bumped_output}" = "${changelog_content}" ]; then + echo "should-release=false" >> $GITHUB_ENV + else + echo "should-release=true" >> $GITHUB_ENV + fi + + - name: Get next version + id: next-version + if: env.should-release == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: echo "NEXT_VERSION=$(git cliff 8.22.0.. --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 8.22.0.. --bump --unreleased >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + - name: Update CHANGELOG.md + if: env.should-release == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: git cliff 8.22.0.. --bump -o CHANGELOG.md + - name: Commit changes + id: auto-commit-action + uses: stefanzweifel/git-auto-commit-action@v7 + if: env.should-release == 'true' + with: + commit_options: "--allow-empty" + tagging_message: ${{ steps.next-version.outputs.NEXT_VERSION }} + skip_dirty_check: true + commit_message: "[Release] XcodeProj ${{ steps.next-version.outputs.NEXT_VERSION }}" + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + if: env.should-release == 'true' + with: + draft: false + repository: tuist/XcodeProj + 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 }} diff --git a/.github/workflows/xcodeproj.yml b/.github/workflows/xcodeproj.yml new file mode 100644 index 000000000..5fe21d62f --- /dev/null +++ b/.github/workflows/xcodeproj.yml @@ -0,0 +1,87 @@ +# https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname +name: XcodeProj + +on: + push: + branches: + - main + pull_request: {} + +concurrency: + group: xcodeproj-${{ github.head_ref }} + cancel-in-progress: true + +env: + MISE_EXPERIMENTAL: 1 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + build: + name: Build (macOS) + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v3 + - name: Build + run: mise run build + build-linux: + name: Build (Linux) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v3 + - run: mise use swift@6.0.2 + - name: Build + run: swift build --configuration release + build-ios: + name: Build (iOS) + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + - name: Build for iOS Simulator + run: xcodebuild build -scheme XcodeProj -destination 'generic/platform=iOS Simulator' + test: + name: Test (macOS / Xcode) + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v3 + - name: Run tests + run: mise run test + test-linux: + name: Test (Linux) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v3 + - run: mise use swift@6.0.2 + - run: | + git config --global user.email 'xcodeproj@tuist.dev' + git config --global user.name 'xcodeproj' + git config --global init.defaultBranch main + - name: Test + run: swift test + test-ios: + name: Test (iOS / Xcode) + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - name: Select iOS Simulator + run: | + simulator_id="$(xcrun simctl list devices available | awk -F '[()]' '/iPhone/ { print $2; exit }')" + if [ -z "$simulator_id" ]; then + echo "No available iPhone simulator found" + xcrun simctl list devices available + exit 1 + fi + echo "IOS_SIMULATOR_ID=$simulator_id" >> "$GITHUB_ENV" + - name: Run tests on iOS Simulator + run: xcodebuild test -scheme XcodeProj -destination "platform=iOS Simulator,id=$IOS_SIMULATOR_ID" + + lint: + name: Lint + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v3 + - run: mise run lint diff --git a/.gitignore b/.gitignore index 69946c1e9..a53f04dd9 100644 --- a/.gitignore +++ b/.gitignore @@ -185,6 +185,11 @@ XcodeProj.xcodeproj tmp/ XcodeProj.framework.zip -# Tapestry -tapestries/.build -tapestries/.swiftpm +!Fixtures/**/xcuserdata/ + +Derived/ +*.xcworkspace/ +*.xcodeproj/ + +!Fixtures/**/*.xcodeproj/ + diff --git a/.jazzy.yaml b/.jazzy.yaml deleted file mode 100644 index 4605b3c64..000000000 --- a/.jazzy.yaml +++ /dev/null @@ -1,56 +0,0 @@ -author: Tuist -module: XcodeProj -github_url: https://github.com/tuist/xcodeproj -copyright: 'Copyright © from 2018 Pedro Piñera Buendía. All rights reserved.' -readme: ABOUT.md -documentation: guides/*.md -sdk: macosx -xcodebuild_arguments: - - -project - - XcodeProj.xcodeproj - - -scheme - - XcodeProj-Package -skip_undocumented: true -custom_categories: - - name: Models - children: - - BuildSettings - - XCConfig - - XCWorkspace - - XcodeProj - - XCBuildConfiguration - - XCConfigurationList - - XCScheme - - XCSharedData - - PBXProductType - - PBXNativeTarget - - PBXProj - - PBXObject - - PBXSourceTree - - PBXAggregateTarget - - PBXBuildFile - - PBXContainerItemProxy - - PBXFileElement - - PBXFileReference - - PBXGroup - - PBXProject - - PBXFrameworksBuildPhase - - PBXHeadersBuildPhase - - PBXResourcesBuildPhase - - PBXCopyFilesBuildPhase - - PBXShellScriptBuildPhase - - PBXSourcesBuildPhase - - PBXTargetDependency - - PBXVariantGroup - - name: Protocols - children: - - ProjectElement - - name: Errors - children: - - XCConfigError - - XCSchemeError - - XCSharedDataError - - XCWorkspaceDataError - - XCWorkspaceError - - XCodeProjError - - PBXObjectError diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 000000000..2564d3fa8 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,5 @@ +[tools] +swiftformat = "0.54.3" +tuist = "4.195.17" +swiftlint = "0.63.3" +"git-cliff" = "2.13.1" diff --git a/.mise/tasks/build b/.mise/tasks/build new file mode 100755 index 000000000..59b111039 --- /dev/null +++ b/.mise/tasks/build @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# mise description="Build the package in the host" + +swift build --package-path $MISE_PROJECT_ROOT --configuration debug diff --git a/.mise/tasks/build-linux b/.mise/tasks/build-linux new file mode 100755 index 000000000..ca969937a --- /dev/null +++ b/.mise/tasks/build-linux @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# mise description="Build on Linux" + +CONTAINER_RUNTIME=$(command -v podman || command -v docker) + +if [ -z "$CONTAINER_RUNTIME" ]; then + echo "Neither podman nor docker is available. Please install one to proceed." + exit 1 +fi + +$CONTAINER_RUNTIME run --rm --interactive --tty --volume "$(pwd):/package" --workdir "/package" swift:5.10.1 bash -c " + git config --global user.email 'xcodeproj@tuist.io' && + git config --global user.name 'xcodeproj' && + git config --global init.defaultBranch main && + swift build --configuration release +" diff --git a/.mise/tasks/lint b/.mise/tasks/lint new file mode 100755 index 000000000..d1a523f1d --- /dev/null +++ b/.mise/tasks/lint @@ -0,0 +1,6 @@ +#!/bin/bash +# mise description="Lint the workspace" +set -euo pipefail + +swiftformat $MISE_PROJECT_ROOT --lint +swiftlint lint --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources diff --git a/.mise/tasks/lint-fix b/.mise/tasks/lint-fix new file mode 100755 index 000000000..cfc035959 --- /dev/null +++ b/.mise/tasks/lint-fix @@ -0,0 +1,6 @@ +#!/bin/bash +# mise description="Lint the workspace fixing issues" +set -euo pipefail + +swiftformat $MISE_PROJECT_ROOT +swiftlint lint --fix --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources diff --git a/.mise/tasks/test b/.mise/tasks/test new file mode 100755 index 000000000..8103d2d5f --- /dev/null +++ b/.mise/tasks/test @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# mise description="Run tests in the host" + +swift test --package-path $MISE_PROJECT_ROOT diff --git a/.mise/tasks/test-linux b/.mise/tasks/test-linux new file mode 100755 index 000000000..f60874e80 --- /dev/null +++ b/.mise/tasks/test-linux @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# mise description="Run tests on Linux" + +CONTAINER_RUNTIME=docker + +if [ -z "$CONTAINER_RUNTIME" ]; then + echo "Neither podman nor docker is available. Please install one to proceed." + exit 1 +fi + +$CONTAINER_RUNTIME run --rm --interactive --tty --volume "$(pwd):/package" --workdir "/package" swift:5.10.1 bash -c " + git config --global user.email 'xcodeproj@tuist.io' && + git config --global user.name 'xcodeproj' && + git config --global init.defaultBranch main && + swift test +" diff --git a/.rspec b/.rspec deleted file mode 100644 index c99d2e739..000000000 --- a/.rspec +++ /dev/null @@ -1 +0,0 @@ ---require spec_helper diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 57cf282eb..000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.6.5 diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 000000000..c3c607785 --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [XcodeProj] diff --git a/.swift-version b/.swift-version index a75b92f1e..e0ea36fee 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.1 +6.0 diff --git a/.swiftformat b/.swiftformat index cce74ebea..d9bc86bea 100644 --- a/.swiftformat +++ b/.swiftformat @@ -5,6 +5,8 @@ # format options +--disable wrapMultilineStatementBraces +--disable braces --allman false --binarygrouping 4,8 --commas always @@ -32,4 +34,4 @@ --stripunusedargs always --trimwhitespace always --wraparguments preserve ---wrapcollections preserve \ No newline at end of file +--wrapcollections preserve diff --git a/CHANGELOG.md b/CHANGELOG.md index 96a77052e..3b5ceb00b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,672 +1,334 @@ -🚀 Check out the guidelines [here](https://github.com/xcodeswift/contributors/blob/master/CHANGELOG_GUIDELINES.md) - -## Next - -## 7.5.0 - -### Fixed - -- Provide default build settings for unit and ui test targets https://github.com/tuist/XcodeProj/pull/501 by @kwridan -- Remove "Shell" Carthage dependency from project manifest as it's no longer used https://github.com/tuist/XcodeProj/pull/505 by @kwridan - -## 7.4.0 - -### Changed - -- Update list of recognized file extensions https://github.com/tuist/XcodeProj/pull/500 by @dflems - -## 7.3.0 - -### Changed - -- Update BuildSettingsProvider to include extension settings https://github.com/tuist/XcodeProj/pull/497 by @kwridan -- Remove the dependency with the Swift Package Manager https://github.com/tuist/XcodeProj/pull/499 by @elliottwilliams - -## 7.2.2 - -### Fixed - -- Make test plans deserialise correctly https://github.com/tuist/XcodeProj/pull/496 by @adamkhazi - -## 7.2.1 - -### Fixed - -- Make test plans optional https://github.com/tuist/XcodeProj/commit/c15034948a2a132bf559f14d3c6b4d1b73749663 by @pepibumur - -### Changed - -- Replaced CircleCI with GitHub actions https://github.com/tuist/XcodeProj/pull/493 by @pepibumur -- Replace CircleCI with GitHub actions https://github.com/tuist/XcodeProj/pull/493 by @pepibumur -- Replace Shell with the SPM's Process utility class https://github.com/tuist/XcodeProj/pull/492 by @pepibumur - -### Added - -- Automating release process with [tapestry](https://github.com/ackeecz/tapestry) https://github.com/tuist/XcodeProj/pull/495 by @fortmarek - -## 7.2.0 - -### Added - -- Added support for Xcode 11 test plans https://github.com/tuist/XcodeProj/pull/491 by @maniramezan - -### Fixed - -- Add remote Swift packages to the Frameworks build phase https://github.com/tuist/XcodeProj/pull/487 by @kwridan -- System library added to a group has empty path https://github.com/tuist/XcodeProj/pull/488 by @damirdavletov -- Fix Products group serialisation with temporary ids https://github.com/tuist/XcodeProj/pull/489 by @damirdavletov - -## 7.1.0 - -### Added - -- Add `onlyGenerateCoverageForSpecifiedTargets` parameter to `TestAction` https://github.com/tuist/XcodeProj/pull/473 by @kateinoigakukun -- Added support for `PBXTargetDependency.product` https://github.com/tuist/XcodeProj/pull/481 by @yonaskolb -- Xcode 11 support. - -## 7.0.1 - -### Changed - -- Update `BuildSettingProvider` to return `LD_RUNPATH_SEARCH_PATHS` as `Array` https://github.com/tuist/xcodeproj/pull/463 by @marciniwanicki -- Update `Project.swift` to make it compatible with tuist 0.17.0 https://github.com/tuist/xcodeproj/pull/469 by @marciniwanicki - -### Added - -- Adding support for adding local Swift packages https://github.com/tuist/XcodeProj/pull/468 by @fortmarek -- Adding additional `lastKnownFileType`s https://github.com/tuist/xcodeproj/pull/458 by @kwridan -- Adding possibility to create variant group for referencing localized resources https://github.com/tuist/xcodeproj/pull/462 by @timbaev - -### Fixed - -- Duplication of packages https://github.com/tuist/XcodeProj/pull/470 by @fortmarek - -## 7.0.0 - -### Changed - -- **Breaking** Change the UUID generation logic to generate ids with a length of 24 https://github.com/tuist/xcodeproj/pull/432 by @pepibumur. -- **Breaking** Renamed module from `xcodeproj` to `XcodeProj` https://github.com/tuist/xcodeproj/pull/398 by @pepibumur. -- Add `override` flag to `PBXGroup.addFile(at:,sourceTree:,sourceRoot:)` https://github.com/tuist/xcodeproj/pull/410 by @mrylmz -- Rename some internal variables to have a more representative name https://github.com/tuist/xcodeproj/pull/415 by @pepibumur. - -### Added - -- **Breaking** Add `SWIFT_COMPILATION_MODE` and `CODE_SIGN_IDENTITY` build settings, remove `DEBUG` flag for Release https://github.com/tuist/xcodeproj/pull/417 @dangthaison91 -- **Breaking** Added throwing an error in case group path can't be resolved by @damirdavletov -- **Breaking** Added remote project support to PBXContainerItemProxy by @damirdavletov -- **Breaking** Add support for `RemoteRunnable` https://github.com/tuist/xcodeproj/pull/400 by @pepibumur. -- **Breaking** Swift 5 support https://github.com/tuist/xcodeproj/pull/397 by @pepibumur. -- Added `com.apple.product-type.application.watchapp2-container` to `PBXProductType`. https://github.com/tuist/xcodeproj/pull/441 by @leogdion. -- Add BatchUpdater to quickly add files to the group https://github.com/tuist/xcodeproj/pull/388 by @CognitiveDisson. -- `WorkspaceSettings.autoCreateSchemes` attribute https://github.com/tuist/xcodeproj/pull/399 by @pepibumur -- Additional Swift 5 fixes: https://github.com/tuist/xcodeproj/pull/402 by @samisuteria -- Make build phase name public by @llinardos. -- Can access embed frameworks build phase for a target by @llinardos. -- Added `com.apple.product-type.framework.static` to `PBXProductType`. https://github.com/tuist/xcodeproj/pull/347 by @ileitch. -- Can add a not existing file to a group https://github.com/tuist/xcodeproj/pull/418 by @llinardos. -- Support for Swift PM Packages https://github.com/tuist/xcodeproj/pull/439 https://github.com/tuist/xcodeproj/pull/444 by @pepibumur @yonaskolb. -- `LaunchAction.customLaunchCommand` attribute https://github.com/tuist/xcodeproj/pull/451 by @pepibumur. -- `XCBuildConfiguration.append` method https://github.com/tuist/xcodeproj/pull/450 by @pepibumur. - -### Fixed - -- Carthage integration https://github.com/tuist/xcodeproj/pull/416 by @pepibumur. -- Relative path is wrong when referencing file outside of project folder https://github.com/tuist/xcodeproj/issues/423 by @damirdavletov -- [crash] Fatal error: Duplicate values for key https://github.com/tuist/xcodeproj/issues/426 by @toshi0383 -- Change PBXContainerItemProxy.remoteGlobalID attribute to support object references https://github.com/tuist/xcodeproj/pull/445 by @yonaskolb -- Dead lock in the `PBXObjects.delete` method https://github.com/tuist/xcodeproj/pull/449 by @pepibumur - -### Removed - -- OSLogs https://github.com/tuist/xcodeproj/pull/453 by @pepibumur. - -## 6.7.0 - -### Changed - -- **Breaking** Make `PBXBuildPhase.files` optional to match Xcode's behavior https://github.com/tuist/xcodeproj/pull/391 by @pepibumur. - -### Added - -- Add location variable to XCWorkspaceDataElement https://github.com/tuist/xcodeproj/pull/387 by @pepibumur. - -### Fixed - -- Fixed file full path performance issue https://github.com/tuist/xcodeproj/pull/372 by @CognitiveDisson. -- Diffing issues when writing the project https://github.com/tuist/xcodeproj/pull/391 by @pepibumur. - -## 6.6.0 - -### Fixed - -- Fix adding files to `PBXBuildPhase` https://github.com/tuist/xcodeproj/pull/380 @danilsmakotin. -- Improve project encoding performance https://github.com/tuist/xcodeproj/pull/371 by @CognitiveDisson. -- Project decoding performance issue https://github.com/tuist/xcodeproj/pull/365 by @CognitiveDisson. -- Fix PBXTarget extension methods https://github.com/tuist/xcodeproj/pull/367 by @danilsmakotin. - -### Added - -- Added `GPUFrameCaptureMode` and `GPUValidationMode` options to `LaunchAction` https://github.com/tuist/xcodeproj/pull/368 by @schiewe. -- Add Swiftformat https://github.com/tuist/xcodeproj/pull/375 by @pepibumur. - -### Changed - -- **Breaking** Rename GPUFrameCaptureMode cases to start with a lowercase letter https://github.com/tuist/xcodeproj/pull/375 by @pepibumur. -- Fix linting issues https://github.com/tuist/xcodeproj/pull/375 by @pepibumur. - -## 6.5.0 - -### Changed - -- Make Xcode.Supported.xcschemeFormatVersion public https://github.com/tuist/xcodeproj/pull/361 by @yonaskolb. - -### Added - -- Fix remote target dependency https://github.com/tuist/xcodeproj/pull/362 by @mxcl. - -## 6.4.0 - -### Added - -- Added `projReferenceFormat` to `PBXOutputSettings` to allow changing the output format of generated references. `withPrefixAndSuffix` will give the legacy behaviour `xcode` will generate 32 character references as XCode does. https://github.com/tuist/xcodeproj/pull/345 by @samskiter. -- Danger https://github.com/tuist/xcodeproj/pull/357 by @pepibumur. -- Support for WorkspaceSettings https://github.com/tuist/xcodeproj/pull/359 by @pepibumur. - -## 6.3.0 - -### Added - -- Added `parallelizable` and `randomExecutionOrdering` attributes to `XCScheme.TestableReference` https://github.com/tuist/xcodeproj/pull/340 by @alvarhansen. - -### Fixed - -- Fixed possible generated UUID conflicts https://github.com/tuist/xcodeproj/pull/342 by @yonaskolb. -- Fixed not working PBXFileElement.fullPath(sourceRoot:) method https://github.com/tuist/xcodeproj/pull/343 by @Vyeczorny. - -## 6.2.0 - -### Added - -- Carthage and CocoaPods support https://github.com/tuist/xcodeproj/pull/339 by @pepibumur. - -### Changed - -- Improved writing performance https://github.com/tuist/xcodeproj/pull/336 https://github.com/tuist/xcodeproj/pull/337 https://github.com/tuist/xcodeproj/pull/338 by @yonaskolb. -- Replaced Swift Package Manager dependency with PathKit https://github.com/tuist/xcodeproj/pull/334 by @yonaskolb. - -## 6.1.0 - -### Added - -- Added ability to pass in a `PBXObject` into the `PBXProject.targetAttributes` dictionary, which will be encoded into its UUID. Can be used for `TestTargetID` https://github.com/tuist/xcodeproj/pull/333 by @yonaskolb. - -### Changed - -- Changed `XCScheme.BuildableReference` init to make `blueprint` a `PBXObject` and added a `setBlueprint(:)` function https://github.com/tuist/xcodeproj/pull/320 by @yonaskolb. -- Bump AEXML version to 4.3.3 https://github.com/tuist/xcodeproj/pull/310 by @pepibumur. -- Improves performance of object references https://github.com/tuist/xcodeproj/pull/332 by @yonaskolb. -- Prefix reference with object type acronym. eg. `PBXFileReference` becomes `FR_XXXXXXXXXXXXXXXXX` https://github.com/tuist/xcodeproj/pull/332 by @yonaskolb. -- Add `TEMP` prefix to temporary unfixed reference values https://github.com/tuist/xcodeproj/pull/332 by @yonaskolb. - -### Fixed - -- Fixed written order of scheme attributes in Swift 4.2 https://github.com/tuist/xcodeproj/pull/325 and https://github.com/tuist/xcodeproj/pull/331 by @yonaskolb and @drekka - -## 6.0.1 - -### Fixed - -- Fixes `PBXProject` attributes not being set properly https://github.com/tuist/xcodeproj/pull/318 by @yonaskolb. -- Fixed remoteGlobalID typo https://github.com/tuist/xcodeproj/pull/315 by @yonaskolb. -- Fixed `XCBuildConfiguration.buildConfiguration` type https://github.com/tuist/xcodeproj/pull/316 by @yonaskolb. - -## 6.0.0 - -Note: Migration guidelines are included in the project README. - -### Changed - -- **Breaking** Make `PBXObjectReference` internal https://github.com/tuist/xcodeproj/pull/300 by @pepibumur. -- **Breaking** Make `PBXObjects` internal https://github.com/tuist/xcodeproj/pull/300 by @pepibumur. -- **Breaking** Move `PBXObjects` helpers to `PBXProj` https://github.com/tuist/xcodeproj/pull/300 by @pepibumur. - -## 5.2.0 - -### Changed - -- Some tweaks to support Xcode 10 https://github.com/tuist/xcodeproj/pull/298 by @pepibumur. - -## 5.1.1 - -### Changed - -- **Breaking** Change `PBXBuildFile.file` attribute to be of type `PBXFileElement` https://github.com/tuist/xcodeproj/pull/297 by @pepibumur. - -### Added - -- Add `PBXBuildPhase.add(file:)` method that takes a file element and returns a build file https://github.com/tuist/xcodeproj/pull/297 by @pepibumur. -- Add `PBXProj.rootObject` attribute https://github.com/tuist/xcodeproj/pull/297 by @pepibumur. - -### Fixed - -- `XCBuildConfiguration.baseConfiguration` type https://github.com/tuist/xcodeproj/pull/297 @pepibumur. - -## 5.1.0 - -### Added - -- `setAttributes`, `removeAttributes` and `attributes` to `PBXProject` https://github.com/tuist/xcodeproj/pull/295 by @pepibumur - -### Changed - -- **Breaking** Change `blueprintIdentifier` type to `PBXObjectReference` https://github.com/tuist/xcodeproj/pull/289 by @pepibumur - -### Fixed - -- Fix grammatical issues and add some convenient getters https://github.com/tuist/xcodeproj/pull/291 by @pepibumur -- Fix targets not getting the reference generated https://github.com/tuist/xcodeproj/pull/290 by @pepibumur -- Product references not being generated https://github.com/tuist/xcodeproj/pull/294 by @pepibumur - -### Removed - -- **Breaking** Make `PBXProject.attributes` internal https://github.com/tuist/xcodeproj/pull/295 by @pepibumur - -## 5.0.0 - -Nothing new since the release rc2. - -## 5.0.0-rc2 - -### Changed - -- **Breaking** Rename `filesReferences` to `fileReferences` https://github.com/tuist/xcodeproj/pull/271 by @pepibumur - -### Added - -- Xcode 10 inputFileListPaths and outputFileListPaths attributes https://github.com/tuist/xcodeproj/pull/271 by @pepibumur -- Split up `XCScheme` models and make them conform the `Equatable` protocol https://github.com/tuist/xcodeproj/pull/273 by @pepibumur -- Convenient methods to add and fetch build configurations https://github.com/tuist/xcodeproj/pull/283 by @pepibumur -- `.inc` extension to the header file extensions by @pepibumur - -## 5.0.0-rc1 - -### Breaking - -- Rename project to xcodeproj by @pepibumur. -- Drop Carthage and CocoaPods support by @pepibumur. -- Use Basic AbsolutePath, RelativePath and Process extensions by @pepibumur. -- Use `PBXObjectReference` instead of `String` to reference objects from `PBXProj.Objects` by @pepibumur. -- Remove `ObjectReference` by @pepibumur. -- Update `PBXNativeTarget` reference attributes to be of type `PBXObjectReference` by @pepibumur. -- Add convenient methods to materialize objects references https://github.com/tuist/xcodeproj/pull/12 by @pepibumur. -- Rename some PBXProject attributes for consistency https://github.com/tuist/xcodeproj/pull/268 by @pepibumur. - -### Added - -- Add `addDependency` method to `PBXNativeTarget` by @pepibumur. -- Danger check that reports Swiftlint results https://github.com/xcodeswift/xcproj/pull/257 by @pepibumur. -- Xcode constants by @pepibumur. -- Convenient API from objects by @pepibumur. -- `BuildSettingsProvider` by @pepibumur. -- Add `addDependency` method to `PBXNativeTarget` by @pepibumur. -- Method in `XCConfigurationList` to get the build configurations objects @pepibumur. -- Method to get the configuration list from any target https://github.com/tuist/xcodeproj/pull/10 by @pepibumur. -- Migration guidelines https://github.com/tuist/xcodeproj/pull/264 by @pepibumur. - -### Removed - -- Deprecated elements by @pepibumur. -- Tests that test the conformance of `Equatable` by @pepibumur. - -### Fixed - -- XCConfig parser strips the trailing semicolon from a configuration value https://github.com/xcodeswift/xcproj/pull/250 by @briantkelley -- `fullPath(fileElement:reference:sourceRoot:)` now returns the correct path for files that exist within a variant group https://github.com/xcodeswift/xcproj/pull/255 by @ileitch - -### Added - -- Update Danger to warn if the PR title contains WIP https://github.com/xcodeswift/xcproj/pull/259 by @pepibumur. -- Test coverage reports https://github.com/xcodeswift/xcproj/pull/258 by @pepibumur - -## 4.3.0 - -### Added - -- CI pipeline runs also on a Linux environment https://github.com/xcodeswift/xcproj/pull/249 by @pepibumur. -- Auto-generation of Equatable conformances using Sourcery https://github.com/xcodeswift/xcproj/pull/189 @by pepibumur. - -### Fixed - -- Some updates to match the Xcode 9.3 project format https://github.com/xcodeswift/xcproj/pull/247 by @LinusU - -## 4.2.0 - -### Added - -- `PBXNativeTarget.productInstallPath`, `PBXTargetDependency.name` https://github.com/xcodeswift/xcproj/pull/241 by @briantkelley -- `PBXContainerItem` super class of `PBXBuildPhase` and `PBXTarget` https://github.com/xcodeswift/xcproj/pull/243 by @briantkelley -- `PBXFileElement.wrapsLines`property https://github.com/xcodeswift/xcproj/pull/244 by @briantkelley -- `PBXFileReference` `languageSpecificationIdentifier` and `plistStructureDefinitionIdentifier` properties https://github.com/xcodeswift/xcproj/pull/244 by @briantkelley - -### Changed - -- Support for `XCConfig` project-relative includes https://github.com/xcodeswift/xcproj/pull/238 by @briantkelley -- Migrated `PBXProject.projectRoot` to `PBXProject.projectRoots` https://github.com/xcodeswift/xcproj/pull/242 by @briantkelley -- Moved `PBXFileElement.includeInIndex` and `PBXGroup`'s `usesTabs`, `indentWidth`, and `tabWidth` properties to `PBXFileElement` https://github.com/xcodeswift/xcproj/pull/244 by @briantkelley -- `PBXContainerItem` super class of `PBXFileElement` https://github.com/xcodeswift/xcproj/pull/244 by @briantkelley -- `PBXVariantGroup` and `XCVersionGroup` now inherit from `PBXGroup` https://github.com/xcodeswift/xcproj/pull/244 by @briantkelley - -### Fixed - -- `PBXObject.isEqual(to:)` overrides correctly call super https://github.com/xcodeswift/xcproj/pull/239 by @briantkelley -- `PBXAggregateTarget` does not write `buildRules` https://github.com/xcodeswift/xcproj/pull/241 by @briantkelley -- Writes showEnvVarsInLog only when false https://github.com/xcodeswift/xcproj/pull/240 by @briantkelley -- Writes `PBXProject.projectReferences` to the plist https://github.com/xcodeswift/xcproj/pull/242 by @briantkelley -- Comment generation for `PBXProject`, `PBXTarget`, and `PBXVariantGroup` https://github.com/xcodeswift/xcproj/pull/243 by @briantkelley -- `fullPath` now returns the path for a file inside a group without a folder https://github.com/xcodeswift/xcproj/pull/246 by @ileitch -- Quotes strings containing a triple underscore or double forward slash in .pbxproj file https://github.com/xcodeswift/xcproj/pull/245 by @briantkelley - -## 4.1.0 - -### Added - -- Added `tvOS` and `watchOS` Carthage support https://github.com/xcodeswift/xcproj/pull/232 by @yonaskolb -- Added support for scheme environment variables https://github.com/xcodeswift/xcproj/pull/227 by @turekj - -### Fixed - -- Fixed PBXObject sublasses from checking Equatable properly https://github.com/xcodeswift/xcproj/pull/224 by @yonaskolb -- Fix Carthage support https://github.com/xcodeswift/xcproj/pull/226 by @ileitch -- Fix adding file reference to bundle and package files https://github.com/xcodeswift/xcproj/pull/234 by @fuzza -- Fix adding PBXGroup without folder reference https://github.com/xcodeswift/xcproj/pull/235 by @fuzza -- Fixed some more diffs from Xcode https://github.com/xcodeswift/xcproj/pull/233 by @yonaskolb - -### Changed - -- Carthage minimum Deployment Target https://github.com/xcodeswift/xcproj/pull/229 by @olbrichj - -### 4.0.0 - -### Added - -- Added support for scheme pre-actions and post-actions https://github.com/xcodeswift/xcproj/pull/217 by @kastiglione - -### Changed - -- **Breaking:** Changed the return type of some helper functions that create or fetch PBXObjects to be `ObjectReference`, which includes the reference as well as the object https://github.com/xcodeswift/xcproj/pull/218 by @yonaskolb -- **Breaking:** Changed some `Int` properties into `Bool` or `UInt` https://github.com/xcodeswift/xcproj/pull/221 by @yonaskolb -- Changed the writing of some properties to minimise diffs when opening projects in Xcode https://github.com/xcodeswift/xcproj/pull/220 by @yonaskolb - -## 3.0.0 - -### Fixed - -- Fix Xcode 9.2 warning https://github.com/xcodeswift/xcproj/pull/209 by @keith -- macOS CLI targets now have a nil extension, instead of an empty string https://github.com/xcodeswift/xcproj/pull/208 by @keith -- Fix unnecessary quotations in CommentedString https://github.com/xcodeswift/xcproj/pull/211 by @allu22 -- Fixed xml files format not matching Xcode format, added some missing actions attributes. https://github.com/xcodeswift/xcproj/pull/216 by @ilyapuchka - -### Changed - -- **Breaking:** `XCWorkspace.Data` renamed to `XCWorkspaceData` and removed `references`. -- Improved README examples. https://github.com/xcodeswift/xcproj/pull/212 by @ilyapuchka -- Added methods to get paths to workspace, project and breakpoints and shemes files, added public methods to write them separatery. https://github.com/xcodeswift/xcproj/pull/215 by @ilyapuchka -- Added helper methods for adding source file to the project. https://github.com/xcodeswift/xcproj/pull/213 by @ilyapuchka - -## 2.0.0 - -### Added - -- Deterministic reference generation https://github.com/xcodeswift/xcproj/pull/185 by @pepibumur - -### Removed - -- **Breaking Change** `Referenceable` protocol https://github.com/xcodeswift/xcproj/pull/185 by @pepibumur. -- **Breaking Change** Deprecated methods to access objects from the `PBXProj`. Developers should use the `PBXProj.objects` property instead. https://github.com/xcodeswift/xcproj/pull/185 by @pepibumur. - -### Fixed - -- **Breaking:** `PBXSourceTree` no longer has raw values and gained an associated value case to support custom locations https://github.com/xcodeswift/xcproj/pull/198 by @briantkelley - -### Changed - -- **Breaking:** The `buildableProductRunnable` property on`XCScheme.LaunchAction` and `XCScheme.ProfileAction` is now optional. Similarly, `macroExpansion` on `XCScheme.TestAction` is also optional. https://github.com/xcodeswift/xcproj/pull/194 by @briantkelley -- The `XCScheme` initialization from an XML file has been relaxed, better matching Xcode's behavior. Default values will be used if the XML file is missing the relevant element or attribute. https://github.com/xcodeswift/xcproj/pull/194 by @briantkelley - -### Migrate from 1.x.x to 2.x.x - -- If you were using objects getters in `PBXProj` you should use the getters in `PBXProj.objects` instead. -- Objects don't include a `reference` property anymore. Objects associated references are the keys in the dictionary that contains them. -- When objects are added to the `PBXProj.objects` collection a reference needs to be passed. The reference can be calculated using the function `PBXProj.objects.generateReference` that generates a unique and deterministic reference based on the given object and identifier. -- If you were using `buildableProductRunnable` and `macroExpansion` properties from `XCScheme` actions they are now optionals. - -## 1.8.0 - -### Fixed - -- Optimised performance of object lookups https://github.com/xcodeswift/xcproj/pull/191 by @kastiglione - -### Added - -- Add breakpoint `condition` parameter by [@alexruperez](https://github.com/alexruperez). -- Support Xcode Extension product type https://github.com/xcodeswift/xcproj/pull/190 by @briantkelley -- Support for the legacy Build Carbon Resources build phase https://github.com/xcodeswift/xcproj/pull/196 by @briantkelley -- Support for custom build rules by https://github.com/xcodeswift/xcproj/pull/197 @briantkelley - -### Fixed - -- Optimised escaping of CommentedString https://github.com/xcodeswift/xcproj/pull/195 by @kastiglione -- Optimised performance of object lookups https://github.com/xcodeswift/xcproj/pull/191 by @kastiglione -- fixed PBXLegacyTarget write order https://github.com/xcodeswift/xcproj/pull/199 by @kastiglione -- fixed comment generation of PBXBuildFiles without a name https://github.com/xcodeswift/xcproj/pull/203 by @briantkelley -- fixed PBXReferenceTarget encoding in pbxproj file https://github.com/xcodeswift/xcproj/pull/202 by @briantkelley - -## 1.7.0 - -### Added - -- Support more indentation options on PBXGroups https://github.com/xcodeswift/xcproj/pull/168 by @bkase. -- Support `PBXLegacyTarget` https://github.com/xcodeswift/xcproj/pull/171 by @bkase. -- Breakpoint support through `XCBreakpointList`. https://github.com/xcodeswift/xcproj/pull/172 by [@alexruperez](https://github.com/alexruperez) -- Add convenience method to find targets with a given name https://github.com/xcodeswift/xcproj/pull/184 by @pepibumur. -- Danger plugin that fails earlier if files have been added/deleted and the Carthage project hasn't been regenerated afterwards https://github.com/xcodeswift/xcproj/pull/187 by @pepibumur. - -## 1.6.1 - -### Fixed - -- Fix encoded line breaks in PBXFileReference https://github.com/xcodeswift/xcproj/pull/177 by @yonaskolb - -## 1.6.0 - -### Added - -- PBXLegacyTarget support https://github.com/xcodeswift/xcproj/pull/171 by @bkase -- Integration tests https://github.com/xcodeswift/xcproj/pull/168 by @pepibumur -- More examples to the README https://github.com/xcodeswift/xcproj/pull/116 by @pepibumur. -- Add adding / editing command line arguments for Launch, Test and Profile Actions in `XCScheme`. https://github.com/xcodeswift/xcproj/pull/167 by @rahul-malik -- Test the contract with XcodeGen https://github.com/xcodeswift/xcproj/pull/170 by @pepibumur -- Add `PBXProj.Objects.getFileElement` https://github.com/xcodeswift/xcproj/pull/175 by @yonaskolb - -### Fixed - -- `PBXGroup` not generating the comment properly for its children https://github.com/xcodeswift/xcproj/pull/169 by @pepibumur. -- Make `PBXFileElement` a superclass for `PBXFileReference`, `PBXGroup`, and `PBXVariantGroup` https://github.com/xcodeswift/xcproj/pull/173 by @gubikmic -- Added `path` to `PBXVariantGroup` init https://github.com/xcodeswift/xcproj/pull/174 by @yonaskolb - -## 1.5.0 - -### Added - -- Add `codeCoverageEnabled` parameter to `TestAction` https://github.com/xcodeswift/xcproj/pull/166 by @kastiglione -- Make `final` classes that are not extendible https://github.com/xcodeswift/xcproj/pull/164 by @pepibumur. - -### Fixed - -- Fix `PBXProject` `productRefGroup` comment https://github.com/xcodeswift/xcproj/pull/161 by @allu22 -- Fix deprecation warnings for `PBXProj` objects usage https://github.com/xcodeswift/xcproj/pull/162 by @rahul-malik - -## 1.4.0 - Take me out - -### Added - -- Danger integration https://github.com/xcodeswift/xcproj/pull/158 by @pepibumur - -### Changed - -- Improve efficiency of looking up `PBXObject`'s from `PBXProj` https://github.com/xcodeswift/xcproj/pull/136 by @rahul-malik - -### Deprecated - -- `PBXObject` objects accessors https://github.com/xcodeswift/xcproj/pull/136/files#diff-f4369d9af58a6914f0e5cdf81ed18530R6 by @rahul-malik. - -### Fixed - -- Fix `PBXBuildFile` wrongly defaulting the settings attribute when it was nil https://github.com/xcodeswift/xcproj/pull/149 by @allu22 -- Fix `PBXTarget` generating the wrong comment for the `productReference` property https://github.com/xcodeswift/xcproj/pull/151 by @allu22. -- Add missing `usesTabs` property to `PBXGroup` https://github.com/xcodeswift/xcproj/pull/147 by @allu22. -- Fix generated comment for `PBXHeadersBuildPhase` by @allu22. -- Fix wrong `BuidlSettings.swift` file name https://github.com/xcodeswift/xcproj/pull/146 by @allu22. -- Fix `projectReferences` type https://github.com/xcodeswift/xcproj/pull/135 by @solgar. - -### Added - -- Danger checks https://github.com/xcodeswift/xcproj/pull/160 by @pepibumur -- New product type `ocUnitTestBundle` https://github.com/xcodeswift/xcproj/pull/134 by @solgar. - -## 1.3.0 - Esbarzers - -### Added - -- Add `PBXSourceTree.developerDir` type https://github.com/xcodeswift/xcproj/commit/5504fcde00bc56cf6c240ecd7cc36c05296861f8 by @pepibumur. - -### Fixed - -- Fix `PBXShellScriptBuildPhase` bug decoding `showEnvVarsInLog` https://github.com/xcodeswift/xcproj/commit/521b4e62b70f5fc43a06d00c43916d4899138553 by @pepibumur. -- Fix `PBXFileReference` bug decoding `useTabs` https://github.com/xcodeswift/xcproj/commit/c533987496959a3e32c0ddfe45a0f2db8d5daae0 by @pepibumur. -- Fix `PBXFileReference` bug decoding `lineEnding` https://github.com/xcodeswift/xcproj/commit/8a2c94effbe94859a68d58e0c49d66156ba1eaea by @pepibumur. - -## 1.2.0 - Two shoes - -### Added - -- Carthage support https://github.com/xcodeswift/xcproj/pull/125 by @pepibumur. -- `buildPhases` property to `PBXProj` https://github.com/xcodeswift/xcproj/pull/132 by @pepibumur. - -### Fixed - -- Build phase `buildActionMask` wrong default value https://github.com/xcodeswift/xcproj/pull/131 by @pepibumur. - -## 1.1.0 - Muerdo - -### Added - -- It supports now SPM-generated projects https://github.com/xcodeswift/xcproj/pull/124 by @pepibumur. Thanks @josefdolezal for the report. -- Project and workspace initializer that takes the path as a string https://github.com/xcodeswift/xcproj/pull/123 by @pepibumur. - -### Fixed - -- Fix the decoding of the `PBXFileReference.fileEncoding` property https://github.com/xcodeswift/xcproj/pull/127 by @gubikmic. -- Fix some wrong comments and typos https://github.com/xcodeswift/xcproj/pull/126 by @gubikmic - -## 1.0.0 - Acho - -### Changed - -- **Breaking:** Review optionality of attributes to align it with Xcode one https://github.com/xcodeswift/xcproj/pull/107 by @pepibumur. -- Contributing, and code of conduct point to the organization ones by @pepibumur. -- New changelog format introduced by @pepibumur. - -### Fixed - -- Use the super init to decode reference in some objects https://github.com/xcodeswift/xcproj/pull/110 by @yonaskolb -- Schemes being shared with an extension https://github.com/xcodeswift/xcproj/pull/113 by @esttorhe. -- Contributors link in the README.md https://github.com/xcodeswift/xcproj/pull/117 by @tapanprakasht. - -### Security - -## 0.4.1 - -- Add back the `BuildSettings` typelias removed by mistake https://github.com/xcodeswift/xcproj/pull/109 by @pepibumur. -- Fix a bug decoding the `PBXProject.projectRoot` property that should be decoded as an optional https://github.com/xcodeswift/xcproj/issues/108 by @pepibumur. - -## 0.4.0 - -- Remove dependency with Unbox and use the language coding/decoding features https://github.com/xcodeswift/xcproj/pull/99 by @pepibumur and @artemnovichkov. -- Enable xcproj in [Open Collective](https://opencollective.com/xcproj) by @pepibumur. -- Support parsing XCVersionGroup objects https://github.com/xcodeswift/xcproj/pull/96 by @pepibumur. -- Add iOS support to the `.podspec` https://github.com/xcodeswift/xcproj/pull/92 by @pepibumur. -- Fix comment for buildConfigurationList https://github.com/xcodeswift/xcproj/pull/93 by @toshi0383. -- Update `PBXProj` classes property to be a dictionary https://github.com/xcodeswift/xcproj/pull/94 by @toshi0383. -- Fix comment in the `BuildPhase` object https://github.com/xcodeswift/xcproj/pull/95 by @toshi0383. - -## 0.3.0 - -- Turn `PBXVariantGroup` children property into an array https://github.com/xcodeswift/xcproj/pull/88 by @pepibumur -- Add `PBXReferenceProxy` object https://github.com/xcodeswift/xcproj/pull/85 by @pepibumur -- Migrate project to Swift 4 https://github.com/xcodeswift/xcproj/pull/84 by @artemnovichkov -- Fix build phase script error undoer Xcode 9 https://github.com/xcodeswift/xcproj/pull/81 by @kixswift - -## 0.2.0 - -- Add how to use section https://github.com/xcodeswift/xcproj/pull/77 by @pepibumur -- Add contributing guidelines https://github.com/xcodeswift/xcproj/pull/76 by @pepibumur - -## 0.1.2 - -- Update shell build script phase input and output files to be array instead of set https://github.com/xcodeswift/xcproj/issues/65 by @pepibumur -- Fix wrong comment in the shell script build phase https://github.com/xcodeswift/xcproj/issues/67 by @ppeibumur -- Fix wron gcomment in `PBXSourcesBuildPhase` files property https://github.com/xcodeswift/xcproj/issues/68 by @pepibumur -- Add `XCVersionGroup` project element used by Core Data models https://github.com/xcodeswift/xcproj/issues/69 by @pepibumur -- Update `XCConcigurationList` build configurations to be an array https://github.com/xcodeswift/xcproj/issues/70 by @pepibumur - -## 0.1.1 - -- Change `BuildSettings` to `[String: Any]` https://github.com/xcodeswift/xcproj/pull/52 by @yonaskolb -- Plist fixes https://github.com/xcodeswift/xcproj/pull/54 by @yonaskolb - -## 0.1.0 - -- Update struct to classes and clean up API https://github.com/xcodeswift/xcproj/pull/51 by @yonaskolb -- Fix and cleanup strings escaping https://github.com/xcodeswift/xcproj/pull/48 by @yonaskolb -- Add `runOnlyForDeploymentPostprocessing` to `PBXShellScriptBuildPhase` by @yonaskolb -- Remove force unwrap for `XCScheme` https://github.com/xcodeswift/xcproj/pull/39 by @Shakarang - -## 0.0.9 - -- CocoaPods support https://github.com/xcodeswift/xcproj/pull/35 by @pepibumur -- Make project models mutable https://github.com/xcodeswift/xcproj/pull/33 by @yonaskolb - -## 0.0.7 - -- Downgrade Swift Tools versions to 4.0 https://github.com/xcodeswift/xcproj/pull/27 by @yonaskolb -- Make Scheme intializers public https://github.com/xcodeswift/xcproj/pull/28 by @yonaskolb -- Change PBXGroup.children to be an array https://github.com/xcodeswift/xcproj/pull/26 by @yonaskolb -- Make XcodeProj writable https://github.com/xcodeswift/xcproj/pull/20 by @yonaskolb -- Write baseConfigurationReference https://github.com/xcodeswift/xcproj/pull/24 by @yonaskolb -- Convert booleans to YES or NO https://github.com/xcodeswift/xcproj/pull/23 by @yonaskolb -- Make more properties public https://github.com/xcodeswift/xcproj/pull/19 by @yonaskolb - -## 0.0.6 - -- Fix an issue with unescaped strings by @yonaskolb https://github.com/xcodeswift/xcproj/issues/16 -- Update Swift Tools Version to 4.0 https://github.com/xcodeswift/xcproj/commit/f0f5ffe58ce0d29bb986189abf6391c6552fd347 -- Remove CryptoSwift dependency https://github.com/xcodeswift/xcproj/commit/f0f5ffe58ce0d29bb986189abf6391c6552fd347 - -## 0.0.5 - -- Remove `UUID` typealias https://github.com/xcodeswift/xcproj/pull/15 -- Add `UUID` identifier generation from `PBXProj` https://github.com/xcodeswift/xcproj/pull/14 - -## 0.0.4 - -- Writing support for `PBXProj` - https://github.com/xcodeswift/xcproj/pull/8 -- Document RELEASE process https://github.com/xcodeswift/xcproj/pull/7. -- Add documentation https://github.com/xcodeswift/xcproj/pull/6 - -## 0.0.1 - -- First version of the Swift library. -- It supports **reading** and parsing the following models: - xcodeproj. - xcworkspace. - pbxproj. - > This version doesn't support writing yet +# Changelog + +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). + +## [9.13.0] - 2026-06-05 +### Details +#### Features +- Compatibility with the iOS platform by @nmcc24 in [#1016](https://github.com/tuist/XcodeProj/pull/1016) + +## New Contributors +* @nmcc24 made their first contribution in [#1016](https://github.com/tuist/XcodeProj/pull/1016) +## [9.12.0] - 2026-05-05 +### Details +#### Features +- Support new Xcode 16 XCBuildConfiguration format by @johnrbent in [#1037](https://github.com/tuist/XcodeProj/pull/1037) + +## New Contributors +* @johnrbent made their first contribution in [#1037](https://github.com/tuist/XcodeProj/pull/1037) +## [9.11.0] - 2026-04-12 +### Details +#### Features +- Add optional debugAsWhichUser to XCScheme.LaunchAction by @macblazer in [#1100](https://github.com/tuist/XcodeProj/pull/1100) + +## New Contributors +* @macblazer made their first contribution in [#1100](https://github.com/tuist/XcodeProj/pull/1100) +## [9.10.1] - 2026-02-26 +### Details +#### Performance +- Optimized validString in CommentedString by @ChrisBenua in [#1067](https://github.com/tuist/XcodeProj/pull/1067) + +## New Contributors +* @ChrisBenua made their first contribution in [#1067](https://github.com/tuist/XcodeProj/pull/1067) +## [9.10.0] - 2026-02-26 +### Details +#### Features +- Added support for Xcode 26's `dstSubfolder` by @myaumura in [#1038](https://github.com/tuist/XcodeProj/pull/1038) + +## New Contributors +* @myaumura made their first contribution in [#1038](https://github.com/tuist/XcodeProj/pull/1038) +## [9.9.0] - 2026-02-24 +### Details +#### Features +- Add delete support for XCLocalSwiftPackageReference by @Kosikowski in [#1044](https://github.com/tuist/XcodeProj/pull/1044) + +## New Contributors +* @Kosikowski made their first contribution in [#1044](https://github.com/tuist/XcodeProj/pull/1044) +## [9.8.0] - 2026-02-20 +### Details +#### Features +- Add platformFiltersByRelativePath to PBXFileSystemSynchronizedBuildFileExceptionSet by @fortmarek in [#1065](https://github.com/tuist/XcodeProj/pull/1065) + +## [9.7.2] - 2025-12-30 +### Details +#### Documentation +- Add michaelversus as a contributor for code by @allcontributors[bot] in [#1036](https://github.com/tuist/XcodeProj/pull/1036) + +## [9.7.1] - 2025-12-26 +### Details +#### Bug Fixes +- Support for shellScript in the Xcode 16 format by @michaelversus in [#1018](https://github.com/tuist/XcodeProj/pull/1018) + +## New Contributors +* @michaelversus made their first contribution in [#1018](https://github.com/tuist/XcodeProj/pull/1018) +## [9.7.0] - 2025-12-19 +### Details +#### Features +- Handle references for exception sets in Synchronized Groups by @mirkokg in [#1014](https://github.com/tuist/XcodeProj/pull/1014) + +## New Contributors +* @mirkokg made their first contribution in [#1014](https://github.com/tuist/XcodeProj/pull/1014) +* @wojciech-kulik made their first contribution in [#1009](https://github.com/tuist/XcodeProj/pull/1009) +## [9.6.0] - 2025-10-06 +### Details +#### Features +- Speed up object parsing by @karlpuusepp in [#980](https://github.com/tuist/XcodeProj/pull/980) + +## New Contributors +* @karlpuusepp made their first contribution in [#980](https://github.com/tuist/XcodeProj/pull/980) +## [9.5.0] - 2025-08-08 +### Details +#### Features +- Possibility to get child group using path by @ToMark1881 in [#959](https://github.com/tuist/XcodeProj/pull/959) + +## New Contributors +* @ToMark1881 made their first contribution in [#959](https://github.com/tuist/XcodeProj/pull/959) +## [9.4.3] - 2025-06-25 +### Details +#### Bug Fixes +- SWIFT_OPTIMIZATION_LEVEL -O not -Owholemodule by @stefanfessler in [#954](https://github.com/tuist/XcodeProj/pull/954) + +## New Contributors +* @stefanfessler made their first contribution in [#954](https://github.com/tuist/XcodeProj/pull/954) +## [9.4.2] - 2025-05-20 +### Details +#### Bug Fixes +- Align the sorting of project references with Xcode 16 by @mikhailmulyar in [#937](https://github.com/tuist/XcodeProj/pull/937) + +## New Contributors +* @mikhailmulyar made their first contribution in [#937](https://github.com/tuist/XcodeProj/pull/937) +## [9.4.1] - 2025-05-19 +### Details +#### Documentation +- Add mikhailmulyar as a contributor for code by @allcontributors[bot] in [#938](https://github.com/tuist/XcodeProj/pull/938) + +#### Refactor +- Make XCScheme static path functions public by @trentguillory-gc in [#940](https://github.com/tuist/XcodeProj/pull/940) + +## New Contributors +* @trentguillory-gc made their first contribution in [#940](https://github.com/tuist/XcodeProj/pull/940) +## [9.4.0] - 2025-05-15 +### Details +#### Features +- Add .apinotes support by @rw-garmin in [#931](https://github.com/tuist/XcodeProj/pull/931) + +## New Contributors +* @rw-garmin made their first contribution in [#931](https://github.com/tuist/XcodeProj/pull/931) +## [9.3.0] - 2025-04-30 +### Details +#### Features +- Add expectedSignature property by @TamarMilchtaich in [#913](https://github.com/tuist/XcodeProj/pull/913) + +## New Contributors +* @TamarMilchtaich made their first contribution in [#913](https://github.com/tuist/XcodeProj/pull/913) +## [9.2.0] - 2025-04-29 +### Details +#### Features +- Add fileSystemSynchronized properties to public helpers by @Econa77 in [#928](https://github.com/tuist/XcodeProj/pull/928) + +## New Contributors +* @Econa77 made their first contribution in [#928](https://github.com/tuist/XcodeProj/pull/928) +## [9.1.0] - 2025-04-28 +### Details +#### Features +- Implement support for the App Clip's URL by @ns-vasilev in [#927](https://github.com/tuist/XcodeProj/pull/927) + +## New Contributors +* @ns-vasilev made their first contribution in [#927](https://github.com/tuist/XcodeProj/pull/927) +## [9.0.2] - 2025-04-07 +### Details +#### Bug Fixes +- Allow empty `PBXProject.TargetAttributes` by @MouadBenjrinija in [#924](https://github.com/tuist/XcodeProj/pull/924) + +## New Contributors +* @MouadBenjrinija made their first contribution in [#924](https://github.com/tuist/XcodeProj/pull/924) +## [9.0.1] - 2025-04-07 +### Details +#### Bug Fixes +- Add missing global accent color name build setting for apps by @damien-rivet in [#920](https://github.com/tuist/XcodeProj/pull/920) + +#### Documentation +- Add bryansum as a contributor for code by @allcontributors[bot] in [#899](https://github.com/tuist/XcodeProj/pull/899) + +## New Contributors +* @damien-rivet made their first contribution in [#920](https://github.com/tuist/XcodeProj/pull/920) +## [9.0.0] - 2025-03-17 +### Details +#### Refactor +- Strongly typed values in `BuildSettings` and `BuildFileSettings` by @waltflanagan in [#903](https://github.com/tuist/XcodeProj/pull/903) + +## [8.27.7] - 2025-03-14 +### Details +#### Chore +- Update dependency tadija/aexml to from: "4.7.0" by @renovate[bot] in [#912](https://github.com/tuist/XcodeProj/pull/912) + +## [8.27.6] - 2025-03-14 +### Details +#### Miscellaneous Tasks +- Update changelog and renovate configuration by @pepicrft in [#911](https://github.com/tuist/XcodeProj/pull/911) + +## [8.27.0] - 2025-02-18 +### Details +#### Features +- Support `PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet` by @adincebic in [#894](https://github.com/tuist/XcodeProj/pull/894) + +## New Contributors +* @adincebic made their first contribution in [#894](https://github.com/tuist/XcodeProj/pull/894) +## [8.26.8] - 2025-02-18 +### Details +#### Refactor +- Strongly type plist values to achieve full sendability by @waltflanagan in [#904](https://github.com/tuist/XcodeProj/pull/904) + +## [8.26.5] - 2025-01-27 +### Details +#### Bug Fixes +- Ensure that file references are fixed for filesystem synchronized root groups by @bryansum in [#897](https://github.com/tuist/XcodeProj/pull/897) + +## New Contributors +* @bryansum made their first contribution in [#897](https://github.com/tuist/XcodeProj/pull/897) +## [8.26.4] - 2025-01-24 +### Details +#### Bug Fixes +- Add missing BuildSettingsProvider for visionOS by @alexanderwe in [#898](https://github.com/tuist/XcodeProj/pull/898) + +## New Contributors +* @alexanderwe made their first contribution in [#898](https://github.com/tuist/XcodeProj/pull/898) +## [8.26.0] - 2024-12-21 +### Details +#### Features +- Add path to XcodeProj and XCWorkspace by @ajkolean in [#892](https://github.com/tuist/XcodeProj/pull/892) + +## New Contributors +* @ajkolean made their first contribution in [#892](https://github.com/tuist/XcodeProj/pull/892) +## [8.25.1] - 2024-12-19 +### Details +#### Bug Fixes +- Infinite recursion and Incorrect deprecation notice in PathRunnable by @georgenavarro in [#889](https://github.com/tuist/XcodeProj/pull/889) + +## [8.25.0] - 2024-12-03 +### Details +#### Features +- Add handling for Swift Testing Only Parallelization by @kelvinharron in [#871](https://github.com/tuist/XcodeProj/pull/871) + +## New Contributors +* @kelvinharron made their first contribution in [#871](https://github.com/tuist/XcodeProj/pull/871) +## [8.24.13] - 2024-12-03 +### Details +#### Documentation +- Add Speakus as a contributor for code by @allcontributors[bot] in [#887](https://github.com/tuist/XcodeProj/pull/887) + +#### Refactor +- Update PathRunnable so that it subclasses Runnable by @georgenavarro in [#883](https://github.com/tuist/XcodeProj/pull/883) + +## New Contributors +* @georgenavarro made their first contribution in [#883](https://github.com/tuist/XcodeProj/pull/883) +## [8.24.12] - 2024-12-03 +### Details +#### Bug Fixes +- Inconsistent behaviour with Xcode 16 when `PBXProject.TargetAttributes` is empty by @Speakus in [#865](https://github.com/tuist/XcodeProj/pull/865) + +#### Documentation +- Add kelvinharron as a contributor for code by @allcontributors[bot] in [#886](https://github.com/tuist/XcodeProj/pull/886) +- Add georgenavarro as a contributor for code by @allcontributors[bot] in [#885](https://github.com/tuist/XcodeProj/pull/885) + +## New Contributors +* @Speakus made their first contribution in [#865](https://github.com/tuist/XcodeProj/pull/865) +## [8.24.10] - 2024-11-20 +### Details +#### Refactor +- Align ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES with the Xcode default by @yungu0010 in [#881](https://github.com/tuist/XcodeProj/pull/881) + +## New Contributors +* @yungu0010 made their first contribution in [#881](https://github.com/tuist/XcodeProj/pull/881) +## [8.24.2] - 2024-10-10 +### Details +#### Bug Fixes +- Issues parsing Xcode 16 projects by @pepicrft in [#862](https://github.com/tuist/XcodeProj/pull/862) + +## [8.24.1] - 2024-09-27 +### Details +#### Bug Fixes +- Repository cleanup by @pepicrft in [#859](https://github.com/tuist/XcodeProj/pull/859) + +## [8.24.0] - 2024-09-27 +### Details +#### Features +- Make `PBXProject.compatibilityVersion` optional and add `PBXProject.preferredProjectObjectVersion` to support Xcode 16 by @kimdv in [#854](https://github.com/tuist/XcodeProj/pull/854) + +## [8.23.9] - 2024-09-27 +### Details +#### Bug Fixes +- Order for `XCLocalSwiftPackageReference` and `XCRemoteSwiftPackageReference` by @kimdv in [#855](https://github.com/tuist/XcodeProj/pull/855) + +## New Contributors +* @kimdv made their first contribution in [#855](https://github.com/tuist/XcodeProj/pull/855) +## [8.23.8] - 2024-09-26 +### Details +#### Bug Fixes +- Error: ambiguous use of 'arc4random_uniform' on Linux distros by @Howler4695 in [#846](https://github.com/tuist/XcodeProj/pull/846) + +## New Contributors +* @Howler4695 made their first contribution in [#846](https://github.com/tuist/XcodeProj/pull/846) +## [8.23.0] - 2024-08-11 +### Details +#### Documentation +- Add filipracki as a contributor for code by @allcontributors[bot] in [#832](https://github.com/tuist/XcodeProj/pull/832) + +#### Features +- Introduce the new Xcode 16 models `PBXFileSystemSynchronizedRootGroup` and `PBXFileSystemSynchronizedBuildFileExceptionSet` by @pepicrft in [#827](https://github.com/tuist/XcodeProj/pull/827) + +#### Miscellaneous Tasks +- Continuously release releasable changes by @pepicrft in [#842](https://github.com/tuist/XcodeProj/pull/842) +- Disable the renovatebot dashboard by @pepicrft in [#840](https://github.com/tuist/XcodeProj/pull/840) +- Set up SwiftLint and SwiftFormat, run them against the project, and run them as part of the CI workflows by @pepicrft in [#836](https://github.com/tuist/XcodeProj/pull/836) + +## New Contributors +* @filipracki made their first contribution in [#834](https://github.com/tuist/XcodeProj/pull/834) +[9.13.0]: https://github.com/tuist/XcodeProj/compare/9.12.0..9.13.0 +[9.12.0]: https://github.com/tuist/XcodeProj/compare/9.11.0..9.12.0 +[9.11.0]: https://github.com/tuist/XcodeProj/compare/9.10.1..9.11.0 +[9.10.1]: https://github.com/tuist/XcodeProj/compare/9.10.0..9.10.1 +[9.10.0]: https://github.com/tuist/XcodeProj/compare/9.9.0..9.10.0 +[9.9.0]: https://github.com/tuist/XcodeProj/compare/9.8.0..9.9.0 +[9.8.0]: https://github.com/tuist/XcodeProj/compare/9.7.2..9.8.0 +[9.7.2]: https://github.com/tuist/XcodeProj/compare/9.7.1..9.7.2 +[9.7.1]: https://github.com/tuist/XcodeProj/compare/9.7.0..9.7.1 +[9.7.0]: https://github.com/tuist/XcodeProj/compare/9.6.0..9.7.0 +[9.6.0]: https://github.com/tuist/XcodeProj/compare/9.5.0..9.6.0 +[9.5.0]: https://github.com/tuist/XcodeProj/compare/9.4.3..9.5.0 +[9.4.3]: https://github.com/tuist/XcodeProj/compare/9.4.2..9.4.3 +[9.4.2]: https://github.com/tuist/XcodeProj/compare/9.4.1..9.4.2 +[9.4.1]: https://github.com/tuist/XcodeProj/compare/9.4.0..9.4.1 +[9.4.0]: https://github.com/tuist/XcodeProj/compare/9.3.0..9.4.0 +[9.3.0]: https://github.com/tuist/XcodeProj/compare/9.2.0..9.3.0 +[9.2.0]: https://github.com/tuist/XcodeProj/compare/9.1.0..9.2.0 +[9.1.0]: https://github.com/tuist/XcodeProj/compare/9.0.2..9.1.0 +[9.0.2]: https://github.com/tuist/XcodeProj/compare/9.0.1..9.0.2 +[9.0.1]: https://github.com/tuist/XcodeProj/compare/9.0.0..9.0.1 +[9.0.0]: https://github.com/tuist/XcodeProj/compare/8.27.7..9.0.0 +[8.27.7]: https://github.com/tuist/XcodeProj/compare/8.27.6..8.27.7 +[8.27.6]: https://github.com/tuist/XcodeProj/compare/8.27.5..8.27.6 +[8.27.0]: https://github.com/tuist/XcodeProj/compare/8.26.8..8.27.0 +[8.26.8]: https://github.com/tuist/XcodeProj/compare/8.26.7..8.26.8 +[8.26.5]: https://github.com/tuist/XcodeProj/compare/8.26.4..8.26.5 +[8.26.4]: https://github.com/tuist/XcodeProj/compare/8.26.3..8.26.4 +[8.26.0]: https://github.com/tuist/XcodeProj/compare/8.25.1..8.26.0 +[8.25.1]: https://github.com/tuist/XcodeProj/compare/8.25.0..8.25.1 +[8.25.0]: https://github.com/tuist/XcodeProj/compare/8.24.13..8.25.0 +[8.24.13]: https://github.com/tuist/XcodeProj/compare/8.24.12..8.24.13 +[8.24.12]: https://github.com/tuist/XcodeProj/compare/8.24.11..8.24.12 +[8.24.10]: https://github.com/tuist/XcodeProj/compare/8.24.9..8.24.10 +[8.24.2]: https://github.com/tuist/XcodeProj/compare/8.24.1..8.24.2 +[8.24.1]: https://github.com/tuist/XcodeProj/compare/8.24.0..8.24.1 +[8.24.0]: https://github.com/tuist/XcodeProj/compare/8.23.11..8.24.0 +[8.23.9]: https://github.com/tuist/XcodeProj/compare/8.23.8..8.23.9 +[8.23.8]: https://github.com/tuist/XcodeProj/compare/8.23.7..8.23.8 +[8.23.0]: https://github.com/tuist/XcodeProj/compare/8.22.0..8.23.0 + + +Check out [GitHub releases](https://github.com/tuist/XcodeProj/releases) for older releases. + + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1ca7fcff6..a74f7e61f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1 +1,128 @@ -Check out the organization [CODE OF CONDUCT](https://github.com/tuist/tuist/blob/master/docs/contribution/code-of-conduct.md) +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +oss@tuist.io. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 42e0a4aa7..000000000 --- a/Cartfile +++ /dev/null @@ -1,2 +0,0 @@ -github "tuist/PathKit" == 1.0.0 -github "tadija/AEXML" == 4.4.0 diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index 1f8d154c6..000000000 --- a/Cartfile.resolved +++ /dev/null @@ -1,2 +0,0 @@ -github "tadija/AEXML" "4.4.0" -github "tuist/PathKit" "1.0.0" diff --git a/Documentation/getting-started.md b/Documentation/getting-started.md index 26d6512e9..384fddbc7 100644 --- a/Documentation/getting-started.md +++ b/Documentation/getting-started.md @@ -4,26 +4,6 @@ If you want to use xcodeproj in your projects, you need to add the library as a ## 1. Adding the dependency/package -### CocoaPods - -If you would like to integrate xcodeproj into your project using CocoaPods, you only need to add the following line to your `Podfile`: - -```ruby -pod 'xcodeproj' -``` - -After that, you can run `pod install` *(or `bundle install` if you are using Bundler for managing your gems)* and you should get the latest version of xcodeproj integrated into your project. - -### Carthage - -If you are using Carthage instead, you can add the following file to the project `Cartfile`: - -```bash -github "tuist/xcodeproj" -``` - -Then run `carthage update`. It'll pull the latest version available and compile a dynamic framework that you can link from your apps. - ### Swift Package Manager If you are developing a package instead *(with the SwiftPM)* you can integrate xcodeproj by adding the following line to the dependencies list of your `Package.swift`: @@ -57,7 +37,11 @@ import PathKit import XcodeProj let path = Path("/path/to/my/Project.xcodeproj") // Your project path -let xcodeproj = XcodeProj(path: path) +do { + let xcodeproj = try XcodeProj(path: path) +} catch { + print(error) +} ``` xcodeproj will parse and map the project structure into Swift classes that you can interact with. @@ -68,7 +52,7 @@ We the project already in memory, we are going to output all the targets that ar ```swift let pbxproj = xcodeproj.pbxproj // Returns a PBXProj -pbxproj.nativeTargets.each { target in +pbxproj.nativeTargets.forEach { target in print(target.name) } ``` @@ -104,4 +88,4 @@ try xcodeproj.write(path: path) If something goes wrong during the project writing, `write` will throw an error. In most cases, writing issues are related to misconfigured projects. For that reason it's important that you understand the modifications that we are introducing to your projects. -**Bear in mind that xcodeproj makes the process of configuring Xcode project more convenient, but doesn't prevent you from having to read and understand the Xcode project structure** \ No newline at end of file +**Bear in mind that xcodeproj makes the process of configuring Xcode project more convenient, but doesn't prevent you from having to read and understand the Xcode project structure** diff --git a/Documentation/migration-guides.md b/Documentation/migration-guides.md index c96c03259..0f256aec4 100644 --- a/Documentation/migration-guides.md +++ b/Documentation/migration-guides.md @@ -8,13 +8,14 @@ This improvement makes the API easier, safer and more convenient, but at the cos - `PBXObjectReference` is an internal class now. Object references to other objects are attributes with the type of the object that is being referred. For example, a `XCConfigurationList` object has an attribute `buildConfigurations` of type `XCBuildConfiguration`. Adding a new configuration is as easy as calling `list.buildConfigurations.append(config)`. - Note that object references have different types of optionals based on the type of attribute: - - **Implicitly unwrapped optional:** When the attribute is required by Xcode. [Example](https://github.com/tuist/xcodeproj/blob/master/Sources/XcodeProj/Objects/Project/PBXProject.swift#L38) - - **Explicitly unwrapped optional:** When the attribute is optional by Xcode. [Example](https://github.com/tuist/xcodeproj/blob/master/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift#L11) -- `PBXObjects` has also been made internal. It was exposed through the attribute `objects` on the `PBXProj` class. If you used to use this class for adding, removing, or getting objects, those methods have been moved to the `PBXProj` class - [Public helpers](https://github.com/tuist/xcodeproj/blob/master/Sources/XcodeProj/Objects/Project/PBXProj.swift#L85) + - **Implicitly unwrapped optional:** When the attribute is required by Xcode. [Example](https://github.com/tuist/xcodeproj/blob/main/Sources/XcodeProj/Objects/Project/PBXProject.swift#L38) + - **Explicitly unwrapped optional:** When the attribute is optional by Xcode. [Example](https://github.com/tuist/xcodeproj/blob/main/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift#L11) +- `PBXObjects` has also been made internal. It was exposed through the attribute `objects` on the `PBXProj` class. If you used to use this class for adding, removing, or getting objects, those methods have been moved to the `PBXProj` class - [Public helpers](https://github.com/tuist/xcodeproj/blob/main/Sources/XcodeProj/Objects/Project/PBXProj.swift#L85) **And yes, in case you are wondering, it fully supports Xcode 10 🎉** ## To xcodeproj 5 + `xcodeproj` 5 is a major release with important changes in the API focused on making it more convenient, and simplify the references handling. This version hasn't been officially released yet but you can already start updating your project for the new version. These are the changes you'd need to make in your projects: - `xcproj` has been renamed to `xcodeproj` so you need to update all your import statements to use the new name. @@ -22,7 +23,7 @@ This improvement makes the API easier, safer and more convenient, but at the cos - We've replaced `Path` with `AbsolutePath` and `RelativePath` from the Swift Package Manager's `Basic` framework. You might need to change some of the usages to use the new type. - Reference attributes have been renamed to use the naming convention `attributeReference` where `attribute` is the name of the attribute. If you are interested in materializing the reference to get the object, objects provide convenient getters that you can use for that purpose. Those getters throw if the object is not found in the project. -There are some useful additions to the API that you can check out on the [CHANGELOG](https://github.com/tuist/xcodeproj/blob/master/CHANGELOG.md). +There are some useful additions to the API that you can check out on the [CHANGELOG](https://github.com/tuist/xcodeproj/blob/main/CHANGELOG.md). One of those additions is an improvement on how references are managed. - When new objects are added to the project, you get the object reference. The reference is an instance that should be used to refer that object from any other. **The value of that reference is an implementation detail that has been abstracted away from you.** +When new objects are added to the project, you get the object reference. The reference is an instance that should be used to refer that object from any other. **The value of that reference is an implementation detail that has been abstracted away from you.** diff --git a/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.pbxproj b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.pbxproj new file mode 100644 index 000000000..e5059b59e --- /dev/null +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.pbxproj @@ -0,0 +1,462 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 6C103C032A49CC5400D7EFE4 /* FileSharedAcrossTargets.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */; }; + 6C103C082A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C103C072A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift */; }; + 6C103C092A49CC5400D7EFE4 /* FileSharedAcrossTargets.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C103BFD2A49CC5300D7EFE4 /* FileSharedAcrossTargets.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6C103C132A49CC7300D7EFE4 /* SharedHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C103C122A49CC7300D7EFE4 /* SharedHeader.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6C103C042A49CC5400D7EFE4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6C103BF12A49CC5300D7EFE4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6C103BF92A49CC5300D7EFE4; + remoteInfo = FileSharedAcrossTargets; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileSharedAcrossTargets.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C103BFD2A49CC5300D7EFE4 /* FileSharedAcrossTargets.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileSharedAcrossTargets.h; sourceTree = ""; }; + 6C103C022A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FileSharedAcrossTargetsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C103C072A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSharedAcrossTargetsTests.swift; sourceTree = ""; }; + 6C103C122A49CC7300D7EFE4 /* SharedHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SharedHeader.h; sourceTree = ""; }; + 6CB965012A49DC1F009186C6 /* FileSharedAcrossTargetsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSharedAcrossTargetsTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6C103BF72A49CC5300D7EFE4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6C103BFF2A49CC5400D7EFE4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C103C032A49CC5400D7EFE4 /* FileSharedAcrossTargets.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6C103BF02A49CC5300D7EFE4 = { + isa = PBXGroup; + children = ( + 6C103BFC2A49CC5300D7EFE4 /* FileSharedAcrossTargets */, + 6C103C062A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests */, + 6C103BFB2A49CC5300D7EFE4 /* Products */, + ); + sourceTree = ""; + }; + 6C103BFB2A49CC5300D7EFE4 /* Products */ = { + isa = PBXGroup; + children = ( + 6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */, + 6C103C022A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6C103BFC2A49CC5300D7EFE4 /* FileSharedAcrossTargets */ = { + isa = PBXGroup; + children = ( + 6C103BFD2A49CC5300D7EFE4 /* FileSharedAcrossTargets.h */, + 6C103C122A49CC7300D7EFE4 /* SharedHeader.h */, + ); + path = FileSharedAcrossTargets; + sourceTree = ""; + }; + 6C103C062A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests */ = { + isa = PBXGroup; + children = ( + 6CB965012A49DC1F009186C6 /* FileSharedAcrossTargetsTests.swift */, + 6C103C072A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift */, + ); + path = FileSharedAcrossTargetsTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6C103BF52A49CC5300D7EFE4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C103C132A49CC7300D7EFE4 /* SharedHeader.h in Headers */, + 6C103C092A49CC5400D7EFE4 /* FileSharedAcrossTargets.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6C103BF92A49CC5300D7EFE4 /* FileSharedAcrossTargets */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6C103C0C2A49CC5400D7EFE4 /* Build configuration list for PBXNativeTarget "FileSharedAcrossTargets" */; + buildPhases = ( + 6C103BF52A49CC5300D7EFE4 /* Headers */, + 6C103BF62A49CC5300D7EFE4 /* Sources */, + 6C103BF72A49CC5300D7EFE4 /* Frameworks */, + 6C103BF82A49CC5300D7EFE4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FileSharedAcrossTargets; + productName = FileSharedAcrossTargets; + productReference = 6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */; + productType = "com.apple.product-type.framework"; + }; + 6C103C012A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6C103C0F2A49CC5400D7EFE4 /* Build configuration list for PBXNativeTarget "FileSharedAcrossTargetsTests" */; + buildPhases = ( + 6C103BFE2A49CC5400D7EFE4 /* Sources */, + 6C103BFF2A49CC5400D7EFE4 /* Frameworks */, + 6C103C002A49CC5400D7EFE4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6C103C052A49CC5400D7EFE4 /* PBXTargetDependency */, + ); + name = FileSharedAcrossTargetsTests; + productName = FileSharedAcrossTargetsTests; + productReference = 6C103C022A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6C103BF12A49CC5300D7EFE4 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 6C103BF92A49CC5300D7EFE4 = { + CreatedOnToolsVersion = 14.3.1; + }; + 6C103C012A49CC5400D7EFE4 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 6C103BF42A49CC5300D7EFE4 /* Build configuration list for PBXProject "FileSharedAcrossTargets" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6C103BF02A49CC5300D7EFE4; + productRefGroup = 6C103BFB2A49CC5300D7EFE4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6C103BF92A49CC5300D7EFE4 /* FileSharedAcrossTargets */, + 6C103C012A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6C103BF82A49CC5300D7EFE4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6C103C002A49CC5400D7EFE4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6C103BF62A49CC5300D7EFE4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6C103BFE2A49CC5400D7EFE4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C103C082A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6C103C052A49CC5400D7EFE4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6C103BF92A49CC5300D7EFE4 /* FileSharedAcrossTargets */; + targetProxy = 6C103C042A49CC5400D7EFE4 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 6C103C0A2A49CC5400D7EFE4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6C103C0B2A49CC5400D7EFE4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6C103C0D2A49CC5400D7EFE4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj.FileSharedAcrossTargets; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6C103C0E2A49CC5400D7EFE4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj.FileSharedAcrossTargets; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6C103C102A49CC5400D7EFE4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj.FileSharedAcrossTargetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6C103C112A49CC5400D7EFE4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj.FileSharedAcrossTargetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6C103BF42A49CC5300D7EFE4 /* Build configuration list for PBXProject "FileSharedAcrossTargets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C103C0A2A49CC5400D7EFE4 /* Debug */, + 6C103C0B2A49CC5400D7EFE4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6C103C0C2A49CC5400D7EFE4 /* Build configuration list for PBXNativeTarget "FileSharedAcrossTargets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C103C0D2A49CC5400D7EFE4 /* Debug */, + 6C103C0E2A49CC5400D7EFE4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6C103C0F2A49CC5400D7EFE4 /* Build configuration list for PBXNativeTarget "FileSharedAcrossTargetsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C103C102A49CC5400D7EFE4 /* Debug */, + 6C103C112A49CC5400D7EFE4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6C103BF12A49CC5300D7EFE4 /* Project object */; +} diff --git a/XcodeProj_Carthage.xcworkspace/contents.xcworkspacedata b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 66% rename from XcodeProj_Carthage.xcworkspace/contents.xcworkspacedata rename to Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/contents.xcworkspacedata index fefc83e8a..919434a62 100644 --- a/XcodeProj_Carthage.xcworkspace/contents.xcworkspacedata +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/XcodeProj_Carthage.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from XcodeProj_Carthage.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..891ed6fd3 --- /dev/null +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + FileSharedAcrossTargets.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/FileSharedAcrossTargets.h b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/FileSharedAcrossTargets.h new file mode 100644 index 000000000..ca50f46c0 --- /dev/null +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/FileSharedAcrossTargets.h @@ -0,0 +1,7 @@ +#import + +//! Project version number for FileSharedAcrossTargets. +FOUNDATION_EXPORT double FileSharedAcrossTargetsVersionNumber; + +//! Project version string for FileSharedAcrossTargets. +FOUNDATION_EXPORT const unsigned char FileSharedAcrossTargetsVersionString[]; diff --git a/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/SharedHeader.h b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/SharedHeader.h new file mode 100644 index 000000000..0a277a70f --- /dev/null +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/SharedHeader.h @@ -0,0 +1,5 @@ +#ifndef SharedHeader_h +#define SharedHeader_h + + +#endif /* SharedHeader_h */ diff --git a/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargetsTests/FileSharedAcrossTargetsTests.swift b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargetsTests/FileSharedAcrossTargetsTests.swift new file mode 100644 index 000000000..5b957f983 --- /dev/null +++ b/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargetsTests/FileSharedAcrossTargetsTests.swift @@ -0,0 +1,27 @@ +import XCTest +@testable import FileSharedAcrossTargets + +final class FileSharedAcrossTargetsTests: XCTestCase { + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } +} diff --git a/Fixtures/Schemes/AppClip.xcscheme b/Fixtures/Schemes/AppClip.xcscheme new file mode 100644 index 000000000..0fcd6b540 --- /dev/null +++ b/Fixtures/Schemes/AppClip.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/Schemes/BuildArchitectures.xcscheme b/Fixtures/Schemes/BuildArchitectures.xcscheme new file mode 100644 index 000000000..675e26d27 --- /dev/null +++ b/Fixtures/Schemes/BuildArchitectures.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme b/Fixtures/Schemes/DebugAsRoot.xcscheme similarity index 65% rename from XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme rename to Fixtures/Schemes/DebugAsRoot.xcscheme index 47588195d..17c540ce6 100644 --- a/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme +++ b/Fixtures/Schemes/DebugAsRoot.xcscheme @@ -1,10 +1,11 @@ + LastUpgradeVersion = "2640" + version = "1.7"> + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + ReferencedContainer = "container:"> @@ -29,10 +30,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - + allowLocationSimulation = "YES" + debugAsWhichUser = "root"> + + BlueprintIdentifier = "5CC725022DA91FB6004D43D4" + BuildableName = "app_clip.app" + BlueprintName = "app_clip" + ReferencedContainer = "container:example.xcodeproj"> - - - - - - - + + ReferencedContainer = "container:"> diff --git a/Fixtures/Schemes/NoBlueprintID.xcscheme b/Fixtures/Schemes/NoBlueprintID.xcscheme new file mode 100644 index 000000000..ef67cbfcf --- /dev/null +++ b/Fixtures/Schemes/NoBlueprintID.xcscheme @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/Schemes/RunPostActionsOnFailure.xcscheme b/Fixtures/Schemes/RunPostActionsOnFailure.xcscheme new file mode 100644 index 000000000..995928c83 --- /dev/null +++ b/Fixtures/Schemes/RunPostActionsOnFailure.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/Schemes/RunnableWithoutBuildableReference.xcscheme b/Fixtures/Schemes/RunnableWithoutBuildableReference.xcscheme new file mode 100644 index 000000000..edc29a307 --- /dev/null +++ b/Fixtures/Schemes/RunnableWithoutBuildableReference.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/Schemes/xcschememanagement.plist b/Fixtures/Schemes/xcschememanagement.plist new file mode 100644 index 000000000..fa9e03037 --- /dev/null +++ b/Fixtures/Schemes/xcschememanagement.plist @@ -0,0 +1,48 @@ + + + + + SchemeUserState + + App.xcscheme + + isShown + + orderHint + 0 + + Test 0.xcscheme + + orderHint + 3 + + Test 1.xcscheme + + isShown + + orderHint + 4 + + Tuist.xcscheme_^#shared#^_ + + isShown + + orderHint + 1 + + XcodeProj.xcscheme + + orderHint + 2 + + + SuppressBuildableAutocreation + + E525238B16245A900012E2BA + + primary + + + + + diff --git a/Fixtures/SynchronizedRootGroups/.gitignore b/Fixtures/SynchronizedRootGroups/.gitignore new file mode 100644 index 000000000..9e69ba792 --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/.gitignore @@ -0,0 +1 @@ +*.xcodeproj/xcuserdata diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj new file mode 100644 index 000000000..a8db250fe --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj @@ -0,0 +1,387 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 73; + objects = { + +/* Begin PBXCopyFilesBuildPhase section */ + F841A9CA2D63AFBB00059ED6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SynchronizedRootGroups.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Exception/Exception.swift, + ); + target = 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = { + isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + XPCService.xpc = ( + RemoveHeadersOnCopy, + ); + }; + buildPhase = F841A9CA2D63AFBB00059ED6 /* CopyFiles */; + membershipExceptions = ( + XPCService.xpc, + ); + }; +/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6CF05B892C53F5F200EF267F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6CF05B822C53F5F200EF267F = { + isa = PBXGroup; + children = ( + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */, + 6CF05B8D2C53F5F200EF267F /* Products */, + ); + sourceTree = ""; + }; + 6CF05B8D2C53F5F200EF267F /* Products */ = { + isa = PBXGroup; + children = ( + 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6CF05B872C53F5F200EF267F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6CF05B922C53F5F200EF267F /* Build configuration list for PBXNativeTarget "SynchronizedRootGroups" */; + buildPhases = ( + 6CF05B872C53F5F200EF267F /* Headers */, + 6CF05B882C53F5F200EF267F /* Sources */, + 6CF05B892C53F5F200EF267F /* Frameworks */, + 6CF05B8A2C53F5F200EF267F /* Resources */, + F841A9CA2D63AFBB00059ED6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */, + ); + name = SynchronizedRootGroups; + packageProductDependencies = ( + ); + productName = SynchronizedRootGroups; + productReference = 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6CF05B832C53F5F200EF267F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1600; + TargetAttributes = { + 6CF05B8B2C53F5F200EF267F = { + CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; + }; + }; + }; + buildConfigurationList = 6CF05B862C53F5F200EF267F /* Build configuration list for PBXProject "SynchronizedRootGroups" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6CF05B822C53F5F200EF267F; + preferredProjectObjectVersion = 60; + productRefGroup = 6CF05B8D2C53F5F200EF267F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6CF05B8A2C53F5F200EF267F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6CF05B882C53F5F200EF267F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6CF05B932C53F5F200EF267F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.SynchronizedRootGroups; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6CF05B942C53F5F200EF267F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.SynchronizedRootGroups; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6CF05B952C53F5F200EF267F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6CF05B962C53F5F200EF267F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6CF05B862C53F5F200EF267F /* Build configuration list for PBXProject "SynchronizedRootGroups" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CF05B952C53F5F200EF267F /* Debug */, + 6CF05B962C53F5F200EF267F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6CF05B922C53F5F200EF267F /* Build configuration list for PBXNativeTarget "SynchronizedRootGroups" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CF05B932C53F5F200EF267F /* Debug */, + 6CF05B942C53F5F200EF267F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6CF05B832C53F5F200EF267F /* Project object */; +} diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..e04d3354a --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + SynchronizedRootGroups.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift new file mode 100644 index 000000000..190cf0a74 --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift @@ -0,0 +1,6 @@ +// +// Exception.swift +// SynchronizedRootGroups +// +// Created by Pedro Piñera Buendía on 26.07.24. +// diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift new file mode 100644 index 000000000..9286b7807 --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift @@ -0,0 +1,6 @@ +// +// SynchronizedRootGroups.swift +// SynchronizedRootGroups +// +// Created by Pedro Piñera Buendía on 26.07.24. +// diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.pbxproj b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.pbxproj new file mode 100644 index 000000000..16c682fb8 --- /dev/null +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.pbxproj @@ -0,0 +1,332 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 6CAD68172A56E30800662D8A /* TargetWithCustomBuildRules.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CAD68162A56E30800662D8A /* TargetWithCustomBuildRules.h */; }; + 6CAD68192A56E30800662D8A /* TargetWithCustomBuildRules.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CAD68182A56E30800662D8A /* TargetWithCustomBuildRules.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 6CAD681F2A56E30C00662D8A /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + name = "Custom 1"; + outputFiles = ( + ); + script = "# Type a script or drag a script file from your workspace to insert its path.\n"; + }; + 6CAD68202A56E31400662D8A /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + dependencyFile = "$(DERIVED_FILES_DIR)/$(INPUT_FILE_PATH).d"; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + name = "Custom 2 with dependency file"; + outputFiles = ( + ); + script = "# Type a script or drag a script file from your workspace to insert its path.\n"; + }; + 6CAD68212A56E9CE00662D8A /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + fileType = file.skybox; + inputFiles = ( + ); + isEditable = 1; + outputFiles = ( + ); + script = "# rctool\n"; + }; +/* End PBXBuildRule section */ + +/* Begin PBXFileReference section */ + 6CAD68132A56E30800662D8A /* libTargetWithCustomBuildRules.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTargetWithCustomBuildRules.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6CAD68162A56E30800662D8A /* TargetWithCustomBuildRules.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TargetWithCustomBuildRules.h; sourceTree = ""; }; + 6CAD68182A56E30800662D8A /* TargetWithCustomBuildRules.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TargetWithCustomBuildRules.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6CAD68112A56E30800662D8A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6CAD680A2A56E30800662D8A = { + isa = PBXGroup; + children = ( + 6CAD68152A56E30800662D8A /* TargetWithCustomBuildRules */, + 6CAD68142A56E30800662D8A /* Products */, + ); + sourceTree = ""; + }; + 6CAD68142A56E30800662D8A /* Products */ = { + isa = PBXGroup; + children = ( + 6CAD68132A56E30800662D8A /* libTargetWithCustomBuildRules.a */, + ); + name = Products; + sourceTree = ""; + }; + 6CAD68152A56E30800662D8A /* TargetWithCustomBuildRules */ = { + isa = PBXGroup; + children = ( + 6CAD68162A56E30800662D8A /* TargetWithCustomBuildRules.h */, + 6CAD68182A56E30800662D8A /* TargetWithCustomBuildRules.m */, + ); + path = TargetWithCustomBuildRules; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6CAD680F2A56E30800662D8A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6CAD68172A56E30800662D8A /* TargetWithCustomBuildRules.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6CAD68122A56E30800662D8A /* TargetWithCustomBuildRules */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6CAD681C2A56E30800662D8A /* Build configuration list for PBXNativeTarget "TargetWithCustomBuildRules" */; + buildPhases = ( + 6CAD680F2A56E30800662D8A /* Headers */, + 6CAD68102A56E30800662D8A /* Sources */, + 6CAD68112A56E30800662D8A /* Frameworks */, + ); + buildRules = ( + 6CAD68212A56E9CE00662D8A /* PBXBuildRule */, + 6CAD68202A56E31400662D8A /* PBXBuildRule */, + 6CAD681F2A56E30C00662D8A /* PBXBuildRule */, + ); + dependencies = ( + ); + name = TargetWithCustomBuildRules; + productName = TargetWithCustomBuildRules; + productReference = 6CAD68132A56E30800662D8A /* libTargetWithCustomBuildRules.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6CAD680B2A56E30800662D8A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1500; + TargetAttributes = { + 6CAD68122A56E30800662D8A = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = 6CAD680E2A56E30800662D8A /* Build configuration list for PBXProject "TargetWithCustomBuildRules" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6CAD680A2A56E30800662D8A; + productRefGroup = 6CAD68142A56E30800662D8A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6CAD68122A56E30800662D8A /* TargetWithCustomBuildRules */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 6CAD68102A56E30800662D8A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6CAD68192A56E30800662D8A /* TargetWithCustomBuildRules.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6CAD681A2A56E30800662D8A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6CAD681B2A56E30800662D8A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 6CAD681D2A56E30800662D8A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 6CAD681E2A56E30800662D8A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6CAD680E2A56E30800662D8A /* Build configuration list for PBXProject "TargetWithCustomBuildRules" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CAD681A2A56E30800662D8A /* Debug */, + 6CAD681B2A56E30800662D8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6CAD681C2A56E30800662D8A /* Build configuration list for PBXNativeTarget "TargetWithCustomBuildRules" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CAD681D2A56E30800662D8A /* Debug */, + 6CAD681E2A56E30800662D8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6CAD680B2A56E30800662D8A /* Project object */; +} diff --git a/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 61% rename from XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 94b2795e2..919434a62 100644 --- a/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,4 +1,7 @@ + + diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 000000000..34d12db6d Binary files /dev/null and b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..9278c11ca --- /dev/null +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + TargetWithCustomBuildRules.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.h b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.h new file mode 100644 index 000000000..90fcbc85a --- /dev/null +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.h @@ -0,0 +1,12 @@ +// +// TargetWithCustomBuildRules.h +// TargetWithCustomBuildRules +// +// Created by Pedro Piñera Buendía on 06.07.23. +// + +#import + +@interface TargetWithCustomBuildRules : NSObject + +@end diff --git a/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.m b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.m new file mode 100644 index 000000000..3728f76f1 --- /dev/null +++ b/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.m @@ -0,0 +1,12 @@ +// +// TargetWithCustomBuildRules.m +// TargetWithCustomBuildRules +// +// Created by Pedro Piñera Buendía on 06.07.23. +// + +#import "TargetWithCustomBuildRules.h" + +@implementation TargetWithCustomBuildRules + +@end diff --git a/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/project.pbxproj b/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/project.pbxproj index c49e88a39..d364afeda 100644 --- a/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/project.pbxproj +++ b/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/project.pbxproj @@ -359,7 +359,7 @@ "watchsimulator", ); SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; USE_HEADERMAP = "NO"; }; name = "Release"; diff --git a/Fixtures/WorkspaceSettings/OriginalAbsoluteDerivedData.xcsettings b/Fixtures/WorkspaceSettings/OriginalAbsoluteDerivedData.xcsettings new file mode 100644 index 000000000..a8063b134 --- /dev/null +++ b/Fixtures/WorkspaceSettings/OriginalAbsoluteDerivedData.xcsettings @@ -0,0 +1,12 @@ + + + + + BuildSystemType + Original + DerivedDataCustomLocation + /User/xcodeproj/DerivedData + DerivedDataLocationStyle + AbsolutePath + + diff --git a/Fixtures/WorkspaceSettings/OriginalRelativeDerivedData.xcsettings b/Fixtures/WorkspaceSettings/OriginalRelativeDerivedData.xcsettings new file mode 100644 index 000000000..236929fd8 --- /dev/null +++ b/Fixtures/WorkspaceSettings/OriginalRelativeDerivedData.xcsettings @@ -0,0 +1,12 @@ + + + + + BuildSystemType + Original + DerivedDataCustomLocation + CustomizedDerivedData + DerivedDataLocationStyle + WorkspaceRelativePath + + diff --git a/Fixtures/XCConfigs/Children.xcconfig b/Fixtures/XCConfigs/Children.xcconfig index fca8692d3..de28539ca 100644 --- a/Fixtures/XCConfigs/Children.xcconfig +++ b/Fixtures/XCConfigs/Children.xcconfig @@ -1,5 +1,5 @@ #include "Parent.xcconfig" -CONFIGURATION_BUILD_DIR = Test/ +CONFIGURATION_BUILD_DIR = Test/ // NOTE: Test comment line to check several slashes GCC_PREPROCESSOR_DEFINITIONS = $(inherited) -WARNING_CFLAGS = -Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded \ No newline at end of file +WARNING_CFLAGS = -Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded diff --git a/Fixtures/XCConfigs/Parent.xcconfig b/Fixtures/XCConfigs/Parent.xcconfig index d70cdb84d..796528af6 100644 --- a/Fixtures/XCConfigs/Parent.xcconfig +++ b/Fixtures/XCConfigs/Parent.xcconfig @@ -1,2 +1,5 @@ +// NOTE: Top level comment OTHER_SWIFT_FLAGS_XCODE_0821 = $(inherited) -OTHER_SWIFT_FLAGS_XCODE_0830 = $(inherited) -enable-bridging-pch \ No newline at end of file +OTHER_SWIFT_FLAGS_XCODE_0830 = $(inherited) -enable-bridging-pch +PRODUCT_NAME = $(TARGET_NAME) // NOTE: Test Comment +SWIFT_OPTIMIZATION_LEVEL = -Onone// Edge-case when a comment has no space diff --git a/Fixtures/Xcode16/README.md b/Fixtures/Xcode16/README.md new file mode 100644 index 000000000..e1673acd1 --- /dev/null +++ b/Fixtures/Xcode16/README.md @@ -0,0 +1,3 @@ +# Xcode 16 project + +Xcode 16 introduced some changes in Xcode projects, like [this one](https://github.com/tuist/XcodeProj/issues/861), so this fixture tries to capture those changes to run tests against them. diff --git a/Fixtures/Xcode16/Test.xcodeproj/project.pbxproj b/Fixtures/Xcode16/Test.xcodeproj/project.pbxproj new file mode 100644 index 000000000..38d0a9761 --- /dev/null +++ b/Fixtures/Xcode16/Test.xcodeproj/project.pbxproj @@ -0,0 +1,340 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + A4C5307E2CAAC8EA00EDC73B /* Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Test.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + A4C530802CAAC8EA00EDC73B /* Test */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Test; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + A4C5307B2CAAC8EA00EDC73B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A4C530752CAAC8EA00EDC73B = { + isa = PBXGroup; + children = ( + A4C530802CAAC8EA00EDC73B /* Test */, + A4C5307F2CAAC8EA00EDC73B /* Products */, + ); + sourceTree = ""; + }; + A4C5307F2CAAC8EA00EDC73B /* Products */ = { + isa = PBXGroup; + children = ( + A4C5307E2CAAC8EA00EDC73B /* Test.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A4C5307D2CAAC8EA00EDC73B /* Test */ = { + isa = PBXNativeTarget; + buildConfigurationList = A4C5308D2CAAC8EC00EDC73B /* Build configuration list for PBXNativeTarget "Test" */; + buildPhases = ( + A4C5307A2CAAC8EA00EDC73B /* Sources */, + A4C5307B2CAAC8EA00EDC73B /* Frameworks */, + A4C5307C2CAAC8EA00EDC73B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + A4C530802CAAC8EA00EDC73B /* Test */, + ); + name = Test; + packageProductDependencies = ( + ); + productName = Test; + productReference = A4C5307E2CAAC8EA00EDC73B /* Test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A4C530762CAAC8EA00EDC73B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1610; + LastUpgradeCheck = 1610; + TargetAttributes = { + A4C5307D2CAAC8EA00EDC73B = { + CreatedOnToolsVersion = 16.1; + }; + }; + }; + buildConfigurationList = A4C530792CAAC8EA00EDC73B /* Build configuration list for PBXProject "Test" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A4C530752CAAC8EA00EDC73B; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = A4C5307F2CAAC8EA00EDC73B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A4C5307D2CAAC8EA00EDC73B /* Test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A4C5307C2CAAC8EA00EDC73B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A4C5307A2CAAC8EA00EDC73B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A4C5308B2CAAC8EC00EDC73B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A4C5308C2CAAC8EC00EDC73B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + A4C5308E2CAAC8EC00EDC73B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Test/Test.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Test/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Test; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Debug; + }; + A4C5308F2CAAC8EC00EDC73B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Test/Test.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Test/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Test; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A4C530792CAAC8EA00EDC73B /* Build configuration list for PBXProject "Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A4C5308B2CAAC8EC00EDC73B /* Debug */, + A4C5308C2CAAC8EC00EDC73B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A4C5308D2CAAC8EC00EDC73B /* Build configuration list for PBXNativeTarget "Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A4C5308E2CAAC8EC00EDC73B /* Debug */, + A4C5308F2CAAC8EC00EDC73B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A4C530762CAAC8EA00EDC73B /* Project object */; +} diff --git a/Fixtures/Xcode16/Test/Assets.xcassets/AccentColor.colorset/Contents.json b/Fixtures/Xcode16/Test/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Fixtures/Xcode16/Test/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16/Test/Assets.xcassets/AppIcon.appiconset/Contents.json b/Fixtures/Xcode16/Test/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..ffdfe150b --- /dev/null +++ b/Fixtures/Xcode16/Test/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,85 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16/Test/Assets.xcassets/Contents.json b/Fixtures/Xcode16/Test/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/Xcode16/Test/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16/Test/ContentView.swift b/Fixtures/Xcode16/Test/ContentView.swift new file mode 100644 index 000000000..32435833a --- /dev/null +++ b/Fixtures/Xcode16/Test/ContentView.swift @@ -0,0 +1,24 @@ +// +// ContentView.swift +// Test +// +// Created by F1248 on 30.09.24. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/Fixtures/Xcode16/Test/Preview Content/Preview Assets.xcassets/Contents.json b/Fixtures/Xcode16/Test/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/Xcode16/Test/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16/Test/Test.entitlements b/Fixtures/Xcode16/Test/Test.entitlements new file mode 100644 index 000000000..f2ef3ae02 --- /dev/null +++ b/Fixtures/Xcode16/Test/Test.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/Fixtures/Xcode16/Test/TestApp.swift b/Fixtures/Xcode16/Test/TestApp.swift new file mode 100644 index 000000000..bb7480e72 --- /dev/null +++ b/Fixtures/Xcode16/Test/TestApp.swift @@ -0,0 +1,17 @@ +// +// TestApp.swift +// Test +// +// Created by F1248 on 30.09.24. +// + +import SwiftUI + +@main +struct TestApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Fixtures/Xcode16/copy.xcodeproj/project.pbxproj b/Fixtures/Xcode16/copy.xcodeproj/project.pbxproj new file mode 100644 index 000000000..4a66fedfd --- /dev/null +++ b/Fixtures/Xcode16/copy.xcodeproj/project.pbxproj @@ -0,0 +1,335 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + A4C5307E2CAAC8EA00EDC73B /* Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Test.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + A4C530802CAAC8EA00EDC73B /* Test */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Test; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + A4C5307B2CAAC8EA00EDC73B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A4C530752CAAC8EA00EDC73B = { + isa = PBXGroup; + children = ( + A4C530802CAAC8EA00EDC73B /* Test */, + A4C5307F2CAAC8EA00EDC73B /* Products */, + ); + sourceTree = ""; + }; + A4C5307F2CAAC8EA00EDC73B /* Products */ = { + isa = PBXGroup; + children = ( + A4C5307E2CAAC8EA00EDC73B /* Test.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A4C5307D2CAAC8EA00EDC73B /* Test */ = { + isa = PBXNativeTarget; + buildConfigurationList = A4C5308D2CAAC8EC00EDC73B /* Build configuration list for PBXNativeTarget "Test" */; + buildPhases = ( + A4C5307A2CAAC8EA00EDC73B /* Sources */, + A4C5307B2CAAC8EA00EDC73B /* Frameworks */, + A4C5307C2CAAC8EA00EDC73B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + A4C530802CAAC8EA00EDC73B /* Test */, + ); + name = Test; + packageProductDependencies = ( + ); + productName = Test; + productReference = A4C5307E2CAAC8EA00EDC73B /* Test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A4C530762CAAC8EA00EDC73B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1610; + LastUpgradeCheck = 1610; + TargetAttributes = { + A4C5307D2CAAC8EA00EDC73B = { + CreatedOnToolsVersion = 16.1; + }; + }; + }; + buildConfigurationList = A4C530792CAAC8EA00EDC73B /* Build configuration list for PBXProject "copy" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A4C530752CAAC8EA00EDC73B; + preferredProjectObjectVersion = 77; + productRefGroup = A4C5307F2CAAC8EA00EDC73B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A4C5307D2CAAC8EA00EDC73B /* Test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A4C5307C2CAAC8EA00EDC73B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A4C5307A2CAAC8EA00EDC73B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A4C5308B2CAAC8EC00EDC73B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A4C5308C2CAAC8EC00EDC73B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + A4C5308E2CAAC8EC00EDC73B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Test/Test.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Test/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Test; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Debug; + }; + A4C5308F2CAAC8EC00EDC73B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Test/Test.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Test/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Test; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A4C530792CAAC8EA00EDC73B /* Build configuration list for PBXProject "copy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A4C5308B2CAAC8EC00EDC73B /* Debug */, + A4C5308C2CAAC8EC00EDC73B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A4C5308D2CAAC8EC00EDC73B /* Build configuration list for PBXNativeTarget "Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A4C5308E2CAAC8EC00EDC73B /* Debug */, + A4C5308F2CAAC8EC00EDC73B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A4C530762CAAC8EA00EDC73B /* Project object */; +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations.xcodeproj/project.pbxproj b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations.xcodeproj/project.pbxproj new file mode 100644 index 000000000..fbb1bc5f0 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations.xcodeproj/project.pbxproj @@ -0,0 +1,341 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 918309852EFB16F800EE08DF /* Xcode16BuildConfigurations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Xcode16BuildConfigurations.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Xcode16BuildConfigurations; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 918309822EFB16F800EE08DF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9183097C2EFB16F800EE08DF = { + isa = PBXGroup; + children = ( + 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */, + 918309862EFB16F800EE08DF /* Products */, + ); + sourceTree = ""; + }; + 918309862EFB16F800EE08DF /* Products */ = { + isa = PBXGroup; + children = ( + 918309852EFB16F800EE08DF /* Xcode16BuildConfigurations.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 918309842EFB16F800EE08DF /* Xcode16BuildConfigurations */ = { + isa = PBXNativeTarget; + buildConfigurationList = 918309902EFB16F800EE08DF /* Build configuration list for PBXNativeTarget "Xcode16BuildConfigurations" */; + buildPhases = ( + 918309812EFB16F800EE08DF /* Sources */, + 918309822EFB16F800EE08DF /* Frameworks */, + 918309832EFB16F800EE08DF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */, + ); + name = Xcode16BuildConfigurations; + packageProductDependencies = ( + ); + productName = Xcode16BuildConfigurations; + productReference = 918309852EFB16F800EE08DF /* Xcode16BuildConfigurations.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9183097D2EFB16F800EE08DF /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2610; + LastUpgradeCheck = 2610; + TargetAttributes = { + 918309842EFB16F800EE08DF = { + CreatedOnToolsVersion = 26.1; + }; + }; + }; + buildConfigurationList = 918309802EFB16F800EE08DF /* Build configuration list for PBXProject "Xcode16BuildConfigurations" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9183097C2EFB16F800EE08DF; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 918309862EFB16F800EE08DF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 918309842EFB16F800EE08DF /* Xcode16BuildConfigurations */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 918309832EFB16F800EE08DF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 918309812EFB16F800EE08DF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 9183098E2EFB16F800EE08DF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */; + baseConfigurationReferenceRelativePath = Configs/DevConfig.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9183098F2EFB16F800EE08DF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */; + baseConfigurationReferenceRelativePath = Configs/ReleaseConfig.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 918309912EFB16F800EE08DF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */; + baseConfigurationReferenceRelativePath = Configs/TargetConfig.xcconfig; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Xcode16BuildConfigurations; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 918309922EFB16F800EE08DF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */; + baseConfigurationReferenceRelativePath = Configs/TargetConfig.xcconfig; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.Xcode16BuildConfigurations; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 918309802EFB16F800EE08DF /* Build configuration list for PBXProject "Xcode16BuildConfigurations" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9183098E2EFB16F800EE08DF /* Debug */, + 9183098F2EFB16F800EE08DF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 918309902EFB16F800EE08DF /* Build configuration list for PBXNativeTarget "Xcode16BuildConfigurations" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 918309912EFB16F800EE08DF /* Debug */, + 918309922EFB16F800EE08DF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 9183097D2EFB16F800EE08DF /* Project object */; +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AccentColor.colorset/Contents.json b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AppIcon.appiconset/Contents.json b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/Contents.json b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/DevConfig.xcconfig b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/DevConfig.xcconfig new file mode 100644 index 000000000..0abfe77dc --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/DevConfig.xcconfig @@ -0,0 +1,3 @@ +API_BASE_URL = api.staging.example.com +ASSETCATALOG_COMPILER_OPTIMIZATION = speed +TARGETED_DEVICE_FAMILY = 1 diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/ReleaseConfig.xcconfig b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/ReleaseConfig.xcconfig new file mode 100644 index 000000000..56375b0d1 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/ReleaseConfig.xcconfig @@ -0,0 +1,3 @@ +API_BASE_URL = api.example.com +ASSETCATALOG_COMPILER_OPTIMIZATION = space +TARGETED_DEVICE_FAMILY = 1 diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/TargetConfig.xcconfig b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/TargetConfig.xcconfig new file mode 100644 index 000000000..565be09fe --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Configs/TargetConfig.xcconfig @@ -0,0 +1 @@ +TARGETED_DEVICE_FAMILY = 1,2 diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/ContentView.swift b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/ContentView.swift new file mode 100644 index 000000000..b000a7e46 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/ContentView.swift @@ -0,0 +1,17 @@ +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Xcode16BuildConfigurationsApp.swift b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Xcode16BuildConfigurationsApp.swift new file mode 100644 index 000000000..152617081 --- /dev/null +++ b/Fixtures/Xcode16BuildConfigurations/Xcode16BuildConfigurations/Xcode16BuildConfigurationsApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct Xcode16BuildConfigurationsApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Fixtures/Xcode16ProjectReferenceOrder/Sources/AppDelegate.swift b/Fixtures/Xcode16ProjectReferenceOrder/Sources/AppDelegate.swift new file mode 100644 index 000000000..6cf500afb --- /dev/null +++ b/Fixtures/Xcode16ProjectReferenceOrder/Sources/AppDelegate.swift @@ -0,0 +1,16 @@ +import Framework1 +import Framework2 +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + func applicationDidFinishLaunching(_: UIApplication) { + print(hello()) + } + + func hello() -> String { + "AppDelegate.hello()" + } +} diff --git a/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/project.pbxproj b/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/project.pbxproj new file mode 100644 index 000000000..9194eb0ae --- /dev/null +++ b/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/project.pbxproj @@ -0,0 +1,447 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 90; + objects = { + +/* Begin PBXBuildFile section */ + 3108C05C7B0C4FD6AC077294 /* Framework2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6D728BF5C49E97251DA378AE /* Framework2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 5D8C48ABB7D5D013DDBD67AA /* Framework1.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */; }; + C2317A164A6A4F635FE4CCD7 /* Framework1.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C57CB036110A3D5DCC9E437A /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D728BF5C49E97251DA378AE /* Framework2.framework */; }; + D2AE54DED6608D5B956A5E45 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020631B5E0BB47399A2CE86B /* AppDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 063E6B9654162D044FC79E65 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = C779969BD4C211DD5C3B641C; + remoteInfo = Framework2; + }; + 811B516259EFC3D33371D664 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E44C53B0829E6D20396C5BB6; + remoteInfo = Framework1; + }; + CA6C894E871DDE6DC2017E72 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = DC40FA3F02AF8EB1A758B163; + remoteInfo = Framework2; + }; + E6EBF03BE94A8028ED821B7B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 006F8DE332EBBD68331EC394; + remoteInfo = Framework1; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BF31C3A479ECD7CBA0640A2F /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + dstPath = ""; + dstSubfolder = Frameworks; + files = ( + C2317A164A6A4F635FE4CCD7 /* Framework1.framework in Embed Frameworks */, + 3108C05C7B0C4FD6AC077294 /* Framework2.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 020631B5E0BB47399A2CE86B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 08931D1475E84509040F7FEA /* Framework2.xcodeproj */ = {isa = PBXFileReference; explicitFileType = "wrapper.pb-project"; includeInIndex = 0; name = Framework2.xcodeproj; path = ../Framework2/Framework2.xcodeproj; sourceTree = SOURCE_ROOT; }; + 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework1.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6D728BF5C49E97251DA378AE /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */ = {isa = PBXFileReference; explicitFileType = "wrapper.pb-project"; includeInIndex = 0; name = Framework1.xcodeproj; path = ../Framework1/Framework1.xcodeproj; sourceTree = SOURCE_ROOT; }; + E203E65EDD1A90FCBAA42C9E /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B27C7CF4B617049554976EFD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + files = ( + 5D8C48ABB7D5D013DDBD67AA /* Framework1.framework in Frameworks */, + C57CB036110A3D5DCC9E437A /* Framework2.framework in Frameworks */, + ); + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0767F0D2E9F7C0B23673A1B1 = { + isa = PBXGroup; + children = ( + C7A7B9165A3588E4002596F4 /* Project */, + 67F182DC08786FF3C82D39E8 /* Frameworks */, + 73B13978C93862E5AD69BCC8 /* Products */, + ); + sourceTree = ""; + }; + 15ACBAE4CF0A973AED8CC371 /* Products */ = { + isa = PBXGroup; + children = ( + BBE8CAF8BA821ED44E58C6F5 /* Framework2.framework */, + ); + name = Products; + sourceTree = ""; + }; + 30CD57449DD0BAD1F51809C3 /* Products */ = { + isa = PBXGroup; + children = ( + F8CBE43B9129D34973556D4F /* Framework1.framework */, + ); + name = Products; + sourceTree = ""; + }; + 67F182DC08786FF3C82D39E8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 6BB3FD9AE4757E7AE14C43B7 /* Sources */ = { + isa = PBXGroup; + children = ( + 020631B5E0BB47399A2CE86B /* AppDelegate.swift */, + ); + path = Sources; + sourceTree = ""; + }; + 73B13978C93862E5AD69BCC8 /* Products */ = { + isa = PBXGroup; + children = ( + E203E65EDD1A90FCBAA42C9E /* App.app */, + 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */, + 6D728BF5C49E97251DA378AE /* Framework2.framework */, + ); + name = Products; + sourceTree = ""; + }; + C7A7B9165A3588E4002596F4 /* Project */ = { + isa = PBXGroup; + children = ( + 6BB3FD9AE4757E7AE14C43B7 /* Sources */, + ); + name = Project; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8359F535E312088875AB7F4E /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = BAA38452720711B406475F28 /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + 2053EE4C6238D6C3AEB2ADB3 /* Sources */, + F1BC716AD69EACCBD2A2DDBC /* Resources */, + BF31C3A479ECD7CBA0640A2F /* Embed Frameworks */, + B27C7CF4B617049554976EFD /* Frameworks */, + 0579081E2ED74460006A6AFB /* custom script */, + ); + buildRules = ( + ); + dependencies = ( + 58FB5C08E20463C938366127 /* PBXTargetDependency */, + CD591349E38A6A3EB5AA81DD /* PBXTargetDependency */, + ); + name = App; + productName = App; + productReference = E203E65EDD1A90FCBAA42C9E /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 22E4F0B4EBDBD2631840392B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1640; + }; + buildConfigurationList = C94346B8B75719D8319921C2 /* Build configuration list for PBXProject "Test" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 0767F0D2E9F7C0B23673A1B1; + preferredProjectObjectVersion = 90; + productRefGroup = 73B13978C93862E5AD69BCC8 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 30CD57449DD0BAD1F51809C3 /* Products */; + ProjectRef = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + }, + { + ProductGroup = 15ACBAE4CF0A973AED8CC371 /* Products */; + ProjectRef = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8359F535E312088875AB7F4E /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + BBE8CAF8BA821ED44E58C6F5 /* Framework2.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Framework2.framework; + remoteRef = 063E6B9654162D044FC79E65 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F8CBE43B9129D34973556D4F /* Framework1.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Framework1.framework; + remoteRef = 811B516259EFC3D33371D664 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + F1BC716AD69EACCBD2A2DDBC /* Resources */ = { + isa = PBXResourcesBuildPhase; + files = ( + ); + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0579081E2ED74460006A6AFB /* custom script */ = { + isa = PBXShellScriptBuildPhase; + name = "custom script"; + shellPath = /bin/sh; + shellScript = ( + "# comment", + "ls -la", + "ls -la", + "", + ); + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2053EE4C6238D6C3AEB2ADB3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + files = ( + D2AE54DED6608D5B956A5E45 /* AppDelegate.swift in Sources */, + ); + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 58FB5C08E20463C938366127 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Framework2; + targetProxy = CA6C894E871DDE6DC2017E72 /* PBXContainerItemProxy */; + }; + CD591349E38A6A3EB5AA81DD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Framework1; + targetProxy = E6EBF03BE94A8028ED821B7B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0B98A64D6F621FDCEEA0CE44 /* Debug configuration for PBXNativeTarget "App" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Derived/InfoPlists/App-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.App; + PRODUCT_NAME = App; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 58BAFCCFAE3AD62113BBEDEF /* Debug configuration for PBXProject "Test" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5904C1CCA86A83838723C772 /* Release configuration for PBXProject "Test" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E15950A8DD3B82ED1ED7849 /* Release configuration for PBXNativeTarget "App" */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Derived/InfoPlists/App-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.App; + PRODUCT_NAME = App; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BAA38452720711B406475F28 /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0B98A64D6F621FDCEEA0CE44 /* Debug configuration for PBXNativeTarget "App" */, + 5E15950A8DD3B82ED1ED7849 /* Release configuration for PBXNativeTarget "App" */, + ); + defaultConfigurationName = Release; + }; + C94346B8B75719D8319921C2 /* Build configuration list for PBXProject "Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58BAFCCFAE3AD62113BBEDEF /* Debug configuration for PBXProject "Test" */, + 5904C1CCA86A83838723C772 /* Release configuration for PBXProject "Test" */, + ); + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 22E4F0B4EBDBD2631840392B /* Project object */; +} diff --git a/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/xcshareddata/xcschemes/App.xcscheme new file mode 100644 index 000000000..fc8b076c1 --- /dev/null +++ b/Fixtures/Xcode16ProjectReferenceOrder/Test.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/project.pbxproj b/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/project.pbxproj new file mode 100644 index 000000000..342173cf6 --- /dev/null +++ b/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/project.pbxproj @@ -0,0 +1,450 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 3108C05C7B0C4FD6AC077294 /* Framework2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6D728BF5C49E97251DA378AE /* Framework2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 5D8C48ABB7D5D013DDBD67AA /* Framework1.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */; }; + C2317A164A6A4F635FE4CCD7 /* Framework1.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C57CB036110A3D5DCC9E437A /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D728BF5C49E97251DA378AE /* Framework2.framework */; }; + D2AE54DED6608D5B956A5E45 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020631B5E0BB47399A2CE86B /* AppDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 063E6B9654162D044FC79E65 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = C779969BD4C211DD5C3B641C; + remoteInfo = Framework2; + }; + 811B516259EFC3D33371D664 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E44C53B0829E6D20396C5BB6; + remoteInfo = Framework1; + }; + CA6C894E871DDE6DC2017E72 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = DC40FA3F02AF8EB1A758B163; + remoteInfo = Framework2; + }; + E6EBF03BE94A8028ED821B7B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 006F8DE332EBBD68331EC394; + remoteInfo = Framework1; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BF31C3A479ECD7CBA0640A2F /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C2317A164A6A4F635FE4CCD7 /* Framework1.framework in Embed Frameworks */, + 3108C05C7B0C4FD6AC077294 /* Framework2.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 020631B5E0BB47399A2CE86B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 08931D1475E84509040F7FEA /* Framework2.xcodeproj */ = {isa = PBXFileReference; explicitFileType = "wrapper.pb-project"; includeInIndex = 0; name = Framework2.xcodeproj; path = ../Framework2/Framework2.xcodeproj; sourceTree = SOURCE_ROOT; }; + 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework1.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6D728BF5C49E97251DA378AE /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */ = {isa = PBXFileReference; explicitFileType = "wrapper.pb-project"; includeInIndex = 0; name = Framework1.xcodeproj; path = ../Framework1/Framework1.xcodeproj; sourceTree = SOURCE_ROOT; }; + E203E65EDD1A90FCBAA42C9E /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F09268C492FF78A6C82868C6 /* App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "App-Info.plist"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B27C7CF4B617049554976EFD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D8C48ABB7D5D013DDBD67AA /* Framework1.framework in Frameworks */, + C57CB036110A3D5DCC9E437A /* Framework2.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0767F0D2E9F7C0B23673A1B1 = { + isa = PBXGroup; + children = ( + C7A7B9165A3588E4002596F4 /* Project */, + 67F182DC08786FF3C82D39E8 /* Frameworks */, + 73B13978C93862E5AD69BCC8 /* Products */, + ); + sourceTree = ""; + }; + 15ACBAE4CF0A973AED8CC371 /* Products */ = { + isa = PBXGroup; + children = ( + BBE8CAF8BA821ED44E58C6F5 /* Framework2.framework */, + ); + name = Products; + sourceTree = ""; + }; + 20C83B4860AC239B5E5FDF3C /* InfoPlists */ = { + isa = PBXGroup; + children = ( + F09268C492FF78A6C82868C6 /* App-Info.plist */, + ); + path = InfoPlists; + sourceTree = ""; + }; + 30CD57449DD0BAD1F51809C3 /* Products */ = { + isa = PBXGroup; + children = ( + F8CBE43B9129D34973556D4F /* Framework1.framework */, + ); + name = Products; + sourceTree = ""; + }; + 67F182DC08786FF3C82D39E8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 6BB3FD9AE4757E7AE14C43B7 /* Sources */ = { + isa = PBXGroup; + children = ( + 020631B5E0BB47399A2CE86B /* AppDelegate.swift */, + ); + path = Sources; + sourceTree = ""; + }; + 73B13978C93862E5AD69BCC8 /* Products */ = { + isa = PBXGroup; + children = ( + E203E65EDD1A90FCBAA42C9E /* App.app */, + 0FCDEDB6CEA64BD069DC9EA9 /* Framework1.framework */, + 6D728BF5C49E97251DA378AE /* Framework2.framework */, + ); + name = Products; + sourceTree = ""; + }; + C7A7B9165A3588E4002596F4 /* Project */ = { + isa = PBXGroup; + children = ( + 6BB3FD9AE4757E7AE14C43B7 /* Sources */, + ); + name = Project; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8359F535E312088875AB7F4E /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = BAA38452720711B406475F28 /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + 2053EE4C6238D6C3AEB2ADB3 /* Sources */, + F1BC716AD69EACCBD2A2DDBC /* Resources */, + BF31C3A479ECD7CBA0640A2F /* Embed Frameworks */, + B27C7CF4B617049554976EFD /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 58FB5C08E20463C938366127 /* PBXTargetDependency */, + CD591349E38A6A3EB5AA81DD /* PBXTargetDependency */, + ); + name = App; + packageProductDependencies = ( + ); + productName = App; + productReference = E203E65EDD1A90FCBAA42C9E /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 22E4F0B4EBDBD2631840392B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + }; + buildConfigurationList = C94346B8B75719D8319921C2 /* Build configuration list for PBXProject "MainApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 0767F0D2E9F7C0B23673A1B1; + productRefGroup = 73B13978C93862E5AD69BCC8 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 15ACBAE4CF0A973AED8CC371 /* Products */; + ProjectRef = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */; + }, + { + ProductGroup = 30CD57449DD0BAD1F51809C3 /* Products */; + ProjectRef = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8359F535E312088875AB7F4E /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + BBE8CAF8BA821ED44E58C6F5 /* Framework2.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Framework2.framework; + remoteRef = 063E6B9654162D044FC79E65 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F8CBE43B9129D34973556D4F /* Framework1.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Framework1.framework; + remoteRef = 811B516259EFC3D33371D664 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + F1BC716AD69EACCBD2A2DDBC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2053EE4C6238D6C3AEB2ADB3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D2AE54DED6608D5B956A5E45 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 58FB5C08E20463C938366127 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Framework2; + targetProxy = CA6C894E871DDE6DC2017E72 /* PBXContainerItemProxy */; + }; + CD591349E38A6A3EB5AA81DD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Framework1; + targetProxy = E6EBF03BE94A8028ED821B7B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0B98A64D6F621FDCEEA0CE44 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Derived/InfoPlists/App-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.App; + PRODUCT_NAME = App; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 58BAFCCFAE3AD62113BBEDEF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5904C1CCA86A83838723C772 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E15950A8DD3B82ED1ED7849 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Derived/InfoPlists/App-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.App; + PRODUCT_NAME = App; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BAA38452720711B406475F28 /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0B98A64D6F621FDCEEA0CE44 /* Debug */, + 5E15950A8DD3B82ED1ED7849 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C94346B8B75719D8319921C2 /* Build configuration list for PBXProject "MainApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58BAFCCFAE3AD62113BBEDEF /* Debug */, + 5904C1CCA86A83838723C772 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 22E4F0B4EBDBD2631840392B /* Project object */; +} diff --git a/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/xcshareddata/xcschemes/App.xcscheme new file mode 100644 index 000000000..aefdccd72 --- /dev/null +++ b/Fixtures/Xcode16ProjectReferenceOrder/Wrong.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.pbxproj b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.pbxproj new file mode 100644 index 000000000..5a12da512 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.pbxproj @@ -0,0 +1,947 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 42BF873E24B0E8EE00C4B605 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF873D24B0E8EE00C4B605 /* AppDelegate.swift */; }; + 42BF874024B0E8EE00C4B605 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF873F24B0E8EE00C4B605 /* SceneDelegate.swift */; }; + 42BF874224B0E8EE00C4B605 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF874124B0E8EE00C4B605 /* ContentView.swift */; }; + 42BF874424B0E8EF00C4B605 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF874324B0E8EF00C4B605 /* Assets.xcassets */; }; + 42BF874724B0E8EF00C4B605 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF874624B0E8EF00C4B605 /* Preview Assets.xcassets */; }; + 42BF874A24B0E8EF00C4B605 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42BF874824B0E8EF00C4B605 /* LaunchScreen.storyboard */; }; + 42BF875824B0E9F300C4B605 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42BF875724B0E9F300C4B605 /* NotificationCenter.framework */; }; + 42BF875B24B0E9F300C4B605 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF875A24B0E9F300C4B605 /* TodayViewController.swift */; }; + 42BF875E24B0E9F300C4B605 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42BF875C24B0E9F300C4B605 /* MainInterface.storyboard */; }; + 42BF876224B0E9F300C4B605 /* TodayViewExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42BF875524B0E9F300C4B605 /* TodayViewExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 42BF876D24B0EA0700C4B605 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42BF876B24B0EA0700C4B605 /* Interface.storyboard */; }; + 42BF876F24B0EA0800C4B605 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF876E24B0EA0800C4B605 /* Assets.xcassets */; }; + 42BF877624B0EA0800C4B605 /* WatchApp Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42BF877524B0EA0800C4B605 /* WatchApp Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 42BF877B24B0EA0800C4B605 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF877A24B0EA0800C4B605 /* ContentView.swift */; }; + 42BF877D24B0EA0800C4B605 /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF877C24B0EA0800C4B605 /* HostingController.swift */; }; + 42BF877F24B0EA0800C4B605 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF877E24B0EA0800C4B605 /* ExtensionDelegate.swift */; }; + 42BF878124B0EA0800C4B605 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF878024B0EA0800C4B605 /* NotificationController.swift */; }; + 42BF878324B0EA0800C4B605 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF878224B0EA0800C4B605 /* NotificationView.swift */; }; + 42BF878524B0EA0900C4B605 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF878424B0EA0900C4B605 /* Assets.xcassets */; }; + 42BF878824B0EA0900C4B605 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF878724B0EA0900C4B605 /* Preview Assets.xcassets */; }; + 42BF878D24B0EA0900C4B605 /* WatchApp.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 42BF876924B0EA0700C4B605 /* WatchApp.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 42BF879C24B0EA2500C4B605 /* Messages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42BF879B24B0EA2500C4B605 /* Messages.framework */; }; + 42BF879F24B0EA2500C4B605 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42BF879E24B0EA2500C4B605 /* MessagesViewController.swift */; }; + 42BF87A224B0EA2500C4B605 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42BF87A024B0EA2500C4B605 /* MainInterface.storyboard */; }; + 42BF87A424B0EA2500C4B605 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42BF87A324B0EA2500C4B605 /* Assets.xcassets */; }; + 42BF87A824B0EA2500C4B605 /* MessagesExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 42BF879A24B0EA2500C4B605 /* MessagesExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 42BF876024B0E9F300C4B605 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 42BF873224B0E8EE00C4B605 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 42BF875424B0E9F300C4B605; + remoteInfo = TodayViewExtension; + }; + 42BF877724B0EA0800C4B605 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 42BF873224B0E8EE00C4B605 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 42BF877424B0EA0800C4B605; + remoteInfo = "WatchApp Extension"; + }; + 42BF878B24B0EA0900C4B605 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 42BF873224B0E8EE00C4B605 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 42BF876824B0EA0700C4B605; + remoteInfo = WatchApp; + }; + 42BF87A624B0EA2500C4B605 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 42BF873224B0E8EE00C4B605 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 42BF879924B0EA2500C4B605; + remoteInfo = MessagesExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 42BF876624B0E9F300C4B605 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 42BF87A824B0EA2500C4B605 /* MessagesExtension.appex in Embed App Extensions */, + 42BF876224B0E9F300C4B605 /* TodayViewExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF879124B0EA0900C4B605 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 42BF877624B0EA0800C4B605 /* WatchApp Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF879524B0EA0900C4B605 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 42BF878D24B0EA0900C4B605 /* WatchApp.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 42BF873A24B0E8EE00C4B605 /* AppWithExtensions.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppWithExtensions.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 42BF873D24B0E8EE00C4B605 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 42BF873F24B0E8EE00C4B605 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 42BF874124B0E8EE00C4B605 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 42BF874324B0E8EF00C4B605 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 42BF874624B0E8EF00C4B605 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 42BF874924B0E8EF00C4B605 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 42BF874B24B0E8EF00C4B605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42BF875524B0E9F300C4B605 /* TodayViewExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TodayViewExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 42BF875724B0E9F300C4B605 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; + 42BF875A24B0E9F300C4B605 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; + 42BF875D24B0E9F300C4B605 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 42BF875F24B0E9F300C4B605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42BF876924B0EA0700C4B605 /* WatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 42BF876C24B0EA0700C4B605 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; + 42BF876E24B0EA0800C4B605 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 42BF877024B0EA0800C4B605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42BF877524B0EA0800C4B605 /* WatchApp Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchApp Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 42BF877A24B0EA0800C4B605 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 42BF877C24B0EA0800C4B605 /* HostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = ""; }; + 42BF877E24B0EA0800C4B605 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; + 42BF878024B0EA0800C4B605 /* NotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = ""; }; + 42BF878224B0EA0800C4B605 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; + 42BF878424B0EA0900C4B605 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 42BF878724B0EA0900C4B605 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 42BF878924B0EA0900C4B605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 42BF878A24B0EA0900C4B605 /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = ""; }; + 42BF879A24B0EA2500C4B605 /* MessagesExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MessagesExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 42BF879B24B0EA2500C4B605 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; + 42BF879E24B0EA2500C4B605 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; + 42BF87A124B0EA2500C4B605 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 42BF87A324B0EA2500C4B605 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 42BF87A524B0EA2500C4B605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 42BF873724B0E8EE00C4B605 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF875224B0E9F300C4B605 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF875824B0E9F300C4B605 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF877224B0EA0800C4B605 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF879724B0EA2500C4B605 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF879C24B0EA2500C4B605 /* Messages.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 42BF873124B0E8EE00C4B605 = { + isa = PBXGroup; + children = ( + 42BF873C24B0E8EE00C4B605 /* AppWithExtensions */, + 42BF875924B0E9F300C4B605 /* TodayViewExtension */, + 42BF876A24B0EA0700C4B605 /* WatchApp */, + 42BF877924B0EA0800C4B605 /* WatchApp Extension */, + 42BF879D24B0EA2500C4B605 /* MessagesExtension */, + 42BF875624B0E9F300C4B605 /* Frameworks */, + 42BF873B24B0E8EE00C4B605 /* Products */, + ); + sourceTree = ""; + }; + 42BF873B24B0E8EE00C4B605 /* Products */ = { + isa = PBXGroup; + children = ( + 42BF873A24B0E8EE00C4B605 /* AppWithExtensions.app */, + 42BF875524B0E9F300C4B605 /* TodayViewExtension.appex */, + 42BF876924B0EA0700C4B605 /* WatchApp.app */, + 42BF877524B0EA0800C4B605 /* WatchApp Extension.appex */, + 42BF879A24B0EA2500C4B605 /* MessagesExtension.appex */, + ); + name = Products; + sourceTree = ""; + }; + 42BF873C24B0E8EE00C4B605 /* AppWithExtensions */ = { + isa = PBXGroup; + children = ( + 42BF873D24B0E8EE00C4B605 /* AppDelegate.swift */, + 42BF873F24B0E8EE00C4B605 /* SceneDelegate.swift */, + 42BF874124B0E8EE00C4B605 /* ContentView.swift */, + 42BF874324B0E8EF00C4B605 /* Assets.xcassets */, + 42BF874824B0E8EF00C4B605 /* LaunchScreen.storyboard */, + 42BF874B24B0E8EF00C4B605 /* Info.plist */, + 42BF874524B0E8EF00C4B605 /* Preview Content */, + ); + path = AppWithExtensions; + sourceTree = ""; + }; + 42BF874524B0E8EF00C4B605 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 42BF874624B0E8EF00C4B605 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42BF875624B0E9F300C4B605 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 42BF875724B0E9F300C4B605 /* NotificationCenter.framework */, + 42BF879B24B0EA2500C4B605 /* Messages.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 42BF875924B0E9F300C4B605 /* TodayViewExtension */ = { + isa = PBXGroup; + children = ( + 42BF875A24B0E9F300C4B605 /* TodayViewController.swift */, + 42BF875C24B0E9F300C4B605 /* MainInterface.storyboard */, + 42BF875F24B0E9F300C4B605 /* Info.plist */, + ); + path = TodayViewExtension; + sourceTree = ""; + }; + 42BF876A24B0EA0700C4B605 /* WatchApp */ = { + isa = PBXGroup; + children = ( + 42BF876B24B0EA0700C4B605 /* Interface.storyboard */, + 42BF876E24B0EA0800C4B605 /* Assets.xcassets */, + 42BF877024B0EA0800C4B605 /* Info.plist */, + ); + path = WatchApp; + sourceTree = ""; + }; + 42BF877924B0EA0800C4B605 /* WatchApp Extension */ = { + isa = PBXGroup; + children = ( + 42BF877A24B0EA0800C4B605 /* ContentView.swift */, + 42BF877C24B0EA0800C4B605 /* HostingController.swift */, + 42BF877E24B0EA0800C4B605 /* ExtensionDelegate.swift */, + 42BF878024B0EA0800C4B605 /* NotificationController.swift */, + 42BF878224B0EA0800C4B605 /* NotificationView.swift */, + 42BF878424B0EA0900C4B605 /* Assets.xcassets */, + 42BF878924B0EA0900C4B605 /* Info.plist */, + 42BF878A24B0EA0900C4B605 /* PushNotificationPayload.apns */, + 42BF878624B0EA0900C4B605 /* Preview Content */, + ); + path = "WatchApp Extension"; + sourceTree = ""; + }; + 42BF878624B0EA0900C4B605 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 42BF878724B0EA0900C4B605 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42BF879D24B0EA2500C4B605 /* MessagesExtension */ = { + isa = PBXGroup; + children = ( + 42BF879E24B0EA2500C4B605 /* MessagesViewController.swift */, + 42BF87A024B0EA2500C4B605 /* MainInterface.storyboard */, + 42BF87A324B0EA2500C4B605 /* Assets.xcassets */, + 42BF87A524B0EA2500C4B605 /* Info.plist */, + ); + path = MessagesExtension; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 42BF873924B0E8EE00C4B605 /* AppWithExtensions */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42BF874E24B0E8EF00C4B605 /* Build configuration list for PBXNativeTarget "AppWithExtensions" */; + buildPhases = ( + 42BF873624B0E8EE00C4B605 /* Sources */, + 42BF873724B0E8EE00C4B605 /* Frameworks */, + 42BF873824B0E8EE00C4B605 /* Resources */, + 42BF876624B0E9F300C4B605 /* Embed App Extensions */, + 42BF879524B0EA0900C4B605 /* Embed Watch Content */, + ); + buildRules = ( + ); + dependencies = ( + 42BF876124B0E9F300C4B605 /* PBXTargetDependency */, + 42BF878C24B0EA0900C4B605 /* PBXTargetDependency */, + 42BF87A724B0EA2500C4B605 /* PBXTargetDependency */, + ); + name = AppWithExtensions; + productName = AppWithExtensions; + productReference = 42BF873A24B0E8EE00C4B605 /* AppWithExtensions.app */; + productType = "com.apple.product-type.application"; + }; + 42BF875424B0E9F300C4B605 /* TodayViewExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42BF876324B0E9F300C4B605 /* Build configuration list for PBXNativeTarget "TodayViewExtension" */; + buildPhases = ( + 42BF875124B0E9F300C4B605 /* Sources */, + 42BF875224B0E9F300C4B605 /* Frameworks */, + 42BF875324B0E9F300C4B605 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TodayViewExtension; + productName = TodayViewExtension; + productReference = 42BF875524B0E9F300C4B605 /* TodayViewExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 42BF876824B0EA0700C4B605 /* WatchApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42BF879224B0EA0900C4B605 /* Build configuration list for PBXNativeTarget "WatchApp" */; + buildPhases = ( + 42BF876724B0EA0700C4B605 /* Resources */, + 42BF879124B0EA0900C4B605 /* Embed App Extensions */, + ); + buildRules = ( + ); + dependencies = ( + 42BF877824B0EA0800C4B605 /* PBXTargetDependency */, + ); + name = WatchApp; + productName = WatchApp; + productReference = 42BF876924B0EA0700C4B605 /* WatchApp.app */; + productType = "com.apple.product-type.application.watchapp2"; + }; + 42BF877424B0EA0800C4B605 /* WatchApp Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42BF878E24B0EA0900C4B605 /* Build configuration list for PBXNativeTarget "WatchApp Extension" */; + buildPhases = ( + 42BF877124B0EA0800C4B605 /* Sources */, + 42BF877224B0EA0800C4B605 /* Frameworks */, + 42BF877324B0EA0800C4B605 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "WatchApp Extension"; + productName = "WatchApp Extension"; + productReference = 42BF877524B0EA0800C4B605 /* WatchApp Extension.appex */; + productType = "com.apple.product-type.watchkit2-extension"; + }; + 42BF879924B0EA2500C4B605 /* MessagesExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42BF87A924B0EA2500C4B605 /* Build configuration list for PBXNativeTarget "MessagesExtension" */; + buildPhases = ( + 42BF879624B0EA2500C4B605 /* Sources */, + 42BF879724B0EA2500C4B605 /* Frameworks */, + 42BF879824B0EA2500C4B605 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MessagesExtension; + productName = MessagesExtension; + productReference = 42BF879A24B0EA2500C4B605 /* MessagesExtension.appex */; + productType = "com.apple.product-type.app-extension.messages"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 42BF873224B0E8EE00C4B605 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1140; + ORGANIZATIONNAME = Tuist; + TargetAttributes = { + 42BF873924B0E8EE00C4B605 = { + CreatedOnToolsVersion = 11.4; + }; + 42BF875424B0E9F300C4B605 = { + CreatedOnToolsVersion = 11.4; + }; + 42BF876824B0EA0700C4B605 = { + CreatedOnToolsVersion = 11.4; + }; + 42BF877424B0EA0800C4B605 = { + CreatedOnToolsVersion = 11.4; + }; + 42BF879924B0EA2500C4B605 = { + CreatedOnToolsVersion = 11.4; + }; + }; + }; + buildConfigurationList = 42BF873524B0E8EE00C4B605 /* Build configuration list for PBXProject "AppWithExtensions" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 42BF873124B0E8EE00C4B605; + productRefGroup = 42BF873B24B0E8EE00C4B605 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 42BF873924B0E8EE00C4B605 /* AppWithExtensions */, + 42BF875424B0E9F300C4B605 /* TodayViewExtension */, + 42BF876824B0EA0700C4B605 /* WatchApp */, + 42BF877424B0EA0800C4B605 /* WatchApp Extension */, + 42BF879924B0EA2500C4B605 /* MessagesExtension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 42BF873824B0E8EE00C4B605 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF874A24B0E8EF00C4B605 /* LaunchScreen.storyboard in Resources */, + 42BF874724B0E8EF00C4B605 /* Preview Assets.xcassets in Resources */, + 42BF874424B0E8EF00C4B605 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF875324B0E9F300C4B605 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF875E24B0E9F300C4B605 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF876724B0EA0700C4B605 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF876F24B0EA0800C4B605 /* Assets.xcassets in Resources */, + 42BF876D24B0EA0700C4B605 /* Interface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF877324B0EA0800C4B605 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF878824B0EA0900C4B605 /* Preview Assets.xcassets in Resources */, + 42BF878524B0EA0900C4B605 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF879824B0EA2500C4B605 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF87A424B0EA2500C4B605 /* Assets.xcassets in Resources */, + 42BF87A224B0EA2500C4B605 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 42BF873624B0E8EE00C4B605 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF873E24B0E8EE00C4B605 /* AppDelegate.swift in Sources */, + 42BF874024B0E8EE00C4B605 /* SceneDelegate.swift in Sources */, + 42BF874224B0E8EE00C4B605 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF875124B0E9F300C4B605 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF875B24B0E9F300C4B605 /* TodayViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF877124B0EA0800C4B605 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF877D24B0EA0800C4B605 /* HostingController.swift in Sources */, + 42BF877B24B0EA0800C4B605 /* ContentView.swift in Sources */, + 42BF878124B0EA0800C4B605 /* NotificationController.swift in Sources */, + 42BF877F24B0EA0800C4B605 /* ExtensionDelegate.swift in Sources */, + 42BF878324B0EA0800C4B605 /* NotificationView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 42BF879624B0EA2500C4B605 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42BF879F24B0EA2500C4B605 /* MessagesViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 42BF876124B0E9F300C4B605 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 42BF875424B0E9F300C4B605 /* TodayViewExtension */; + targetProxy = 42BF876024B0E9F300C4B605 /* PBXContainerItemProxy */; + }; + 42BF877824B0EA0800C4B605 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 42BF877424B0EA0800C4B605 /* WatchApp Extension */; + targetProxy = 42BF877724B0EA0800C4B605 /* PBXContainerItemProxy */; + }; + 42BF878C24B0EA0900C4B605 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 42BF876824B0EA0700C4B605 /* WatchApp */; + targetProxy = 42BF878B24B0EA0900C4B605 /* PBXContainerItemProxy */; + }; + 42BF87A724B0EA2500C4B605 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 42BF879924B0EA2500C4B605 /* MessagesExtension */; + targetProxy = 42BF87A624B0EA2500C4B605 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 42BF874824B0E8EF00C4B605 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 42BF874924B0E8EF00C4B605 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 42BF875C24B0E9F300C4B605 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 42BF875D24B0E9F300C4B605 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; + 42BF876B24B0EA0700C4B605 /* Interface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 42BF876C24B0EA0700C4B605 /* Base */, + ); + name = Interface.storyboard; + sourceTree = ""; + }; + 42BF87A024B0EA2500C4B605 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 42BF87A124B0EA2500C4B605 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 42BF874C24B0E8EF00C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 42BF874D24B0E8EF00C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 42BF874F24B0E8EF00C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"AppWithExtensions/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AppWithExtensions/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 42BF875024B0E8EF00C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"AppWithExtensions/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AppWithExtensions/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 42BF876424B0E9F300C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TodayViewExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.TodayViewExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 42BF876524B0E9F300C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TodayViewExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.TodayViewExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 42BF878F24B0EA0900C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"WatchApp Extension/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "WatchApp Extension/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Debug; + }; + 42BF879024B0EA0900C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"WatchApp Extension/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "WatchApp Extension/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Release; + }; + 42BF879324B0EA0900C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + IBSC_MODULE = WatchApp_Extension; + INFOPLIST_FILE = WatchApp/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Debug; + }; + 42BF879424B0EA0900C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + IBSC_MODULE = WatchApp_Extension; + INFOPLIST_FILE = WatchApp/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Release; + }; + 42BF87AA24B0EA2500C4B605 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MessagesExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.MessagesExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 42BF87AB24B0EA2500C4B605 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MessagesExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.xcodeproj.fixtures.AppWithExtensions.MessagesExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 42BF873524B0E8EE00C4B605 /* Build configuration list for PBXProject "AppWithExtensions" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF874C24B0E8EF00C4B605 /* Debug */, + 42BF874D24B0E8EF00C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42BF874E24B0E8EF00C4B605 /* Build configuration list for PBXNativeTarget "AppWithExtensions" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF874F24B0E8EF00C4B605 /* Debug */, + 42BF875024B0E8EF00C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42BF876324B0E9F300C4B605 /* Build configuration list for PBXNativeTarget "TodayViewExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF876424B0E9F300C4B605 /* Debug */, + 42BF876524B0E9F300C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42BF878E24B0EA0900C4B605 /* Build configuration list for PBXNativeTarget "WatchApp Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF878F24B0EA0900C4B605 /* Debug */, + 42BF879024B0EA0900C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42BF879224B0EA0900C4B605 /* Build configuration list for PBXNativeTarget "WatchApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF879324B0EA0900C4B605 /* Debug */, + 42BF879424B0EA0900C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42BF87A924B0EA2500C4B605 /* Build configuration list for PBXNativeTarget "MessagesExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42BF87AA24B0EA2500C4B605 /* Debug */, + 42BF87AB24B0EA2500C4B605 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 42BF873224B0E8EE00C4B605 /* Project object */; +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..45e757e3e --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/AppWithExtensions.xcscheme b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/AppWithExtensions.xcscheme new file mode 100644 index 000000000..5d686fcd9 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/AppWithExtensions.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/MessagesExtension.xcscheme b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/MessagesExtension.xcscheme new file mode 100644 index 000000000..94affadc2 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/MessagesExtension.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/TodayViewExtension.xcscheme b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/TodayViewExtension.xcscheme new file mode 100644 index 000000000..131af9f86 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/TodayViewExtension.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp (Notification).xcscheme b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp (Notification).xcscheme new file mode 100644 index 000000000..bfc623111 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp (Notification).xcscheme @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme new file mode 100644 index 000000000..8be6e209f --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/AppDelegate.swift b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/AppDelegate.swift new file mode 100644 index 000000000..5d0695c4a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/AppDelegate.swift @@ -0,0 +1,23 @@ +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + true + } + + // MARK: UISceneSession Lifecycle + + func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_: UIApplication, didDiscardSceneSessions _: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/AppIcon.appiconset/Contents.json b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..9221b9bb1 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Base.lproj/LaunchScreen.storyboard b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/ContentView.swift b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/ContentView.swift new file mode 100644 index 000000000..dfe33f922 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/ContentView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Info.plist b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Info.plist new file mode 100644 index 000000000..9742bf0f4 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Preview Content/Preview Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/AppWithExtensions/SceneDelegate.swift b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/SceneDelegate.swift new file mode 100644 index 000000000..04d77e854 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/AppWithExtensions/SceneDelegate.swift @@ -0,0 +1,51 @@ +import SwiftUI +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo _: UISceneSession, options _: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView() + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } +} diff --git a/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json new file mode 100644 index 000000000..bc3c4eea7 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json @@ -0,0 +1,78 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x45" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x45" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "67x50" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "74x55" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "27x20" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "27x20" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "32x24" + }, + { + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "32x24" + }, + { + "idiom" : "ios-marketing", + "platform" : "ios", + "scale" : "1x", + "size" : "1024x768" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/MessagesExtension/Base.lproj/MainInterface.storyboard b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Base.lproj/MainInterface.storyboard new file mode 100644 index 000000000..36e2d4917 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/MessagesExtension/Info.plist b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Info.plist new file mode 100644 index 000000000..be315f88e --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/MessagesExtension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + MessagesExtension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.message-payload-provider + + + diff --git a/Fixtures/iOS/AppWithExtensions/MessagesExtension/MessagesViewController.swift b/Fixtures/iOS/AppWithExtensions/MessagesExtension/MessagesViewController.swift new file mode 100644 index 000000000..dc198e59c --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/MessagesExtension/MessagesViewController.swift @@ -0,0 +1,57 @@ +import Messages +import UIKit + +class MessagesViewController: MSMessagesAppViewController { + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + // MARK: - Conversation Handling + + override func willBecomeActive(with _: MSConversation) { + // Called when the extension is about to move from the inactive to active state. + // This will happen when the extension is about to present UI. + + // Use this method to configure the extension and restore previously stored state. + } + + override func didResignActive(with _: MSConversation) { + // Called when the extension is about to move from the active to inactive state. + // This will happen when the user dissmises the extension, changes to a different + // conversation or quits Messages. + + // Use this method to release shared resources, save user data, invalidate timers, + // and store enough state information to restore your extension to its current state + // in case it is terminated later. + } + + override func didReceive(_: MSMessage, conversation _: MSConversation) { + // Called when a message arrives that was generated by another instance of this + // extension on a remote device. + + // Use this method to trigger UI updates in response to the message. + } + + override func didStartSending(_: MSMessage, conversation _: MSConversation) { + // Called when the user taps the send button. + } + + override func didCancelSending(_: MSMessage, conversation _: MSConversation) { + // Called when the user deletes the message without sending it. + + // Use this to clean up state related to the deleted message. + } + + override func willTransition(to _: MSMessagesAppPresentationStyle) { + // Called before the extension transitions to a new presentation style. + + // Use this method to prepare for the change in presentation style. + } + + override func didTransition(to _: MSMessagesAppPresentationStyle) { + // Called after the extension transitions to a new presentation style. + + // Use this method to finalize any behaviors associated with the change in presentation style. + } +} diff --git a/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Base.lproj/MainInterface.storyboard b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Base.lproj/MainInterface.storyboard new file mode 100644 index 000000000..6ed74344e --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Info.plist b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Info.plist similarity index 66% rename from Info.plist rename to Fixtures/iOS/AppWithExtensions/TodayViewExtension/Info.plist index a11b3ce78..2d0c77cc0 100644 --- a/Info.plist +++ b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + TodayViewExtension CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,14 +15,17 @@ CFBundleName $(PRODUCT_NAME) CFBundlePackageType - FMWK + $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright ©. All rights reserved. - NSPrincipalClass - + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + diff --git a/Fixtures/iOS/AppWithExtensions/TodayViewExtension/TodayViewController.swift b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/TodayViewController.swift new file mode 100644 index 000000000..77b53fbb5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/TodayViewExtension/TodayViewController.swift @@ -0,0 +1,19 @@ +import NotificationCenter +import UIKit + +class TodayViewController: UIViewController, NCWidgetProviding { + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + func widgetPerformUpdate(completionHandler: @escaping (NCUpdateResult) -> Void) { + // Perform any setup necessary in order to update the view. + + // If an error is encountered, use NCUpdateResult.Failed + // If there's no update required, use NCUpdateResult.NoData + // If there's an update, use NCUpdateResult.NewData + + completionHandler(NCUpdateResult.newData) + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Contents.json new file mode 100644 index 000000000..df73a6bba --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Contents.json @@ -0,0 +1,48 @@ +{ + "assets" : [ + { + "filename" : "Circular.imageset", + "idiom" : "watch", + "role" : "circular" + }, + { + "filename" : "Extra Large.imageset", + "idiom" : "watch", + "role" : "extra-large" + }, + { + "filename" : "Graphic Bezel.imageset", + "idiom" : "watch", + "role" : "graphic-bezel" + }, + { + "filename" : "Graphic Circular.imageset", + "idiom" : "watch", + "role" : "graphic-circular" + }, + { + "filename" : "Graphic Corner.imageset", + "idiom" : "watch", + "role" : "graphic-corner" + }, + { + "filename" : "Graphic Large Rectangular.imageset", + "idiom" : "watch", + "role" : "graphic-large-rectangular" + }, + { + "filename" : "Modular.imageset", + "idiom" : "watch", + "role" : "modular" + }, + { + "filename" : "Utilitarian.imageset", + "idiom" : "watch", + "role" : "utilitarian" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json new file mode 100644 index 000000000..ed7de25e5 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">161" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ContentView.swift b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ContentView.swift new file mode 100644 index 000000000..dfe33f922 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ContentView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ExtensionDelegate.swift b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ExtensionDelegate.swift new file mode 100644 index 000000000..4af5a3d36 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ExtensionDelegate.swift @@ -0,0 +1,46 @@ +import WatchKit + +class ExtensionDelegate: NSObject, WKExtensionDelegate { + func applicationDidFinishLaunching() { + // Perform any final initialization of your application. + } + + func applicationDidBecomeActive() { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillResignActive() { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, etc. + } + + func handle(_ backgroundTasks: Set) { + // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. + for task in backgroundTasks { + // Use a switch statement to check the task type + switch task { + case let backgroundTask as WKApplicationRefreshBackgroundTask: + // Be sure to complete the background task once you’re done. + backgroundTask.setTaskCompletedWithSnapshot(false) + case let snapshotTask as WKSnapshotRefreshBackgroundTask: + // Snapshot tasks have a unique completion call, make sure to set your expiration date + snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) + case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: + // Be sure to complete the connectivity task once you’re done. + connectivityTask.setTaskCompletedWithSnapshot(false) + case let urlSessionTask as WKURLSessionRefreshBackgroundTask: + // Be sure to complete the URL session task once you’re done. + urlSessionTask.setTaskCompletedWithSnapshot(false) + case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask: + // Be sure to complete the relevant-shortcut task once you're done. + relevantShortcutTask.setTaskCompletedWithSnapshot(false) + case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask: + // Be sure to complete the intent-did-run task once you're done. + intentDidRunTask.setTaskCompletedWithSnapshot(false) + default: + // make sure to complete unhandled task types + task.setTaskCompletedWithSnapshot(false) + } + } + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/HostingController.swift b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/HostingController.swift new file mode 100644 index 000000000..b0003125e --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/HostingController.swift @@ -0,0 +1,9 @@ +import Foundation +import SwiftUI +import WatchKit + +class HostingController: WKHostingController { + override var body: ContentView { + ContentView() + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Info.plist b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Info.plist new file mode 100644 index 000000000..1a922d13c --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + WatchApp Extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + WKAppBundleIdentifier + io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp + + NSExtensionPointIdentifier + com.apple.watchkit + + WKExtensionDelegateClassName + $(PRODUCT_MODULE_NAME).ExtensionDelegate + + diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationController.swift b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationController.swift new file mode 100644 index 000000000..31ae26f25 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationController.swift @@ -0,0 +1,25 @@ +import SwiftUI +import UserNotifications +import WatchKit + +class NotificationController: WKUserNotificationHostingController { + override var body: NotificationView { + NotificationView() + } + + override func willActivate() { + // This method is called when watch view controller is about to be visible to user + super.willActivate() + } + + override func didDeactivate() { + // This method is called when watch view controller is no longer visible + super.didDeactivate() + } + + override func didReceive(_: UNNotification) { + // This method is called when a notification needs to be presented. + // Implement it if you use a dynamic notification interface. + // Populate your dynamic notification interface as quickly as possible. + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationView.swift b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationView.swift new file mode 100644 index 000000000..63211dd15 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct NotificationView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct NotificationView_Previews: PreviewProvider { + static var previews: some View { + NotificationView() + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Preview Content/Preview Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp Extension/PushNotificationPayload.apns b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/PushNotificationPayload.apns new file mode 100644 index 000000000..c18b00ad9 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp Extension/PushNotificationPayload.apns @@ -0,0 +1,20 @@ +{ + "aps": { + "alert": { + "body": "Test message", + "title": "Optional title", + "subtitle": "Optional subtitle" + }, + "category": "myCategory", + "thread-id": "5280" + }, + + "WatchKit Simulator Actions": [ + { + "title": "First Button", + "identifier": "firstButtonAction" + } + ], + + "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App." +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d06b66af9 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,81 @@ +{ + "images" : [ + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/Contents.json b/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp/Base.lproj/Interface.storyboard b/Fixtures/iOS/AppWithExtensions/WatchApp/Base.lproj/Interface.storyboard new file mode 100644 index 000000000..aa3bd9c21 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp/Base.lproj/Interface.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/AppWithExtensions/WatchApp/Info.plist b/Fixtures/iOS/AppWithExtensions/WatchApp/Info.plist new file mode 100644 index 000000000..f67806d50 --- /dev/null +++ b/Fixtures/iOS/AppWithExtensions/WatchApp/Info.plist @@ -0,0 +1,33 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + AppWithExtensions + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + WKCompanionAppBundleIdentifier + io.tuist.xcodeproj.fixtures.AppWithExtensions + WKWatchKitApp + + + diff --git a/Fixtures/iOS/BuildSettings.xcodeproj/project.pbxproj b/Fixtures/iOS/BuildSettings.xcodeproj/project.pbxproj index 59c5ef482..41985dc88 100644 --- a/Fixtures/iOS/BuildSettings.xcodeproj/project.pbxproj +++ b/Fixtures/iOS/BuildSettings.xcodeproj/project.pbxproj @@ -94,7 +94,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 11.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; a = a; a_escapedNewLine_a = "a\\na"; diff --git a/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/XCTestManifests.swift b/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/XCTestManifests.swift index 6aa89aa49..513220367 100644 --- a/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/XCTestManifests.swift +++ b/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/XCTestManifests.swift @@ -2,7 +2,7 @@ import XCTest #if !canImport(ObjectiveC) public func allTests() -> [XCTestCaseEntry] { - return [ + [ testCase(MyLocalPackageTests.allTests), ] } diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/.gitignore b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/.gitignore new file mode 100644 index 000000000..02c087533 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Package.swift b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Package.swift new file mode 100644 index 000000000..d36035345 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyOtherLocalPackage", + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "MyOtherLocalPackage", + targets: ["MyOtherLocalPackage"] + ), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "MyOtherLocalPackage", + dependencies: [] + ), + .testTarget( + name: "MyOtherLocalPackageTests", + dependencies: ["MyOtherLocalPackage"] + ), + ] +) diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/README.md b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/README.md new file mode 100644 index 000000000..08352a959 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/README.md @@ -0,0 +1,3 @@ +# MyLocalPackage + +A description of this package. diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Sources/MyOtherLocalPackage/MyOtherLocalPackage.swift b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Sources/MyOtherLocalPackage/MyOtherLocalPackage.swift new file mode 100644 index 000000000..b0980e30e --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Sources/MyOtherLocalPackage/MyOtherLocalPackage.swift @@ -0,0 +1,3 @@ +struct MyOtherLocalPackage { + var text = "Hello, World!" +} diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/LinuxMain.swift b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/LinuxMain.swift new file mode 100644 index 000000000..b4283c4b5 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import MyLocalPackageTests + +var tests = [XCTestCaseEntry]() +tests += MyLocalPackageTests.allTests() +XCTMain(tests) diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/MyLocalPackageTests.swift b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/MyLocalPackageTests.swift new file mode 100644 index 000000000..e33a4c7c2 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/MyLocalPackageTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import MyOtherLocalPackage + +final class MyLocalPackageTests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(MyOtherLocalPackage().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/XCTestManifests.swift b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/XCTestManifests.swift new file mode 100644 index 000000000..513220367 --- /dev/null +++ b/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) + public func allTests() -> [XCTestCaseEntry] { + [ + testCase(MyLocalPackageTests.allTests), + ] + } +#endif diff --git a/Fixtures/iOS/Project.xcodeproj/project.pbxproj b/Fixtures/iOS/Project.xcodeproj/project.pbxproj index 226781f1e..c4b6b20ee 100644 --- a/Fixtures/iOS/Project.xcodeproj/project.pbxproj +++ b/Fixtures/iOS/Project.xcodeproj/project.pbxproj @@ -439,7 +439,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 10.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate b/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 000000000..b62069202 Binary files /dev/null and b/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist index a03326804..80fbab8a7 100644 --- a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist @@ -17,6 +17,8 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme index ee296ad61..5ec735651 100644 --- a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme +++ b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme @@ -19,7 +19,8 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = "echo postbuild" + shellToInvoke = "/bin/sh"> @@ -44,9 +45,10 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + preferredScreenCaptureFormat = "screenshots" codeCoverageEnabled = "YES" - onlyGenerateCoverageForSpecifiedTargets = "YES" - shouldUseLaunchSchemeArgsEnv = "YES"> + onlyGenerateCoverageForSpecifiedTargets = "YES"> @@ -79,6 +81,12 @@ + + + + + + + + + + - - - - - - - - - - + allowLocationSimulation = "YES" + customLaunchCommand = "custom command"> @@ -177,6 +180,10 @@ ReferencedContainer = "container:Project.xcodeproj"> + + - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme new file mode 100644 index 000000000..7cb38d44f --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-other.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-other.xcscheme new file mode 100644 index 000000000..7cb38d44f --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-other.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-release.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-release.xcscheme new file mode 100644 index 000000000..3549252de --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-release.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/xcschememanagement.plist b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..0bcfc80ee --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,45 @@ + + + + + SchemeUserState + + Rx (Playground) 1.xcscheme + + isShown + + orderHint + 5 + + Rx (Playground) 2.xcscheme + + isShown + + orderHint + 6 + + Rx (Playground).xcscheme + + isShown + + orderHint + 4 + + iOS-debug.xcscheme + + orderHint + 0 + + iOS-release.xcscheme + + orderHint + 1 + + iOS.xcscheme_^#shared#^_ + + orderHint + 3 + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username2.xcuserdatad/xcschemes/iOSTests.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username2.xcuserdatad/xcschemes/iOSTests.xcscheme new file mode 100644 index 000000000..972b4579e --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username2.xcuserdatad/xcschemes/iOSTests.xcscheme @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username3.xcuserdatad/xcschemes/custom-scheme.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username3.xcuserdatad/xcschemes/custom-scheme.xcscheme new file mode 100644 index 000000000..7cb38d44f --- /dev/null +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username3.xcuserdatad/xcschemes/custom-scheme.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fixtures/iOS/ProjectWithRelativeXCLocalSwiftPackageReference/ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj/project.pbxproj b/Fixtures/iOS/ProjectWithRelativeXCLocalSwiftPackageReference/ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj/project.pbxproj new file mode 100644 index 000000000..9db1d6fa2 --- /dev/null +++ b/Fixtures/iOS/ProjectWithRelativeXCLocalSwiftPackageReference/ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj/project.pbxproj @@ -0,0 +1,478 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04D5C09E1F153824008A2F98 /* CoreData.framework */; }; + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C32AD603E50096A37A /* MyLocalPackage */; }; + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C62AD604310096A37A /* MyLocalPackage */; }; + C9FDF5CA2AD8AE400096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C92AD8AE400096A37A /* MyLocalPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + filePatterns = "*.myrule"; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILE_DIR)/CompiledRule", + ); + script = $TOOL_PATH/transform; + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 23766C271EAA3484007A9026 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 23766C0A1EAA3484007A9026 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 23766C111EAA3484007A9026; + remoteInfo = iOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 239688B71EBCD3B10014B321 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 0; + dstSubfolderSpec = 0; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 04D5C09E1F153824008A2F98 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 23766C121EAA3484007A9026 /* iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 23766C261EAA3484007A9026 /* iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 23766C0F1EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C9FDF5CA2AD8AE400096A37A /* MyLocalPackage in Frameworks */, + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */, + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */, + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C231EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04D5C09D1F153824008A2F98 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 04D5C09E1F153824008A2F98 /* CoreData.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 23766C091EAA3484007A9026 = { + isa = PBXGroup; + children = ( + 23766C131EAA3484007A9026 /* Products */, + 04D5C09D1F153824008A2F98 /* Frameworks */, + ); + sourceTree = ""; + }; + 23766C131EAA3484007A9026 /* Products */ = { + isa = PBXGroup; + children = ( + 23766C121EAA3484007A9026 /* iOS.app */, + 23766C261EAA3484007A9026 /* iOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 23BB67531EE326A800BE9E79 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 23766C111EAA3484007A9026 /* iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */; + buildPhases = ( + 23766C0E1EAA3484007A9026 /* Sources */, + 23766C0F1EAA3484007A9026 /* Frameworks */, + 23766C101EAA3484007A9026 /* Resources */, + 239688B71EBCD3B10014B321 /* CopyFiles */, + 23BB67521EE325E600BE9E79 /* Run Script */, + 23BB67531EE326A800BE9E79 /* Headers */, + ); + buildRules = ( + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = iOS; + packageProductDependencies = ( + C9FDF5C32AD603E50096A37A /* MyLocalPackage */, + C9FDF5C62AD604310096A37A /* MyLocalPackage */, + C9FDF5C92AD8AE400096A37A /* MyLocalPackage */, + ); + productName = iOS; + productReference = 23766C121EAA3484007A9026 /* iOS.app */; + productType = "com.apple.product-type.application"; + }; + 23766C251EAA3484007A9026 /* iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */; + buildPhases = ( + 23766C221EAA3484007A9026 /* Sources */, + 23766C231EAA3484007A9026 /* Frameworks */, + 23766C241EAA3484007A9026 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 23766C281EAA3484007A9026 /* PBXTargetDependency */, + ); + name = iOSTests; + productName = iOSTests; + productReference = 23766C261EAA3484007A9026 /* iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 23766C0A1EAA3484007A9026 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = es.ppinera; + TargetAttributes = { + 23766C111EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + }; + 23766C251EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + TestTargetID = 23766C111EAA3484007A9026; + }; + }; + }; + buildConfigurationList = 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReferences" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + Base, + ); + mainGroup = 23766C091EAA3484007A9026; + packageReferences = ( + C9FDF5C82AD8AE400096A37A /* XCLocalSwiftPackageReference "../MyLocalPackage" */, + ); + productRefGroup = 23766C131EAA3484007A9026 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 23766C111EAA3484007A9026 /* iOS */, + 23766C251EAA3484007A9026 /* iOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 23766C101EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C241EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 23BB67521EE325E600BE9E79 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/myfile", + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"/test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 23766C0E1EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C221EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 23766C281EAA3484007A9026 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 23766C111EAA3484007A9026 /* iOS */; + targetProxy = 23766C271EAA3484007A9026 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 23766C2D1EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 23766C2E1EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 23766C301EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 23766C311EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 23766C331EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Debug; + }; + 23766C341EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReferences" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C2D1EAA3484007A9026 /* Debug */, + 23766C2E1EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C301EAA3484007A9026 /* Debug */, + 23766C311EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C331EAA3484007A9026 /* Debug */, + 23766C341EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + C9FDF5C82AD8AE400096A37A /* XCLocalSwiftPackageReference "../MyLocalPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../MyLocalPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C9FDF5C32AD603E50096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; + C9FDF5C62AD604310096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; + C9FDF5C92AD8AE400096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 23766C0A1EAA3484007A9026 /* Project object */; +} diff --git a/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReference.xcodeproj/project.pbxproj b/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReference.xcodeproj/project.pbxproj new file mode 100644 index 000000000..4929f97c1 --- /dev/null +++ b/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReference.xcodeproj/project.pbxproj @@ -0,0 +1,596 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04D5C09E1F153824008A2F98 /* CoreData.framework */; }; + 04D5C0A31F153924008A2F98 /* Public.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A01F153915008A2F98 /* Public.h */; }; + 04D5C0A41F153924008A2F98 /* Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A11F15391B008A2F98 /* Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04D5C0A51F153924008A2F98 /* Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A21F153921008A2F98 /* Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C151EAA3484007A9026 /* AppDelegate.swift */; }; + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C171EAA3484007A9026 /* ViewController.swift */; }; + 23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23766C191EAA3484007A9026 /* Main.storyboard */; }; + 23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23766C1C1EAA3484007A9026 /* Assets.xcassets */; }; + 23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */; }; + 23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C2A1EAA3484007A9026 /* iOSTests.swift */; }; + 3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */; }; + 42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 42AA1A1B22AAF48100428760 /* RxSwift */; }; + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C32AD603E50096A37A /* MyLocalPackage */; }; + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C62AD604310096A37A /* MyLocalPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + filePatterns = "*.myrule"; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILE_DIR)/CompiledRule", + ); + script = $TOOL_PATH/transform; + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 23766C271EAA3484007A9026 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 23766C0A1EAA3484007A9026 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 23766C111EAA3484007A9026; + remoteInfo = iOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 239688B71EBCD3B10014B321 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 0; + dstSubfolderSpec = 0; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 04D5C09E1F153824008A2F98 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 04D5C0A01F153915008A2F98 /* Public.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Public.h; sourceTree = ""; }; + 04D5C0A11F15391B008A2F98 /* Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Protected.h; sourceTree = ""; }; + 04D5C0A21F153921008A2F98 /* Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Private.h; sourceTree = ""; }; + 23766C121EAA3484007A9026 /* iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 23766C151EAA3484007A9026 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 23766C171EAA3484007A9026 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 23766C1A1EAA3484007A9026 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 23766C1C1EAA3484007A9026 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 23766C1F1EAA3484007A9026 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 23766C211EAA3484007A9026 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 23766C261EAA3484007A9026 /* iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 23766C2A1EAA3484007A9026 /* iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSTests.swift; sourceTree = ""; }; + 23766C2C1EAA3484007A9026 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = iOS.xctestplan; sourceTree = ""; }; + 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 23766C0F1EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */, + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */, + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */, + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C231EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04D5C09D1F153824008A2F98 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 04D5C09E1F153824008A2F98 /* CoreData.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 23766C091EAA3484007A9026 = { + isa = PBXGroup; + children = ( + 23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */, + 23766C141EAA3484007A9026 /* iOS */, + 23766C291EAA3484007A9026 /* iOSTests */, + 23766C131EAA3484007A9026 /* Products */, + 04D5C09D1F153824008A2F98 /* Frameworks */, + ); + sourceTree = ""; + }; + 23766C131EAA3484007A9026 /* Products */ = { + isa = PBXGroup; + children = ( + 23766C121EAA3484007A9026 /* iOS.app */, + 23766C261EAA3484007A9026 /* iOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 23766C141EAA3484007A9026 /* iOS */ = { + isa = PBXGroup; + children = ( + 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */, + 3CD1EAD92057638200DAEECB /* GroupWithoutFolder */, + 23766C151EAA3484007A9026 /* AppDelegate.swift */, + 23766C171EAA3484007A9026 /* ViewController.swift */, + 23766C191EAA3484007A9026 /* Main.storyboard */, + 23766C1C1EAA3484007A9026 /* Assets.xcassets */, + 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */, + 23766C211EAA3484007A9026 /* Info.plist */, + 04D5C0A01F153915008A2F98 /* Public.h */, + 04D5C0A11F15391B008A2F98 /* Protected.h */, + 04D5C0A21F153921008A2F98 /* Private.h */, + ); + path = iOS; + sourceTree = ""; + }; + 23766C291EAA3484007A9026 /* iOSTests */ = { + isa = PBXGroup; + children = ( + 23766C2A1EAA3484007A9026 /* iOSTests.swift */, + 23766C2C1EAA3484007A9026 /* Info.plist */, + ); + path = iOSTests; + sourceTree = ""; + }; + 3CD1EAD92057638200DAEECB /* GroupWithoutFolder */ = { + isa = PBXGroup; + children = ( + ); + name = GroupWithoutFolder; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 23BB67531EE326A800BE9E79 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 04D5C0A41F153924008A2F98 /* Protected.h in Headers */, + 04D5C0A51F153924008A2F98 /* Private.h in Headers */, + 04D5C0A31F153924008A2F98 /* Public.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 23766C111EAA3484007A9026 /* iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */; + buildPhases = ( + 23766C0E1EAA3484007A9026 /* Sources */, + 23766C0F1EAA3484007A9026 /* Frameworks */, + 23766C101EAA3484007A9026 /* Resources */, + 239688B71EBCD3B10014B321 /* CopyFiles */, + 23BB67521EE325E600BE9E79 /* Run Script */, + 23BB67531EE326A800BE9E79 /* Headers */, + ); + buildRules = ( + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = iOS; + packageProductDependencies = ( + 42AA1A1B22AAF48100428760 /* RxSwift */, + C9FDF5C32AD603E50096A37A /* MyLocalPackage */, + C9FDF5C62AD604310096A37A /* MyLocalPackage */, + ); + productName = iOS; + productReference = 23766C121EAA3484007A9026 /* iOS.app */; + productType = "com.apple.product-type.application"; + }; + 23766C251EAA3484007A9026 /* iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */; + buildPhases = ( + 23766C221EAA3484007A9026 /* Sources */, + 23766C231EAA3484007A9026 /* Frameworks */, + 23766C241EAA3484007A9026 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 23766C281EAA3484007A9026 /* PBXTargetDependency */, + ); + name = iOSTests; + productName = iOSTests; + productReference = 23766C261EAA3484007A9026 /* iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 23766C0A1EAA3484007A9026 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = es.ppinera; + TargetAttributes = { + 23766C111EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + }; + 23766C251EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + TestTargetID = 23766C111EAA3484007A9026; + }; + }; + }; + buildConfigurationList = 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReference" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + Base, + ); + mainGroup = 23766C091EAA3484007A9026; + packageReferences = ( + 42AA19FF22AAF0D600428760 /* XCRemoteSwiftPackageReference "RxSwift" */, + C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference "MyLocalPackage" */, + ); + productRefGroup = 23766C131EAA3484007A9026 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 23766C111EAA3484007A9026 /* iOS */, + 23766C251EAA3484007A9026 /* iOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 23766C101EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */, + 23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */, + 23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C241EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 23BB67521EE325E600BE9E79 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/myfile", + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"/test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 23766C0E1EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */, + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */, + 23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */, + 3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C221EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 23766C281EAA3484007A9026 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 23766C111EAA3484007A9026 /* iOS */; + targetProxy = 23766C271EAA3484007A9026 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 23766C191EAA3484007A9026 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 23766C1A1EAA3484007A9026 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 23766C1F1EAA3484007A9026 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 23766C2D1EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 23766C2E1EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 23766C301EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 23766C311EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 23766C331EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Debug; + }; + 23766C341EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReference" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C2D1EAA3484007A9026 /* Debug */, + 23766C2E1EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C301EAA3484007A9026 /* Debug */, + 23766C311EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C331EAA3484007A9026 /* Debug */, + 23766C341EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference "MyLocalPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyLocalPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 42AA19FF22AAF0D600428760 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 42AA1A1B22AAF48100428760 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 42AA19FF22AAF0D600428760 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + C9FDF5C32AD603E50096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; + C9FDF5C62AD604310096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; +/* End XCSwiftPackageProductDependency section */ + +/* Begin XCVersionGroup section */ + 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */, + ); + currentVersion = 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */; + path = Model.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = 23766C0A1EAA3484007A9026 /* Project object */; +} diff --git a/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReferences.xcodeproj/project.pbxproj b/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReferences.xcodeproj/project.pbxproj new file mode 100644 index 000000000..4ce8b12e6 --- /dev/null +++ b/Fixtures/iOS/ProjectWithXCLocalSwiftPackageReferences.xcodeproj/project.pbxproj @@ -0,0 +1,588 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04D5C09E1F153824008A2F98 /* CoreData.framework */; }; + 04D5C0A31F153924008A2F98 /* Public.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A01F153915008A2F98 /* Public.h */; }; + 04D5C0A41F153924008A2F98 /* Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A11F15391B008A2F98 /* Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04D5C0A51F153924008A2F98 /* Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 04D5C0A21F153921008A2F98 /* Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C151EAA3484007A9026 /* AppDelegate.swift */; }; + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C171EAA3484007A9026 /* ViewController.swift */; }; + 23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23766C191EAA3484007A9026 /* Main.storyboard */; }; + 23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23766C1C1EAA3484007A9026 /* Assets.xcassets */; }; + 23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */; }; + 23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23766C2A1EAA3484007A9026 /* iOSTests.swift */; }; + 3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */; }; + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C32AD603E50096A37A /* MyLocalPackage */; }; + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5C62AD604310096A37A /* MyLocalPackage */; }; + C9FDF5CD2AD8B3B50096A37A /* MyOtherLocalPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9FDF5CC2AD8B3B50096A37A /* MyOtherLocalPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + filePatterns = "*.myrule"; + fileType = pattern.proxy; + inputFiles = ( + ); + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILE_DIR)/CompiledRule", + ); + script = $TOOL_PATH/transform; + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 23766C271EAA3484007A9026 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 23766C0A1EAA3484007A9026 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 23766C111EAA3484007A9026; + remoteInfo = iOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 239688B71EBCD3B10014B321 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 0; + dstSubfolderSpec = 0; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 04D5C09E1F153824008A2F98 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 04D5C0A01F153915008A2F98 /* Public.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Public.h; sourceTree = ""; }; + 04D5C0A11F15391B008A2F98 /* Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Protected.h; sourceTree = ""; }; + 04D5C0A21F153921008A2F98 /* Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Private.h; sourceTree = ""; }; + 23766C121EAA3484007A9026 /* iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 23766C151EAA3484007A9026 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 23766C171EAA3484007A9026 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 23766C1A1EAA3484007A9026 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 23766C1C1EAA3484007A9026 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 23766C1F1EAA3484007A9026 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 23766C211EAA3484007A9026 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 23766C261EAA3484007A9026 /* iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 23766C2A1EAA3484007A9026 /* iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSTests.swift; sourceTree = ""; }; + 23766C2C1EAA3484007A9026 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = iOS.xctestplan; sourceTree = ""; }; + 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 23766C0F1EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C9FDF5C42AD603E50096A37A /* MyLocalPackage in Frameworks */, + 04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */, + C9FDF5CD2AD8B3B50096A37A /* MyOtherLocalPackage in Frameworks */, + C9FDF5C72AD604310096A37A /* MyLocalPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C231EAA3484007A9026 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04D5C09D1F153824008A2F98 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 04D5C09E1F153824008A2F98 /* CoreData.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 23766C091EAA3484007A9026 = { + isa = PBXGroup; + children = ( + 23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */, + 23766C141EAA3484007A9026 /* iOS */, + 23766C291EAA3484007A9026 /* iOSTests */, + 23766C131EAA3484007A9026 /* Products */, + 04D5C09D1F153824008A2F98 /* Frameworks */, + ); + sourceTree = ""; + }; + 23766C131EAA3484007A9026 /* Products */ = { + isa = PBXGroup; + children = ( + 23766C121EAA3484007A9026 /* iOS.app */, + 23766C261EAA3484007A9026 /* iOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 23766C141EAA3484007A9026 /* iOS */ = { + isa = PBXGroup; + children = ( + 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */, + 3CD1EAD92057638200DAEECB /* GroupWithoutFolder */, + 23766C151EAA3484007A9026 /* AppDelegate.swift */, + 23766C171EAA3484007A9026 /* ViewController.swift */, + 23766C191EAA3484007A9026 /* Main.storyboard */, + 23766C1C1EAA3484007A9026 /* Assets.xcassets */, + 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */, + 23766C211EAA3484007A9026 /* Info.plist */, + 04D5C0A01F153915008A2F98 /* Public.h */, + 04D5C0A11F15391B008A2F98 /* Protected.h */, + 04D5C0A21F153921008A2F98 /* Private.h */, + ); + path = iOS; + sourceTree = ""; + }; + 23766C291EAA3484007A9026 /* iOSTests */ = { + isa = PBXGroup; + children = ( + 23766C2A1EAA3484007A9026 /* iOSTests.swift */, + 23766C2C1EAA3484007A9026 /* Info.plist */, + ); + path = iOSTests; + sourceTree = ""; + }; + 3CD1EAD92057638200DAEECB /* GroupWithoutFolder */ = { + isa = PBXGroup; + children = ( + ); + name = GroupWithoutFolder; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 23BB67531EE326A800BE9E79 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 04D5C0A41F153924008A2F98 /* Protected.h in Headers */, + 04D5C0A51F153924008A2F98 /* Private.h in Headers */, + 04D5C0A31F153924008A2F98 /* Public.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 23766C111EAA3484007A9026 /* iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */; + buildPhases = ( + 23766C0E1EAA3484007A9026 /* Sources */, + 23766C0F1EAA3484007A9026 /* Frameworks */, + 23766C101EAA3484007A9026 /* Resources */, + 239688B71EBCD3B10014B321 /* CopyFiles */, + 23BB67521EE325E600BE9E79 /* Run Script */, + 23BB67531EE326A800BE9E79 /* Headers */, + ); + buildRules = ( + 6B7542351FE9CEDE003DFC29 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = iOS; + packageProductDependencies = ( + C9FDF5C32AD603E50096A37A /* MyLocalPackage */, + C9FDF5C62AD604310096A37A /* MyLocalPackage */, + C9FDF5CC2AD8B3B50096A37A /* MyOtherLocalPackage */, + ); + productName = iOS; + productReference = 23766C121EAA3484007A9026 /* iOS.app */; + productType = "com.apple.product-type.application"; + }; + 23766C251EAA3484007A9026 /* iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */; + buildPhases = ( + 23766C221EAA3484007A9026 /* Sources */, + 23766C231EAA3484007A9026 /* Frameworks */, + 23766C241EAA3484007A9026 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 23766C281EAA3484007A9026 /* PBXTargetDependency */, + ); + name = iOSTests; + productName = iOSTests; + productReference = 23766C261EAA3484007A9026 /* iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 23766C0A1EAA3484007A9026 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = es.ppinera; + TargetAttributes = { + 23766C111EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + }; + 23766C251EAA3484007A9026 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Automatic; + TestTargetID = 23766C111EAA3484007A9026; + }; + }; + }; + buildConfigurationList = 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReferences" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + Base, + ); + mainGroup = 23766C091EAA3484007A9026; + packageReferences = ( + C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference "MyLocalPackage" */, + C9FDF5CB2AD8B3B50096A37A /* XCLocalSwiftPackageReference "MyOtherLocalPackage/MyOtherLocalPackage" */, + ); + productRefGroup = 23766C131EAA3484007A9026 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 23766C111EAA3484007A9026 /* iOS */, + 23766C251EAA3484007A9026 /* iOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 23766C101EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */, + 23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */, + 23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C241EAA3484007A9026 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 23BB67521EE325E600BE9E79 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/myfile", + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"/test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 23766C0E1EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */, + 23766C181EAA3484007A9026 /* ViewController.swift in Sources */, + 23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */, + 3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23766C221EAA3484007A9026 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 23766C281EAA3484007A9026 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 23766C111EAA3484007A9026 /* iOS */; + targetProxy = 23766C271EAA3484007A9026 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 23766C191EAA3484007A9026 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 23766C1A1EAA3484007A9026 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 23766C1F1EAA3484007A9026 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 23766C2D1EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 23766C2E1EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 23766C301EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 23766C311EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 23766C331EAA3484007A9026 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Debug; + }; + 23766C341EAA3484007A9026 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = iOSTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.iOSTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOS.app/iOS"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 23766C0D1EAA3484007A9026 /* Build configuration list for PBXProject "ProjectWithXCLocalSwiftPackageReferences" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C2D1EAA3484007A9026 /* Debug */, + 23766C2E1EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C2F1EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C301EAA3484007A9026 /* Debug */, + 23766C311EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23766C321EAA3484007A9026 /* Build configuration list for PBXNativeTarget "iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23766C331EAA3484007A9026 /* Debug */, + 23766C341EAA3484007A9026 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference "MyLocalPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyLocalPackage; + }; + C9FDF5CB2AD8B3B50096A37A /* XCLocalSwiftPackageReference "MyOtherLocalPackage/MyOtherLocalPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyOtherLocalPackage/MyOtherLocalPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C9FDF5C32AD603E50096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; + C9FDF5C62AD604310096A37A /* MyLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLocalPackage; + }; + C9FDF5CC2AD8B3B50096A37A /* MyOtherLocalPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = MyOtherLocalPackage; + }; +/* End XCSwiftPackageProductDependency section */ + +/* Begin XCVersionGroup section */ + 3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */, + ); + currentVersion = 3CD1EADC205763E400DAEECB /* Model.xcdatamodel */; + path = Model.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = 23766C0A1EAA3484007A9026 /* Project object */; +} diff --git a/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Fixtures/iOS/SameName/SameName.h b/Fixtures/iOS/SameName/SameName.h new file mode 100644 index 000000000..520f4062e --- /dev/null +++ b/Fixtures/iOS/SameName/SameName.h @@ -0,0 +1,19 @@ +// +// SameName.h +// SameName +// +// Created by Timothy Costa on 2022/06/10. +// Copyright © 2022 es.ppinera. All rights reserved. +// + +#import + +//! Project version number for SameName. +FOUNDATION_EXPORT double SameNameVersionNumber; + +//! Project version string for SameName. +FOUNDATION_EXPORT const unsigned char SameNameVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Fixtures/iOS/iOS/AppDelegate.swift b/Fixtures/iOS/iOS/AppDelegate.swift index 39daea524..22f27cb41 100644 --- a/Fixtures/iOS/iOS/AppDelegate.swift +++ b/Fixtures/iOS/iOS/AppDelegate.swift @@ -8,13 +8,13 @@ import UIKit -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - return true + true } func applicationWillResignActive(_: UIApplication) { diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 76ee19358..000000000 --- a/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source "https://rubygems.org" - -gem "rake" -gem "jazzy" -gem "cocoapods", "1.8.4" -gem "colorize", "~> 0.8.1" -gem "redcarpet", "~> 3.5.0" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 6141a259e..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,109 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.1) - activesupport (4.2.11.1) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - algoliasearch (1.27.1) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - atomos (0.1.3) - claide (1.0.3) - cocoapods (1.8.4) - activesupport (>= 4.0.2, < 5) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.8.4) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.6.6) - nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.11.1, < 2.0) - cocoapods-core (1.8.4) - activesupport (>= 4.0.2, < 6) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.2.2) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.0) - cocoapods-stats (1.1.0) - cocoapods-trunk (1.4.1) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.1.0) - colored2 (3.1.2) - colorize (0.8.1) - concurrent-ruby (1.1.5) - escape (0.0.4) - ffi (1.11.2) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - httpclient (2.8.3) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - jazzy (0.12.0) - cocoapods (~> 1.5) - mustache (~> 1.1) - open4 - redcarpet (~> 3.4) - rouge (>= 2.0.6, < 4.0) - sassc (~> 2.1) - sqlite3 (~> 1.3) - xcinvoke (~> 0.3.0) - json (2.2.0) - liferaft (0.0.6) - minitest (5.13.0) - molinillo (0.6.6) - mustache (1.1.0) - nanaimo (0.2.6) - nap (1.1.0) - netrc (0.11.0) - open4 (1.3.4) - rake (13.0.1) - redcarpet (3.5.0) - rouge (3.12.0) - ruby-macho (1.4.0) - sassc (2.2.1) - ffi (~> 1.9) - sqlite3 (1.4.1) - thread_safe (0.3.6) - tzinfo (1.2.5) - thread_safe (~> 0.1) - xcinvoke (0.3.0) - liferaft (~> 0.0.6) - xcodeproj (1.13.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.2.6) - -PLATFORMS - ruby - -DEPENDENCIES - cocoapods (= 1.8.4) - colorize (~> 0.8.1) - jazzy - rake - redcarpet (~> 3.5.0) - -BUNDLED WITH - 2.0.2 diff --git a/Package.resolved b/Package.resolved index 45383ed00..8fbe88542 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,34 +1,33 @@ { - "object": { - "pins": [ - { - "package": "AEXML", - "repositoryURL": "https://github.com/tadija/AEXML", - "state": { - "branch": null, - "revision": "e4d517844dd03dac557e35d77a8e9ab438de91a6", - "version": "4.4.0" - } - }, - { - "package": "PathKit", - "repositoryURL": "https://github.com/kylef/PathKit", - "state": { - "branch": null, - "revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511", - "version": "1.0.0" - } - }, - { - "package": "Spectre", - "repositoryURL": "https://github.com/kylef/Spectre.git", - "state": { - "branch": null, - "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5", - "version": "0.9.0" - } + "originHash" : "8a13a61314cb0e10f1d22dddaabfebd1ab407a7b9ab06aa42fc1e38041a202a5", + "pins" : [ + { + "identity" : "aexml", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tadija/AEXML.git", + "state" : { + "revision" : "db806756c989760b35108146381535aec231092b", + "version" : "4.7.0" } - ] - }, - "version": 1 + }, + { + "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" + } + } + ], + "version" : 3 } diff --git a/Package.swift b/Package.swift index 432e74a68..acc91a1be 100644 --- a/Package.swift +++ b/Package.swift @@ -1,21 +1,25 @@ -// swift-tools-version:5.0 +// swift-tools-version:6.0.0 import PackageDescription let package = Package( name: "XcodeProj", + platforms: [.macOS(.v11)], products: [ .library(name: "XcodeProj", targets: ["XcodeProj"]), ], dependencies: [ - .package(url: "https://github.com/tadija/AEXML", .upToNextMinor(from: "4.4.0")), - .package(url: "https://github.com/kylef/PathKit", .upToNextMinor(from: "1.0.0")), + .package(url: "https://github.com/tadija/AEXML.git", .upToNextMinor(from: "4.7.0")), + .package(url: "https://github.com/kylef/PathKit.git", .upToNextMinor(from: "1.0.1")), ], targets: [ .target(name: "XcodeProj", dependencies: [ - "PathKit", - "AEXML", + .product(name: "PathKit", package: "PathKit"), + .product(name: "AEXML", package: "AEXML"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency"), ]), .testTarget(name: "XcodeProjTests", dependencies: ["XcodeProj"]), ] diff --git a/Project.swift b/Project.swift deleted file mode 100644 index d96f3f5ac..000000000 --- a/Project.swift +++ /dev/null @@ -1,15 +0,0 @@ -import ProjectDescription - -let project = Project(name: "XcodeProj_Carthage", - targets: [ - Target(name: "XcodeProj", - platform: .macOS, - product: .framework, - bundleId: "io.tuist.XcodeProj", - infoPlist: "Info.plist", - sources: "Sources/XcodeProj/**", - dependencies: [ - .framework(path: "Carthage/Build/Mac/AEXML.framework"), - .framework(path: "Carthage/Build/Mac/PathKit.framework"), - ]), - ]) diff --git a/README.md b/README.md index 1480f70a7..87e12e720 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,42 @@ # XcodeProj + +[![All Contributors](https://img.shields.io/badge/all_contributors-47-orange.svg?style=flat-square)](#contributors-) + + [![Swift Package Manager](https://img.shields.io/badge/swift%20package%20manager-compatible-brightgreen.svg)](https://swift.org/package-manager/) [![Release](https://img.shields.io/github/release/tuist/xcodeproj.svg)](https://github.com/tuist/xcodeproj/releases) -[![Code Coverage](https://codecov.io/gh/tuist/xcodeproj/branch/master/graph/badge.svg)](https://codecov.io/gh/tuist/xcodeproj) -[![Slack](http://slack.tuist.io/badge.svg)](http://slack.tuist.io/) -[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/tuist/xcodeproj/blob/master/LICENSE.md) +[![Code Coverage](https://codecov.io/gh/tuist/xcodeproj/branch/main/graph/badge.svg)](https://codecov.io/gh/tuist/xcodeproj) +[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/tuist/xcodeproj/blob/main/LICENSE.md) +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ftuist%2FXcodeProj%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/tuist/XcodeProj) -XcodeProj is a library written in Swift for parsing and working with Xcode projects. It's heavily inspired in [CocoaPods XcodeProj](https://github.com/CocoaPods/Xcodeproj) and [xcode](https://www.npmjs.com/package/xcode). +XcodeProj is a library written in Swift for parsing and working with Xcode projects. It's heavily inspired by [CocoaPods XcodeProj](https://github.com/CocoaPods/Xcodeproj) and [xcode](https://www.npmjs.com/package/xcode). --- -- [Projects Using XcodeProj](#projects-using-xcodeproj) -- [Installation](#installation) -- [Contributing](#contributing) -- [License](#license) +- [XcodeProj](#xcodeproj) + - [Projects Using XcodeProj](#projects-using-xcodeproj) + - [Installation](#installation) + - [Swift Package Manager](#swift-package-manager) + - [Scripting](#scripting) + - [References 📚](#references-) + - [Contributing](#contributing) + - [License](#license) + - [Contributors ✨](#contributors-) ## Projects Using XcodeProj -| Project | Repository | -| -------- | -------------------------------------------------------------------------------------- | -| Tuist | [github.com/tuist/tuist](https://github.com/tuist/tuist) | -| Sourcery | [github.com/krzysztofzablocki/Sourcery](https://github.com/krzysztofzablocki/Sourcery) | -| ProjLint | [github.com/JamitLabs/ProjLint](https://github.com/JamitLabs/ProjLint) | -| XcodeGen | [github.com/yonaskolb/XcodeGen](https://github.com/yonaskolb/XcodeGen) | -| xspm | [gitlab.com/Pyroh/xspm](https://gitlab.com/Pyroh/xspm) | +| Project | Repository | +| --------------- | ------------------------------------------------------------------------------------------------ | +| ProjLint | [github.com/JamitLabs/ProjLint](https://github.com/JamitLabs/ProjLint) | +| rules_xcodeproj | [github.com/buildbuddy-io/rules_xcodeproj](https://github.com/buildbuddy-io/rules_xcodeproj) | +| Rugby | [github.com/swiftyfinch/Rugby](https://github.com/swiftyfinch/Rugby) | +| Sourcery | [github.com/krzysztofzablocki/Sourcery](https://github.com/krzysztofzablocki/Sourcery) | +| Tuist | [github.com/tuist/tuist](https://github.com/tuist/tuist) | +| XcodeGen | [github.com/yonaskolb/XcodeGen](https://github.com/yonaskolb/XcodeGen) | +| xspm | [gitlab.com/Pyroh/xspm](https://gitlab.com/Pyroh/xspm) | +| Privacy Manifest| [github.com/stelabouras/privacy-manifest](https://github.com/stelabouras/privacy-manifest) | +| XcodeProjectCLI | [github.com/wojciech-kulik/XcodeProjectCLI](https://github.com/wojciech-kulik/XcodeProjectCLI) | If you are also leveraging XcodeProj in your project, feel free to open a PR to include it in the list above. @@ -37,8 +50,8 @@ Add the dependency in your `Package.swift` file: let package = Package( name: "myproject", dependencies: [ - .package(url: "https://github.com/tuist/xcodeproj.git", .upToNextMajor(from: "7.5.0")), - ], + .package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.12.0")), + ], targets: [ .target( name: "myproject", @@ -47,21 +60,6 @@ let package = Package( ) ``` -### Carthage - -**Only macOS** - -```bash -# Cartfile -github "tuist/xcodeproj" ~> 7.5.0 -``` - -### CocoaPods - -```ruby -pod 'xcodeproj', '~> 7.5.0' -``` - ### Scripting Using [`swift-sh`] you can automate project-tasks using scripts, for example we @@ -71,7 +69,7 @@ git tag that represents the project’s version: ```swift #!/usr/bin/swift sh import Foundation -import XcodeProj // @tuist ~> 7.5.0 +import XcodeProj // @tuist ~> 8.8.0 import PathKit guard CommandLine.arguments.count == 3 else { @@ -108,34 +106,101 @@ object. [`swift-sh`]: https://github.com/mxcl/swift-sh -## Documentation 📝 - -Want to start using XcodeProj? Start by digging into our [documentation](/Documentation) which will help you get familiar with the API and get to know more about the Xcode projects structure. - ## References 📚 - [Xcode Project File Format](http://www.monobjc.net/xcode-project-file-format.html) -- [A brief look at the Xcode project format](http://danwright.info/blog/2010/10/xcode-pbxproject-files/) - [pbexplorer](https://github.com/mjmsmith/pbxplorer) - [pbxproj identifiers](https://pewpewthespells.com/blog/pbxproj_identifiers.html) - [mob-pbxproj](https://github.com/kronenthaler/mod-pbxproj) - [Xcodeproj](https://github.com/CocoaPods/Xcodeproj) - [Nanaimo](https://github.com/CocoaPods/Nanaimo) - [Facebook Buck](https://buckbuild.com/javadoc/com/facebook/buck/apple/xcode/xcodeproj/package-summary.html) -- [Swift Package Manager - Xcodeproj](https://github.com/apple/swift-package-manager/tree/master/Sources/Xcodeproj) ## Contributing 1. Git clone the repository `git@github.com:tuist/xcodeproj.git`. -2. Generate xcodeproj with `swift package generate-xcodeproj`. -3. Open `XcodeProj.xcodeproj`. +2. Open `Package.swift` with Xcode. ## License -XcodeProj is released under the MIT license. [See LICENSE](https://github.com/tuist/xcodeproj/blob/master/LICENSE.md) for details. - -## Open source - -Tuist is a proud supporter of the [Software Freedom Conservacy](https://sfconservancy.org/) - -Become a Conservancy Supporter! +XcodeProj is released under the MIT license. [See LICENSE](https://github.com/tuist/xcodeproj/blob/main/LICENSE.md) for details. + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Joseph Colicchio
Joseph Colicchio

🤔
deatondg
deatondg

🤔
Dan Fleming
Dan Fleming

💻
Sascha Schwabbauer
Sascha Schwabbauer

🤔
Marcin Iwanicki
Marcin Iwanicki

🚧
Adam Khazi
Adam Khazi

🚧
Elliott Williams
Elliott Williams

💻
Muukii
Muukii

🖋
Yuya Oka
Yuya Oka

💻
Keith Smiley
Keith Smiley

🖋
Ian Leitch
Ian Leitch

💻
Daniil Subbotin
Daniil Subbotin

💻
Florentin Bekier
Florentin Bekier

💻
Vadim Smal
Vadim Smal

🐛
freddi(Yuki Aki)
freddi(Yuki Aki)

💻
Kristopher Jackson
Kristopher Jackson

💻
Jake Prickett
Jake Prickett

💻
Jake Adams
Jake Adams

💻
matsuji
matsuji

💻
Bogdan Belogurov
Bogdan Belogurov

💻
Chuck Grindel
Chuck Grindel

💻
Michael McGuire
Michael McGuire

💻
C-凡
C-凡

💻
Maxwell Elliott
Maxwell Elliott

💻
Brentley Jones
Brentley Jones

💻
Teameh
Teameh

💻
Johannes Ebeling
Johannes Ebeling

💻
baegteun
baegteun

📖
Alex Kovács
Alex Kovács

📖
Christoffer Winterkvist
Christoffer Winterkvist

💻
Timothy Costa
Timothy Costa

💻
Mary
Mary

💻
Md. Ibrahim Hassan
Md. Ibrahim Hassan

💻
tatagrigory
tatagrigory

💻
Ruslan Alikhamov
Ruslan Alikhamov

💻
Ladislas de Toldi
Ladislas de Toldi

💻
Matt Massicotte
Matt Massicotte

💻
Артем Ворхлик
Артем Ворхлик

💻
Jaewon-Yun
Jaewon-Yun

💻
Mike Gerasymenko
Mike Gerasymenko

💻
Filip Racki
Filip Racki

💻
Kelvin Harron
Kelvin Harron

💻
George Navarro
George Navarro

💻
Maxim
Maxim

💻
Bryan Summersett
Bryan Summersett

💻
Mikhail
Mikhail

💻
Michael
Michael

💻
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index c58657b4b..000000000 --- a/RELEASE.md +++ /dev/null @@ -1,7 +0,0 @@ -# Releasing - -In this document you'll find all the necessary steps to release a new version of `xcodeproj`: - -1. Run `tapestry release version-number` (eg `tapestry release 7.1.0`) *(Install [tapestry](https://github.com/ackeecz/tapestry) and [tuist](https://github.com/tuist/tuist) if you don't have them installed already)*. -2. Create a new release on [GitHub](https://github.com/tuist/XcodeProj) including the information from the last entry in the `CHANGELOG.md`. -3. Attach `XcodeProj.framework.zip` to the GitHub release. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 65e21d28c..000000000 --- a/Rakefile +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/rake - -require 'fileutils' -require 'colorize' - -task :style_correct do - system("swiftformat .") - system("swiftlint autocorrect") -end - -task :release_check do - puts "Running tests".colorize(:cyan) - system("swift test") - - puts "Building for release".colorize(:cyan) - system("swift build -c release") - - puts "Compiling Carthage project".colorize(:cyan) - system("xcodebuild -project Xcodeproj_Carthage.xcodeproj -scheme XcodeProj") -end - -task :archive_carthage do - system("carthage build --no-skip-current --platform macOS") - system("carthage archive xcodeproj") -end - -task :carthage_update_dependencies do - system("carthage update --platform macOS") -end - -def system(*args) - Kernel.system(*args) || abort -end diff --git a/Sources/XcodeProj/Errors/Errors.swift b/Sources/XcodeProj/Errors/Errors.swift index 1b781e8b4..2f46663e0 100644 --- a/Sources/XcodeProj/Errors/Errors.swift +++ b/Sources/XcodeProj/Errors/Errors.swift @@ -1,5 +1,5 @@ import Foundation -import PathKit +@preconcurrency import PathKit // MARK: - Xcodeproj @@ -8,7 +8,7 @@ import PathKit /// - notFound: the project cannot be found. /// - pbxProjNotFound: the .pbxproj file couldn't be found inside the project folder. /// - xcworkspaceNotFound: the workspace cannot be found at the given path. -public enum XCodeProjError: Error, CustomStringConvertible { +public enum XCodeProjError: Error, CustomStringConvertible, Sendable { case notFound(path: Path) case pbxprojNotFound(path: Path) case xcworkspaceNotFound(path: Path) @@ -16,11 +16,11 @@ public enum XCodeProjError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return "The project cannot be found at \(path.string)" + "The project cannot be found at \(path.string)" case let .pbxprojNotFound(path): - return "The project doesn't contain a .pbxproj file at path: \(path.string)" + "The project doesn't contain a .pbxproj file at path: \(path.string)" case let .xcworkspaceNotFound(path): - return "The project doesn't contain a .xcworkspace at path: \(path.string)" + "The project doesn't contain a .xcworkspace at path: \(path.string)" } } } @@ -36,7 +36,23 @@ public enum XCSharedDataError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return "xcshareddata not found at path \(path.string)" + "xcshareddata not found at path \(path.string)" + } + } +} + +// MARK: - XCUserData + +/// XCUserData errors. +/// +/// - notFound: the user data hasn't been found. +public enum XCUserDataError: Error, CustomStringConvertible { + case notFound(path: Path) + + public var description: String { + switch self { + case let .notFound(path): + "xcuserdata not found at path \(path.string)" } } } @@ -52,7 +68,7 @@ public enum XCWorkspaceError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return "The project cannot be found at \(path.string)" + "The project cannot be found at \(path.string)" } } } @@ -68,7 +84,7 @@ public enum XCWorkspaceDataError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return "Workspace not found at \(path.string)" + "Workspace not found at \(path.string)" } } } @@ -84,7 +100,7 @@ public enum XcodeprojEditingError: Error, CustomStringConvertible { public var description: String { switch self { case let .unexistingFile(path): - return "The file at path \(path.string) doesn't exist" + "The file at path \(path.string) doesn't exist" } } } @@ -100,7 +116,7 @@ public enum XcodeprojWritingError: Error, CustomStringConvertible { public var description: String { switch self { case let .invalidType(classType, expected): - return "Invalid type for object \(classType) that expects a \(expected)" + "Invalid type for object \(classType) that expects a \(expected)" } } } @@ -116,6 +132,7 @@ public enum XcodeprojWritingError: Error, CustomStringConvertible { /// - orphaned: the object doesn't belong to any project. public enum PBXObjectError: Error, CustomStringConvertible { case missingIsa + case missingReference case unknownElement(String) case objectsReleased case objectNotFound(String) @@ -124,15 +141,17 @@ public enum PBXObjectError: Error, CustomStringConvertible { public var description: String { switch self { case .missingIsa: - return "Isa property is missing." + "Isa property is missing." case let .unknownElement(element): - return "The element \(element) is not supported." + "The element \(element) is not supported." case .objectsReleased: - return "The PBXObjects instance has been released before saving." + "The PBXObjects instance has been released before saving." case let .objectNotFound(reference): - return "PBXObject with reference \"\(reference)\" not found." + "PBXObject with reference \"\(reference)\" not found." case let .orphaned(type, reference): - return "Trying to use object \(type) with reference '\(reference)' before being added to any project" + "Trying to use object \(type) with reference '\(reference)' before being added to any project" + case .missingReference: + "Missing reference value" } } } @@ -148,7 +167,7 @@ enum PBXProjEncoderError: Error, CustomStringConvertible { var description: String { switch self { case .emptyProjectReference: - return "PBXProj should contain a reference to the XcodeProj object that represents the project" + "PBXProj should contain a reference to the XcodeProj object that represents the project" } } } @@ -167,24 +186,27 @@ enum PBXProjError: Error, CustomStringConvertible, Equatable { case pathIsAbsolute(Path) case multipleLocalPackages(productName: String) case multipleRemotePackages(productName: String) + case malformed var description: String { switch self { case let .notFound(path): - return ".pbxproj not found at path \(path.string)" + ".pbxproj not found at path \(path.string)" case let .invalidGroupPath(sourceRoot, elementPath): - return "Cannot calculate full path for file element \"\(elementPath ?? "")\" in source root: \"\(sourceRoot)\"" + "Cannot calculate full path for file element \"\(elementPath ?? "")\" in source root: \"\(sourceRoot)\"" case let .targetNotFound(targetName): - return "Could not find target with \(targetName)" + "Could not find target with \(targetName)" case let .frameworksBuildPhaseNotFound(targetName): - return "Could not find frameworks build phase for target \(targetName)" + "Could not find frameworks build phase for target \(targetName)" case let .sourcesBuildPhaseNotFound(targetName): - return "Could not find sources build phase for target \(targetName)" + "Could not find sources build phase for target \(targetName)" case let .pathIsAbsolute(path): - return "Path must be relative, but path \(path.string) is absolute" + "Path must be relative, but path \(path.string) is absolute" case let .multipleLocalPackages(productName: productName): - return "Found multiple top-level packages named \(productName)" + "Found multiple top-level packages named \(productName)" case let .multipleRemotePackages(productName: productName): - return "Can not resolve dependency \(productName) - conflicting version requirements" + "Can not resolve dependency \(productName) - conflicting version requirements" + case .malformed: + "The .pbxproj is malformed." } } } @@ -202,9 +224,9 @@ public enum XCBreakpointListError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return "Breakpoints_v2.xcbkptlist couldn't be found at path \(path.string)" + "Breakpoints_v2.xcbkptlist couldn't be found at path \(path.string)" case let .missing(property): - return "Property \(property) missing" + "Property \(property) missing" } } } @@ -219,7 +241,7 @@ public enum XCConfigError: Error, CustomStringConvertible { public var description: String { switch self { case let .notFound(path): - return ".xcconfig file not found at \(path.string)" + ".xcconfig file not found at \(path.string)" } } } diff --git a/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift b/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift index 6b6ae4a90..85f5bf96d 100644 --- a/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift +++ b/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift @@ -13,6 +13,8 @@ let attributesOrder: [String: [String]] = [ "BuildAction": [ "parallelizeBuildables", "buildImplicitDependencies", + "buildArchitectures", + "runPostActionsOnFailure", ], "BuildActionEntry": [ "buildForTesting", @@ -32,32 +34,43 @@ let attributesOrder: [String: [String]] = [ "buildConfiguration", "selectedDebuggerIdentifier", "selectedLauncherIdentifier", + "customLLDBInitFile", "language", + "shouldUseLaunchSchemeArgsEnv", + "disableMainThreadChecker", "region", + "preferredScreenCaptureFormat", "codeCoverageEnabled", "onlyGenerateCoverageForSpecifiedTargets", - "shouldUseLaunchSchemeArgsEnv", ], "LaunchAction": [ "buildConfiguration", "selectedDebuggerIdentifier", "selectedLauncherIdentifier", + "customLLDBInitFile", + "disableMainThreadChecker", + "disablePerformanceAntipatternChecker", "language", "region", "launchStyle", + "askForAppToLaunch", "useCustomWorkingDirectory", + "customWorkingDirectory", "ignoresPersistentStateOnLaunch", "debugDocumentVersioning", "debugServiceExtension", "enableGPUFrameCaptureMode", "enableGPUValidationMode", "allowLocationSimulation", + "debugAsWhichUser", + "storeKitConfigurationFileReference", ], "ProfileAction": [ "buildConfiguration", "shouldUseLaunchSchemeArgsEnv", "savedToolIdentifier", "useCustomWorkingDirectory", + "customWorkingDirectory", "ignoresPersistentStateOnLaunch", "debugDocumentVersioning", "enableTestabilityWhenProfilingTests", @@ -88,6 +101,16 @@ let attributesOrder: [String: [String]] = [ "symbolName", "moduleName", ], + "RemoteRunnable": [ + "runnableDebuggingMode", + "BundleIdentifier", + "RemotePath", + ], + "Scheme": [ + "LastUpgradeVersion", + "wasCreatedForAppExtension", + "version", + ], ] extension AEXMLElement { @@ -106,8 +129,8 @@ extension AEXMLElement { if !attributes.isEmpty { // insert known attributes in the specified order. - var attributes = self.attributes - for key in attributesOrder[self.name] ?? [] { + var attributes = attributes + for key in attributesOrder[name] ?? [] { if let value = attributes.removeValue(forKey: key) { print(key: key, value: value) } diff --git a/Sources/XcodeProj/Extensions/Array+Extras.swift b/Sources/XcodeProj/Extensions/Array+Extras.swift index 310fc3e92..851c00554 100644 --- a/Sources/XcodeProj/Extensions/Array+Extras.swift +++ b/Sources/XcodeProj/Extensions/Array+Extras.swift @@ -1,13 +1,13 @@ import Foundation -extension Array where Element: Hashable { +public extension Array where Element: Hashable { /// Return the array with all duplicates removed. /// /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]` /// /// - note: Taken from stackoverflow.com/a/46354989/3141234, as /// per @Alexander's comment. - public func uniqued() -> [Element] { + func uniqued() -> [Element] { var seen = Set() return filter { seen.insert($0).inserted } } diff --git a/Sources/XcodeProj/Extensions/Bool+Extras.swift b/Sources/XcodeProj/Extensions/Bool+Extras.swift index b485d6f85..b4a7923d1 100644 --- a/Sources/XcodeProj/Extensions/Bool+Extras.swift +++ b/Sources/XcodeProj/Extensions/Bool+Extras.swift @@ -3,11 +3,11 @@ import Foundation public extension Bool { /// Returns a XML string value that represents the boolean. var xmlString: String { - return self ? "YES" : "NO" + self ? "YES" : "NO" } /// Returns a 1 for true and 0 for false var int: UInt { - return self ? 1 : 0 + self ? 1 : 0 } } diff --git a/Sources/XcodeProj/Extensions/Dictionary+Enumerate.swift b/Sources/XcodeProj/Extensions/Dictionary+Enumerate.swift deleted file mode 100644 index b6a6b91a3..000000000 --- a/Sources/XcodeProj/Extensions/Dictionary+Enumerate.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -extension Dictionary { - func enumerateKeysAndObjects( - options opts: NSEnumerationOptions = [], - using block: (Any, Any, UnsafeMutablePointer) throws -> Void - ) throws { - var blockError: Error? - // For performance it is very important to create a separate dictionary instance. - // (self as NSDictionary).enumerateKeys... - works much slower - let dictionary = NSDictionary(dictionary: self) - dictionary.enumerateKeysAndObjects(options: opts) { key, obj, stops in - do { - try block(key, obj, stops) - } catch { - blockError = error - stops.pointee = true - } - } - if let error = blockError { - throw error - } - } -} diff --git a/Sources/XcodeProj/Extensions/Dictionary+Extras.swift b/Sources/XcodeProj/Extensions/Dictionary+Extras.swift deleted file mode 100644 index af9389332..000000000 --- a/Sources/XcodeProj/Extensions/Dictionary+Extras.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -/// Static initializer that creates a Dictionary from a .plist file. -/// -/// - Parameter path: the path of the .plist file. -/// - Returns: initialized dictionary. -public func loadPlist(path: String) -> [String: AnyObject]? { - return NSDictionary(contentsOfFile: path) as? [String: AnyObject] -} diff --git a/Sources/XcodeProj/Extensions/KeyedDecodingContainer+Additions.swift b/Sources/XcodeProj/Extensions/KeyedDecodingContainer+Additions.swift index b944ef6d8..7ff253646 100644 --- a/Sources/XcodeProj/Extensions/KeyedDecodingContainer+Additions.swift +++ b/Sources/XcodeProj/Extensions/KeyedDecodingContainer+Additions.swift @@ -2,31 +2,43 @@ import Foundation extension KeyedDecodingContainer { func decode(_ key: KeyedDecodingContainer.Key) throws -> T where T: Decodable { - return try decode(T.self, forKey: key) + try decode(T.self, forKey: key) } func decodeIfPresent(_ key: KeyedDecodingContainer.Key) throws -> T? where T: Decodable { - return try decodeIfPresent(T.self, forKey: key) + try decodeIfPresent(T.self, forKey: key) } func decodeIntIfPresent(_ key: KeyedDecodingContainer.Key) throws -> UInt? { - guard let string: String = try decodeIfPresent(key) else { - return nil + if let string: String = try? decodeIfPresent(key) { + UInt(string) + } else if let bool: Bool = try? decodeIfPresent(key) { + bool ? 0 : 1 + } else if let int: UInt = try decodeIfPresent(key) { + // don't `try?` here in case key _does_ exist but isn't an expected type + // ie. not a string/bool/int + int + } else { + nil } - return UInt(string) } func decodeIntBool(_ key: KeyedDecodingContainer.Key) throws -> Bool { - guard let int = try decodeIntIfPresent(key) else { + guard let bool = try decodeIntBoolIfPresent(key) else { return false } - return int == 1 + return bool } func decodeIntBoolIfPresent(_ key: KeyedDecodingContainer.Key) throws -> Bool? { guard let int = try decodeIntIfPresent(key) else { return nil } + + guard int <= 2 else { + throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Expected to decode Bool but found a number that is not 0 or 1") + } + return int == 1 } } diff --git a/Sources/XcodeProj/Extensions/Path+Extras.swift b/Sources/XcodeProj/Extensions/Path+Extras.swift index 823c45bfb..6327ac822 100644 --- a/Sources/XcodeProj/Extensions/Path+Extras.swift +++ b/Sources/XcodeProj/Extensions/Path+Extras.swift @@ -1,10 +1,17 @@ import Foundation + // swiftlint:disable all import PathKit // MARK: - Path extras. -let systemGlob = Darwin.glob +func systemGlob(_ pattern: UnsafePointer!, _ flags: Int32, _ errfunc: (@convention(c) (UnsafePointer?, Int32) -> Int32)!, _ vector_ptr: UnsafeMutablePointer!) -> Int32 { + #if os(macOS) || os(iOS) + return Darwin.glob(pattern, flags, errfunc, vector_ptr) + #else + return Glibc.glob(pattern, flags, errfunc, vector_ptr) + #endif +} extension Path { /// Creates a directory @@ -20,7 +27,9 @@ extension Path { /// - Returns: found directories and files. func glob(_ pattern: String) -> [Path] { var gt = glob_t() - let cPattern = strdup((self + pattern).string) + guard let cPattern = strdup((self + pattern).string) else { + fatalError("strdup returned null: Likely out of memory") + } defer { globfree(>) free(cPattern) @@ -28,9 +37,13 @@ extension Path { let flags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK if systemGlob(cPattern, flags, nil, >) == 0 { - let matchc = gt.gl_matchc + #if os(macOS) + let matchc = gt.gl_matchc + #else + let matchc = gt.gl_pathc + #endif return (0 ..< Int(matchc)).compactMap { index in - if let path = String(validatingUTF8: gt.gl_pathv[index]!) { + if let path = String(validatingCString: gt.gl_pathv[index]!) { return Path(path) } return nil diff --git a/Sources/XcodeProj/Extensions/String+Utils.swift b/Sources/XcodeProj/Extensions/String+Utils.swift index 6765cb31b..b5f4a2614 100644 --- a/Sources/XcodeProj/Extensions/String+Utils.swift +++ b/Sources/XcodeProj/Extensions/String+Utils.swift @@ -1,29 +1,21 @@ import Foundation -#if os(Linux) - import SwiftGlibc - - public func arc4random_uniform(_ max: UInt32) -> Int32 { - return (SwiftGlibc.rand() % Int32(max - 1)) - } -#endif - -extension String { - public var quoted: String { - return "\"\(self)\"" +public extension String { + var quoted: String { + "\"\(self)\"" } - public var isQuoted: Bool { - return hasPrefix("\"") && hasSuffix("\"") + var isQuoted: Bool { + hasPrefix("\"") && hasSuffix("\"") } - public static func random(length: Int = 20) -> String { + static func random(length: Int = 20) -> String { let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var randomString: String = "" + var randomString = "" for _ in 0 ..< length { - let randomValue = arc4random_uniform(UInt32(base.count)) - randomString += "\(base[base.index(base.startIndex, offsetBy: Int(randomValue))])" + let randomValue = Int.random(in: 0 ..< base.count) + randomString += "\(base[base.index(base.startIndex, offsetBy: randomValue)])" } return randomString } diff --git a/Sources/XcodeProj/Extensions/String+md5.swift b/Sources/XcodeProj/Extensions/String+md5.swift index c1e7bc000..04be85b4c 100644 --- a/Sources/XcodeProj/Extensions/String+md5.swift +++ b/Sources/XcodeProj/Extensions/String+md5.swift @@ -1,6 +1,6 @@ // swiftlint:disable all // -// String+MD5.swift +// String+md5.swift // Kingfisher // // To date, adding CommonCrypto to a Swift framework is problematic. See: @@ -21,29 +21,66 @@ */ import Foundation +#if canImport(CryptoKit) + import CryptoKit +#endif extension String { var md5: String { - if let data = data(using: .utf8, allowLossyConversion: true) { - let message = data.withUnsafeBytes { bufferPointer in - Array(UnsafeBufferPointer( - start: bufferPointer.baseAddress?.assumingMemoryBound(to: UInt8.self), - count: data.count - )) + guard let data = data(using: .utf8, allowLossyConversion: true) else { + return self + } + #if canImport(CryptoKit) + if #available(OSX 10.15, iOS 13.0, *) { + return Insecure.MD5.hash(data: data) + .withUnsafeBytes { Array($0) }.hexString + } else { + return data.slowMD5 } + #else + return data.slowMD5 + #endif + } +} - let MD5Calculator = MD5(message) - let MD5Data = MD5Calculator.calculate() +private let charA = UInt8(UnicodeScalar("a").value) +private let char0 = UInt8(UnicodeScalar("0").value) - var MD5String = String() - for c in MD5Data { - MD5String += String(format: "%02x", c) +private extension DataProtocol { + var hexString: String { + let hexLen = count * 2 + var hexChars = [UInt8](repeating: 0, count: hexLen) + var offset = 0 + + for _ in regions { + for i in self { + hexChars[Int(offset * 2)] = itoh((i >> 4) & 0xF) + hexChars[Int(offset * 2 + 1)] = itoh(i & 0xF) + offset += 1 } - return MD5String + } - } else { - return self + return String(bytes: hexChars, encoding: .utf8)! + } + + func itoh(_ value: UInt8) -> UInt8 { + (value > 9) ? (charA + value - 10) : (char0 + value) + } +} + +private extension Data { + // Custom md5 for systems without CryptoKit. + var slowMD5: String { + let message = withUnsafeBytes { bufferPointer in + Array(UnsafeBufferPointer( + start: bufferPointer.baseAddress?.assumingMemoryBound(to: UInt8.self), + count: count + )) } + + let MD5Calculator = MD5(message) + let MD5Data = MD5Calculator.calculate() + return MD5Data.hexString } } @@ -54,7 +91,7 @@ func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) valuePointer.pointee = value - let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in + let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { bytesPointer -> [UInt8] in var bytes = [UInt8](repeating: 0, count: totalBytes) for j in 0 ..< min(MemoryLayout.size, totalBytes) { bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee @@ -76,7 +113,7 @@ func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { extension Int { /** Array of bytes with optional padding (little-endian) */ func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { - return arrayOfBytes(self, length: totalBytes) + arrayOfBytes(self, length: totalBytes) } } @@ -155,12 +192,12 @@ struct BytesSequence: Sequence { let data: [UInt8] func makeIterator() -> BytesIterator { - return BytesIterator(chunkSize: chunkSize, data: data) + BytesIterator(chunkSize: chunkSize, data: data) } } func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { - return ((value << bits) & 0xFFFF_FFFF) | (value >> (32 - bits)) + ((value << bits) & 0xFFFF_FFFF) | (value >> (32 - bits)) } class MD5: HashProtocol { @@ -245,7 +282,7 @@ class MD5: HashProtocol { F = B ^ C ^ D g = (3 * j + 5) % 16 case 48 ... 63: - F = C ^ (B | (~D)) + F = C ^ (B | ~D) g = (7 * j) % 16 default: break @@ -266,8 +303,8 @@ class MD5: HashProtocol { var result = [UInt8]() result.reserveCapacity(hh.count / 4) - hh.forEach { - let itemLE = $0.littleEndian + for item in hh { + let itemLE = item.littleEndian let r1 = UInt8(itemLE & 0xFF) let r2 = UInt8((itemLE >> 8) & 0xFF) let r3 = UInt8((itemLE >> 16) & 0xFF) diff --git a/Sources/XcodeProj/Objects/BuildPhase/BuildFileSetting.swift b/Sources/XcodeProj/Objects/BuildPhase/BuildFileSetting.swift new file mode 100644 index 000000000..0c2a101f1 --- /dev/null +++ b/Sources/XcodeProj/Objects/BuildPhase/BuildFileSetting.swift @@ -0,0 +1,55 @@ +public enum BuildFileSetting: Sendable, Equatable, Hashable { + case string(String) + case array([String]) + + public var stringValue: String? { + if case let .string(value) = self { + value + } else { + nil + } + } + + public var arrayValue: [String]? { + if case let .array(value) = self { + value + } else { + nil + } + } +} + +extension BuildFileSetting: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + do { + let string = try container.decode(String.self) + self = .string(string) + } catch { + let array = try container.decode([String].self) + self = .array(array) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .string(string): + try container.encode(string) + case let .array(array): + try container.encode(array) + } + } +} + +extension BuildFileSetting: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: String...) { + self = .array(elements) + } +} + +extension BuildFileSetting: ExpressibleByStringInterpolation { + public init(stringLiteral value: StringLiteralType) { + self = .string(value) + } +} diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift index 8531e5e04..094132d79 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift @@ -10,7 +10,7 @@ public final class PBXBuildFile: PBXObject { /// Returns the file the build file refers to. public var file: PBXFileElement? { get { - return fileReference?.getObject() + fileReference?.getObject() } set { fileReference = newValue?.reference @@ -23,7 +23,7 @@ public final class PBXBuildFile: PBXObject { /// Product. public var product: XCSwiftPackageProductDependency? { get { - return productReference?.getObject() + productReference?.getObject() } set { productReference = newValue?.reference @@ -31,12 +31,33 @@ public final class PBXBuildFile: PBXObject { } /// Element settings - public var settings: [String: Any]? + public var settings: [String: BuildFileSetting]? + + /// Potentially present for `PBXHeadersBuildPhase` : https://buck.build/javadoc/com/facebook/buck/apple/xcode/xcodeproj/PBXBuildFile.html + public var attributes: [String]? { + if case let .array(attributes) = settings?["ATTRIBUTES"] { + attributes + } else { + nil + } + } + + /// Potentially present for `PBXSourcesBuildPhase` : https://buck.build/javadoc/com/facebook/buck/apple/xcode/xcodeproj/PBXBuildFile.html + public var compilerFlags: String? { + if case let .string(compilerFlags) = settings?["COMPILER_FLAGS"] { + compilerFlags + } else { + nil + } + } /// Platform filter attribute. /// Introduced in: Xcode 11 public var platformFilter: String? + /// Platform filters attribute. + public var platformFilters: [String]? + /// The cached build phase this build file belongs to weak var buildPhase: PBXBuildPhase? @@ -50,10 +71,14 @@ public final class PBXBuildFile: PBXObject { /// - settings: build file settings. public init(file: PBXFileElement? = nil, product: XCSwiftPackageProductDependency? = nil, - settings: [String: Any]? = nil) { + settings: [String: BuildFileSetting]? = nil, + platformFilter: String? = nil, + platformFilters: [String]? = nil) { fileReference = file?.reference productReference = product?.reference self.settings = settings + self.platformFilter = platformFilter + self.platformFilters = platformFilters super.init() } @@ -64,6 +89,7 @@ public final class PBXBuildFile: PBXObject { case settings case productRef case platformFilter + case platformFilters } public required init(from decoder: Decoder) throws { @@ -76,10 +102,16 @@ public final class PBXBuildFile: PBXObject { if let productRefString: String = try container.decodeIfPresent(.productRef) { productReference = objectReferenceRepository.getOrCreate(reference: productRefString, objects: objects) } - settings = try container.decodeIfPresent([String: Any].self, forKey: .settings) + settings = try container.decodeIfPresent([String: BuildFileSetting].self, forKey: .settings) platformFilter = try container.decodeIfPresent(.platformFilter) + platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXBuildFile else { return false } + return isEqual(to: rhs) + } } // MARK: - Internal Helpers @@ -100,28 +132,28 @@ extension PBXBuildFile { /// - Returns: build phase type. /// - Throws: an error if this method is called before the build file is added to any project. func getBuildPhase() throws -> PBXBuildPhase? { - if let buildPhase = buildPhase { + if let buildPhase { return buildPhase } let projectObjects = try objects() if let buildPhase = projectObjects.sourcesBuildPhases.values - .first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } else if let buildPhase = projectObjects.frameworksBuildPhases - .values.first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .values.first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } else if let buildPhase = projectObjects .resourcesBuildPhases.values - .first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } else if let buildPhase = projectObjects.copyFilesBuildPhases - .values.first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .values.first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } else if let buildPhase = projectObjects.headersBuildPhases - .values.first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .values.first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } else if let buildPhase = projectObjects.carbonResourcesBuildPhases - .values.first(where: { $0.fileReferences?.map { $0.value }.contains(reference.value) == true }) { + .values.first(where: { $0.fileReferences?.map(\.value).contains(reference.value) == true }) { return buildPhase } return nil @@ -143,7 +175,7 @@ extension PBXBuildFile { // Helper for serialize the BuildFile with associated BuildPhase final class PBXBuildPhaseFile: PlistSerializable, Equatable { - var multiline: Bool { return false } + var multiline: Bool { false } let buildFile: PBXBuildFile let buildPhase: PBXBuildPhase @@ -169,12 +201,15 @@ final class PBXBuildPhaseFile: PlistSerializable, Equatable { if let platformFilter = buildFile.platformFilter { dictionary["platformFilter"] = .string(.init(platformFilter)) } - let comment = try buildPhase.name().flatMap { "\(try buildFile.fileName() ?? "(null)") in \($0)" } + if let platformFilters = buildFile.platformFilters { + dictionary["platformFilters"] = .array(platformFilters.map { .string(.init($0)) }) + } + let comment = try buildPhase.name().flatMap { try "\(buildFile.fileName() ?? "(null)") in \($0)" } return (key: CommentedString(reference, comment: comment), value: .dictionary(dictionary)) } static func == (lhs: PBXBuildPhaseFile, rhs: PBXBuildPhaseFile) -> Bool { - return lhs.buildFile == rhs.buildFile && lhs.buildPhase == rhs.buildPhase + lhs.buildFile == rhs.buildFile && lhs.buildPhase == rhs.buildPhase } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildPhase.swift index 31abb120d..953be5c8b 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildPhase.swift @@ -14,7 +14,7 @@ public class PBXBuildPhase: PBXContainerItem { /// Build files. public var files: [PBXBuildFile]? { get { - return fileReferences?.objects() + fileReferences?.objects() } set { newValue?.forEach { $0.buildPhase = self } @@ -86,7 +86,7 @@ public class PBXBuildPhase: PBXContainerItem { override func plistValues(proj: PBXProj, reference: String) throws -> [CommentedString: PlistValue] { var dictionary = try super.plistValues(proj: proj, reference: reference) dictionary["buildActionMask"] = .string(CommentedString("\(buildActionMask)")) - if let fileReferences = fileReferences { + if let fileReferences { let files: PlistValue = .array(fileReferences.map { fileReference in let buildFile: PBXBuildFile? = fileReference.getObject() let name = buildFile.flatMap { try? $0.fileName() } ?? nil @@ -97,15 +97,20 @@ public class PBXBuildPhase: PBXContainerItem { }) dictionary["files"] = files } - if let inputFileListPaths = inputFileListPaths { + if let inputFileListPaths { dictionary["inputFileListPaths"] = .array(inputFileListPaths.map { .string(CommentedString($0)) }) } - if let outputFileListPaths = outputFileListPaths { + if let outputFileListPaths { dictionary["outputFileListPaths"] = .array(outputFileListPaths.map { .string(CommentedString($0)) }) } dictionary["runOnlyForDeploymentPostprocessing"] = .string(CommentedString("\(runOnlyForDeploymentPostprocessing.int)")) return dictionary } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXBuildPhase else { return false } + return isEqual(to: rhs) + } } // MARK: - Helpers @@ -134,8 +139,9 @@ public extension PBXBuildPhase { /// Returns the build phase type. /// /// - Returns: build phase type. + @available(*, deprecated, message: "Please use buildPhase property instead") func type() -> BuildPhase? { - return buildPhase + buildPhase } /// Build phase name. diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildRule.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildRule.swift index b68e5b5e4..45b498078 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildRule.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildRule.swift @@ -19,6 +19,9 @@ public final class PBXBuildRule: PBXObject { /// Element name. public var name: String? + /// The discovered dependency file to use + public var dependencyFile: String? + /// Element output files. public var outputFiles: [String] @@ -31,6 +34,9 @@ public final class PBXBuildRule: PBXObject { /// Element script. public var script: String? + /// Element run once per architecture. + public var runOncePerArchitecture: Bool? + // MARK: - Init public init(compilerSpec: String, @@ -38,19 +44,23 @@ public final class PBXBuildRule: PBXObject { isEditable: Bool = true, filePatterns: String? = nil, name: String? = nil, + dependencyFile: String? = nil, outputFiles: [String] = [], inputFiles: [String]? = nil, outputFilesCompilerFlags: [String]? = nil, - script: String? = nil) { + script: String? = nil, + runOncePerArchitecture: Bool? = nil) { self.compilerSpec = compilerSpec self.filePatterns = filePatterns self.fileType = fileType self.isEditable = isEditable self.name = name + self.dependencyFile = dependencyFile self.outputFiles = outputFiles self.inputFiles = inputFiles self.outputFilesCompilerFlags = outputFilesCompilerFlags self.script = script + self.runOncePerArchitecture = runOncePerArchitecture super.init() } @@ -62,10 +72,12 @@ public final class PBXBuildRule: PBXObject { case fileType case isEditable case name + case dependencyFile case outputFiles case inputFiles case outputFilesCompilerFlags case script + case runOncePerArchitecture } public required init(from decoder: Decoder) throws { @@ -75,41 +87,54 @@ public final class PBXBuildRule: PBXObject { fileType = try container.decodeIfPresent(.fileType) ?? "" isEditable = try container.decodeIntBool(.isEditable) name = try container.decodeIfPresent(.name) + dependencyFile = try container.decodeIfPresent(.dependencyFile) outputFiles = try container.decodeIfPresent(.outputFiles) ?? [] inputFiles = try container.decodeIfPresent(.inputFiles) outputFilesCompilerFlags = try container.decodeIfPresent(.outputFilesCompilerFlags) script = try container.decodeIfPresent(.script) + runOncePerArchitecture = try container.decodeIntBoolIfPresent(.runOncePerArchitecture) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXBuildRule else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXBuildRule Extension (PlistSerializable) extension PBXBuildRule: PlistSerializable { - var multiline: Bool { return true } + var multiline: Bool { true } func plistKeyAndValue(proj _: PBXProj, reference: String) -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(PBXBuildRule.isa)) dictionary["compilerSpec"] = .string(CommentedString(compilerSpec)) - if let filePatterns = filePatterns { + if let dependencyFile { + dictionary["dependencyFile"] = .string(CommentedString(dependencyFile)) + } + if let filePatterns { dictionary["filePatterns"] = .string(CommentedString(filePatterns)) } dictionary["fileType"] = .string(CommentedString(fileType)) dictionary["isEditable"] = .string(CommentedString("\(isEditable.int)")) - if let name = name { + if let name { dictionary["name"] = .string(CommentedString(name)) } dictionary["outputFiles"] = .array(outputFiles.map { .string(CommentedString($0)) }) - if let inputFiles = inputFiles { + if let inputFiles { dictionary["inputFiles"] = .array(inputFiles.map { .string(CommentedString($0)) }) } - if let outputFilesCompilerFlags = outputFilesCompilerFlags { + if let outputFilesCompilerFlags { dictionary["outputFilesCompilerFlags"] = .array(outputFilesCompilerFlags.map { PlistValue.string(CommentedString($0)) }) } - if let script = script { + if let script { dictionary["script"] = .string(CommentedString(script)) } + if let runOncePerArchitecture { + dictionary["runOncePerArchitecture"] = .string(CommentedString("\(runOncePerArchitecture.int)")) + } return (key: CommentedString(reference, comment: PBXBuildRule.isa), value: .dictionary(dictionary)) } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXCopyFilesBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXCopyFilesBuildPhase.swift index ee5b5e369..58f8817bf 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXCopyFilesBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXCopyFilesBuildPhase.swift @@ -16,6 +16,67 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase { case other } + public enum DstSubfolder: Equatable, Decodable { + case absolutePath + case productsDirectory + case wrapper + case executables + case resources + case javaResources + case frameworks + case sharedFrameworks + case sharedSupport + case plugins + case other + case product + case none + case unknown(String) + + public init(rawValue: String) { + switch rawValue { + case "AbsolutePath": self = .absolutePath + case "ProductsDirectory": self = .productsDirectory + case "Wrapper": self = .wrapper + case "Executables": self = .executables + case "Resources": self = .resources + case "JavaResources": self = .javaResources + case "Frameworks": self = .frameworks + case "SharedFrameworks": self = .sharedFrameworks + case "SharedSupport": self = .sharedSupport + case "PlugIns": self = .plugins + case "Other": self = .other + case "Product": self = .product + case "None": self = .none + default: self = .unknown(rawValue) + } + } + + public var rawValue: String { + switch self { + case .absolutePath: "AbsolutePath" + case .productsDirectory: "ProductsDirectory" + case .wrapper: "Wrapper" + case .executables: "Executables" + case .resources: "Resources" + case .javaResources: "JavaResources" + case .frameworks: "Frameworks" + case .sharedFrameworks: "SharedFrameworks" + case .sharedSupport: "SharedSupport" + case .plugins: "PlugIns" + case .other: "Other" + case .product: "Product" + case .none: "None" + case let .unknown(rawValue): rawValue + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(String.self) + self = .init(rawValue: rawValue) + } + } + // MARK: - Attributes /// Element destination path @@ -24,11 +85,13 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase { /// Element destination subfolder spec public var dstSubfolderSpec: SubFolder? + public var dstSubfolder: DstSubfolder? + /// Copy files build phase name public var name: String? - public override var buildPhase: BuildPhase { - return .copyFiles + override public var buildPhase: BuildPhase { + .copyFiles } // MARK: - Init @@ -38,17 +101,20 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase { /// - Parameters: /// - dstPath: Destination path. /// - dstSubfolderSpec: Destination subfolder spec. + /// - dstSubfolder: Destination subfolder. /// - buildActionMask: Build action mask. /// - files: Build files to copy. /// - runOnlyForDeploymentPostprocessing: Run only for deployment post processing. public init(dstPath: String? = nil, dstSubfolderSpec: SubFolder? = nil, + dstSubfolder: DstSubfolder? = nil, name: String? = nil, buildActionMask: UInt = defaultBuildActionMask, files: [PBXBuildFile] = [], runOnlyForDeploymentPostprocessing: Bool = false) { self.dstPath = dstPath self.dstSubfolderSpec = dstSubfolderSpec + self.dstSubfolder = dstSubfolder self.name = name super.init(files: files, buildActionMask: buildActionMask, @@ -61,6 +127,7 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase { fileprivate enum CodingKeys: String, CodingKey { case dstPath case dstSubfolderSpec + case dstSubfolder case name } @@ -68,9 +135,15 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase { let container = try decoder.container(keyedBy: CodingKeys.self) dstPath = try container.decodeIfPresent(.dstPath) dstSubfolderSpec = try container.decodeIntIfPresent(.dstSubfolderSpec).flatMap(SubFolder.init) + dstSubfolder = try container.decodeIfPresent(.dstSubfolder) name = try container.decodeIfPresent(.name) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXCopyFilesBuildPhase else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXCopyFilesBuildPhase Extension (PlistSerializable) @@ -79,15 +152,18 @@ extension PBXCopyFilesBuildPhase: PlistSerializable { func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference) dictionary["isa"] = .string(CommentedString(PBXCopyFilesBuildPhase.isa)) - if let dstPath = dstPath { + if let dstPath { dictionary["dstPath"] = .string(CommentedString(dstPath)) } - if let name = name { + if let name { dictionary["name"] = .string(CommentedString(name)) } - if let dstSubfolderSpec = dstSubfolderSpec { + if let dstSubfolderSpec { dictionary["dstSubfolderSpec"] = .string(CommentedString("\(dstSubfolderSpec.rawValue)")) } + if let dstSubfolder { + dictionary["dstSubfolder"] = .string(CommentedString("\(dstSubfolder.rawValue)")) + } return (key: CommentedString(reference, comment: name ?? "CopyFiles"), value: .dictionary(dictionary)) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXFrameworksBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXFrameworksBuildPhase.swift index ae7b82d29..4eecc3a1b 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXFrameworksBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXFrameworksBuildPhase.swift @@ -2,8 +2,13 @@ import Foundation /// This is the element for the framework link build phase. public final class PBXFrameworksBuildPhase: PBXBuildPhase { - public override var buildPhase: BuildPhase { - return .frameworks + override public var buildPhase: BuildPhase { + .frameworks + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFrameworksBuildPhase else { return false } + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXHeadersBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXHeadersBuildPhase.swift index 4dc2e520d..9bffc8559 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXHeadersBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXHeadersBuildPhase.swift @@ -3,8 +3,13 @@ import PathKit /// This is the element for the framework headers build phase. public final class PBXHeadersBuildPhase: PBXBuildPhase { - public override var buildPhase: BuildPhase { - return .headers + override public var buildPhase: BuildPhase { + .headers + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXHeadersBuildPhase else { return false } + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXResourcesBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXResourcesBuildPhase.swift index 05858df1a..a937087d6 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXResourcesBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXResourcesBuildPhase.swift @@ -2,8 +2,13 @@ import Foundation /// This is the element for the resources copy build phase. public final class PBXResourcesBuildPhase: PBXBuildPhase { - public override var buildPhase: BuildPhase { - return .resources + override public var buildPhase: BuildPhase { + .resources + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXResourcesBuildPhase else { return false } + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXRezBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXRezBuildPhase.swift index 8854edf77..79a225d19 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXRezBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXRezBuildPhase.swift @@ -3,8 +3,13 @@ import Foundation /// This is the element for the Build Carbon Resources build phase. /// These are legacy .r files from the Classic Mac OS era. public final class PBXRezBuildPhase: PBXBuildPhase { - public override var buildPhase: BuildPhase { - return .carbonResources + override public var buildPhase: BuildPhase { + .carbonResources + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXRezBuildPhase else { return false } + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXShellScriptBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXShellScriptBuildPhase.swift index 939c5f679..c08f25ac3 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXShellScriptBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXShellScriptBuildPhase.swift @@ -22,8 +22,14 @@ public final class PBXShellScriptBuildPhase: PBXBuildPhase { /// Show environment variables in the logs. public var showEnvVarsInLog: Bool - public override var buildPhase: BuildPhase { - return .runScript + /// Force script to run in all incremental builds. + public var alwaysOutOfDate: Bool + + /// Path to the discovery .d dependency file + public var dependencyFile: String? + + override public var buildPhase: BuildPhase { + .runScript } // MARK: - Init @@ -39,6 +45,8 @@ public final class PBXShellScriptBuildPhase: PBXBuildPhase { /// - shellPath: shell path. /// - shellScript: shell script. /// - buildActionMask: build action mask. + /// - alwaysOutOfDate: always out of date. + /// - dependencyFile: discovery dependency file path. public init(files: [PBXBuildFile] = [], name: String? = nil, inputPaths: [String] = [], @@ -49,13 +57,17 @@ public final class PBXShellScriptBuildPhase: PBXBuildPhase { shellScript: String? = nil, buildActionMask: UInt = defaultBuildActionMask, runOnlyForDeploymentPostprocessing: Bool = false, - showEnvVarsInLog: Bool = true) { + showEnvVarsInLog: Bool = true, + alwaysOutOfDate: Bool = false, + dependencyFile: String? = nil) { self.name = name self.inputPaths = inputPaths self.outputPaths = outputPaths self.shellPath = shellPath self.shellScript = shellScript self.showEnvVarsInLog = showEnvVarsInLog + self.alwaysOutOfDate = alwaysOutOfDate + self.dependencyFile = dependencyFile super.init(files: files, inputFileListPaths: inputFileListPaths, outputFileListPaths: outputFileListPaths, @@ -72,18 +84,32 @@ public final class PBXShellScriptBuildPhase: PBXBuildPhase { case shellPath case shellScript case showEnvVarsInLog + case alwaysOutOfDate + case dependencyFile } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decodeIfPresent(.name) - inputPaths = (try container.decodeIfPresent(.inputPaths)) ?? [] - outputPaths = (try container.decodeIfPresent(.outputPaths)) ?? [] + inputPaths = try (container.decodeIfPresent(.inputPaths)) ?? [] + outputPaths = try (container.decodeIfPresent(.outputPaths)) ?? [] shellPath = try container.decodeIfPresent(.shellPath) - shellScript = try container.decodeIfPresent(.shellScript) + // Xcode 16.0 introduced a new format for shellScript, so we need to handle both cases. + if let scriptArray = try? container.decodeIfPresent([String].self, forKey: .shellScript) { + shellScript = scriptArray.joined(separator: "\n") + } else { + shellScript = try container.decodeIfPresent(.shellScript) + } showEnvVarsInLog = try container.decodeIntBoolIfPresent(.showEnvVarsInLog) ?? true + alwaysOutOfDate = try container.decodeIntBoolIfPresent(.alwaysOutOfDate) ?? false + dependencyFile = try container.decodeIfPresent(.dependencyFile) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXShellScriptBuildPhase else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXShellScriptBuildPhase Extension (PlistSerializable) @@ -92,21 +118,28 @@ extension PBXShellScriptBuildPhase: PlistSerializable { func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference) dictionary["isa"] = .string(CommentedString(PBXShellScriptBuildPhase.isa)) - if let shellPath = shellPath { + if let shellPath { dictionary["shellPath"] = .string(CommentedString(shellPath)) } dictionary["inputPaths"] = .array(inputPaths.map { .string(CommentedString($0)) }) - if let name = name { + if let name { dictionary["name"] = .string(CommentedString(name)) } dictionary["outputPaths"] = .array(outputPaths.map { .string(CommentedString($0)) }) - if let shellScript = shellScript { + if let shellScript { dictionary["shellScript"] = .string(CommentedString(shellScript)) } + if let dependencyFile { + dictionary["dependencyFile"] = .string(CommentedString(dependencyFile)) + } if !showEnvVarsInLog { // Xcode only writes this key if it's set to false; default is true and is omitted dictionary["showEnvVarsInLog"] = .string(CommentedString("\(showEnvVarsInLog.int)")) } + if alwaysOutOfDate { + // Xcode only write this key if it's set to true; default is false and is omitted + dictionary["alwaysOutOfDate"] = .string(CommentedString("\(alwaysOutOfDate.int)")) + } return (key: CommentedString(reference, comment: name ?? "ShellScript"), value: .dictionary(dictionary)) } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXSourcesBuildPhase.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXSourcesBuildPhase.swift index 4524b2636..2b6ed8f89 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXSourcesBuildPhase.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXSourcesBuildPhase.swift @@ -2,8 +2,13 @@ import Foundation /// This is the element for the sources compilation build phase. public final class PBXSourcesBuildPhase: PBXBuildPhase { - public override var buildPhase: BuildPhase { - return .sources + override public var buildPhase: BuildPhase { + .sources + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXSourcesBuildPhase else { return false } + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/Configuration/BuildSettings.swift b/Sources/XcodeProj/Objects/Configuration/BuildSettings.swift index 066dbb50d..5ca94d202 100644 --- a/Sources/XcodeProj/Objects/Configuration/BuildSettings.swift +++ b/Sources/XcodeProj/Objects/Configuration/BuildSettings.swift @@ -1,4 +1,82 @@ import Foundation /// Build settings. -public typealias BuildSettings = [String: Any] +public typealias BuildSettings = [String: BuildSetting] + +private let yes = "YES" +private let no = "NO" + +public enum BuildSetting: Sendable, Equatable { + case string(String) + case array([String]) + + public var stringValue: String? { + if case let .string(value) = self { + value + } else { + nil + } + } + + public var boolValue: Bool? { + if case let .string(value) = self { + switch value { + case yes: true + case no: false + default: nil + } + } else { + nil + } + } + + public var arrayValue: [String]? { + if case let .array(value) = self { + value + } else { + nil + } + } +} + +extension BuildSetting: CustomStringConvertible { + public var description: String { + switch self { + case let .string(string): + string + case let .array(array): + array.joined(separator: " ") + } + } +} + +extension BuildSetting: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + do { + let string = try container.decode(String.self) + self = .string(string) + } catch { + let array = try container.decode([String].self) + self = .array(array) + } + } +} + +extension BuildSetting: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: String...) { + self = .array(elements) + } +} + +extension BuildSetting: ExpressibleByStringInterpolation { + public init(stringLiteral value: StringLiteralType) { + self = .string(value) + } +} + +extension BuildSetting: ExpressibleByBooleanLiteral { + public init(booleanLiteral value: Bool) { + self = .string(value ? yes : no) + } +} diff --git a/Sources/XcodeProj/Objects/Configuration/XCBuildConfiguration.swift b/Sources/XcodeProj/Objects/Configuration/XCBuildConfiguration.swift index 109a1838d..ac4d62f9e 100644 --- a/Sources/XcodeProj/Objects/Configuration/XCBuildConfiguration.swift +++ b/Sources/XcodeProj/Objects/Configuration/XCBuildConfiguration.swift @@ -4,21 +4,39 @@ import Foundation public final class XCBuildConfiguration: PBXObject { // MARK: - Attributes - /// Base xcconfig file reference. + /// Base xcconfig file reference if the file belongs to a ``PBXGroup``. var baseConfigurationReference: PBXObjectReference? - /// Base xcconfig file reference. + /// Base xcconfig file reference if the file belongs to a ``PBXGroup``. public var baseConfiguration: PBXFileReference? { get { - return baseConfigurationReference?.getObject() + baseConfigurationReference?.getObject() } set { - if let newValue = newValue { + if let newValue { baseConfigurationReference = newValue.reference } } } + /// Reference to a ``PBXFileSystemSynchronizedRootGroup`` containing the base xcconfig file. + var baseConfigurationReferenceAnchor: PBXObjectReference? + + /// Base xcconfig file path relative to the `baseConfigurationAnchor`. + public var baseConfigurationReferenceRelativePath: String? + + /// ``PBXFileSystemSynchronizedRootGroup`` containing the base xcconfig file. + public var baseConfigurationAnchor: PBXFileSystemSynchronizedRootGroup? { + get { + baseConfigurationReferenceAnchor?.getObject() + } + set { + if let newValue { + baseConfigurationReferenceAnchor = newValue.reference + } + } + } + /// A map of build settings. public var buildSettings: BuildSettings @@ -27,12 +45,18 @@ public final class XCBuildConfiguration: PBXObject { // MARK: - Init - /// Initializes a build configuration. + /// Initializes a build configuration whose base xcconfig (if any) lives in a ``PBXGroup``. /// /// - Parameters: /// - name: build configuration name. - /// - baseConfiguration: base configuration. + /// - baseConfiguration: base xcconfig file reference belonging to a ``PBXGroup``. /// - buildSettings: dictionary that contains the build settings for this configuration. + /// + /// - Important: + /// If the base xcconfig file lives inside a ``PBXFileSystemSynchronizedRootGroup`` (Xcode 16+), use + /// ``init(name:baseConfigurationAnchor:baseConfigurationRelativePath:buildSettings:)`` instead. The two + /// reference styles are mutually exclusive: a single configuration cannot use both `baseConfiguration` and + /// the anchor/relative-path pair. public init(name: String, baseConfiguration: PBXFileReference? = nil, buildSettings: BuildSettings = [:]) { @@ -42,10 +66,36 @@ public final class XCBuildConfiguration: PBXObject { super.init() } + /// Initializes a build configuration whose base xcconfig lives inside a ``PBXFileSystemSynchronizedRootGroup``. + /// + /// - Parameters: + /// - name: build configuration name. + /// - baseConfigurationAnchor: the file system synchronized root group that contains the base xcconfig file as a descendant. + /// - baseConfigurationRelativePath: relative path from `baseConfigurationAnchor` to the base xcconfig file. + /// - buildSettings: dictionary that contains the build settings for this configuration. + /// + /// - Important: + /// This initializer is for the Xcode 16+ representation, where xcconfig files inside a synchronized root + /// group are referenced by an anchor + relative path pair. If the base xcconfig file belongs to a regular + /// ``PBXGroup``, use ``init(name:baseConfiguration:buildSettings:)`` instead. The two reference styles are + /// mutually exclusive: a single configuration cannot use both `baseConfiguration` and the anchor/relative-path pair. + public init(name: String, + baseConfigurationAnchor: PBXFileSystemSynchronizedRootGroup, + baseConfigurationRelativePath: String, + buildSettings: BuildSettings = [:]) { + baseConfigurationReferenceAnchor = baseConfigurationAnchor.reference + baseConfigurationReferenceRelativePath = baseConfigurationRelativePath + self.buildSettings = buildSettings + self.name = name + super.init() + } + // MARK: - Decodable fileprivate enum CodingKeys: String, CodingKey { case baseConfigurationReference + case baseConfigurationReferenceAnchor + case baseConfigurationReferenceRelativePath case buildSettings case name } @@ -54,12 +104,18 @@ public final class XCBuildConfiguration: PBXObject { let objects = decoder.context.objects let objectReferenceRepository = decoder.context.objectReferenceRepository let container = try decoder.container(keyedBy: CodingKeys.self) - if let baseConfigurationReference: String = try container.decodeIfPresent(.baseConfigurationReference) { + // Configuration files may be referenced either by a pair of (PBXFileSystemSynchronizedRootGroup, relative path) + // when the xcconfig lives inside a synchronized root group (Xcode 16+), or by a PBXFileReference when it + // belongs to a regular PBXGroup. + if let baseConfigurationReferenceAnchor: String = try container.decodeIfPresent(.baseConfigurationReferenceAnchor), + let baseConfigurationReferenceRelativePath: String = try container.decodeIfPresent(.baseConfigurationReferenceRelativePath) + { + self.baseConfigurationReferenceAnchor = objectReferenceRepository.getOrCreate(reference: baseConfigurationReferenceAnchor, objects: objects) + self.baseConfigurationReferenceRelativePath = baseConfigurationReferenceRelativePath + } else if let baseConfigurationReference: String = try container.decodeIfPresent(.baseConfigurationReference) { self.baseConfigurationReference = objectReferenceRepository.getOrCreate(reference: baseConfigurationReference, objects: objects) - } else { - baseConfigurationReference = nil } - buildSettings = try container.decode([String: Any].self, forKey: .buildSettings) + buildSettings = try container.decode(BuildSettings.self, forKey: .buildSettings) name = try container.decode(.name) try super.init(from: decoder) } @@ -75,20 +131,25 @@ public final class XCBuildConfiguration: PBXObject { public func append(setting name: String, value: String) { guard !value.isEmpty else { return } - let existing: Any = buildSettings[name] ?? "$(inherited)" + let existing: BuildSetting = buildSettings[name] ?? "$(inherited)" switch existing { - case let string as String where string != value: + case let .string(string) where string != value: let newValue = [string, value].joined(separator: " ") - buildSettings[name] = newValue - case let array as [String]: + buildSettings[name] = .string(newValue) + case let .array(array): var newValue = array newValue.append(value) - buildSettings[name] = newValue.uniqued() + buildSettings[name] = .array(newValue.uniqued()) default: break } } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? XCBuildConfiguration else { return false } + return isEqual(to: rhs) + } } // MARK: - PlistSerializable @@ -99,7 +160,13 @@ extension XCBuildConfiguration: PlistSerializable { dictionary["isa"] = .string(CommentedString(XCBuildConfiguration.isa)) dictionary["name"] = .string(CommentedString(name)) dictionary["buildSettings"] = buildSettings.plist() - if let baseConfigurationReference = baseConfigurationReference { + if let baseConfigurationReferenceAnchor, + let baseConfigurationReferenceRelativePath + { + let synchronizedGroup: PBXFileSystemSynchronizedRootGroup? = baseConfigurationReferenceAnchor.getObject() + dictionary["baseConfigurationReferenceAnchor"] = .string(CommentedString(baseConfigurationReferenceAnchor.value, comment: synchronizedGroup?.path)) + dictionary["baseConfigurationReferenceRelativePath"] = .string(CommentedString(baseConfigurationReferenceRelativePath)) + } else if let baseConfigurationReference { let fileElement: PBXFileElement? = baseConfigurationReference.getObject() dictionary["baseConfigurationReference"] = .string(CommentedString(baseConfigurationReference.value, comment: fileElement?.fileName())) } diff --git a/Sources/XcodeProj/Objects/Configuration/XCConfigurationList.swift b/Sources/XcodeProj/Objects/Configuration/XCConfigurationList.swift index 57c5de7b0..8cf06af86 100644 --- a/Sources/XcodeProj/Objects/Configuration/XCConfigurationList.swift +++ b/Sources/XcodeProj/Objects/Configuration/XCConfigurationList.swift @@ -13,7 +13,7 @@ public final class XCConfigurationList: PBXObject { buildConfigurationReferences = newValue.references() } get { - return buildConfigurationReferences.objects() + buildConfigurationReferences.objects() } } @@ -59,23 +59,28 @@ public final class XCConfigurationList: PBXObject { defaultConfigurationName = try container.decodeIfPresent(.defaultConfigurationName) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? XCConfigurationList else { return false } + return isEqual(to: rhs) + } } // MARK: - Helpers -extension XCConfigurationList { +public extension XCConfigurationList { /// Returns the build configuration with the given name (if it exists) /// /// - Parameter name: configuration name. /// - Returns: build configuration if it exists. - public func configuration(name: String) -> XCBuildConfiguration? { - return buildConfigurations.first(where: { $0.name == name }) + func configuration(name: String) -> XCBuildConfiguration? { + buildConfigurations.first(where: { $0.name == name }) } /// Adds the default configurations, debug and release /// /// - Returns: the created configurations. - public func addDefaultConfigurations() throws -> [XCBuildConfiguration] { + func addDefaultConfigurations() throws -> [XCBuildConfiguration] { var configurations: [XCBuildConfiguration] = [] let debug = XCBuildConfiguration(name: "Debug") @@ -94,7 +99,7 @@ extension XCConfigurationList { /// /// - Parameter reference: configuration list reference. /// - Returns: target or project with the given configuration list. - public func objectWithConfigurationList() throws -> PBXObject? { + func objectWithConfigurationList() throws -> PBXObject? { let projectObjects = try objects() return projectObjects.projects.first(where: { $0.value.buildConfigurationListReference == reference })?.value ?? projectObjects.nativeTargets.first(where: { $0.value.buildConfigurationListReference == reference })?.value ?? @@ -113,13 +118,13 @@ extension XCConfigurationList: PlistSerializable { .map { configReference in let config: XCBuildConfiguration? = configReference.getObject() return .string(CommentedString(configReference.value, comment: config?.name)) - }) + }) dictionary["defaultConfigurationIsVisible"] = .string(CommentedString("\(defaultConfigurationIsVisible.int)")) - if let defaultConfigurationName = defaultConfigurationName { + if let defaultConfigurationName { dictionary["defaultConfigurationName"] = .string(CommentedString(defaultConfigurationName)) } - return (key: CommentedString(reference, comment: try plistComment()), - value: .dictionary(dictionary)) + return try (key: CommentedString(reference, comment: plistComment()), + value: .dictionary(dictionary)) } private func plistComment() throws -> String? { diff --git a/Sources/XcodeProj/Objects/Files/PBXContainerItem.swift b/Sources/XcodeProj/Objects/Files/PBXContainerItem.swift index 0995f8b90..dfc68661e 100644 --- a/Sources/XcodeProj/Objects/Files/PBXContainerItem.swift +++ b/Sources/XcodeProj/Objects/Files/PBXContainerItem.swift @@ -26,9 +26,14 @@ public class PBXContainerItem: PBXObject { func plistValues(proj _: PBXProj, reference _: String) throws -> [CommentedString: PlistValue] { var dictionary = [CommentedString: PlistValue]() - if let comments = comments { + if let comments { dictionary["comments"] = .string(CommentedString(comments)) } return dictionary } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXContainerItem else { return false } + return isEqual(to: rhs) + } } diff --git a/Sources/XcodeProj/Objects/Files/PBXContainerItemProxy.swift b/Sources/XcodeProj/Objects/Files/PBXContainerItemProxy.swift index bf1116028..bbff85ce0 100644 --- a/Sources/XcodeProj/Objects/Files/PBXContainerItemProxy.swift +++ b/Sources/XcodeProj/Objects/Files/PBXContainerItemProxy.swift @@ -20,8 +20,8 @@ public final class PBXContainerItemProxy: PBXObject { var uuid: String { switch self { - case let .reference(reference): return reference.value - case let .string(string): return string + case let .reference(reference): reference.value + case let .string(string): string } } @@ -29,11 +29,11 @@ public final class PBXContainerItemProxy: PBXObject { switch self { case let .reference(reference): if let object = reference.getObject() { - return .object(object) + .object(object) } else { - return .string(reference.value) + .string(reference.value) } - case let .string(string): return .string(string) + case let .string(string): .string(string) } } } @@ -44,15 +44,15 @@ public final class PBXContainerItemProxy: PBXObject { var uuid: String { switch self { - case let .object(object): return object.uuid - case let .string(string): return string + case let .object(object): object.uuid + case let .string(string): string } } var reference: RemoteGlobalIDReference { switch self { - case let .object(object): return .reference(object.reference) - case let .string(string): return .string(string) + case let .object(object): .reference(object.reference) + case let .string(string): .string(string) } } } @@ -65,7 +65,7 @@ public final class PBXContainerItemProxy: PBXObject { /// Use isContainerPortalFileReference to check if you can use the getter public var containerPortal: ContainerPortal { get { - return ContainerPortal(object: containerPortalReference.getObject()) + ContainerPortal(object: containerPortalReference.getObject()) } set { guard let reference = newValue.reference else { @@ -81,7 +81,7 @@ public final class PBXContainerItemProxy: PBXObject { /// Element remote global ID reference. ID of the proxied object. public var remoteGlobalID: RemoteGlobalID? { get { - return remoteGlobalIDReference?.id + remoteGlobalIDReference?.id } set { remoteGlobalIDReference = newValue?.reference } @@ -142,6 +142,11 @@ public final class PBXContainerItemProxy: PBXObject { remoteInfo = try container.decodeIfPresent(.remoteInfo) try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXContainerItemProxy else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXContainerItemProxy Extension (PlistSerializable) @@ -151,13 +156,13 @@ extension PBXContainerItemProxy: PlistSerializable { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(PBXContainerItemProxy.isa)) dictionary["containerPortal"] = .string(CommentedString(containerPortalReference.value, comment: containerPortal.comment)) - if let proxyType = proxyType { + if let proxyType { dictionary["proxyType"] = .string(CommentedString("\(proxyType.rawValue)")) } - if let remoteGlobalID = remoteGlobalID { + if let remoteGlobalID { dictionary["remoteGlobalIDString"] = .string(CommentedString(remoteGlobalID.uuid)) } - if let remoteInfo = remoteInfo { + if let remoteInfo { dictionary["remoteInfo"] = .string(CommentedString(remoteInfo)) } return (key: CommentedString(reference, @@ -180,11 +185,11 @@ private extension PBXContainerItemProxy.ContainerPortal { var reference: PBXObjectReference? { switch self { case let .project(project): - return project.reference + project.reference case let .fileReference(fileReference): - return fileReference.reference + fileReference.reference case .unknownObject: - return nil + nil } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift index a869c2c09..5f3e80dca 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift @@ -93,34 +93,34 @@ public class PBXFileElement: PBXContainerItem, PlistSerializable { // MARK: - PlistSerializable - var multiline: Bool { return true } + var multiline: Bool { true } func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(PBXFileElement.isa)) - if let name = name { + if let name { dictionary["name"] = .string(CommentedString(name)) } - if let path = path { + if let path { dictionary["path"] = .string(CommentedString(path)) } - if let sourceTree = sourceTree { + if let sourceTree { dictionary["sourceTree"] = sourceTree.plist() } - if let includeInIndex = includeInIndex { + if let includeInIndex { dictionary["includeInIndex"] = .string(CommentedString("\(includeInIndex.int)")) } - if let usesTabs = usesTabs { + if let usesTabs { dictionary["usesTabs"] = .string(CommentedString("\(usesTabs.int)")) } - if let indentWidth = indentWidth { + if let indentWidth { dictionary["indentWidth"] = .string(CommentedString("\(indentWidth)")) } - if let tabWidth = tabWidth { + if let tabWidth { dictionary["tabWidth"] = .string(CommentedString("\(tabWidth)")) } - if let wrapsLines = wrapsLines { + if let wrapsLines { dictionary["wrapsLines"] = .string(CommentedString("\(wrapsLines.int)")) } return (key: CommentedString(reference, @@ -132,13 +132,27 @@ public class PBXFileElement: PBXContainerItem, PlistSerializable { /// /// - Returns: file name. func fileName() -> String? { - return name ?? path + name ?? path + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFileElement else { return false } + return isEqual(to: rhs) } } // MARK: - Helpers public extension PBXFileElement { + /// Returns a file absolute path. + /// + /// - Parameter sourceRoot: project source root. + /// - Returns: file element absolute path. + /// - Throws: an error if the absolute path cannot be obtained. + func fullPath(sourceRoot: String) throws -> String? { + try fullPath(sourceRoot: Path(sourceRoot))?.absolute().string + } + /// Returns a file absolute path. /// /// - Parameter sourceRoot: project source root. @@ -159,7 +173,7 @@ public extension PBXFileElement { let projectObjects = try objects() let isThisElementRoot = projectObjects.projects.values.first(where: { $0.mainGroup == self }) != nil if isThisElementRoot { - if let path = path { + if let path { return sourceRoot + Path(path) } return sourceRoot @@ -185,7 +199,7 @@ public extension PBXFileElement { /// - Returns: path to the variant group base file. /// - Throws: an error if the path cannot be obtained. private func baseVariantGroupPath() throws -> String? { - guard let variantGroup: PBXVariantGroup = self.reference.getObject() else { return nil } + guard let variantGroup: PBXVariantGroup = reference.getObject() else { return nil } guard let baseReference = try variantGroup .childrenReferences .compactMap({ try $0.getThrowingObject() as PBXFileElement }) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileReference.swift b/Sources/XcodeProj/Objects/Files/PBXFileReference.swift index 32b60b53f..f596f32cc 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileReference.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileReference.swift @@ -15,6 +15,9 @@ public final class PBXFileReference: PBXFileElement { /// Derived file type. For a file named "foo.swift" this value would be "sourcecode.swift" public var lastKnownFileType: String? + /// The XCFramework's expected code signature. `nil` if not an XCFramework reference or if the XCFramework is not signed. + public var expectedSignature: String? + /// Line ending type for the file public var lineEnding: UInt? @@ -44,6 +47,7 @@ public final class PBXFileReference: PBXFileElement { /// - indentWidth: the number of positions to indent blocks of code /// - tabWidth: the visual width of tab characters /// - lineEnding: the line ending type for the file. + /// - expectedSignature: code signature for signed XCFrameworks, `nil` otherwise. /// - languageSpecificationIdentifier: legacy programming language identifier. /// - xcLanguageSpecificationIdentifier: the programming language identifier. /// - plistStructureDefinitionIdentifier: the plist organizational family identifier. @@ -59,6 +63,7 @@ public final class PBXFileReference: PBXFileElement { indentWidth: UInt? = nil, tabWidth: UInt? = nil, lineEnding: UInt? = nil, + expectedSignature: String? = nil, languageSpecificationIdentifier: String? = nil, xcLanguageSpecificationIdentifier: String? = nil, plistStructureDefinitionIdentifier: String? = nil) { @@ -66,6 +71,7 @@ public final class PBXFileReference: PBXFileElement { self.explicitFileType = explicitFileType self.lastKnownFileType = lastKnownFileType self.lineEnding = lineEnding + self.expectedSignature = expectedSignature self.languageSpecificationIdentifier = languageSpecificationIdentifier self.xcLanguageSpecificationIdentifier = xcLanguageSpecificationIdentifier self.plistStructureDefinitionIdentifier = plistStructureDefinitionIdentifier @@ -86,6 +92,7 @@ public final class PBXFileReference: PBXFileElement { case explicitFileType case lastKnownFileType case lineEnding + case expectedSignature case languageSpecificationIdentifier case xcLanguageSpecificationIdentifier case plistStructureDefinitionIdentifier @@ -97,6 +104,7 @@ public final class PBXFileReference: PBXFileElement { explicitFileType = try container.decodeIfPresent(.explicitFileType) lastKnownFileType = try container.decodeIfPresent(.lastKnownFileType) lineEnding = try container.decodeIntIfPresent(.lineEnding) + expectedSignature = try container.decodeIfPresent(.expectedSignature) languageSpecificationIdentifier = try container.decodeIfPresent(.languageSpecificationIdentifier) xcLanguageSpecificationIdentifier = try container.decodeIfPresent(.xcLanguageSpecificationIdentifier) plistStructureDefinitionIdentifier = try container.decodeIfPresent(.plistStructureDefinitionIdentifier) @@ -105,33 +113,41 @@ public final class PBXFileReference: PBXFileElement { // MARK: - PlistSerializable - override var multiline: Bool { return false } + override var multiline: Bool { false } override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(PBXFileReference.isa)) - if let lastKnownFileType = lastKnownFileType { + if let lastKnownFileType { dictionary["lastKnownFileType"] = .string(CommentedString(lastKnownFileType)) } - if let fileEncoding = fileEncoding { + if let fileEncoding { dictionary["fileEncoding"] = .string(CommentedString("\(fileEncoding)")) } - if let explicitFileType = self.explicitFileType { + if let explicitFileType { dictionary["explicitFileType"] = .string(CommentedString(explicitFileType)) } - if let lineEnding = lineEnding { + if let lineEnding { dictionary["lineEnding"] = .string(CommentedString("\(lineEnding)")) } - if let languageSpecificationIdentifier = languageSpecificationIdentifier { + if let expectedSignature { + dictionary["expectedSignature"] = .string(CommentedString(expectedSignature)) + } + if let languageSpecificationIdentifier { dictionary["languageSpecificationIdentifier"] = .string(CommentedString(languageSpecificationIdentifier)) } - if let xcLanguageSpecificationIdentifier = xcLanguageSpecificationIdentifier { + if let xcLanguageSpecificationIdentifier { dictionary["xcLanguageSpecificationIdentifier"] = .string(CommentedString(xcLanguageSpecificationIdentifier)) } - if let plistStructureDefinitionIdentifier = plistStructureDefinitionIdentifier { + if let plistStructureDefinitionIdentifier { dictionary["plistStructureDefinitionIdentifier"] = .string(CommentedString(plistStructureDefinitionIdentifier)) } return (key: CommentedString(reference, comment: name ?? path), value: .dictionary(dictionary)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFileReference else { return false } + return isEqual(to: rhs) + } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift new file mode 100644 index 000000000..e007bb879 --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -0,0 +1,129 @@ +import Foundation + +/// Class representing an element that may contain other elements. +public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXFileSystemSynchronizedExceptionSet, PlistSerializable { + // MARK: - Attributes + + /// A list of relative paths to children subfolders for which exceptions are applied. + public var membershipExceptions: [String]? + + /// Changes the default header visibility (project) to public for the following headers. + /// Every item in the list is the relative path inside the root synchronized group. + public var publicHeaders: [String]? + + /// Changes the default header visibility (project) to private for the following headers. + /// Every item in the list is the relative path inside the root synchronized group. + public var privateHeaders: [String]? + + /// Additional compiler flags by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// The value is the additional compiler flags. + public var additionalCompilerFlagsByRelativePath: [String: String]? + + /// Attributes by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// This is used for example when linking frameworks to specify that they are optional with the attribute "Weak" + public var attributesByRelativePath: [String: [String]]? + + /// Platform filters by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// The value is the list of platform filters (e.g. "ios", "tvos") that the file should be included for. + public var platformFiltersByRelativePath: [String: [String]]? + + var targetReference: PBXObjectReference + + public var target: PBXTarget! { + get { + targetReference.getObject() as? PBXTarget + } + set { + targetReference = newValue.reference + } + } + + // MARK: - Init + + public init(target: PBXTarget, + membershipExceptions: [String]?, + publicHeaders: [String]?, + privateHeaders: [String]?, + additionalCompilerFlagsByRelativePath: [String: String]?, + attributesByRelativePath: [String: [String]]?, + platformFiltersByRelativePath: [String: [String]]? = nil) { + targetReference = target.reference + self.membershipExceptions = membershipExceptions + self.publicHeaders = publicHeaders + self.privateHeaders = privateHeaders + self.additionalCompilerFlagsByRelativePath = additionalCompilerFlagsByRelativePath + self.attributesByRelativePath = attributesByRelativePath + self.platformFiltersByRelativePath = platformFiltersByRelativePath + super.init() + } + + // MARK: - Decodable + + fileprivate enum CodingKeys: String, CodingKey { + case target + case membershipExceptions + case publicHeaders + case privateHeaders + case additionalCompilerFlagsByRelativePath + case attributesByRelativePath + case platformFiltersByRelativePath + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let referenceRepository = decoder.context.objectReferenceRepository + let objects = decoder.context.objects + let targetReference: String = try container.decode(.target) + self.targetReference = referenceRepository.getOrCreate(reference: targetReference, objects: objects) + membershipExceptions = try container.decodeIfPresent(.membershipExceptions) + publicHeaders = try container.decodeIfPresent(.publicHeaders) + privateHeaders = try container.decodeIfPresent(.privateHeaders) + additionalCompilerFlagsByRelativePath = try container.decodeIfPresent(.additionalCompilerFlagsByRelativePath) + attributesByRelativePath = try container.decodeIfPresent(.attributesByRelativePath) + platformFiltersByRelativePath = try container.decodeIfPresent(.platformFiltersByRelativePath) + try super.init(from: decoder) + } + + // MARK: - Equatable + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFileSystemSynchronizedBuildFileExceptionSet else { return false } + return isEqual(to: rhs) + } + + // MARK: - PlistSerializable + + func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary: [CommentedString: PlistValue] = [:] + dictionary["isa"] = .string(CommentedString(PBXFileSystemSynchronizedBuildFileExceptionSet.isa)) + if let membershipExceptions { + dictionary["membershipExceptions"] = .array(membershipExceptions.map { .string(CommentedString($0)) }) + } + if let publicHeaders { + dictionary["publicHeaders"] = .array(publicHeaders.map { .string(CommentedString($0)) }) + } + if let privateHeaders { + dictionary["privateHeaders"] = .array(privateHeaders.map { .string(CommentedString($0)) }) + } + if let additionalCompilerFlagsByRelativePath { + dictionary["additionalCompilerFlagsByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: additionalCompilerFlagsByRelativePath.map { key, value in + (CommentedString(key), PlistValue.string(CommentedString(value))) + })) + } + if let attributesByRelativePath { + dictionary["attributesByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: attributesByRelativePath.map { key, value in + (CommentedString(key), .array(value.map { .string(CommentedString($0)) })) + })) + } + if let platformFiltersByRelativePath { + dictionary["platformFiltersByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: platformFiltersByRelativePath.map { key, value in + (CommentedString(key), .array(value.map { .string(CommentedString($0)) })) + })) + } + dictionary["target"] = .string(CommentedString(target.reference.value, comment: target.name)) + return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet"), value: .dictionary(dictionary)) + } +} diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift new file mode 100644 index 000000000..095bd24e9 --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift @@ -0,0 +1,4 @@ +import Foundation + +/// Common class for exception sets, such as `PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet` and `PBXFileSystemSynchronizedBuildFileExceptionSet` +public class PBXFileSystemSynchronizedExceptionSet: PBXObject {} diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift new file mode 100644 index 000000000..b2326893e --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift @@ -0,0 +1,83 @@ +import Foundation + +public class PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: PBXFileSystemSynchronizedExceptionSet, PlistSerializable { + // MARK: - Attributes + + /// A list of relative paths to children subfolders for which exceptions are applied. + public var membershipExceptions: [String]? + + /// Build phase that this exception set applies to. + public var buildPhase: PBXBuildPhase! { + get { + buildPhaseReference.getObject() as? PBXBuildPhase + } + set { + buildPhaseReference = newValue.reference + } + } + + /// Attributes by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// For example `RemoveHeadersOnCopy` and `CodeSignOnCopy`. + public var attributesByRelativePath: [String: [String]]? + + var buildPhaseReference: PBXObjectReference + + // MARK: - Init + + public init( + buildPhase: PBXBuildPhase, + membershipExceptions: [String]?, + attributesByRelativePath: [String: [String]]? + ) { + buildPhaseReference = buildPhase.reference + self.membershipExceptions = membershipExceptions + self.attributesByRelativePath = attributesByRelativePath + super.init() + } + + // MARK: - Decodable + + fileprivate enum CodingKeys: String, CodingKey { + case buildPhase + case membershipExceptions + case attributesByRelativePath + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let referenceRepository = decoder.context.objectReferenceRepository + let objects = decoder.context.objects + let buildPhaseReference: String = try container.decode(.buildPhase) + self.buildPhaseReference = referenceRepository.getOrCreate(reference: buildPhaseReference, objects: objects) + membershipExceptions = try container.decodeIfPresent(.membershipExceptions) + attributesByRelativePath = try container.decodeIfPresent(.attributesByRelativePath) + try super.init(from: decoder) + } + + // MARK: - Equatable + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet else { + return false + } + return isEqual(to: rhs) + } + + // MARK: - PlistSerializable + + func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary: [CommentedString: PlistValue] = [:] + dictionary["isa"] = .string(CommentedString(type(of: self).isa)) + if let membershipExceptions { + dictionary["membershipExceptions"] = .array(membershipExceptions.map { .string(CommentedString($0)) }) + } + if let attributesByRelativePath { + dictionary["attributesByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: attributesByRelativePath.map { key, value in + (CommentedString(key), .array(value.map { .string(CommentedString($0)) })) + })) + } + dictionary["buildPhase"] = .string(CommentedString(buildPhase.reference.value, comment: buildPhase.name() ?? "CopyFiles")) + return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet"), value: .dictionary(dictionary)) + } +} diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift new file mode 100644 index 000000000..43e090eff --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -0,0 +1,113 @@ +import Foundation +import PathKit + +public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { + /// It maps relative paths inside the synchronized root group to a particular file type. + /// If a path doesn't have a particular file type specified, Xcode defaults to the default file type + /// based on the extension of the file. + public var explicitFileTypes: [String: String]? + + /// Returns the references of the exceptions. + var exceptionsReferences: [PBXObjectReference]? + + /// It returns a list of exception objects that override the configuration for some children + /// in the synchronized root group. + public var exceptions: [PBXFileSystemSynchronizedExceptionSet]? { + set { + exceptionsReferences = newValue?.references() + } + get { + exceptionsReferences?.objects() + } + } + + /// A list of relative paths to children folder whose configuration is overriden. + public var explicitFolders: [String]? + + /// Initializes the file element with its properties. + /// + /// - Parameters: + /// - sourceTree: file source tree. + /// - path: object relative path from `sourceTree`, if different than `name`. + /// - name: object name. + /// - includeInIndex: should the IDE index the object? + /// - usesTabs: object uses tabs. + /// - indentWidth: the number of positions to indent blocks of code + /// - tabWidth: the visual width of tab characters + /// - wrapsLines: should the IDE wrap lines when editing the object? + /// - explicitFileTypes: It maps relative paths inside the synchronized root group to a particular file type. + /// - exceptions: It returns a list of exception objects that override the configuration for some children in the synchronized root group. + /// - explicitFolders: A list of relative paths to children folder whose configuration is overriden. + public init(sourceTree: PBXSourceTree? = nil, + path: String? = nil, + name: String? = nil, + includeInIndex: Bool? = nil, + usesTabs: Bool? = nil, + indentWidth: UInt? = nil, + tabWidth: UInt? = nil, + wrapsLines: Bool? = nil, + explicitFileTypes: [String: String] = [:], + exceptions: [PBXFileSystemSynchronizedExceptionSet] = [], + explicitFolders: [String] = []) { + self.explicitFileTypes = explicitFileTypes + exceptionsReferences = exceptions.references() + self.explicitFolders = explicitFolders + super.init(sourceTree: sourceTree, + path: path, + name: name, + includeInIndex: includeInIndex, + usesTabs: usesTabs, + indentWidth: indentWidth, + tabWidth: tabWidth, + wrapsLines: wrapsLines) + } + + // MARK: - Decodable + + fileprivate enum CodingKeys: String, CodingKey { + case explicitFileTypes + case exceptions + case explicitFolders + } + + public required init(from decoder: Decoder) throws { + let objects = decoder.context.objects + let objectReferenceRepository = decoder.context.objectReferenceRepository + let container = try decoder.container(keyedBy: CodingKeys.self) + explicitFileTypes = try container.decodeIfPresent(.explicitFileTypes) + let exceptionsReferences: [String] = try (container.decodeIfPresent(.exceptions)) ?? [] + self.exceptionsReferences = exceptionsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } + explicitFolders = try container.decodeIfPresent(.explicitFolders) + try super.init(from: decoder) + } + + // MARK: - PlistSerializable + + override var multiline: Bool { (exceptions?.count ?? 0) < 2 } + + override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] + dictionary["isa"] = .string(CommentedString(type(of: self).isa)) + if let exceptions, !exceptions.isEmpty { + dictionary["exceptions"] = .array(exceptions.map { exception in + .string(CommentedString(exception.reference.value, comment: type(of: exception).isa)) + }) + } + if let explicitFileTypes { + dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: explicitFileTypes.map { relativePath, fileType in + (CommentedString(relativePath), .string(CommentedString(fileType))) + })) + } + if let explicitFolders { + dictionary["explicitFolders"] = .array(explicitFolders.map { .string(CommentedString($0)) }) + } + return (key: CommentedString(reference, + comment: name ?? path), + value: .dictionary(dictionary)) + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFileSystemSynchronizedRootGroup else { return false } + return isEqual(to: rhs) + } +} diff --git a/Sources/XcodeProj/Objects/Files/PBXGroup.swift b/Sources/XcodeProj/Objects/Files/PBXGroup.swift index c187b9257..c720a1a9d 100644 --- a/Sources/XcodeProj/Objects/Files/PBXGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXGroup.swift @@ -13,7 +13,7 @@ public class PBXGroup: PBXFileElement { childrenReferences = newValue.references() } get { - return childrenReferences.objects() + childrenReferences.objects() } } @@ -61,7 +61,7 @@ public class PBXGroup: PBXFileElement { let objects = decoder.context.objects let objectReferenceRepository = decoder.context.objectReferenceRepository let container = try decoder.container(keyedBy: CodingKeys.self) - let childrenReferences: [String] = (try container.decodeIfPresent(.children)) ?? [] + let childrenReferences: [String] = try (container.decodeIfPresent(.children)) ?? [] self.childrenReferences = childrenReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } try super.init(from: decoder) } @@ -71,7 +71,7 @@ public class PBXGroup: PBXFileElement { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - dictionary["children"] = .array(childrenReferences.map { (fileReference) -> PlistValue in + dictionary["children"] = .array(childrenReferences.map { fileReference -> PlistValue in let fileElement: PBXFileElement? = fileReference.getObject() return .string(CommentedString(fileReference.value, comment: fileElement?.fileName())) }) @@ -80,12 +80,17 @@ public class PBXGroup: PBXFileElement { comment: name ?? path), value: .dictionary(dictionary)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXGroup else { return false } + return isEqual(to: rhs) + } } // MARK: - Helpers /// Options passed when adding new groups. -public struct GroupAddingOptions: OptionSet { +public struct GroupAddingOptions: OptionSet, Sendable { /// Raw value. public let rawValue: Int @@ -106,7 +111,27 @@ public extension PBXGroup { /// - Parameter groupName: group name. /// - Returns: group with the given name contained in the given parent group. func group(named name: String) -> PBXGroup? { - return childrenReferences + childrenReferences + .objects() + .first(where: { $0.name == name }) + } + + /// Returns group with the given path contained in the given parent group. + /// + /// - Parameter path: path name. + /// - Returns: group with the given path contained in the given parent group. + func group(with path: String) -> PBXGroup? { + childrenReferences + .objects() + .first(where: { ($0 as? PBXFileElement)?.path == path }) as? PBXGroup + } + + /// Returns the synchronized root group with the given name contained in the given parent group. + /// + /// - Parameter groupName: group name. + /// - Returns: the synchronized root group with the given name contained in the given parent group. + func synchronizedRootGroup(named name: String) -> PBXFileSystemSynchronizedRootGroup? { + childrenReferences .objects() .first(where: { $0.name == name }) } @@ -116,7 +141,7 @@ public extension PBXGroup { /// - Parameter name: file name. /// - Returns: file with the given name contained in the given parent group. func file(named name: String) -> PBXFileReference? { - return childrenReferences + childrenReferences .objects() .first(where: { $0.name == name }) } @@ -129,11 +154,11 @@ public extension PBXGroup { /// - Returns: created groups. @discardableResult func addGroup(named groupName: String, options: GroupAddingOptions = []) throws -> [PBXGroup] { - let objects = try self.objects() + let objects = try objects() return groupName.components(separatedBy: "/").reduce(into: [PBXGroup]()) { groups, name in let group = groups.last ?? self let newGroup = PBXGroup(children: [], sourceTree: .group, name: name, path: options.contains(.withoutFolder) ? nil : name) - newGroup.parent = self + newGroup.parent = group group.childrenReferences.append(newGroup.reference) objects.add(object: newGroup) groups.append(newGroup) @@ -147,7 +172,7 @@ public extension PBXGroup { /// - Returns: created groups. @discardableResult func addVariantGroup(named groupName: String) throws -> [PBXVariantGroup] { - let objects = try self.objects() + let objects = try objects() return groupName.components(separatedBy: "/").reduce(into: [PBXVariantGroup]()) { groups, name in let group = groups.last ?? self @@ -200,18 +225,17 @@ public extension PBXGroup { return existingFileReference.value } - let path: String? - switch sourceTree { + let path: String? = switch sourceTree { case .group: - path = groupPath.map { filePath.relative(to: $0) }?.string + groupPath.map { filePath.relative(to: $0) }?.string case .sourceRoot: - path = filePath.relative(to: sourceRoot).string + filePath.relative(to: sourceRoot).string case .absolute, .sdkRoot, .developerDir: - path = filePath.string + filePath.string default: - path = nil + nil } let fileReference = PBXFileReference( sourceTree: sourceTree, diff --git a/Sources/XcodeProj/Objects/Files/PBXSourceTree.swift b/Sources/XcodeProj/Objects/Files/PBXSourceTree.swift index c68dbde38..8d230376d 100644 --- a/Sources/XcodeProj/Objects/Files/PBXSourceTree.swift +++ b/Sources/XcodeProj/Objects/Files/PBXSourceTree.swift @@ -54,10 +54,10 @@ public enum PBXSourceTree: CustomStringConvertible, Equatable, Decodable { (.buildProductsDir, .buildProductsDir), (.sdkRoot, .sdkRoot), (.developerDir, .developerDir): - return true + true case let (.custom(lhsValue), .custom(rhsValue)): - return lhsValue == rhsValue + lhsValue == rhsValue case (.none, _), (.absolute, _), @@ -67,28 +67,28 @@ public enum PBXSourceTree: CustomStringConvertible, Equatable, Decodable { (.sdkRoot, _), (.developerDir, _), (.custom, _): - return false + false } } public var description: String { switch self { case .none: - return PBXSourceTree.noneValue + PBXSourceTree.noneValue case .absolute: - return PBXSourceTree.absoluteValue + PBXSourceTree.absoluteValue case .group: - return PBXSourceTree.groupValue + PBXSourceTree.groupValue case .sourceRoot: - return PBXSourceTree.sourceRootValue + PBXSourceTree.sourceRootValue case .buildProductsDir: - return PBXSourceTree.buildProductsDirValue + PBXSourceTree.buildProductsDirValue case .sdkRoot: - return PBXSourceTree.sdkRootValue + PBXSourceTree.sdkRootValue case .developerDir: - return PBXSourceTree.developerDirValue + PBXSourceTree.developerDirValue case let .custom(value): - return value + value } } } @@ -97,6 +97,6 @@ public enum PBXSourceTree: CustomStringConvertible, Equatable, Decodable { extension PBXSourceTree { func plist() -> PlistValue { - return .string(CommentedString(String(describing: self))) + .string(CommentedString(String(describing: self))) } } diff --git a/Sources/XcodeProj/Objects/Files/PBXVariantGroup.swift b/Sources/XcodeProj/Objects/Files/PBXVariantGroup.swift index e0fcc01a5..53d3659a9 100644 --- a/Sources/XcodeProj/Objects/Files/PBXVariantGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXVariantGroup.swift @@ -1,4 +1,9 @@ import Foundation // This is the element for referencing localized resources. -public final class PBXVariantGroup: PBXGroup {} +public final class PBXVariantGroup: PBXGroup { + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXVariantGroup else { return false } + return isEqual(to: rhs) + } +} diff --git a/Sources/XcodeProj/Objects/Files/XCVersionGroup.swift b/Sources/XcodeProj/Objects/Files/XCVersionGroup.swift index b643295bc..d548a01ec 100644 --- a/Sources/XcodeProj/Objects/Files/XCVersionGroup.swift +++ b/Sources/XcodeProj/Objects/Files/XCVersionGroup.swift @@ -12,7 +12,7 @@ public final class XCVersionGroup: PBXGroup { /// Returns the current version file reference. public var currentVersion: PBXFileReference? { get { - return currentVersionReference?.getObject() + currentVersionReference?.getObject() } set { currentVersionReference = newValue?.reference @@ -87,14 +87,19 @@ public final class XCVersionGroup: PBXGroup { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(XCVersionGroup.isa)) - if let versionGroupType = versionGroupType { + if let versionGroupType { dictionary["versionGroupType"] = .string(CommentedString(versionGroupType)) } - if let currentVersionReference = currentVersionReference { + if let currentVersionReference { let fileElement: PBXFileElement? = currentVersionReference.getObject() dictionary["currentVersion"] = .string(CommentedString(currentVersionReference.value, comment: fileElement?.fileName())) } return (key: CommentedString(reference, comment: path?.split(separator: "/").last.map(String.init)), value: .dictionary(dictionary)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? XCVersionGroup else { return false } + return isEqual(to: rhs) + } } diff --git a/Sources/XcodeProj/Objects/Project/PBXObject.swift b/Sources/XcodeProj/Objects/Project/PBXObject.swift index a27ffeec8..6f205084a 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObject.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObject.swift @@ -7,7 +7,7 @@ public class PBXObject: Hashable, Decodable, Equatable, AutoEquatable { /// Note: The unique identifier of an object might change when the project gets written. /// If you use this identifier from a scheme, make sure the project is written before the project is. public var uuid: String { - return reference.value + reference.value } /// The object reference in the project that contains it. @@ -45,23 +45,28 @@ public class PBXObject: Hashable, Decodable, Equatable, AutoEquatable { let referenceRepository = decoder.context.objectReferenceRepository let objects = decoder.context.objects let container = try decoder.container(keyedBy: CodingKeys.self) - let reference: String = try container.decode(.reference) + + // The reference to an object in a pbxproj is the key of a dictionary, so read the value from the codingPath + guard let reference = container.codingPath.last?.stringValue else { + throw PBXObjectError.missingReference + } + self.reference = referenceRepository.getOrCreate(reference: reference, objects: objects) self.reference.setObject(self) } /// Object isa (type id) public static var isa: String { - return String(describing: self) + String(describing: self) } public static func == (lhs: PBXObject, rhs: PBXObject) -> Bool { - return lhs.isEqual(to: rhs) + lhs.isEqual(to: rhs) } - @objc dynamic func isEqual(to _: Any?) -> Bool { - return true + func isEqual(to _: Any?) -> Bool { + true } public func hash(into hasher: inout Hasher) { @@ -73,7 +78,7 @@ public class PBXObject: Hashable, Decodable, Equatable, AutoEquatable { /// - Returns: objects the object belongs to. /// - Throws: an error if this method is accessed before the object has been added to a project. func objects() throws -> PBXObjects { - guard let objects = self.reference.objects else { + guard let objects = reference.objects else { let objectType = String(describing: type(of: self)) throw PBXObjectError.orphaned(type: objectType, reference: reference.value) } diff --git a/Sources/XcodeProj/Objects/Project/PBXObjectDictionaryEntry.swift b/Sources/XcodeProj/Objects/Project/PBXObjectDictionaryEntry.swift new file mode 100644 index 000000000..8df892ce0 --- /dev/null +++ b/Sources/XcodeProj/Objects/Project/PBXObjectDictionaryEntry.swift @@ -0,0 +1,48 @@ +import Foundation + +struct PBXObjectDictionaryEntry: Decodable { + let object: PBXObject + + enum CodingKeys: CodingKey { + case isa + } + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let isa = try container.decode(String.self, forKey: .isa) + + object = switch isa { + case "PBXFileElement": try PBXFileElement(from: decoder) + case "PBXBuildFile": try PBXBuildFile(from: decoder) + case "PBXFileReference": try PBXFileReference(from: decoder) + case "PBXLegacyTarget": try PBXLegacyTarget(from: decoder) + case "PBXNativeTarget": try PBXNativeTarget(from: decoder) + case "PBXAggregateTarget": try PBXAggregateTarget(from: decoder) + case "PBXProject": try PBXProject(from: decoder) + case "PBXGroup": try PBXGroup(from: decoder) + case "PBXHeadersBuildPhase": try PBXHeadersBuildPhase(from: decoder) + case "PBXFrameworksBuildPhase": try PBXFrameworksBuildPhase(from: decoder) + case "XCConfigurationList": try XCConfigurationList(from: decoder) + case "PBXResourcesBuildPhase": try PBXResourcesBuildPhase(from: decoder) + case "PBXShellScriptBuildPhase": try PBXShellScriptBuildPhase(from: decoder) + case "PBXSourcesBuildPhase": try PBXSourcesBuildPhase(from: decoder) + case "PBXTargetDependency": try PBXTargetDependency(from: decoder) + case "PBXVariantGroup": try PBXVariantGroup(from: decoder) + case "XCBuildConfiguration": try XCBuildConfiguration(from: decoder) + case "PBXCopyFilesBuildPhase": try PBXCopyFilesBuildPhase(from: decoder) + case "PBXContainerItemProxy": try PBXContainerItemProxy(from: decoder) + case "PBXReferenceProxy": try PBXReferenceProxy(from: decoder) + case "XCVersionGroup": try XCVersionGroup(from: decoder) + case "PBXRezBuildPhase": try PBXRezBuildPhase(from: decoder) + case "PBXBuildRule": try PBXBuildRule(from: decoder) + case "XCRemoteSwiftPackageReference": try XCRemoteSwiftPackageReference(from: decoder) + case "XCLocalSwiftPackageReference": try XCLocalSwiftPackageReference(from: decoder) + case "XCSwiftPackageProductDependency": try XCSwiftPackageProductDependency(from: decoder) + case "PBXFileSystemSynchronizedRootGroup": try PBXFileSystemSynchronizedRootGroup(from: decoder) + case "PBXFileSystemSynchronizedBuildFileExceptionSet": try PBXFileSystemSynchronizedBuildFileExceptionSet(from: decoder) + case "PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet": try PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet(from: decoder) + default: + throw PBXObjectError.unknownElement(isa) + } + } +} diff --git a/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift b/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift deleted file mode 100644 index 5e868b364..000000000 --- a/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift +++ /dev/null @@ -1,75 +0,0 @@ -import Foundation - -final class PBXObjectParser { - private let userInfo: [CodingUserInfoKey: Any] - private let decoder = XcodeprojJSONDecoder() - - init(userInfo: [CodingUserInfoKey: Any]) { - self.userInfo = userInfo - } - - // swiftlint:disable function_body_length - public func parse(reference: String, dictionary: [String: Any]) throws -> PBXObject { - var mutableDictionary = dictionary - mutableDictionary["reference"] = reference - let data = try JSONSerialization.data(withJSONObject: mutableDictionary, options: []) - guard let isa = dictionary["isa"] as? String else { throw PBXObjectError.missingIsa } - // Order is important for performance - switch isa { - case PBXFileElement.isa: - return try decoder.decode(PBXFileElement.self, from: data) - case PBXBuildFile.isa: - return try decoder.decode(PBXBuildFile.self, from: data) - case PBXFileReference.isa: - return try decoder.decode(PBXFileReference.self, from: data) - case PBXLegacyTarget.isa: - return try decoder.decode(PBXLegacyTarget.self, from: data) - case PBXNativeTarget.isa: - return try decoder.decode(PBXNativeTarget.self, from: data) - case PBXAggregateTarget.isa: - return try decoder.decode(PBXAggregateTarget.self, from: data) - case PBXProject.isa: - return try decoder.decode(PBXProject.self, from: data) - case PBXGroup.isa: - return try decoder.decode(PBXGroup.self, from: data) - case PBXHeadersBuildPhase.isa: - return try decoder.decode(PBXHeadersBuildPhase.self, from: data) - case PBXFrameworksBuildPhase.isa: - return try decoder.decode(PBXFrameworksBuildPhase.self, from: data) - case XCConfigurationList.isa: - return try decoder.decode(XCConfigurationList.self, from: data) - case PBXResourcesBuildPhase.isa: - return try decoder.decode(PBXResourcesBuildPhase.self, from: data) - case PBXShellScriptBuildPhase.isa: - return try decoder.decode(PBXShellScriptBuildPhase.self, from: data) - case PBXSourcesBuildPhase.isa: - return try decoder.decode(PBXSourcesBuildPhase.self, from: data) - case PBXTargetDependency.isa: - return try decoder.decode(PBXTargetDependency.self, from: data) - case PBXVariantGroup.isa: - return try decoder.decode(PBXVariantGroup.self, from: data) - case XCBuildConfiguration.isa: - return try decoder.decode(XCBuildConfiguration.self, from: data) - case PBXCopyFilesBuildPhase.isa: - return try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - case PBXContainerItemProxy.isa: - return try decoder.decode(PBXContainerItemProxy.self, from: data) - case PBXReferenceProxy.isa: - return try decoder.decode(PBXReferenceProxy.self, from: data) - case XCVersionGroup.isa: - return try decoder.decode(XCVersionGroup.self, from: data) - case PBXRezBuildPhase.isa: - return try decoder.decode(PBXRezBuildPhase.self, from: data) - case PBXBuildRule.isa: - return try decoder.decode(PBXBuildRule.self, from: data) - case XCRemoteSwiftPackageReference.isa: - return try decoder.decode(XCRemoteSwiftPackageReference.self, from: data) - case XCSwiftPackageProductDependency.isa: - return try decoder.decode(XCSwiftPackageProductDependency.self, from: data) - default: - throw PBXObjectError.unknownElement(isa) - } - } - - // swiftlint:enable function_body_length -} diff --git a/Sources/XcodeProj/Objects/Project/PBXObjectReference.swift b/Sources/XcodeProj/Objects/Project/PBXObjectReference.swift index acba62d79..3f544af26 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjectReference.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjectReference.swift @@ -1,5 +1,4 @@ import Foundation -import os.signpost /// Object used as a reference to PBXObjects from PBXObjects. class PBXObjectReference: NSObject, Comparable, NSCopying { @@ -55,7 +54,7 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { let object = objects?.delete(reference: self) self.value = value temporary = false - if let object = object { + if let object { objects?.add(object: object) } } @@ -65,18 +64,18 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { let object = objects?.delete(reference: self) value = "TEMP_\(UUID().uuidString)" temporary = true - if let object = object { + if let object { objects?.add(object: object) } } /// Hash value. override var hash: Int { - return value.hashValue + value.hashValue } func copy(with _: NSZone? = nil) -> Any { - return type(of: self).init(self) + type(of: self).init(self) } /// Compares two instances of PBXObjectReference @@ -86,7 +85,7 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { /// - rhs: second instance to be compared. /// - Returns: true if the two instances are equal. static func == (lhs: PBXObjectReference, rhs: PBXObjectReference) -> Bool { - return lhs.isEqual(rhs) + lhs.isEqual(rhs) } /// Compares with another instance of PBXObjectReference. @@ -106,7 +105,7 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { /// - rhs: second instance to be compared. /// - Returns: comparison result. static func < (lhs: PBXObjectReference, rhs: PBXObjectReference) -> Bool { - return lhs.value < rhs.value + lhs.value < rhs.value } /// Sets the object so it can be retrieved quickly again later @@ -120,7 +119,7 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { /// /// - Returns: object the reference is referring to. Returns nil if the objects property has been released or the reference doesn't exist func getObject() -> T? { - return try? getThrowingObject() + try? getThrowingObject() } /// Returns the object the reference is referfing to. @@ -131,7 +130,7 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { if let object = object as? T { return object } - guard let objects = objects else { + guard let objects else { throw PBXObjectError.objectsReleased } guard let object = objects.get(reference: self) as? T else { @@ -144,12 +143,12 @@ class PBXObjectReference: NSObject, Comparable, NSCopying { extension Array where Element: PBXObject { func references() -> [PBXObjectReference] { - return map { $0.reference } + map(\.reference) } } extension Array where Element: PBXObjectReference { func objects() -> [T] { - return compactMap { $0.getObject() } + compactMap { $0.getObject() } } } diff --git a/Sources/XcodeProj/Objects/Project/PBXObjects.swift b/Sources/XcodeProj/Objects/Project/PBXObjects.swift index 09c8fa7bf..67388444b 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjects.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjects.swift @@ -8,126 +8,146 @@ class PBXObjects: Equatable { private var _projects: [PBXObjectReference: PBXProject] = [:] var projects: [PBXObjectReference: PBXProject] { - return lock.whileLocked { _projects } + lock.whileLocked { _projects } } private var _referenceProxies: [PBXObjectReference: PBXReferenceProxy] = [:] var referenceProxies: [PBXObjectReference: PBXReferenceProxy] { - return lock.whileLocked { _referenceProxies } + lock.whileLocked { _referenceProxies } } // File elements private var _fileReferences: [PBXObjectReference: PBXFileReference] = [:] var fileReferences: [PBXObjectReference: PBXFileReference] { - return lock.whileLocked { _fileReferences } + lock.whileLocked { _fileReferences } } private var _versionGroups: [PBXObjectReference: XCVersionGroup] = [:] var versionGroups: [PBXObjectReference: XCVersionGroup] { - return lock.whileLocked { _versionGroups } + lock.whileLocked { _versionGroups } } private var _variantGroups: [PBXObjectReference: PBXVariantGroup] = [:] var variantGroups: [PBXObjectReference: PBXVariantGroup] { - return lock.whileLocked { _variantGroups } + lock.whileLocked { _variantGroups } } private var _groups: [PBXObjectReference: PBXGroup] = [:] var groups: [PBXObjectReference: PBXGroup] { - return lock.whileLocked { _groups } + lock.whileLocked { _groups } } // Configuration private var _buildConfigurations: [PBXObjectReference: XCBuildConfiguration] = [:] var buildConfigurations: [PBXObjectReference: XCBuildConfiguration] { - return lock.whileLocked { _buildConfigurations } + lock.whileLocked { _buildConfigurations } } private var _configurationLists: [PBXObjectReference: XCConfigurationList] = [:] var configurationLists: [PBXObjectReference: XCConfigurationList] { - return lock.whileLocked { _configurationLists } + lock.whileLocked { _configurationLists } } // Targets private var _legacyTargets: [PBXObjectReference: PBXLegacyTarget] = [:] var legacyTargets: [PBXObjectReference: PBXLegacyTarget] { - return lock.whileLocked { _legacyTargets } + lock.whileLocked { _legacyTargets } } private var _aggregateTargets: [PBXObjectReference: PBXAggregateTarget] = [:] var aggregateTargets: [PBXObjectReference: PBXAggregateTarget] { - return lock.whileLocked { _aggregateTargets } + lock.whileLocked { _aggregateTargets } } private var _nativeTargets: [PBXObjectReference: PBXNativeTarget] = [:] var nativeTargets: [PBXObjectReference: PBXNativeTarget] { - return lock.whileLocked { _nativeTargets } + lock.whileLocked { _nativeTargets } } private var _targetDependencies: [PBXObjectReference: PBXTargetDependency] = [:] var targetDependencies: [PBXObjectReference: PBXTargetDependency] { - return lock.whileLocked { _targetDependencies } + lock.whileLocked { _targetDependencies } } private var _containerItemProxies: [PBXObjectReference: PBXContainerItemProxy] = [:] var containerItemProxies: [PBXObjectReference: PBXContainerItemProxy] { - return lock.whileLocked { _containerItemProxies } + lock.whileLocked { _containerItemProxies } } private var _buildRules: [PBXObjectReference: PBXBuildRule] = [:] var buildRules: [PBXObjectReference: PBXBuildRule] { - return lock.whileLocked { _buildRules } + lock.whileLocked { _buildRules } } // Build Phases private var _buildFiles: [PBXObjectReference: PBXBuildFile] = [:] var buildFiles: [PBXObjectReference: PBXBuildFile] { - return lock.whileLocked { _buildFiles } + lock.whileLocked { _buildFiles } } private var _copyFilesBuildPhases: [PBXObjectReference: PBXCopyFilesBuildPhase] = [:] var copyFilesBuildPhases: [PBXObjectReference: PBXCopyFilesBuildPhase] { - return lock.whileLocked { _copyFilesBuildPhases } + lock.whileLocked { _copyFilesBuildPhases } } private var _shellScriptBuildPhases: [PBXObjectReference: PBXShellScriptBuildPhase] = [:] var shellScriptBuildPhases: [PBXObjectReference: PBXShellScriptBuildPhase] { - return lock.whileLocked { _shellScriptBuildPhases } + lock.whileLocked { _shellScriptBuildPhases } } private var _resourcesBuildPhases: [PBXObjectReference: PBXResourcesBuildPhase] = [:] var resourcesBuildPhases: [PBXObjectReference: PBXResourcesBuildPhase] { - return lock.whileLocked { _resourcesBuildPhases } + lock.whileLocked { _resourcesBuildPhases } } private var _frameworksBuildPhases: [PBXObjectReference: PBXFrameworksBuildPhase] = [:] var frameworksBuildPhases: [PBXObjectReference: PBXFrameworksBuildPhase] { - return lock.whileLocked { _frameworksBuildPhases } + lock.whileLocked { _frameworksBuildPhases } } private var _headersBuildPhases: [PBXObjectReference: PBXHeadersBuildPhase] = [:] var headersBuildPhases: [PBXObjectReference: PBXHeadersBuildPhase] { - return lock.whileLocked { _headersBuildPhases } + lock.whileLocked { _headersBuildPhases } } private var _sourcesBuildPhases: [PBXObjectReference: PBXSourcesBuildPhase] = [:] var sourcesBuildPhases: [PBXObjectReference: PBXSourcesBuildPhase] { - return lock.whileLocked { _sourcesBuildPhases } + lock.whileLocked { _sourcesBuildPhases } } private var _carbonResourcesBuildPhases: [PBXObjectReference: PBXRezBuildPhase] = [:] var carbonResourcesBuildPhases: [PBXObjectReference: PBXRezBuildPhase] { - return lock.whileLocked { _carbonResourcesBuildPhases } + lock.whileLocked { _carbonResourcesBuildPhases } } private var _remoteSwiftPackageReferences: [PBXObjectReference: XCRemoteSwiftPackageReference] = [:] var remoteSwiftPackageReferences: [PBXObjectReference: XCRemoteSwiftPackageReference] { - return lock.whileLocked { _remoteSwiftPackageReferences } + lock.whileLocked { _remoteSwiftPackageReferences } + } + + private var _localSwiftPackageReferences: [PBXObjectReference: XCLocalSwiftPackageReference] = [:] + var localSwiftPackageReferences: [PBXObjectReference: XCLocalSwiftPackageReference] { + lock.whileLocked { _localSwiftPackageReferences } } private var _swiftPackageProductDependencies: [PBXObjectReference: XCSwiftPackageProductDependency] = [:] var swiftPackageProductDependencies: [PBXObjectReference: XCSwiftPackageProductDependency] { - return lock.whileLocked { _swiftPackageProductDependencies } + lock.whileLocked { _swiftPackageProductDependencies } + } + + private var _fileSystemSynchronizedRootGroups: [PBXObjectReference: PBXFileSystemSynchronizedRootGroup] = [:] + var fileSystemSynchronizedRootGroups: [PBXObjectReference: PBXFileSystemSynchronizedRootGroup] { + lock.whileLocked { _fileSystemSynchronizedRootGroups } + } + + private var _fileSystemSynchronizedBuildFileExceptionSets: [PBXObjectReference: PBXFileSystemSynchronizedBuildFileExceptionSet] = [:] + var fileSystemSynchronizedBuildFileExceptionSets: [PBXObjectReference: PBXFileSystemSynchronizedBuildFileExceptionSet] { + lock.whileLocked { _fileSystemSynchronizedBuildFileExceptionSets } + } + + private var _fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: [PBXObjectReference: PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet] = [:] + var fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: [PBXObjectReference: PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet] { + lock.whileLocked { _fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet } } // XCSwiftPackageProductDependency @@ -137,15 +157,15 @@ class PBXObjects: Equatable { /// - Parameters: /// - objects: project objects init(objects: [PBXObject] = []) { - objects.forEach { - _ = self.add(object: $0) + for item in objects { + add(object: item) } } // MARK: - Equatable public static func == (lhs: PBXObjects, rhs: PBXObjects) -> Bool { - return lhs.buildFiles == rhs.buildFiles && + lhs.buildFiles == rhs.buildFiles && lhs.legacyTargets == rhs.legacyTargets && lhs.aggregateTargets == rhs.aggregateTargets && lhs.containerItemProxies == rhs.containerItemProxies && @@ -168,7 +188,10 @@ class PBXObjects: Equatable { lhs.carbonResourcesBuildPhases == rhs.carbonResourcesBuildPhases && lhs.buildRules == rhs.buildRules && lhs.swiftPackageProductDependencies == rhs._swiftPackageProductDependencies && - lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences + lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences && + lhs.fileSystemSynchronizedRootGroups == rhs.fileSystemSynchronizedRootGroups && + lhs.fileSystemSynchronizedBuildFileExceptionSets == rhs.fileSystemSynchronizedBuildFileExceptionSets && + lhs.fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet == rhs.fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet } // MARK: - Helpers @@ -189,7 +212,6 @@ class PBXObjects: Equatable { // subclasses of PBXGroup; must be tested before PBXGroup case let object as PBXVariantGroup: _variantGroups[objectReference] = object case let object as XCVersionGroup: _versionGroups[objectReference] = object - // everything else case let object as PBXBuildFile: _buildFiles[objectReference] = object case let object as PBXAggregateTarget: _aggregateTargets[objectReference] = object @@ -212,8 +234,12 @@ class PBXObjects: Equatable { case let object as PBXRezBuildPhase: _carbonResourcesBuildPhases[objectReference] = object case let object as PBXBuildRule: _buildRules[objectReference] = object case let object as XCRemoteSwiftPackageReference: _remoteSwiftPackageReferences[objectReference] = object + case let object as XCLocalSwiftPackageReference: _localSwiftPackageReferences[objectReference] = object case let object as XCSwiftPackageProductDependency: _swiftPackageProductDependencies[objectReference] = object - + case let object as PBXFileSystemSynchronizedRootGroup: _fileSystemSynchronizedRootGroups[objectReference] = object + case let object as PBXFileSystemSynchronizedBuildFileExceptionSet: _fileSystemSynchronizedBuildFileExceptionSets[objectReference] = object + case let object as PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: + _fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet[objectReference] = object default: fatalError("Unhandled PBXObject type for \(object), this is likely a bug / todo") } } @@ -272,9 +298,18 @@ class PBXObjects: Equatable { return _buildRules.remove(at: index).value } else if let index = remoteSwiftPackageReferences.index(forKey: reference) { return _remoteSwiftPackageReferences.remove(at: index).value + } else if let index = localSwiftPackageReferences.index(forKey: reference) { + return _localSwiftPackageReferences.remove(at: index).value } else if let index = swiftPackageProductDependencies.index(forKey: reference) { return _swiftPackageProductDependencies.remove(at: index).value + } else if let index = fileSystemSynchronizedRootGroups.index(forKey: reference) { + return _fileSystemSynchronizedRootGroups.remove(at: index).value + } else if let index = fileSystemSynchronizedBuildFileExceptionSets.index(forKey: reference) { + return _fileSystemSynchronizedBuildFileExceptionSets.remove(at: index).value + } else if let index = fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.index(forKey: reference) { + return _fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.remove(at: index).value } + return nil } @@ -289,55 +324,61 @@ class PBXObjects: Equatable { // Expression was too complex to be solved in reasonable time; // consider breaking up the expression into distinct sub-expressions if let object = buildFiles[reference] { - return object + object } else if let object = aggregateTargets[reference] { - return object + object } else if let object = legacyTargets[reference] { - return object + object } else if let object = containerItemProxies[reference] { - return object + object } else if let object = groups[reference] { - return object + object } else if let object = configurationLists[reference] { - return object + object } else if let object = buildConfigurations[reference] { - return object + object } else if let object = variantGroups[reference] { - return object + object } else if let object = targetDependencies[reference] { - return object + object } else if let object = nativeTargets[reference] { - return object + object } else if let object = fileReferences[reference] { - return object + object } else if let object = projects[reference] { - return object + object } else if let object = versionGroups[reference] { - return object + object } else if let object = referenceProxies[reference] { - return object + object } else if let object = copyFilesBuildPhases[reference] { - return object + object } else if let object = shellScriptBuildPhases[reference] { - return object + object } else if let object = resourcesBuildPhases[reference] { - return object + object } else if let object = frameworksBuildPhases[reference] { - return object + object } else if let object = headersBuildPhases[reference] { - return object + object } else if let object = sourcesBuildPhases[reference] { - return object + object } else if let object = carbonResourcesBuildPhases[reference] { - return object + object } else if let object = buildRules[reference] { - return object + object } else if let object = remoteSwiftPackageReferences[reference] { - return object + object } else if let object = swiftPackageProductDependencies[reference] { - return object + object + } else if let object = fileSystemSynchronizedRootGroups[reference] { + object + } else if let object = fileSystemSynchronizedBuildFileExceptionSets[reference] { + object + } else if let object = fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet[reference] { + object } else { - return nil + nil } } } @@ -427,5 +468,8 @@ extension PBXObjects { carbonResourcesBuildPhases.values.forEach(closure) remoteSwiftPackageReferences.values.forEach(closure) swiftPackageProductDependencies.values.forEach(closure) + fileSystemSynchronizedRootGroups.values.forEach(closure) + fileSystemSynchronizedBuildFileExceptionSets.values.forEach(closure) + fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.values.forEach(closure) } } diff --git a/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift b/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift index 92a6c4849..0d825794b 100644 --- a/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift +++ b/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift @@ -5,19 +5,26 @@ import Foundation // MARK: - Core sort functions // Because of the number of optional data items in PBXBuildFiles, we've externalised the core code in these two functions. -// Note, PBXBuildFile's contains PBXFileElements so this first function is an optional handling wrapper driving the second. +// Note, the first function should be preferred as it considers more context when filenames are equal. // Also note that we use the .fileName() function to retrieve the name as both .path and .name properties can be nil. -private func sortUsingNames(_ lhs: PBXBuildFile, _ rhs: PBXBuildFile) -> Bool { - if let lhsFile = lhs.file, let rhsFile = rhs.file { - return sortUsingNames(lhsFile, rhsFile) +private func sortBuildFilesByName(_ lhs: PBXBuildFile, _ rhs: PBXBuildFile) -> Bool { + guard let lhsFile = lhs.file, let rhsFile = rhs.file else { + return lhs.uuid < rhs.uuid + } + // If the filename is the same, the PBXFileElement UUIDs may or may not match. + if let lhsFilename = lhsFile.fileName(), let rhsFilename = rhsFile.fileName(), lhsFilename != rhsFilename { + return lhsFilename < rhsFilename + } + if lhsFile.uuid != rhsFile.uuid { + return lhsFile.uuid < rhsFile.uuid } return lhs.uuid < rhs.uuid } -private func sortUsingNames(_ lhs: PBXFileElement, _ rhs: PBXFileElement) -> Bool { - if let lhsFilename = lhs.fileName(), let rhsFilename = rhs.fileName() { - return lhsFilename == rhsFilename ? lhs.uuid < rhs.uuid : lhsFilename < rhsFilename +private func sortFileElementsByName(_ lhs: PBXFileElement, _ rhs: PBXFileElement) -> Bool { + if let lhsFilename = lhs.fileName(), let rhsFilename = rhs.fileName(), lhsFilename != rhsFilename { + return lhsFilename < rhsFilename } return lhs.uuid < rhs.uuid } @@ -32,36 +39,36 @@ public enum PBXFileOrder { /// Sort files by their file name. This is a case sensistive sort with lower case names coming after uppercase names. case byFilename - internal func sort(lhs: (PBXObjectReference, Object), rhs: (PBXObjectReference, Object)) -> Bool + func sort(lhs: (PBXObjectReference, Object), rhs: (PBXObjectReference, Object)) -> Bool where Object: PlistSerializable & Equatable { - return lhs.0 < rhs.0 + lhs.0 < rhs.0 } - internal func sort(lhs: (PBXObjectReference, PBXBuildFile), rhs: (PBXObjectReference, PBXBuildFile)) -> Bool { + func sort(lhs: (PBXObjectReference, PBXBuildFile), rhs: (PBXObjectReference, PBXBuildFile)) -> Bool { switch self { case .byFilename: - return sortUsingNames(lhs.1, rhs.1) + sortBuildFilesByName(lhs.1, rhs.1) default: - return lhs.0 < rhs.0 + lhs.0 < rhs.0 } } - internal func sort(lhs: (PBXObjectReference, PBXBuildPhaseFile), rhs: (PBXObjectReference, PBXBuildPhaseFile)) -> Bool { + func sort(lhs: (PBXObjectReference, PBXBuildPhaseFile), rhs: (PBXObjectReference, PBXBuildPhaseFile)) -> Bool { switch self { case .byFilename: - return sortUsingNames(lhs.1.buildFile, rhs.1.buildFile) + sortBuildFilesByName(lhs.1.buildFile, rhs.1.buildFile) default: - return lhs.0 < rhs.0 + lhs.0 < rhs.0 } } - internal func sort(lhs: (PBXObjectReference, PBXFileReference), rhs: (PBXObjectReference, PBXFileReference)) -> Bool { + func sort(lhs: (PBXObjectReference, PBXFileReference), rhs: (PBXObjectReference, PBXFileReference)) -> Bool { switch self { case .byFilename: - return sortUsingNames(lhs.1, rhs.1) + sortFileElementsByName(lhs.1, rhs.1) default: - return lhs.0 < rhs.0 + lhs.0 < rhs.0 } } } @@ -69,9 +76,9 @@ public enum PBXFileOrder { private extension PBXFileElement { var isGroup: Bool { switch self { - case is PBXVariantGroup, is XCVersionGroup: return false - case is PBXGroup: return true - default: return false + case is PBXVariantGroup, is XCVersionGroup: false + case is PBXGroup: true + default: false } } } @@ -81,28 +88,28 @@ public enum PBXNavigatorFileOrder { /// Leave the files unsorted. case unsorted - /// Sort the file by their file name. This is a case sensitive sort with uppercase name preceeding lowercase names. + /// Sort the file by their file name. This is a case sensitive sort with uppercase name preceding lowercase names. case byFilename /// Sorts the files by their file names with all groups appear at the top of the list. case byFilenameGroupsFirst - internal var sort: ((PBXFileElement, PBXFileElement) -> Bool)? { + var sort: ((PBXFileElement, PBXFileElement) -> Bool)? { switch self { case .byFilename: - return { sortUsingNames($0, $1) } + { sortFileElementsByName($0, $1) } case .byFilenameGroupsFirst: - return { lhs, rhs in + { lhs, rhs in let lhsIsGroup = lhs.isGroup if lhsIsGroup != rhs.isGroup { return lhsIsGroup } - return sortUsingNames(lhs, rhs) + return sortFileElementsByName(lhs, rhs) } default: - return nil // Don't sort. + nil // Don't sort. } } } @@ -115,15 +122,15 @@ public enum PBXBuildPhaseFileOrder { /// Sort the files by their file name. This is a case sensitive sort with uppercase names appearing before lowercase names. case byFilename - internal var sort: ((PBXBuildFile, PBXBuildFile) -> Bool)? { + var sort: ((PBXBuildFile, PBXBuildFile) -> Bool)? { switch self { case .byFilename: - return { lhs, rhs in - sortUsingNames(lhs, rhs) + { lhs, rhs in + sortBuildFilesByName(lhs, rhs) } default: - return nil // Don't sort. + nil // Don't sort. } } } diff --git a/Sources/XcodeProj/Objects/Project/PBXProj.swift b/Sources/XcodeProj/Objects/Project/PBXProj.swift index cd13ffdae..700add6b7 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProj.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProj.swift @@ -14,7 +14,8 @@ public final class PBXProj: Decodable { public var objectVersion: UInt /// Project classes. - public var classes: [String: Any] + /// This appears to always be empty as defined here: http://www.monobjc.net/xcode-project-file-format.html + public var classes: [String: [String]] /// Project root object. var rootObjectReference: PBXObjectReference? @@ -25,7 +26,7 @@ public final class PBXProj: Decodable { rootObjectReference = newValue?.reference } get { - return rootObjectReference?.getObject() + rootObjectReference?.getObject() } } @@ -40,7 +41,7 @@ public final class PBXProj: Decodable { public init(rootObject: PBXProject? = nil, objectVersion: UInt = Xcode.LastKnown.objectVersion, archiveVersion: UInt = Xcode.LastKnown.archiveVersion, - classes: [String: Any] = [:], + classes: [String: [String]] = [:], objects: [PBXObject] = []) { self.archiveVersion = archiveVersion self.objectVersion = objectVersion @@ -52,6 +53,52 @@ public final class PBXProj: Decodable { } } + /// Initializes the project with a path to the pbxproj file. + /// + /// - Parameters: + /// - path: Path to a pbxproj file. + public convenience init(path: Path) throws { + let pbxproj: PBXProj = try PBXProj.createPBXProj(path: path) + self.init( + rootObject: pbxproj.rootObject, + objectVersion: pbxproj.objectVersion, + archiveVersion: pbxproj.archiveVersion, + classes: pbxproj.classes, + objects: pbxproj.objects + ) + } + + /// Initializes the project with the data representation of pbxproj file. + /// + /// - Parameters: + /// - data: data representation of pbxproj file. + public convenience init(data: Data) throws { + let plistDecoder = XcodeprojPropertyListDecoder(context: ProjectDecodingContext()) + let pbxproj: PBXProj = try plistDecoder.decode(PBXProj.self, from: data) + + self.init( + rootObject: pbxproj.rootObject, + objectVersion: pbxproj.objectVersion, + archiveVersion: pbxproj.archiveVersion, + classes: pbxproj.classes, + objects: pbxproj.objects + ) + } + + private init( + rootObject: PBXProject? = nil, + objectVersion: UInt = Xcode.LastKnown.objectVersion, + archiveVersion: UInt = Xcode.LastKnown.archiveVersion, + classes: [String: [String]] = [:], + objects: PBXObjects + ) { + self.archiveVersion = archiveVersion + self.objectVersion = objectVersion + self.classes = classes + rootObjectReference = rootObject?.reference + self.objects = objects + } + // MARK: - Decodable fileprivate enum CodingKeys: String, CodingKey { @@ -70,28 +117,28 @@ public final class PBXProj: Decodable { self.rootObjectReference = objectReferenceRepository.getOrCreate(reference: rootObjectReference, objects: objects) objectVersion = try container.decodeIntIfPresent(.objectVersion) ?? 0 archiveVersion = try container.decodeIntIfPresent(.archiveVersion) ?? 1 - classes = try container.decodeIfPresent([String: Any].self, forKey: .classes) ?? [:] - let objectsDictionary: [String: Any] = try container.decodeIfPresent([String: Any].self, forKey: .objects) ?? [:] - let objectsDictionaries: [String: [String: Any]] = (objectsDictionary as? [String: [String: Any]]) ?? [:] + classes = try container.decodeIfPresent([String: [String]].self, forKey: .classes) ?? [:] + let objectsDictionary: [String: PBXObjectDictionaryEntry] = try container.decodeIfPresent([String: PBXObjectDictionaryEntry].self, forKey: .objects) ?? [:] - let parser = PBXObjectParser( - userInfo: decoder.userInfo - ) - try objectsDictionaries.enumerateKeysAndObjects(options: .concurrent) { key, obj, _ in - // swiftlint:disable force_cast - let reference = key as! String - let dictionary = obj as! [String: Any] - // swiftlint:enable force_cast - let object = try parser.parse( - reference: reference, - dictionary: dictionary - ) - objects.add(object: object) + for entry in objectsDictionary { + objects.add(object: entry.value.object) } + self.objects = objects try rootGroup()?.assignParentToChildren() } + + // MARK: Static Methods + + private static func createPBXProj(path: Path) throws -> PBXProj { + let pbxProjData = try Data(contentsOf: path.url) + + let plistDecoder = XcodeprojPropertyListDecoder(context: ProjectDecodingContext()) + let pbxproj: PBXProj = try plistDecoder.decode(PBXProj.self, from: pbxProjData) + try pbxproj.updateProjectName(path: path) + return pbxproj + } } // MARK: - Public helpers @@ -99,41 +146,49 @@ public final class PBXProj: Decodable { public extension PBXProj { // MARK: - Properties - var projects: [PBXProject] { return Array(objects.projects.values) } - var referenceProxies: [PBXReferenceProxy] { return Array(objects.referenceProxies.values) } + var projects: [PBXProject] { Array(objects.projects.values) } + var referenceProxies: [PBXReferenceProxy] { Array(objects.referenceProxies.values) } // File elements - var fileReferences: [PBXFileReference] { return Array(objects.fileReferences.values) } - var versionGroups: [XCVersionGroup] { return Array(objects.versionGroups.values) } - var variantGroups: [PBXVariantGroup] { return Array(objects.variantGroups.values) } - var groups: [PBXGroup] { return Array(objects.groups.values) } + var fileReferences: [PBXFileReference] { Array(objects.fileReferences.values) } + var versionGroups: [XCVersionGroup] { Array(objects.versionGroups.values) } + var variantGroups: [PBXVariantGroup] { Array(objects.variantGroups.values) } + var groups: [PBXGroup] { Array(objects.groups.values) } // Configuration - var buildConfigurations: [XCBuildConfiguration] { return Array(objects.buildConfigurations.values) } - var configurationLists: [XCConfigurationList] { return Array(objects.configurationLists.values) } + var buildConfigurations: [XCBuildConfiguration] { Array(objects.buildConfigurations.values) } + var configurationLists: [XCConfigurationList] { Array(objects.configurationLists.values) } // Targets - var legacyTargets: [PBXLegacyTarget] { return Array(objects.legacyTargets.values) } - var aggregateTargets: [PBXAggregateTarget] { return Array(objects.aggregateTargets.values) } - var nativeTargets: [PBXNativeTarget] { return Array(objects.nativeTargets.values) } - var targetDependencies: [PBXTargetDependency] { return Array(objects.targetDependencies.values) } - var containerItemProxies: [PBXContainerItemProxy] { return Array(objects.containerItemProxies.values) } - var buildRules: [PBXBuildRule] { return Array(objects.buildRules.values) } + var legacyTargets: [PBXLegacyTarget] { Array(objects.legacyTargets.values) } + var aggregateTargets: [PBXAggregateTarget] { Array(objects.aggregateTargets.values) } + var nativeTargets: [PBXNativeTarget] { Array(objects.nativeTargets.values) } + var targetDependencies: [PBXTargetDependency] { Array(objects.targetDependencies.values) } + var containerItemProxies: [PBXContainerItemProxy] { Array(objects.containerItemProxies.values) } + var buildRules: [PBXBuildRule] { Array(objects.buildRules.values) } // Build - var buildFiles: [PBXBuildFile] { return Array(objects.buildFiles.values) } - var copyFilesBuildPhases: [PBXCopyFilesBuildPhase] { return Array(objects.copyFilesBuildPhases.values) } - var shellScriptBuildPhases: [PBXShellScriptBuildPhase] { return Array(objects.shellScriptBuildPhases.values) } - var resourcesBuildPhases: [PBXResourcesBuildPhase] { return Array(objects.resourcesBuildPhases.values) } - var frameworksBuildPhases: [PBXFrameworksBuildPhase] { return Array(objects.frameworksBuildPhases.values) } - var headersBuildPhases: [PBXHeadersBuildPhase] { return Array(objects.headersBuildPhases.values) } - var sourcesBuildPhases: [PBXSourcesBuildPhase] { return Array(objects.sourcesBuildPhases.values) } - var carbonResourcesBuildPhases: [PBXRezBuildPhase] { return Array(objects.carbonResourcesBuildPhases.values) } - var buildPhases: [PBXBuildPhase] { return Array(objects.buildPhases.values) } + var buildFiles: [PBXBuildFile] { Array(objects.buildFiles.values) } + var copyFilesBuildPhases: [PBXCopyFilesBuildPhase] { Array(objects.copyFilesBuildPhases.values) } + var shellScriptBuildPhases: [PBXShellScriptBuildPhase] { Array(objects.shellScriptBuildPhases.values) } + var resourcesBuildPhases: [PBXResourcesBuildPhase] { Array(objects.resourcesBuildPhases.values) } + var frameworksBuildPhases: [PBXFrameworksBuildPhase] { Array(objects.frameworksBuildPhases.values) } + var headersBuildPhases: [PBXHeadersBuildPhase] { Array(objects.headersBuildPhases.values) } + var sourcesBuildPhases: [PBXSourcesBuildPhase] { Array(objects.sourcesBuildPhases.values) } + var carbonResourcesBuildPhases: [PBXRezBuildPhase] { Array(objects.carbonResourcesBuildPhases.values) } + var buildPhases: [PBXBuildPhase] { Array(objects.buildPhases.values) } + var fileSystemSynchronizedRootGroups: [PBXFileSystemSynchronizedRootGroup] { Array(objects.fileSystemSynchronizedRootGroups.values) } + var fileSystemSynchronizedBuildFileExceptionSets: [PBXFileSystemSynchronizedBuildFileExceptionSet] { + Array(objects.fileSystemSynchronizedBuildFileExceptionSets.values) + } + + var fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: [PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet] { + Array(objects.fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.values) + } /// Returns root project. func rootProject() throws -> PBXProject? { - return try rootObjectReference?.getThrowingObject() + try rootObjectReference?.getThrowingObject() } /// Returns root project's root group. @@ -162,7 +217,7 @@ public extension PBXProj { /// - name: target name. /// - Returns: targets with the given name. func targets(named name: String) -> [PBXTarget] { - return objects.targets(named: name) + objects.targets(named: name) } /// Invalidates all the objects UUIDs. @@ -198,7 +253,7 @@ public extension PBXProj { extension PBXProj { /// Infers project name from Path and sets it as project name /// - /// Project name is needed for certain comments when serialising PBXProj + /// Project name is needed for certain comments when serializing PBXProj /// /// - Parameters: /// - path: path to .xcodeproj directory. @@ -215,10 +270,9 @@ extension PBXProj { extension PBXProj: Equatable { public static func == (lhs: PBXProj, rhs: PBXProj) -> Bool { - let equalClasses = NSDictionary(dictionary: lhs.classes).isEqual(to: rhs.classes) - return lhs.archiveVersion == rhs.archiveVersion && + lhs.archiveVersion == rhs.archiveVersion && lhs.objectVersion == rhs.objectVersion && - equalClasses && + lhs.classes == rhs.classes && lhs.objects == rhs.objects } } @@ -226,13 +280,24 @@ extension PBXProj: Equatable { // MARK: - Writable extension PBXProj: Writable { + public func dataRepresentation(outputSettings: PBXOutputSettings) throws -> Data? { + let encoder = PBXProjEncoder(outputSettings: outputSettings) + return try encoder.encode(proj: self).data(using: .utf8) + } + + public func dataRepresentation() throws -> Data? { + let encoder = PBXProjEncoder(outputSettings: PBXOutputSettings()) + return try encoder.encode(proj: self).data(using: .utf8) + } + public func write(path: Path, override: Bool) throws { try write(path: path, override: override, outputSettings: PBXOutputSettings()) } public func write(path: Path, override: Bool, outputSettings: PBXOutputSettings) throws { - let encoder = PBXProjEncoder(outputSettings: outputSettings) - let output = try encoder.encode(proj: self) + guard let output = try dataRepresentation(outputSettings: outputSettings) else { + return + } if override, path.exists { try path.delete() } diff --git a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift index 4caac010c..fa8710ffc 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift @@ -7,7 +7,7 @@ protocol PlistSerializable { } extension PlistSerializable { - var multiline: Bool { return true } + var multiline: Bool { true } } final class StateHolder { @@ -28,7 +28,7 @@ final class StateHolder { } func copy() -> StateHolder { - return StateHolder(indent: indent, multiline: multiline) + StateHolder(indent: indent, multiline: multiline) } } @@ -54,6 +54,7 @@ final class PBXProjEncoder { sort(buildPhases: proj.objects.resourcesBuildPhases, outputSettings: outputSettings) sort(buildPhases: proj.objects.sourcesBuildPhases, outputSettings: outputSettings) sort(navigatorGroups: proj.objects.groups, outputSettings: outputSettings) + sortProjectReferences(for: proj.projects, outputSettings: outputSettings) var output = [String]() var stateHolder = StateHolder() @@ -113,6 +114,24 @@ final class PBXProjEncoder { outputSettings: outputSettings, stateHolder: &stateHolder, to: &output) + try write(section: "PBXFileSystemSynchronizedBuildFileExceptionSet", + proj: proj, + objects: proj.objects.fileSystemSynchronizedBuildFileExceptionSets, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) + try write(section: "PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet", + proj: proj, + objects: proj.objects.fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) + try write(section: "PBXFileSystemSynchronizedRootGroup", + proj: proj, + objects: proj.objects.fileSystemSynchronizedRootGroups, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) try write(section: "PBXFrameworksBuildPhase", proj: proj, objects: proj.objects.frameworksBuildPhases, @@ -203,6 +222,12 @@ final class PBXProjEncoder { outputSettings: outputSettings, stateHolder: &stateHolder, to: &output) + try write(section: "XCLocalSwiftPackageReference", + proj: proj, + objects: proj.objects.localSwiftPackageReferences, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) try write(section: "XCRemoteSwiftPackageReference", proj: proj, objects: proj.objects.remoteSwiftPackageReferences, @@ -232,7 +257,7 @@ final class PBXProjEncoder { rootObject.value, comment: "Project object" ) - ), stateHolder: &stateHolder, to: &output) + ), stateHolder: &stateHolder, to: &output) writeDictionaryEnd(stateHolder: &stateHolder, to: &output) writeNewLine(stateHolder: &stateHolder, to: &output) @@ -281,12 +306,12 @@ final class PBXProjEncoder { output.append("/* \(comment) */") } - private func write(section: String, - proj: PBXProj, - objects: [PBXObjectReference: T], - outputSettings: PBXOutputSettings, - stateHolder: inout StateHolder, - to output: inout [String]) throws where T: PlistSerializable & Equatable { + private func write(section: String, + proj: PBXProj, + objects: [PBXObjectReference: some PlistSerializable & Equatable], + outputSettings: PBXOutputSettings, + stateHolder: inout StateHolder, + to output: inout [String]) throws { try write(section: section, proj: proj, objects: objects, sort: outputSettings.projFileListOrder.sort, stateHolder: &stateHolder, to: &output) } @@ -308,7 +333,7 @@ final class PBXProjEncoder { try write(section: section, proj: proj, objects: objects, sort: outputSettings.projFileListOrder.sort, stateHolder: &stateHolder, to: &output) } - final class PBXProjElement { + final class PBXProjElement: NSObject { let key: CommentedString let value: PlistValue let multiline: Bool @@ -361,7 +386,7 @@ final class PBXProjEncoder { stateHolder: inout StateHolder, to output: inout [String]) { writeDictionaryStart(stateHolder: &stateHolder, to: &output) - let sorted = dictionary.sorted(by: { (left, right) -> Bool in + let sorted = dictionary.sorted(by: { left, right -> Bool in if left.key == "isa" { return true } else if right.key == "isa" { @@ -370,9 +395,9 @@ final class PBXProjEncoder { return left.key.string < right.key.string } }) - sorted.forEach { - write(dictionaryKey: $0.key, - dictionaryValue: $0.value, + for item in sorted { + write(dictionaryKey: item.key, + dictionaryValue: item.value, multiline: stateHolder.multiline, stateHolder: &stateHolder, to: &output) @@ -450,4 +475,23 @@ final class PBXProjEncoder { navigatorGroups.values.forEach { $0.children = $0.children.sorted(by: sort) } } } + + private func sortProjectReferences(for projects: [PBXProject], outputSettings: PBXOutputSettings) { + guard outputSettings.projReferenceFormat == .xcode else { + return + } + + for project in projects { + /// The project references are sorted alphabetically based on the name of the project it's being referenced. + project.projectReferences = project.projectReferences.sorted(by: { lhs, rhs in + let lProjectRef = lhs["ProjectRef"]! + let lFile: PBXFileElement = lProjectRef.getObject()! + let rProjectRef = rhs["ProjectRef"]! + let rFile: PBXFileElement = rProjectRef.getObject()! + let lName = lFile.name! + let rName = rFile.name! + return lName.compare(rName, options: .caseInsensitive) == .orderedAscending + }) + } + } } diff --git a/Sources/XcodeProj/Objects/Project/PBXProject.swift b/Sources/XcodeProj/Objects/Project/PBXProject.swift index 4e60ce427..1e0444ebc 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProject.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProject.swift @@ -16,12 +16,18 @@ public final class PBXProject: PBXObject { buildConfigurationListReference = newValue.reference } get { - return buildConfigurationListReference.getObject() + buildConfigurationListReference.getObject() } } /// A string representation of the XcodeCompatibilityVersion. - public var compatibilityVersion: String + public var compatibilityVersion: String? + + /// An int representation of the PreferredProjectObjectVersion. + public var preferredProjectObjectVersion: Int? + + /// An int representation of the minimizedProjectReferenceProxies attribute + public var minimizedProjectReferenceProxies: Int? /// The region of development. public var developmentRegion: String? @@ -41,7 +47,7 @@ public final class PBXProject: PBXObject { mainGroupReference = newValue.reference } get { - return mainGroupReference.getObject() + mainGroupReference.getObject() } } @@ -54,7 +60,7 @@ public final class PBXProject: PBXObject { productsGroupReference = newValue?.reference } get { - return productsGroupReference?.getObject() + productsGroupReference?.getObject() } } @@ -76,7 +82,7 @@ public final class PBXProject: PBXObject { } } get { - return projectReferences.map { project in + projectReferences.map { project in project.mapValues { $0.getObject()! } } } @@ -96,54 +102,81 @@ public final class PBXProject: PBXObject { targetReferences = newValue.references() } get { - return targetReferences.objects() + targetReferences.objects() } } /// Project attributes. /// Target attributes will be merged into this - public var attributes: [String: Any] + public var attributes: [String: ProjectAttribute] /// Target attribute references. - var targetAttributeReferences: [PBXObjectReference: [String: Any]] + var targetAttributeReferences: [PBXObjectReference: [String: ProjectAttribute]] /// Target attributes. - public var targetAttributes: [PBXTarget: [String: Any]] { + public var targetAttributes: [PBXTarget: [String: ProjectAttribute]] { set { targetAttributeReferences = [:] - newValue.forEach { - targetAttributeReferences[$0.key.reference] = $0.value + for item in newValue { + targetAttributeReferences[item.key.reference] = item.value } } get { - var attributes: [PBXTarget: [String: Any]] = [:] - targetAttributeReferences.forEach { - if let object: PBXTarget = $0.key.getObject() { - attributes[object] = $0.value + var attributes: [PBXTarget: [String: ProjectAttribute]] = [:] + for targetAttributeReference in targetAttributeReferences { + if let object: PBXTarget = targetAttributeReference.key.getObject() { + attributes[object] = targetAttributeReference.value } } return attributes } } - /// Package references. + /// Remote (`XCRemoteSwiftPackageReference`) and Local (`XCLocalSwiftPackageReference`) Package references. var packageReferences: [PBXObjectReference]? - /// Swift packages. + /// Remote Swift packages. + @available(*, deprecated, message: "use remotePackages or localPackages.") public var packages: [XCRemoteSwiftPackageReference] { + remotePackages + } + + /// Remote Swift packages. + public var remotePackages: [XCRemoteSwiftPackageReference] { set { - packageReferences = newValue.references() + setPackageReferences(newValue) } get { - return packageReferences?.objects() ?? [] + packageReferences?.objects() ?? [] } } + /// Local Swift packages. + public var localPackages: [XCLocalSwiftPackageReference] { + set { + setPackageReferences(newValue) + } + get { + packageReferences?.objects() ?? [] + } + } + + private func setPackageReferences(_ packages: [T]) { + let newReferences = packages.references() + var finalReferences: [PBXObjectReference] = packageReferences?.filter { !($0.getObject() is T) } ?? [] + for reference in newReferences { + if !finalReferences.contains(reference) { + finalReferences.append(reference) + } + } + packageReferences = finalReferences + } + /// Sets the attributes for the given target. /// /// - Parameters: /// - attributes: attributes that will be set. /// - target: target. - public func setTargetAttributes(_ attributes: [String: Any], target: PBXTarget) { + public func setTargetAttributes(_ attributes: [String: ProjectAttribute], target: PBXTarget) { targetAttributeReferences[target.reference] = attributes } @@ -164,7 +197,7 @@ public final class PBXProject: PBXObject { /// - Parameter for: target whose attributes will be returned. /// - Returns: target attributes. public func attributes(for target: PBXTarget) -> [String: Any]? { - return targetAttributeReferences[target.reference] + targetAttributeReferences[target.reference] } /// Adds a remote swift package @@ -178,7 +211,7 @@ public final class PBXProject: PBXObject { productName: String, versionRequirement: XCRemoteSwiftPackageReference.VersionRequirement, targetName: String) throws -> XCRemoteSwiftPackageReference { - let objects = try self.objects() + let objects = try objects() guard let target = targets.first(where: { $0.name == targetName }) else { throw PBXProjError.targetNotFound(targetName: targetName) } @@ -216,7 +249,7 @@ public final class PBXProject: PBXObject { addFileReference: Bool = true) throws -> XCSwiftPackageProductDependency { guard path.isRelative else { throw PBXProjError.pathIsAbsolute(path) } - let objects = try self.objects() + let objects = try objects() guard let target = targets.first(where: { $0.name == targetName }) else { throw PBXProjError.targetNotFound(targetName: targetName) } @@ -259,6 +292,8 @@ public final class PBXProject: PBXObject { /// - name: xcodeproj's name. /// - buildConfigurationList: project build configuration list. /// - compatibilityVersion: project compatibility version. + /// - preferredProjectObjectVersion: preferred project object version + /// - minimizedProjectReferenceProxies: minimized project reference proxies /// - mainGroup: project main group. /// - developmentRegion: project has development region. /// - hasScannedForEncodings: project has scanned for encodings. @@ -268,9 +303,14 @@ public final class PBXProject: PBXObject { /// - projects: projects. /// - projectRoots: project roots. /// - targets: project targets. + /// - packages: project's remote packages. + /// - attributes: project's attributes. + /// - targetAttributes: project target's attributes. public init(name: String, buildConfigurationList: XCConfigurationList, - compatibilityVersion: String, + compatibilityVersion: String?, + preferredProjectObjectVersion: Int?, + minimizedProjectReferenceProxies: Int?, mainGroup: PBXGroup, developmentRegion: String? = nil, hasScannedForEncodings: Int = 0, @@ -281,11 +321,13 @@ public final class PBXProject: PBXObject { projectRoots: [String] = [], targets: [PBXTarget] = [], packages: [XCRemoteSwiftPackageReference] = [], - attributes: [String: Any] = [:], - targetAttributes: [PBXTarget: [String: Any]] = [:]) { + attributes: [String: ProjectAttribute] = [:], + targetAttributes: [PBXTarget: [String: ProjectAttribute]] = [:]) { self.name = name buildConfigurationListReference = buildConfigurationList.reference self.compatibilityVersion = compatibilityVersion + self.preferredProjectObjectVersion = preferredProjectObjectVersion + self.minimizedProjectReferenceProxies = minimizedProjectReferenceProxies mainGroupReference = mainGroup.reference self.developmentRegion = developmentRegion self.hasScannedForEncodings = hasScannedForEncodings @@ -308,6 +350,8 @@ public final class PBXProject: PBXObject { case name case buildConfigurationList case compatibilityVersion + case preferredProjectObjectVersion + case minimizedProjectReferenceProxies case developmentRegion case hasScannedForEncodings case knownRegions @@ -326,14 +370,28 @@ public final class PBXProject: PBXObject { let container = try decoder.container(keyedBy: CodingKeys.self) let referenceRepository = decoder.context.objectReferenceRepository let objects = decoder.context.objects - name = (try container.decodeIfPresent(.name)) ?? "" + name = try (container.decodeIfPresent(.name)) ?? "" let buildConfigurationListReference: String = try container.decode(.buildConfigurationList) self.buildConfigurationListReference = referenceRepository.getOrCreate(reference: buildConfigurationListReference, objects: objects) - compatibilityVersion = try container.decode(.compatibilityVersion) + compatibilityVersion = try container.decodeIfPresent(.compatibilityVersion) + preferredProjectObjectVersion = if let stringValue = try container.decodeIfPresent(String.self, forKey: .preferredProjectObjectVersion) { + Int(stringValue) + } else if let intValue = try container.decodeIfPresent(Int.self, forKey: .preferredProjectObjectVersion) { + intValue + } else { + nil + } + minimizedProjectReferenceProxies = if let stringValue = try container.decodeIfPresent(String.self, forKey: .minimizedProjectReferenceProxies) { + Int(stringValue) + } else if let intValue = try container.decodeIfPresent(Int.self, forKey: .minimizedProjectReferenceProxies) { + intValue + } else { + nil + } developmentRegion = try container.decodeIfPresent(.developmentRegion) let hasScannedForEncodingsString: String? = try container.decodeIfPresent(.hasScannedForEncodings) hasScannedForEncodings = hasScannedForEncodingsString.flatMap { Int($0) } ?? 0 - knownRegions = (try container.decodeIfPresent(.knownRegions)) ?? [] + knownRegions = try (container.decodeIfPresent(.knownRegions)) ?? [] let mainGroupReference: String = try container.decode(.mainGroup) self.mainGroupReference = referenceRepository.getOrCreate(reference: mainGroupReference, objects: objects) if let productRefGroupReference: String = try container.decodeIfPresent(.productRefGroup) { @@ -342,7 +400,7 @@ public final class PBXProject: PBXObject { productsGroupReference = nil } projectDirPath = try container.decodeIfPresent(.projectDirPath) ?? "" - let projectReferences: [[String: String]] = (try container.decodeIfPresent(.projectReferences)) ?? [] + let projectReferences: [[String: String]] = try (container.decodeIfPresent(.projectReferences)) ?? [] self.projectReferences = projectReferences.map { references in references.mapValues { referenceRepository.getOrCreate(reference: $0, objects: objects) } } @@ -353,16 +411,18 @@ public final class PBXProject: PBXObject { } else { projectRoots = [] } - let targetReferences: [String] = (try container.decodeIfPresent(.targets)) ?? [] + let targetReferences: [String] = try (container.decodeIfPresent(.targets)) ?? [] self.targetReferences = targetReferences.map { referenceRepository.getOrCreate(reference: $0, objects: objects) } let packageRefeferenceStrings: [String] = try container.decodeIfPresent(.packageReferences) ?? [] packageReferences = packageRefeferenceStrings.map { referenceRepository.getOrCreate(reference: $0, objects: objects) } - var attributes = (try container.decodeIfPresent([String: Any].self, forKey: .attributes) ?? [:]) - var targetAttributeReferences: [PBXObjectReference: [String: Any]] = [:] - if let targetAttributes = attributes[PBXProject.targetAttributesKey] as? [String: [String: Any]] { - targetAttributes.forEach { targetAttributeReferences[referenceRepository.getOrCreate(reference: $0.key, objects: objects)] = $0.value } + var attributes = try (container.decodeIfPresent([String: ProjectAttribute].self, forKey: .attributes) ?? [:]) + var targetAttributeReferences: [PBXObjectReference: [String: ProjectAttribute]] = [:] + if case let .attributeDictionary(targetAttributes) = attributes[PBXProject.targetAttributesKey] { + for targetAttribute in targetAttributes { + targetAttributeReferences[referenceRepository.getOrCreate(reference: targetAttribute.key, objects: objects)] = targetAttribute.value + } attributes[PBXProject.targetAttributesKey] = nil } self.attributes = attributes @@ -370,6 +430,11 @@ public final class PBXProject: PBXObject { try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXProject else { return false } + return isEqual(to: rhs) + } } // MARK: - Helpers @@ -380,7 +445,7 @@ extension PBXProject { productName: String, versionRequirement: XCRemoteSwiftPackageReference.VersionRequirement) throws -> XCRemoteSwiftPackageReference { let reference: XCRemoteSwiftPackageReference - if let package = packages.first(where: { $0.repositoryURL == repositoryURL }) { + if let package = remotePackages.first(where: { $0.repositoryURL == repositoryURL }) { guard package.versionRequirement == versionRequirement else { throw PBXProjError.multipleRemotePackages(productName: productName) } @@ -388,7 +453,7 @@ extension PBXProject { } else { reference = XCRemoteSwiftPackageReference(repositoryURL: repositoryURL, versionRequirement: versionRequirement) try objects().add(object: reference) - packages.append(reference) + remotePackages.append(reference) } return reference @@ -398,17 +463,17 @@ extension PBXProject { private func addSwiftPackageProduct(reference: XCRemoteSwiftPackageReference, productName: String, target: PBXTarget) throws -> XCSwiftPackageProductDependency { - let objects = try self.objects() + let objects = try objects() let productDependency: XCSwiftPackageProductDependency // Avoid duplication - if let product = objects.swiftPackageProductDependencies.first(where: { $0.value.package == reference })?.value { + if let product = objects.swiftPackageProductDependencies.first(where: { $0.value.package == reference && $0.value.productName == productName })?.value { productDependency = product } else { productDependency = XCSwiftPackageProductDependency(productName: productName, package: reference) objects.add(object: productDependency) } - target.packageProductDependencies.append(productDependency) + target.packageProductDependencies?.append(productDependency) return productDependency } @@ -417,7 +482,7 @@ extension PBXProject { private func addLocalSwiftPackageProduct(path: Path, productName: String, target: PBXTarget) throws -> XCSwiftPackageProductDependency { - let objects = try self.objects() + let objects = try objects() let productDependency: XCSwiftPackageProductDependency // Avoid duplication @@ -430,7 +495,7 @@ extension PBXProject { productDependency = XCSwiftPackageProductDependency(productName: productName) objects.add(object: productDependency) } - target.packageProductDependencies.append(productDependency) + target.packageProductDependencies?.append(productDependency) return productDependency } @@ -447,8 +512,10 @@ extension PBXProject: PlistSerializable { let buildConfigurationListCommentedString = CommentedString(buildConfigurationListReference.value, comment: buildConfigurationListComment) dictionary["buildConfigurationList"] = .string(buildConfigurationListCommentedString) - dictionary["compatibilityVersion"] = .string(CommentedString(compatibilityVersion)) - if let developmentRegion = developmentRegion { + if let compatibilityVersion { + dictionary["compatibilityVersion"] = .string(CommentedString(compatibilityVersion)) + } + if let developmentRegion { dictionary["developmentRegion"] = .string(CommentedString(developmentRegion)) } dictionary["hasScannedForEncodings"] = .string(CommentedString("\(hasScannedForEncodings)")) @@ -459,7 +526,13 @@ extension PBXProject: PlistSerializable { } let mainGroupObject: PBXGroup? = mainGroupReference.getObject() dictionary["mainGroup"] = .string(CommentedString(mainGroupReference.value, comment: mainGroupObject?.fileName())) - if let productsGroupReference = productsGroupReference { + if let preferredProjectObjectVersion { + dictionary["preferredProjectObjectVersion"] = .string(CommentedString(preferredProjectObjectVersion.description)) + } + if let minimizedProjectReferenceProxies { + dictionary["minimizedProjectReferenceProxies"] = .string(CommentedString(minimizedProjectReferenceProxies.description)) + } + if let productsGroupReference { let productRefGroupObject: PBXGroup? = productsGroupReference.getObject() dictionary["productRefGroup"] = .string(CommentedString(productsGroupReference.value, comment: productRefGroupObject?.fileName())) @@ -477,25 +550,30 @@ extension PBXProject: PlistSerializable { .map { targetReference in let target: PBXTarget? = targetReference.getObject() return .string(CommentedString(targetReference.value, comment: target?.name)) - }) - - if !packages.isEmpty { - dictionary["packageReferences"] = PlistValue.array(packages.map { - .string(CommentedString($0.reference.value, comment: "XCRemoteSwiftPackageReference \"\($0.name ?? "")\"")) }) - } - var plistAttributes: [String: Any] = attributes - if !targetAttributeReferences.isEmpty { - // merge target attributes - var plistTargetAttributes: [String: Any] = [:] - for (reference, value) in targetAttributeReferences { - plistTargetAttributes[reference.value] = value.mapValues { value in - (value as? PBXObject)?.reference.value ?? value - } + if !remotePackages.isEmpty || !localPackages.isEmpty { + let remotePackageReferences = remotePackages.map { + PlistValue.string(CommentedString($0.reference.value, comment: "XCRemoteSwiftPackageReference \"\($0.name ?? "")\"")) } - plistAttributes[PBXProject.targetAttributesKey] = plistTargetAttributes + let localPackageReferences = localPackages.map { + PlistValue.string(CommentedString($0.reference.value, comment: "XCLocalSwiftPackageReference \"\($0.name ?? "")\"")) + } + var finalPackageReferences = remotePackageReferences + finalPackageReferences.append(contentsOf: localPackageReferences) + dictionary["packageReferences"] = PlistValue.array(finalPackageReferences) + } + + var plistAttributes: [String: ProjectAttribute] = attributes + + // merge target attributes + var plistTargetAttributes: [String: [String: ProjectAttribute]] = [:] + for (reference, value) in targetAttributeReferences { + plistTargetAttributes[reference.value] = value } + + plistAttributes[PBXProject.targetAttributesKey] = .attributeDictionary(plistTargetAttributes) + dictionary["attributes"] = plistAttributes.plist() return (key: CommentedString(reference, @@ -508,7 +586,9 @@ extension PBXProject: PlistSerializable { return nil } return .array(projectReferences.compactMap { reference in - guard let productGroupReference = reference["ProductGroup"], let projectRef = reference["ProjectRef"] else { + guard let productGroupReference = reference[Xcode.ProjectReference.productGroupKey], + let projectRef = reference[Xcode.ProjectReference.projectReferenceKey] + else { return nil } let producGroup: PBXGroup? = productGroupReference.getObject() @@ -517,8 +597,8 @@ extension PBXProject: PlistSerializable { let fileRefName = project?.fileName() return [ - CommentedString("ProductGroup"): PlistValue.string(CommentedString(productGroupReference.value, comment: groupName)), - CommentedString("ProjectRef"): PlistValue.string(CommentedString(projectRef.value, comment: fileRefName)), + CommentedString(Xcode.ProjectReference.productGroupKey): PlistValue.string(CommentedString(productGroupReference.value, comment: groupName)), + CommentedString(Xcode.ProjectReference.projectReferenceKey): PlistValue.string(CommentedString(projectRef.value, comment: fileRefName)), ] }) } diff --git a/Sources/XcodeProj/Objects/Project/ProjectAttribute.swift b/Sources/XcodeProj/Objects/Project/ProjectAttribute.swift new file mode 100644 index 000000000..02a78f1d7 --- /dev/null +++ b/Sources/XcodeProj/Objects/Project/ProjectAttribute.swift @@ -0,0 +1,55 @@ +public enum ProjectAttribute: Equatable { + case string(String) + case array([String]) + case targetReference(PBXObject) + case attributeDictionary([String: [String: ProjectAttribute]]) + + public var stringValue: String? { + if case let .string(value) = self { + value + } else { + nil + } + } + + public var arrayValue: [String]? { + if case let .array(value) = self { + value + } else { + nil + } + } +} + +extension ProjectAttribute: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + if let string = try? container.decode(String.self) { + self = .string(string) + } else if let array = try? container.decode([String].self) { + self = .array(array) + } else { + let targetAttributes = try container.decode([String: [String: ProjectAttribute]].self) + self = .attributeDictionary(targetAttributes) + } + } +} + +extension ProjectAttribute: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: String...) { + self = .array(elements) + } +} + +extension ProjectAttribute: ExpressibleByStringInterpolation { + public init(stringLiteral value: StringLiteralType) { + self = .string(value) + } +} + +extension ProjectAttribute: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, [String: ProjectAttribute])...) { + self = .attributeDictionary(Dictionary(uniqueKeysWithValues: elements)) + } +} diff --git a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift index 7c0ec85e9..49fe28bb7 100644 --- a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift +++ b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift @@ -1,30 +1,30 @@ -// Generated using Sourcery 0.13.1 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 1.0.0 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT import Foundation extension PBXAggregateTarget { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXAggregateTarget else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXAggregateTarget) -> Bool { + super.isEqual(to: rhs) } } extension PBXBuildFile { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXBuildFile else { return false } + func isEqual(to rhs: PBXBuildFile) -> Bool { if fileReference != rhs.fileReference { return false } - if !NSDictionary(dictionary: settings ?? [:]).isEqual(to: rhs.settings ?? [:]) { return false } + if productReference != rhs.productReference { return false } + if settings != rhs.settings { return false } + if platformFilter != rhs.platformFilter { return false } + if buildPhase != rhs.buildPhase { return false } return super.isEqual(to: rhs) } } extension PBXBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXBuildPhase else { return false } + func isEqual(to rhs: PBXBuildPhase) -> Bool { if buildActionMask != rhs.buildActionMask { return false } if fileReferences != rhs.fileReferences { return false } if inputFileListPaths != rhs.inputFileListPaths { return false } @@ -36,24 +36,24 @@ extension PBXBuildPhase { extension PBXBuildRule { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXBuildRule else { return false } + func isEqual(to rhs: PBXBuildRule) -> Bool { if compilerSpec != rhs.compilerSpec { return false } if filePatterns != rhs.filePatterns { return false } if fileType != rhs.fileType { return false } if isEditable != rhs.isEditable { return false } if name != rhs.name { return false } if outputFiles != rhs.outputFiles { return false } + if inputFiles != rhs.inputFiles { return false } if outputFilesCompilerFlags != rhs.outputFilesCompilerFlags { return false } if script != rhs.script { return false } + if runOncePerArchitecture != rhs.runOncePerArchitecture { return false } return super.isEqual(to: rhs) } } extension PBXContainerItem { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXContainerItem else { return false } + func isEqual(to rhs: PBXContainerItem) -> Bool { if comments != rhs.comments { return false } return super.isEqual(to: rhs) } @@ -61,8 +61,7 @@ extension PBXContainerItem { extension PBXContainerItemProxy { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXContainerItemProxy else { return false } + func isEqual(to rhs: PBXContainerItemProxy) -> Bool { if containerPortalReference != rhs.containerPortalReference { return false } if proxyType != rhs.proxyType { return false } if remoteGlobalIDReference != rhs.remoteGlobalIDReference { return false } @@ -73,10 +72,10 @@ extension PBXContainerItemProxy { extension PBXCopyFilesBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXCopyFilesBuildPhase else { return false } + func isEqual(to rhs: PBXCopyFilesBuildPhase) -> Bool { if dstPath != rhs.dstPath { return false } if dstSubfolderSpec != rhs.dstSubfolderSpec { return false } + if dstSubfolder != rhs.dstSubfolder { return false } if name != rhs.name { return false } return super.isEqual(to: rhs) } @@ -84,8 +83,7 @@ extension PBXCopyFilesBuildPhase { extension PBXFileElement { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXFileElement else { return false } + func isEqual(to rhs: PBXFileElement) -> Bool { if sourceTree != rhs.sourceTree { return false } if path != rhs.path { return false } if name != rhs.name { return false } @@ -94,14 +92,14 @@ extension PBXFileElement { if indentWidth != rhs.indentWidth { return false } if tabWidth != rhs.tabWidth { return false } if wrapsLines != rhs.wrapsLines { return false } + if parent != rhs.parent { return false } return super.isEqual(to: rhs) } } extension PBXFileReference { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXFileReference else { return false } + func isEqual(to rhs: PBXFileReference) -> Bool { if fileEncoding != rhs.fileEncoding { return false } if explicitFileType != rhs.explicitFileType { return false } if lastKnownFileType != rhs.lastKnownFileType { return false } @@ -115,16 +113,14 @@ extension PBXFileReference { extension PBXFrameworksBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXFrameworksBuildPhase else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXFrameworksBuildPhase) -> Bool { + super.isEqual(to: rhs) } } extension PBXGroup { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXGroup else { return false } + func isEqual(to rhs: PBXGroup) -> Bool { if childrenReferences != rhs.childrenReferences { return false } return super.isEqual(to: rhs) } @@ -132,16 +128,14 @@ extension PBXGroup { extension PBXHeadersBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXHeadersBuildPhase else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXHeadersBuildPhase) -> Bool { + super.isEqual(to: rhs) } } extension PBXLegacyTarget { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXLegacyTarget else { return false } + func isEqual(to rhs: PBXLegacyTarget) -> Bool { if buildToolPath != rhs.buildToolPath { return false } if buildArgumentsString != rhs.buildArgumentsString { return false } if passBuildSettingsInEnvironment != rhs.passBuildSettingsInEnvironment { return false } @@ -152,8 +146,7 @@ extension PBXLegacyTarget { extension PBXNativeTarget { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXNativeTarget else { return false } + func isEqual(to rhs: PBXNativeTarget) -> Bool { if productInstallPath != rhs.productInstallPath { return false } return super.isEqual(to: rhs) } @@ -161,8 +154,7 @@ extension PBXNativeTarget { extension PBXProject { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXProject else { return false } + func isEqual(to rhs: PBXProject) -> Bool { if name != rhs.name { return false } if buildConfigurationListReference != rhs.buildConfigurationListReference { return false } if compatibilityVersion != rhs.compatibilityVersion { return false } @@ -175,66 +167,63 @@ extension PBXProject { if projectReferences != rhs.projectReferences { return false } if projectRoots != rhs.projectRoots { return false } if targetReferences != rhs.targetReferences { return false } - if !NSDictionary(dictionary: attributes).isEqual(to: rhs.attributes) { return false } - if !NSDictionary(dictionary: targetAttributeReferences).isEqual(to: rhs.targetAttributeReferences) { return false } + if attributes != rhs.attributes { return false } + if targetAttributeReferences != rhs.targetAttributeReferences { return false } + if packageReferences != rhs.packageReferences { return false } + if remotePackages != rhs.remotePackages { return false } + if localPackages != rhs.localPackages { return false } return super.isEqual(to: rhs) } } extension PBXReferenceProxy { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXReferenceProxy else { return false } + func isEqual(to rhs: PBXReferenceProxy) -> Bool { if fileType != rhs.fileType { return false } - if path != rhs.path { return false } if remoteReference != rhs.remoteReference { return false } - if sourceTree != rhs.sourceTree { return false } return super.isEqual(to: rhs) } } extension PBXResourcesBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXResourcesBuildPhase else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXResourcesBuildPhase) -> Bool { + super.isEqual(to: rhs) } } extension PBXRezBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXRezBuildPhase else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXRezBuildPhase) -> Bool { + super.isEqual(to: rhs) } } extension PBXShellScriptBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXShellScriptBuildPhase else { return false } + func isEqual(to rhs: PBXShellScriptBuildPhase) -> Bool { if name != rhs.name { return false } if inputPaths != rhs.inputPaths { return false } if outputPaths != rhs.outputPaths { return false } if shellPath != rhs.shellPath { return false } if shellScript != rhs.shellScript { return false } if showEnvVarsInLog != rhs.showEnvVarsInLog { return false } + if alwaysOutOfDate != rhs.alwaysOutOfDate { return false } + if dependencyFile != rhs.dependencyFile { return false } return super.isEqual(to: rhs) } } extension PBXSourcesBuildPhase { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXSourcesBuildPhase else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXSourcesBuildPhase) -> Bool { + super.isEqual(to: rhs) } } extension PBXTarget { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXTarget else { return false } + func isEqual(to rhs: PBXTarget) -> Bool { if buildConfigurationListReference != rhs.buildConfigurationListReference { return false } if buildPhaseReferences != rhs.buildPhaseReferences { return false } if buildRuleReferences != rhs.buildRuleReferences { return false } @@ -242,6 +231,7 @@ extension PBXTarget { if name != rhs.name { return false } if productName != rhs.productName { return false } if productReference != rhs.productReference { return false } + if packageProductDependencyReferences != rhs.packageProductDependencyReferences { return false } if productType != rhs.productType { return false } return super.isEqual(to: rhs) } @@ -249,29 +239,28 @@ extension PBXTarget { extension PBXTargetDependency { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXTargetDependency else { return false } + func isEqual(to rhs: PBXTargetDependency) -> Bool { if name != rhs.name { return false } if targetReference != rhs.targetReference { return false } if targetProxyReference != rhs.targetProxyReference { return false } + if productReference != rhs.productReference { return false } + if platformFilter != rhs.platformFilter { return false } return super.isEqual(to: rhs) } } extension PBXVariantGroup { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXVariantGroup else { return false } - return super.isEqual(to: rhs) + func isEqual(to rhs: PBXVariantGroup) -> Bool { + super.isEqual(to: rhs) } } extension XCBuildConfiguration { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? XCBuildConfiguration else { return false } + func isEqual(to rhs: XCBuildConfiguration) -> Bool { if baseConfigurationReference != rhs.baseConfigurationReference { return false } - if !NSDictionary(dictionary: buildSettings).isEqual(to: rhs.buildSettings) { return false } + if buildSettings != rhs.buildSettings { return false } if name != rhs.name { return false } return super.isEqual(to: rhs) } @@ -279,8 +268,7 @@ extension XCBuildConfiguration { extension XCConfigurationList { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? XCConfigurationList else { return false } + func isEqual(to rhs: XCConfigurationList) -> Bool { if buildConfigurationReferences != rhs.buildConfigurationReferences { return false } if defaultConfigurationIsVisible != rhs.defaultConfigurationIsVisible { return false } if defaultConfigurationName != rhs.defaultConfigurationName { return false } @@ -288,12 +276,60 @@ extension XCConfigurationList { } } +extension XCRemoteSwiftPackageReference { + /// :nodoc: + func isEqual(to rhs: XCRemoteSwiftPackageReference) -> Bool { + if repositoryURL != rhs.repositoryURL { return false } + if versionRequirement != rhs.versionRequirement { return false } + return super.isEqual(to: rhs) + } +} + +extension XCSwiftPackageProductDependency { + /// :nodoc: + func isEqual(to rhs: XCSwiftPackageProductDependency) -> Bool { + if productName != rhs.productName { return false } + if packageReference != rhs.packageReference { return false } + return super.isEqual(to: rhs) + } +} + extension XCVersionGroup { /// :nodoc: - @objc public override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? XCVersionGroup else { return false } + func isEqual(to rhs: XCVersionGroup) -> Bool { if currentVersionReference != rhs.currentVersionReference { return false } if versionGroupType != rhs.versionGroupType { return false } return super.isEqual(to: rhs) } } + +extension PBXFileSystemSynchronizedRootGroup { + /// :nodoc: + func isEqual(to rhs: PBXFileSystemSynchronizedRootGroup) -> Bool { + if explicitFileTypes != rhs.explicitFileTypes { return false } + if exceptionsReferences != rhs.exceptionsReferences { return false } + if explicitFolders != rhs.explicitFolders { return false } + return super.isEqual(to: rhs) + } +} + +extension PBXFileSystemSynchronizedBuildFileExceptionSet { + /// :nodoc: + func isEqual(to rhs: PBXFileSystemSynchronizedBuildFileExceptionSet) -> Bool { + if membershipExceptions != rhs.membershipExceptions { return false } + if targetReference != rhs.targetReference { return false } + if publicHeaders != rhs.publicHeaders { return false } + if privateHeaders != rhs.privateHeaders { return false } + if platformFiltersByRelativePath != rhs.platformFiltersByRelativePath { return false } + return super.isEqual(to: rhs) + } +} + +extension PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet { + /// :nodoc: + func isEqual(to rhs: PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet) -> Bool { + if membershipExceptions != rhs.membershipExceptions { return false } + if buildPhaseReference != rhs.buildPhaseReference { return false } + return super.isEqual(to: rhs) + } +} diff --git a/Sources/XcodeProj/Objects/SwiftPackage/XCLocalSwiftPackageReference.swift b/Sources/XcodeProj/Objects/SwiftPackage/XCLocalSwiftPackageReference.swift new file mode 100644 index 000000000..71566b5bc --- /dev/null +++ b/Sources/XcodeProj/Objects/SwiftPackage/XCLocalSwiftPackageReference.swift @@ -0,0 +1,46 @@ +import Foundation + +/// This element is an abstract parent for specialized targets. +public class XCLocalSwiftPackageReference: PBXContainerItem, PlistSerializable { + /// Repository url. + public var relativePath: String + + /// Initializes the local swift package reference with its attributes. + /// + /// - Parameters: + /// - repositoryPath: Package repository path. + public init(relativePath: String) { + self.relativePath = relativePath + super.init() + } + + enum CodingKeys: String, CodingKey { + case relativePath + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + relativePath = try container.decode(String.self, forKey: .relativePath) + + try super.init(from: decoder) + } + + /// It returns the name of the package reference. + public var name: String? { + relativePath + } + + func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary = try super.plistValues(proj: proj, reference: reference) + dictionary["isa"] = .string(CommentedString(XCLocalSwiftPackageReference.isa)) + dictionary["relativePath"] = .string(.init(relativePath)) + return (key: CommentedString(reference, comment: "XCLocalSwiftPackageReference \"\(name ?? "")\""), + value: .dictionary(dictionary)) + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? XCLocalSwiftPackageReference else { return false } + return isEqual(to: rhs) + } +} diff --git a/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift b/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift index d4b11f084..c28dc39b3 100644 --- a/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift +++ b/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift @@ -57,33 +57,33 @@ public class XCRemoteSwiftPackageReference: PBXContainerItem, PlistSerializable func plistValues() -> [CommentedString: PlistValue] { switch self { case let .revision(revision): - return [ + [ "kind": "revision", "revision": .string(.init(revision)), ] case let .branch(branch): - return [ + [ "kind": "branch", "branch": .string(.init(branch)), ] case let .exact(version): - return [ + [ "kind": "exactVersion", "version": .string(.init(version)), ] case let .range(from, to): - return [ + [ "kind": "versionRange", "minimumVersion": .string(.init(from)), "maximumVersion": .string(.init(to)), ] case let .upToNextMinorVersion(version): - return [ + [ "kind": "upToNextMinorVersion", "minimumVersion": .string(.init(version)), ] case let .upToNextMajorVersion(version): - return [ + [ "kind": "upToNextMajorVersion", "minimumVersion": .string(.init(version)), ] @@ -125,28 +125,24 @@ public class XCRemoteSwiftPackageReference: PBXContainerItem, PlistSerializable /// It returns the name of the package reference. public var name: String? { - return repositoryURL?.split(separator: "/").last?.replacingOccurrences(of: ".git", with: "") + repositoryURL?.split(separator: "/").last?.replacingOccurrences(of: ".git", with: "") } func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary = try super.plistValues(proj: proj, reference: reference) dictionary["isa"] = .string(CommentedString(XCRemoteSwiftPackageReference.isa)) - if let repositoryURL = repositoryURL { + if let repositoryURL { dictionary["repositoryURL"] = .string(.init(repositoryURL)) } - if let versionRequirement = versionRequirement { + if let versionRequirement { dictionary["requirement"] = PlistValue.dictionary(versionRequirement.plistValues()) } return (key: CommentedString(reference, comment: "XCRemoteSwiftPackageReference \"\(name ?? "")\""), value: .dictionary(dictionary)) } - // MARK: - Equatable - - @objc public override func isEqual(to object: Any?) -> Bool { + override func isEqual(to object: Any?) -> Bool { guard let rhs = object as? XCRemoteSwiftPackageReference else { return false } - if repositoryURL != rhs.repositoryURL { return false } - if versionRequirement != rhs.versionRequirement { return false } - return super.isEqual(to: rhs) + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/SwiftPackage/XCSwiftPackageProductDependency.swift b/Sources/XcodeProj/Objects/SwiftPackage/XCSwiftPackageProductDependency.swift index d5e7f41aa..cb0abc3d9 100644 --- a/Sources/XcodeProj/Objects/SwiftPackage/XCSwiftPackageProductDependency.swift +++ b/Sources/XcodeProj/Objects/SwiftPackage/XCSwiftPackageProductDependency.swift @@ -11,19 +11,24 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl /// Package the product dependency refers to. public var package: XCRemoteSwiftPackageReference? { get { - return packageReference?.getObject() + packageReference?.getObject() } set { packageReference = newValue?.reference } } + /// Is it a Plugin. + var isPlugin: Bool + // MARK: - Init public init(productName: String, - package: XCRemoteSwiftPackageReference? = nil) { + package: XCRemoteSwiftPackageReference? = nil, + isPlugin: Bool = false) { self.productName = productName packageReference = package?.reference + self.isPlugin = isPlugin super.init() } @@ -31,7 +36,16 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl let objects = decoder.context.objects let repository = decoder.context.objectReferenceRepository let container = try decoder.container(keyedBy: CodingKeys.self) - productName = try container.decode(String.self, forKey: .productName) + let rawProductName = try container.decode(String.self, forKey: .productName) + let pluginPrefix = "plugin:" + if rawProductName.hasPrefix(pluginPrefix) { + productName = String(rawProductName.dropFirst(pluginPrefix.count)) + isPlugin = true + } else { + productName = rawProductName + isPlugin = false + } + if let packageString: String = try container.decodeIfPresent(.package) { packageReference = repository.getOrCreate(reference: packageString, objects: objects) } else { @@ -43,10 +57,14 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary = try super.plistValues(proj: proj, reference: reference) dictionary["isa"] = .string(CommentedString(XCSwiftPackageProductDependency.isa)) - if let package = package { + if let package { dictionary["package"] = .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\"")) } - dictionary["productName"] = .string(.init(productName)) + if isPlugin { + dictionary["productName"] = .string(.init("plugin:" + productName)) + } else { + dictionary["productName"] = .string(.init(productName)) + } return (key: CommentedString(reference, comment: productName), value: .dictionary(dictionary)) @@ -59,12 +77,8 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl case package } - // MARK: - Equatable - - @objc public override func isEqual(to object: Any?) -> Bool { + override func isEqual(to object: Any?) -> Bool { guard let rhs = object as? XCSwiftPackageProductDependency else { return false } - if packageReference != rhs.packageReference { return false } - if productName != rhs.productName { return false } - return super.isEqual(to: rhs) + return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift index d67b66da8..9bfd5674c 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift @@ -1,12 +1,44 @@ import Foundation /// This is the element for a build target that aggregates several others. -public final class PBXAggregateTarget: PBXTarget {} +public final class PBXAggregateTarget: PBXTarget { + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXAggregateTarget else { return false } + return isEqual(to: rhs) + } +} // MARK: - PBXAggregateTarget Extension (PlistSerializable) extension PBXAggregateTarget: PlistSerializable { func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { - return try plistValues(proj: proj, isa: PBXAggregateTarget.isa, reference: reference) + try plistValues(proj: proj, isa: PBXAggregateTarget.isa, reference: reference) + } +} + +// MARK: - Helpers + +public extension PBXAggregateTarget { + /// Adds a local target dependency to the target. + /// + /// - Parameter target: dependency target. + /// - Returns: target dependency reference. + /// - Throws: an error if the dependency cannot be created. + func addDependency(target: PBXTarget) throws -> PBXTargetDependency? { + let objects = try target.objects() + guard let project = objects.projects.first?.value else { + return nil + } + let proxy = PBXContainerItemProxy(containerPortal: .project(project), + remoteGlobalID: .object(target), + proxyType: .nativeTarget, + remoteInfo: target.name) + objects.add(object: proxy) + let targetDependency = PBXTargetDependency(name: target.name, + target: target, + targetProxy: proxy) + objects.add(object: targetDependency) + dependencies.append(targetDependency) + return targetDependency } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXLegacyTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXLegacyTarget.swift index f53297bdf..6d9a5e566 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXLegacyTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXLegacyTarget.swift @@ -79,16 +79,16 @@ public final class PBXLegacyTarget: PBXTarget { switch value { case let .dictionary(dictValue): dict = dictValue - if let buildToolPath = buildToolPath { + if let buildToolPath { dict["buildToolPath"] = PlistValue.string(CommentedString(buildToolPath)) } - if let buildArgumentsString = buildArgumentsString { + if let buildArgumentsString { dict["buildArgumentsString"] = PlistValue.string(CommentedString(buildArgumentsString)) } dict["passBuildSettingsInEnvironment"] = PlistValue.string(CommentedString(passBuildSettingsInEnvironment.int.description)) - if let buildWorkingDirectory = buildWorkingDirectory { + if let buildWorkingDirectory { dict["buildWorkingDirectory"] = PlistValue.string(CommentedString(buildWorkingDirectory)) } @@ -97,12 +97,17 @@ public final class PBXLegacyTarget: PBXTarget { } return (key: key, value: .dictionary(dict)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXLegacyTarget else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXNativeTarget Extension (PlistSerializable) extension PBXLegacyTarget: PlistSerializable { func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { - return try plistValues(proj: proj, isa: PBXLegacyTarget.isa, reference: reference) + try plistValues(proj: proj, isa: PBXLegacyTarget.isa, reference: reference) } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXNativeTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXNativeTarget.swift index aba2a7f5b..c3705422e 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXNativeTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXNativeTarget.swift @@ -9,7 +9,7 @@ public final class PBXNativeTarget: PBXTarget { /// /// - Parameters: /// - name: target name. - /// - buildConfigurationList: build configuratino list. + /// - buildConfigurationList: build configuration list. /// - buildPhases: build phases. /// - buildRules: build rules. /// - dependencies: dependencies. @@ -54,18 +54,23 @@ public final class PBXNativeTarget: PBXTarget { guard case var PlistValue.dictionary(dict) = value else { throw XcodeprojWritingError.invalidType(class: String(describing: type(of: self)), expected: "Dictionary") } - if let productInstallPath = productInstallPath { + if let productInstallPath { dict["productInstallPath"] = .string(CommentedString(productInstallPath)) } return (key: key, value: .dictionary(dict)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXNativeTarget else { return false } + return isEqual(to: rhs) + } } // MARK: - PBXNativeTarget Extension (PlistSerializable) extension PBXNativeTarget: PlistSerializable { func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { - return try plistValues(proj: proj, isa: PBXNativeTarget.isa, reference: reference) + try plistValues(proj: proj, isa: PBXNativeTarget.isa, reference: reference) } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXProductType.swift b/Sources/XcodeProj/Objects/Targets/PBXProductType.swift index 5782b6b0b..3957a72dc 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXProductType.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXProductType.swift @@ -5,12 +5,14 @@ public enum PBXProductType: String, Decodable { case application = "com.apple.product-type.application" case framework = "com.apple.product-type.framework" case staticFramework = "com.apple.product-type.framework.static" + case xcFramework = "com.apple.product-type.xcframework" case dynamicLibrary = "com.apple.product-type.library.dynamic" case staticLibrary = "com.apple.product-type.library.static" case bundle = "com.apple.product-type.bundle" case unitTestBundle = "com.apple.product-type.bundle.unit-test" case uiTestBundle = "com.apple.product-type.bundle.ui-testing" case appExtension = "com.apple.product-type.app-extension" + case extensionKitExtension = "com.apple.product-type.extensionkit-extension" case commandLineTool = "com.apple.product-type.tool" case watchApp = "com.apple.product-type.application.watchapp" case watch2App = "com.apple.product-type.application.watchapp2" @@ -26,34 +28,47 @@ public enum PBXProductType: String, Decodable { case xcodeExtension = "com.apple.product-type.xcode-extension" case instrumentsPackage = "com.apple.product-type.instruments-package" case intentsServiceExtension = "com.apple.product-type.app-extension.intents-service" + case onDemandInstallCapableApplication = "com.apple.product-type.application.on-demand-install-capable" + case metalLibrary = "com.apple.product-type.metal-library" + case driverExtension = "com.apple.product-type.driver-extension" + case systemExtension = "com.apple.product-type.system-extension" /// Returns the file extension for the given product type. public var fileExtension: String? { switch self { - case .application, .watchApp, .watch2App, .watch2AppContainer, .messagesApplication: - return "app" + case .application, .watchApp, .watch2App, .watch2AppContainer, .messagesApplication, .onDemandInstallCapableApplication: + "app" case .framework, .staticFramework: - return "framework" + "framework" case .dynamicLibrary: - return "dylib" + "dylib" case .staticLibrary: - return "a" + "a" case .bundle: - return "bundle" + "bundle" case .unitTestBundle, .uiTestBundle: - return "xctest" - case .appExtension, .tvExtension, .watchExtension, .watch2Extension, .messagesExtension, .stickerPack, .xcodeExtension, .intentsServiceExtension: - return "appex" + "xctest" + case .appExtension, .extensionKitExtension, .tvExtension, .watchExtension, .watch2Extension, .messagesExtension, .stickerPack, .xcodeExtension, + .intentsServiceExtension: + "appex" case .commandLineTool: - return nil + nil case .xpcService: - return "xpc" + "xpc" case .ocUnitTestBundle: - return "octest" + "octest" case .instrumentsPackage: - return "instrpkg" + "instrpkg" + case .xcFramework: + "xcframework" + case .metalLibrary: + "metallib" + case .systemExtension: + "systemextension" + case .driverExtension: + "dext" case .none: - return nil + nil } } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXReferenceProxy.swift b/Sources/XcodeProj/Objects/Targets/PBXReferenceProxy.swift index c92b24860..9c34421ed 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXReferenceProxy.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXReferenceProxy.swift @@ -15,7 +15,7 @@ public final class PBXReferenceProxy: PBXFileElement { /// Element remote. public var remote: PBXContainerItemProxy? { get { - return remoteReference?.getObject() + remoteReference?.getObject() } set { remoteReference = newValue?.reference @@ -64,13 +64,18 @@ public final class PBXReferenceProxy: PBXFileElement { fatalError("super implementation changed and we didn’t realise!") } dictionary["isa"] = .string(CommentedString(PBXReferenceProxy.isa)) - if let fileType = fileType { + if let fileType { dictionary["fileType"] = .string(CommentedString(fileType)) } - if let remoteReference = remoteReference { + if let remoteReference { dictionary["remoteRef"] = .string(CommentedString(remoteReference.value, comment: "PBXContainerItemProxy")) } return (key: CommentedString(reference, comment: path), value: .dictionary(dictionary)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXReferenceProxy else { return false } + return isEqual(to: rhs) + } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 6dd41682c..b23e0b47f 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -8,7 +8,7 @@ public class PBXTarget: PBXContainerItem { /// Build configuration list. public var buildConfigurationList: XCConfigurationList? { get { - return buildConfigurationListReference?.getObject() + buildConfigurationListReference?.getObject() } set { buildConfigurationListReference = newValue?.reference @@ -21,7 +21,7 @@ public class PBXTarget: PBXContainerItem { /// Target build phases. public var buildPhases: [PBXBuildPhase] { get { - return buildPhaseReferences.objects() + buildPhaseReferences.objects() } set { buildPhaseReferences = newValue.references() @@ -34,7 +34,7 @@ public class PBXTarget: PBXContainerItem { /// Target build rules. public var buildRules: [PBXBuildRule] { get { - return buildRuleReferences.objects() + buildRuleReferences.objects() } set { buildRuleReferences = newValue.references() @@ -47,7 +47,7 @@ public class PBXTarget: PBXContainerItem { /// Target dependencies. public var dependencies: [PBXTargetDependency] { get { - return dependencyReferences.objects() + dependencyReferences.objects() } set { dependencyReferences = newValue.references() @@ -58,6 +58,8 @@ public class PBXTarget: PBXContainerItem { public var name: String /// Target product name. + /// + /// This property's value may differ from the value displayed in Xcode if the product name is specified through build settings. public var productName: String? /// Target product reference. @@ -66,7 +68,7 @@ public class PBXTarget: PBXContainerItem { /// Target product. public var product: PBXFileReference? { get { - return productReference?.getObject() + productReference?.getObject() } set { productReference = newValue?.reference @@ -74,15 +76,28 @@ public class PBXTarget: PBXContainerItem { } /// Swift package product references. - var packageProductDependencyReferences: [PBXObjectReference] + var packageProductDependencyReferences: [PBXObjectReference]? /// Swift packages products. - public var packageProductDependencies: [XCSwiftPackageProductDependency] { + public var packageProductDependencies: [XCSwiftPackageProductDependency]? { set { - packageProductDependencyReferences = newValue.references() + packageProductDependencyReferences = newValue?.references() } get { - return packageProductDependencyReferences.objects() + packageProductDependencyReferences?.objects() + } + } + + // File system synchronized groups references. + var fileSystemSynchronizedGroupsReferences: [PBXObjectReference]? + + // File system synchronized groups. + public var fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? { + set { + fileSystemSynchronizedGroupsReferences = newValue?.references() + } + get { + fileSystemSynchronizedGroupsReferences?.objects() } } @@ -108,12 +123,14 @@ public class PBXTarget: PBXContainerItem { packageProductDependencies: [XCSwiftPackageProductDependency] = [], productName: String? = nil, product: PBXFileReference? = nil, - productType: PBXProductType? = nil) { + productType: PBXProductType? = nil, + fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? = nil) { buildConfigurationListReference = buildConfigurationList?.reference buildPhaseReferences = buildPhases.references() buildRuleReferences = buildRules.references() dependencyReferences = dependencies.references() packageProductDependencyReferences = packageProductDependencies.references() + fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroups?.references() self.name = name self.productName = productName productReference = product?.reference @@ -133,6 +150,7 @@ public class PBXTarget: PBXContainerItem { case productReference case productType case packageProductDependencies + case fileSystemSynchronizedGroups } public required init(from decoder: Decoder) throws { @@ -158,12 +176,21 @@ public class PBXTarget: PBXContainerItem { } else { productReference = nil } - - let packageProductDependencyReferenceStrings: [String] = try container.decodeIfPresent(.packageProductDependencies) ?? [] - packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { - objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + let packageProductDependencyReferenceStrings: [String]? = try container.decodeIfPresent(.packageProductDependencies) + if let packageProductDependencyReferenceStrings { + packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { + objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + } + } else { + packageProductDependencyReferences = nil } + let fileSystemSynchronizedGroupsReferences: [String]? = try container.decodeIfPresent(.fileSystemSynchronizedGroups) + if let fileSystemSynchronizedGroupsReferences { + self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { + objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + } + } productType = try container.decodeIfPresent(.productType) try super.init(from: decoder) } @@ -172,7 +199,7 @@ public class PBXTarget: PBXContainerItem { var dictionary = try super.plistValues(proj: proj, reference: reference) dictionary["isa"] = .string(CommentedString(isa)) let buildConfigurationListComment = "Build configuration list for \(isa) \"\(name)\"" - if let buildConfigurationListReference = buildConfigurationListReference { + if let buildConfigurationListReference { dictionary["buildConfigurationList"] = .string(CommentedString(buildConfigurationListReference.value, comment: buildConfigurationListComment)) } @@ -180,7 +207,7 @@ public class PBXTarget: PBXContainerItem { .map { (buildPhaseReference: PBXObjectReference) in let buildPhase: PBXBuildPhase? = buildPhaseReference.getObject() return .string(CommentedString(buildPhaseReference.value, comment: buildPhase?.name())) - }) + }) // Xcode doesn't write PBXAggregateTarget buildRules or empty PBXLegacyTarget buildRules if !(self is PBXAggregateTarget), !(self is PBXLegacyTarget) || !buildRuleReferences.isEmpty { @@ -188,25 +215,38 @@ public class PBXTarget: PBXContainerItem { } dictionary["dependencies"] = .array(dependencyReferences.map { .string(CommentedString($0.value, comment: PBXTargetDependency.isa)) }) + if let fileSystemSynchronizedGroupsReferences { + dictionary["fileSystemSynchronizedGroups"] = .array(fileSystemSynchronizedGroupsReferences.map { fileSystemSynchronizedGroupReference in + let fileSystemSynchronizedGroup: PBXFileSystemSynchronizedRootGroup? = fileSystemSynchronizedGroupReference.getObject() + return .string(CommentedString(fileSystemSynchronizedGroupReference.value, comment: fileSystemSynchronizedGroup?.path)) + }) + } + dictionary["name"] = .string(CommentedString(name)) - if let productName = productName { + if let productName { dictionary["productName"] = .string(CommentedString(productName)) } - if let productType = productType { + if let productType { dictionary["productType"] = .string(CommentedString(productType.rawValue)) } - if let productReference = productReference { + if let productReference { let fileElement: PBXFileElement? = productReference.getObject() dictionary["productReference"] = .string(CommentedString(productReference.value, comment: fileElement?.fileName())) } - if !packageProductDependencies.isEmpty { + if let packageProductDependencies { dictionary["packageProductDependencies"] = .array(packageProductDependencies.map { PlistValue.string(.init($0.reference.value, comment: $0.productName)) }) } + return (key: CommentedString(reference, comment: name), value: .dictionary(dictionary)) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXTarget else { return false } + return isEqual(to: rhs) + } } // MARK: - Helpers @@ -214,10 +254,12 @@ public class PBXTarget: PBXContainerItem { public extension PBXTarget { /// Returns the product name with the extension joined with a period. /// + /// This property's value may differ from the value displayed in Xcode if the product name is specified through build settings. + /// /// - Returns: product name with extension. func productNameWithExtension() -> String? { - guard let productName = self.productName else { return nil } - guard let fileExtension = self.productType?.fileExtension else { return nil } + guard let productName else { return nil } + guard let fileExtension = productType?.fileExtension else { return nil } return "\(productName).\(fileExtension)" } @@ -226,7 +268,7 @@ public extension PBXTarget { /// - Returns: frameworks build phase. /// - Throws: an error if the build phase cannot be obtained. func frameworksBuildPhase() throws -> PBXFrameworksBuildPhase? { - return try buildPhaseReferences + try buildPhaseReferences .compactMap { try $0.getThrowingObject() as? PBXBuildPhase } .filter { $0.buildPhase == .frameworks } .compactMap { $0 as? PBXFrameworksBuildPhase } @@ -238,7 +280,7 @@ public extension PBXTarget { /// - Returns: sources build phase. /// - Throws: an error if the build phase cannot be obtained. func sourcesBuildPhase() throws -> PBXSourcesBuildPhase? { - return try buildPhaseReferences + try buildPhaseReferences .compactMap { try $0.getThrowingObject() as? PBXBuildPhase } .filter { $0.buildPhase == .sources } .compactMap { $0 as? PBXSourcesBuildPhase } @@ -250,7 +292,7 @@ public extension PBXTarget { /// - Returns: sources build phase. /// - Throws: an error if the build phase cannot be obtained. func resourcesBuildPhase() throws -> PBXResourcesBuildPhase? { - return try buildPhaseReferences + try buildPhaseReferences .compactMap { try $0.getThrowingObject() as? PBXResourcesBuildPhase } .filter { $0.buildPhase == .resources } .first @@ -261,7 +303,7 @@ public extension PBXTarget { /// - Returns: source files. /// - Throws: an error if something goes wrong. func sourceFiles() throws -> [PBXFileElement] { - return try sourcesBuildPhase()?.fileReferences? + try sourcesBuildPhase()?.fileReferences? .compactMap { try $0.getThrowingObject() as? PBXBuildFile } .filter { $0.fileReference != nil } .compactMap { try $0.fileReference!.getThrowingObject() as? PBXFileElement } @@ -272,9 +314,18 @@ public extension PBXTarget { /// /// - Returns: Embed frameworks build phases. func embedFrameworksBuildPhases() -> [PBXCopyFilesBuildPhase] { - return buildPhases + buildPhases .filter { $0.buildPhase == .copyFiles } .compactMap { $0 as? PBXCopyFilesBuildPhase } .filter { $0.dstSubfolderSpec == .frameworks } } + + /// Returns the run script build phases. + /// + /// - Returns: Run script build phases. + func runScriptBuildPhases() -> [PBXShellScriptBuildPhase] { + buildPhases + .filter { $0.buildPhase == .runScript } + .compactMap { $0 as? PBXShellScriptBuildPhase } + } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift b/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift index a21e721b7..6da6ba0aa 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift @@ -13,7 +13,7 @@ public final class PBXTargetDependency: PBXObject { /// Target. public var target: PBXTarget? { get { - return targetReference?.getObject() + targetReference?.getObject() } set { targetReference = newValue?.reference @@ -26,7 +26,7 @@ public final class PBXTargetDependency: PBXObject { /// Target proxy. public var targetProxy: PBXContainerItemProxy? { get { - return targetProxyReference?.getObject() + targetProxyReference?.getObject() } set { targetProxyReference = newValue?.reference @@ -39,26 +39,39 @@ public final class PBXTargetDependency: PBXObject { /// Product. public var product: XCSwiftPackageProductDependency? { get { - return productReference?.getObject() + productReference?.getObject() } set { productReference = newValue?.reference } } + /// Platform filter attribute. + /// Introduced in: Xcode 11 + public var platformFilter: String? + + /// Platform filters attribute. + public var platformFilters: [String]? + // MARK: - Init /// Initializes the target dependency with dependencies as objects. /// /// - Parameters: /// - name: Dependency name. + /// - platformFilter: Platform filter. + /// - platformFilters: Platform filters. /// - target: Target. /// - targetProxy: Target proxy. public init(name: String? = nil, + platformFilter: String? = nil, + platformFilters: [String]? = nil, target: PBXTarget? = nil, targetProxy: PBXContainerItemProxy? = nil, product: XCSwiftPackageProductDependency? = nil) { self.name = name + self.platformFilter = platformFilter + self.platformFilters = platformFilters targetReference = target?.reference targetProxyReference = targetProxy?.reference productReference = product?.reference @@ -69,6 +82,8 @@ public final class PBXTargetDependency: PBXObject { fileprivate enum CodingKeys: String, CodingKey { case name + case platformFilter + case platformFilters case target case targetProxy case productRef @@ -79,6 +94,8 @@ public final class PBXTargetDependency: PBXObject { let referenceRepository = decoder.context.objectReferenceRepository let objects = decoder.context.objects name = try container.decodeIfPresent(.name) + platformFilter = try container.decodeIfPresent(.platformFilter) + platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) if let targetReference: String = try container.decodeIfPresent(.target) { self.targetReference = referenceRepository.getOrCreate(reference: targetReference, objects: objects) } @@ -90,6 +107,11 @@ public final class PBXTargetDependency: PBXObject { } try super.init(from: decoder) } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXTargetDependency else { return false } + return isEqual(to: rhs) + } } // MARK: - PlistSerializable @@ -98,17 +120,23 @@ extension PBXTargetDependency: PlistSerializable { func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(PBXTargetDependency.isa)) - if let name = name { + if let name { dictionary["name"] = .string(CommentedString(name)) } - if let targetReference = targetReference { + if let platformFilter { + dictionary["platformFilter"] = .string(CommentedString(platformFilter)) + } + if let platformFilters { + dictionary["platformFilters"] = .array(platformFilters.map { .string(.init($0)) }) + } + if let targetReference { let targetObject: PBXTarget? = targetReference.getObject() dictionary["target"] = .string(CommentedString(targetReference.value, comment: targetObject?.name)) } - if let targetProxyReference = targetProxyReference { + if let targetProxyReference { dictionary["targetProxy"] = .string(CommentedString(targetProxyReference.value, comment: "PBXContainerItemProxy")) } - if let productReference = productReference { + if let productReference { dictionary["productRef"] = .string(CommentedString(productReference.value, comment: product?.productName)) } return (key: CommentedString(reference, diff --git a/Sources/XcodeProj/Project/WorkspaceSettings.swift b/Sources/XcodeProj/Project/WorkspaceSettings.swift index 1a400058e..25eee3029 100644 --- a/Sources/XcodeProj/Project/WorkspaceSettings.swift +++ b/Sources/XcodeProj/Project/WorkspaceSettings.swift @@ -1,7 +1,7 @@ import Foundation -import PathKit +@preconcurrency import PathKit -public enum WorkspaceSettingsError: Error { +public enum WorkspaceSettingsError: Error, Sendable { /// thrown when the settings file was not found. case notFound(path: Path) } @@ -16,9 +16,26 @@ public class WorkspaceSettings: Codable, Equatable, Writable { case new } + public enum DerivedDataLocationStyle: String { + /// Default derived data + case `default` = "Default" + + /// Absolute path + case absolutePath = "AbsolutePath" + + /// Relative paht + case workspaceRelativePath = "WorkspaceRelativePath" + } + /// Workspace build system. public var buildSystem: BuildSystem + /// Workspace DerivedData directory. + public var derivedDataLocationStyle: DerivedDataLocationStyle? + + /// Path to workspace DerivedData directory. + public var derivedDataCustomLocation: String? + /// When true, Xcode auto-creates schemes in the project. public var autoCreateSchemes: Bool? @@ -27,6 +44,8 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// - buildSystem: Build system. enum CodingKeys: String, CodingKey { case buildSystem = "BuildSystemType" + case derivedDataLocationStyle = "DerivedDataLocationStyle" + case derivedDataCustomLocation = "DerivedDataCustomLocation" case autoCreateSchemes = "IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded" } @@ -34,25 +53,38 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// /// - Parameters: /// - buildSystem: Workspace build system. + /// - derivedDataLocationStyle: Workspace DerivedData directory. + /// - derivedDataCustomLocation: Path to workspace DerivedData directory. /// - autoCreateSchemes: When true, Xcode auto-creates schemes in the project. - init(buildSystem: BuildSystem = .new, - autoCreateSchemes: Bool? = nil) { + public init(buildSystem: BuildSystem = .new, + derivedDataLocationStyle: DerivedDataLocationStyle? = nil, + derivedDataCustomLocation: String? = nil, + autoCreateSchemes: Bool? = nil) { self.buildSystem = buildSystem + self.derivedDataLocationStyle = derivedDataLocationStyle + self.derivedDataCustomLocation = derivedDataCustomLocation self.autoCreateSchemes = autoCreateSchemes } /// Initializes the settings decoding the values from the plist representation. /// - /// - Parameter decoder: Propertly list decoder. + /// - Parameter decoder: Property list decoder. /// - Throws: An error if required attributes are missing or have a wrong type. public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if let buildSystemString: String = try container.decodeIfPresent(.buildSystem), - let buildSystem = BuildSystem(rawValue: buildSystemString) { + let buildSystem = BuildSystem(rawValue: buildSystemString) { self.buildSystem = buildSystem } else { buildSystem = .new } + if let derivedDataLocationStyleString: String = try container.decodeIfPresent(.derivedDataLocationStyle), + let derivedDataLocationStyle = DerivedDataLocationStyle(rawValue: derivedDataLocationStyleString) { + self.derivedDataLocationStyle = derivedDataLocationStyle + } else { + derivedDataLocationStyle = .default + } + derivedDataCustomLocation = try container.decodeIfPresent(.derivedDataCustomLocation) autoCreateSchemes = try container.decodeIfPresent(.autoCreateSchemes) } @@ -65,7 +97,13 @@ public class WorkspaceSettings: Codable, Equatable, Writable { if buildSystem == .original { try container.encode(buildSystem.rawValue, forKey: .buildSystem) } - if let autoCreateSchemes = autoCreateSchemes { + if let derivedDataLocationStyle { + try container.encode(derivedDataLocationStyle.rawValue, forKey: .derivedDataLocationStyle) + } + if let derivedDataCustomLocation { + try container.encode(derivedDataCustomLocation, forKey: .derivedDataCustomLocation) + } + if let autoCreateSchemes { try container.encode(autoCreateSchemes, forKey: .autoCreateSchemes) } } @@ -91,8 +129,10 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// - rhs: Second instance to be compared. /// - Returns: True if the two instances are the same. public static func == (lhs: WorkspaceSettings, rhs: WorkspaceSettings) -> Bool { - return lhs.buildSystem == rhs.buildSystem && - lhs.autoCreateSchemes == rhs.autoCreateSchemes + lhs.buildSystem == rhs.buildSystem && + lhs.autoCreateSchemes == rhs.autoCreateSchemes && + lhs.derivedDataLocationStyle == rhs.derivedDataLocationStyle && + lhs.derivedDataCustomLocation == rhs.derivedDataCustomLocation } /// Writes the workspace settings. @@ -101,11 +141,28 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// - Parameter override: True if the content should be overriden if it already exists. /// - Throws: writing error if something goes wrong. public func write(path: Path, override: Bool) throws { - let encoder = PropertyListEncoder() - let data = try encoder.encode(self) + guard let data = try dataRepresentation() else { + return + } if override, path.exists { try path.delete() } try path.write(data) } + + /// Get the workspace settings. + /// + /// - Throws: reading error if something goes wrong. + public func dataRepresentation() throws -> Data? { + let encoder = PropertyListEncoder() + encoder.outputFormat = .xml + let data = try encoder.encode(self) + return data + } +} + +extension WorkspaceSettings { + static func path(_ path: Path) -> Path { + path + "WorkspaceSettings.xcsettings" + } } diff --git a/Sources/XcodeProj/Project/XCBreakpointList.swift b/Sources/XcodeProj/Project/XCBreakpointList.swift index dc3b7202e..d253b5336 100644 --- a/Sources/XcodeProj/Project/XCBreakpointList.swift +++ b/Sources/XcodeProj/Project/XCBreakpointList.swift @@ -70,7 +70,7 @@ public final class XCBreakpointList: Equatable, Writable { attributes["conveyanceType"] = conveyanceType attributes["command"] = command attributes["arguments"] = arguments - if let waitUntilDone = waitUntilDone { + if let waitUntilDone { attributes["waitUntilDone"] = waitUntilDone ? "YES" : "NO" } attributes["script"] = script @@ -85,7 +85,7 @@ public final class XCBreakpointList: Equatable, Writable { // MARK: - Equatable public static func == (lhs: ActionContent, rhs: ActionContent) -> Bool { - return lhs.consoleCommand == rhs.consoleCommand && + lhs.consoleCommand == rhs.consoleCommand && lhs.message == rhs.message && lhs.conveyanceType == rhs.conveyanceType && lhs.command == rhs.command && @@ -123,7 +123,8 @@ public final class XCBreakpointList: Equatable, Writable { init(element: AEXMLElement) throws { guard let actionExtensionIDString = element.attributes["ActionExtensionID"], - let actionExtensionID = ActionExtensionID(rawValue: actionExtensionIDString) else { + let actionExtensionID = ActionExtensionID(rawValue: actionExtensionIDString) + else { throw XCBreakpointListError.missing(property: "ActionExtensionID") } self.actionExtensionID = actionExtensionID @@ -143,7 +144,7 @@ public final class XCBreakpointList: Equatable, Writable { // MARK: - Equatable public static func == (lhs: BreakpointActionProxy, rhs: BreakpointActionProxy) -> Bool { - return lhs.actionExtensionID == rhs.actionExtensionID && + lhs.actionExtensionID == rhs.actionExtensionID && lhs.actionContent == rhs.actionContent } } @@ -163,12 +164,12 @@ public final class XCBreakpointList: Equatable, Writable { } public static func == (_: BreakpointLocationProxy, _: BreakpointLocationProxy) -> Bool { - return true + true } } public static func == (lhs: BreakpointContent, rhs: BreakpointContent) -> Bool { - return lhs.enabled == rhs.enabled && + lhs.enabled == rhs.enabled && lhs.ignoreCount == rhs.ignoreCount && lhs.continueAfterRunningActions == rhs.continueAfterRunningActions && lhs.filePath == rhs.filePath && @@ -316,6 +317,7 @@ public final class XCBreakpointList: Equatable, Writable { case symbolic = "Xcode.Breakpoint.SymbolicBreakpoint" case ideConstraintError = "Xcode.Breakpoint.IDEConstraintErrorBreakpoint" case ideTestFailure = "Xcode.Breakpoint.IDETestFailureBreakpoint" + case runtimeIssue = "Xcode.Breakpoint.RuntimeIssueBreakpoint" } // MARK: - Attributes @@ -333,7 +335,8 @@ public final class XCBreakpointList: Equatable, Writable { init(element: AEXMLElement) throws { guard let breakpointExtensionIDString = element.attributes["BreakpointExtensionID"], - let breakpointExtensionID = BreakpointExtensionID(rawValue: breakpointExtensionIDString) else { + let breakpointExtensionID = BreakpointExtensionID(rawValue: breakpointExtensionIDString) + else { throw XCBreakpointListError.missing(property: "BreakpointExtensionID") } self.breakpointExtensionID = breakpointExtensionID @@ -353,7 +356,7 @@ public final class XCBreakpointList: Equatable, Writable { // MARK: - Equatable public static func == (lhs: BreakpointProxy, rhs: BreakpointProxy) -> Bool { - return lhs.breakpointExtensionID == rhs.breakpointExtensionID && + lhs.breakpointExtensionID == rhs.breakpointExtensionID && lhs.breakpointContent == rhs.breakpointContent } } @@ -374,7 +377,7 @@ public final class XCBreakpointList: Equatable, Writable { if !path.exists { throw XCBreakpointListError.notFound(path: path) } - let document = try AEXMLDocument(xml: try path.read()) + let document = try AEXMLDocument(xml: path.read()) let bucket = document["Bucket"] type = bucket.attributes["type"] version = bucket.attributes["version"] @@ -394,7 +397,7 @@ public final class XCBreakpointList: Equatable, Writable { // MARK: - Helpers public func add(breakpointProxy: BreakpointProxy) -> XCBreakpointList { - var breakpoints = self.breakpoints + var breakpoints = breakpoints breakpoints.append(breakpointProxy) return XCBreakpointList(type: type, version: version, breakpoints: breakpoints) } @@ -402,6 +405,15 @@ public final class XCBreakpointList: Equatable, Writable { // MARK: - Writable public func write(path: Path, override: Bool) throws { + let document = getAEXMLDocument() + + if override, path.exists { + try path.delete() + } + try path.write(document.xmlXcodeFormat) + } + + private func getAEXMLDocument() -> AEXMLDocument { let document = AEXMLDocument() var schemeAttributes: [String: String] = [:] schemeAttributes["type"] = type @@ -411,18 +423,28 @@ public final class XCBreakpointList: Equatable, Writable { let breakpoints = AEXMLElement(name: "Breakpoints", value: nil, attributes: [:]) self.breakpoints.map { $0.xmlElement() }.forEach { breakpoints.addChild($0) } bucket.addChild(breakpoints) + return document + } - if override, path.exists { - try path.delete() - } - try path.write(document.xmlXcodeFormat) + public func dataRepresentation() throws -> Data? { + getAEXMLDocument().xmlXcodeFormat.data(using: .utf8) } // MARK: - Equatable public static func == (lhs: XCBreakpointList, rhs: XCBreakpointList) -> Bool { - return lhs.breakpoints == rhs.breakpoints && + lhs.breakpoints == rhs.breakpoints && lhs.type == rhs.type && lhs.version == rhs.version } } + +public extension XCBreakpointList { + /// Returns breakpoints plist path relative to the given path. + /// + /// - Parameter path: debugger folder + /// - Returns: breakpoints plist path relative to the given path. + static func path(_ path: Path) -> Path { + path + "Breakpoints_v2.xcbkptlist" + } +} diff --git a/Sources/XcodeProj/Project/XCDebugger.swift b/Sources/XcodeProj/Project/XCDebugger.swift new file mode 100644 index 000000000..ce79058aa --- /dev/null +++ b/Sources/XcodeProj/Project/XCDebugger.swift @@ -0,0 +1,13 @@ +import AEXML +import Foundation +import PathKit + +enum XCDebugger { + /// Returns debugger folder path relative to the given path. + /// + /// - Parameter path: parent folder of debugger folder (xcshareddata or xcuserdata) + /// - Returns: debugger folder path relative to the given path. + public static func path(_ path: Path) -> Path { + path + "xcdebugger" + } +} diff --git a/Sources/XcodeProj/Project/XCSharedData.swift b/Sources/XcodeProj/Project/XCSharedData.swift index d3ecffe8d..312a3913c 100644 --- a/Sources/XcodeProj/Project/XCSharedData.swift +++ b/Sources/XcodeProj/Project/XCSharedData.swift @@ -1,7 +1,7 @@ import Foundation import PathKit -public final class XCSharedData: Equatable { +public final class XCSharedData: Equatable, Writable { // MARK: - Attributes /// Shared data schemes. @@ -36,9 +36,11 @@ public final class XCSharedData: Equatable { if !path.exists { throw XCSharedDataError.notFound(path: path) } - schemes = path.glob("xcschemes/*.xcscheme") + schemes = XCScheme.schemesPath(path) + .glob("*.xcscheme") .compactMap { try? XCScheme(path: $0) } - breakpoints = try? XCBreakpointList(path: path + "xcdebugger/Breakpoints_v2.xcbkptlist") + + breakpoints = try? XCBreakpointList(path: XCBreakpointList.path(XCDebugger.path(path))) let workspaceSettingsPath = path + "WorkspaceSettings.xcsettings" if workspaceSettingsPath.exists { @@ -51,8 +53,67 @@ public final class XCSharedData: Equatable { // MARK: - Equatable public static func == (lhs: XCSharedData, rhs: XCSharedData) -> Bool { - return lhs.schemes == rhs.schemes && + lhs.schemes == rhs.schemes && lhs.breakpoints == rhs.breakpoints && lhs.workspaceSettings == rhs.workspaceSettings } + + // MARK: - Writable + + public func write(path: Path, override: Bool) throws { + try writeSchemes(path: path, override: override) + try writeBreakpoints(path: path, override: override) + try writeWorkspaceSettings(path: path, override: override) + } + + func writeSchemes(path: Path, override: Bool) throws { + let schemesPath = XCScheme.schemesPath(path) + if override, schemesPath.exists { + try schemesPath.delete() + } + + guard !schemes.isEmpty else { return } + + try schemesPath.mkpath() + for scheme in schemes { + let schemePath = XCScheme.path(path, schemeName: scheme.name) + try scheme.write(path: schemePath, override: override) + } + } + + func writeBreakpoints(path: Path, override: Bool) throws { + let debuggerPath = XCDebugger.path(path) + if override, debuggerPath.exists { + try debuggerPath.delete() + } + + guard let breakpoints else { return } + + try debuggerPath.mkpath() + try breakpoints.write(path: XCBreakpointList.path(debuggerPath), override: override) + } + + func writeWorkspaceSettings(path: Path, override: Bool) throws { + /** + * We don't want to delete this path when `override` is `true` because + * that will delete everything in the folder, including schemes and breakpoints. + * Instead, just create the path if it doesn't exist and let the `write` method + * in `WorkspaceSettings` handle the override. + */ + if !path.exists { + try path.mkpath() + } + + try workspaceSettings?.write(path: WorkspaceSettings.path(path), override: override) + } +} + +public extension XCSharedData { + /// Returns shared data path relative to the given path. + /// + /// - Parameter path: `.xcodeproj` file path + /// - Returns: shared data path relative to the given path. + static func path(_ path: Path) -> Path { + path + "xcshareddata" + } } diff --git a/Sources/XcodeProj/Project/XCUserData.swift b/Sources/XcodeProj/Project/XCUserData.swift new file mode 100644 index 000000000..272152b54 --- /dev/null +++ b/Sources/XcodeProj/Project/XCUserData.swift @@ -0,0 +1,117 @@ +import AEXML +import Foundation +import PathKit + +public final class XCUserData: Equatable, Writable { + // MARK: - Attributes + + /// User name + public var userName: String + + /// User data schemes. + public var schemes: [XCScheme] + + /// Metdata for schemes + public var schemeManagement: XCSchemeManagement? + + /// User data breakpoints. + public var breakpoints: XCBreakpointList? + + // MARK: - Init + + /// Initializes the shared data with its properties. + /// + /// - Parameters: + /// - userName: User name + /// - schemes: User data schemes. + /// - breakpoints: User data breakpoints. + /// - schemeManagement: Metdata for schemes + public init(userName: String, + schemes: [XCScheme], + breakpoints: XCBreakpointList? = nil, + schemeManagement: XCSchemeManagement? = nil) { + self.userName = userName + self.schemes = schemes + self.breakpoints = breakpoints + self.schemeManagement = schemeManagement + } + + /// Initializes the XCUserData reading the content from the disk. + /// + /// - Parameter path: path where the .xcuserdatad is. + public init(path: Path) throws { + if !path.exists { + throw XCUserDataError.notFound(path: path) + } + userName = path.lastComponentWithoutExtension + + let schemesPath = XCScheme.schemesPath(path) + schemes = schemesPath + .glob("*.xcscheme") + .compactMap { try? XCScheme(path: $0) } + schemeManagement = try? XCSchemeManagement(path: XCSchemeManagement.path(schemesPath)) + + breakpoints = try? XCBreakpointList(path: XCBreakpointList.path(XCDebugger.path(path))) + } + + // MARK: - Equatable + + public static func == (lhs: XCUserData, rhs: XCUserData) -> Bool { + lhs.userName == rhs.userName && + lhs.schemes == rhs.schemes && + lhs.breakpoints == rhs.breakpoints && + lhs.schemeManagement == rhs.schemeManagement + } + + // MARK: - Writable + + public func write(path: Path, override: Bool) throws { + try writeSchemes(path: path, override: override) + try writeBreakpoints(path: path, override: override) + try writeSchemeManagement(path: path, override: override) + } + + func writeSchemes(path: Path, override: Bool) throws { + guard !schemes.isEmpty else { return } + + try XCScheme.schemesPath(path).mkpath() + for scheme in schemes { + let schemePath = XCScheme.path(path, schemeName: scheme.name) + try scheme.write(path: schemePath, override: override) + } + } + + func writeSchemeManagement(path: Path, override: Bool) throws { + guard let schemeManagement else { return } + + let schemesPath = XCScheme.schemesPath(path) + try schemesPath.mkpath() + try schemeManagement.write(path: XCSchemeManagement.path(schemesPath), override: override) + } + + func writeBreakpoints(path: Path, override: Bool) throws { + guard let breakpoints else { return } + + let debuggerPath = XCDebugger.path(path) + try debuggerPath.mkpath() + try breakpoints.write(path: XCBreakpointList.path(debuggerPath), override: override) + } +} + +public extension XCUserData { + /// Returns user data path relative to the given path. + /// + /// - Parameter path: `.xcodeproj` file path + /// - Returns: user data path relative to the given path. + static func path(_ path: Path) -> Path { + path + "xcuserdata" + } + + /// Returns user data path for a specific user relative to the given path. + /// + /// - Parameter path: `.xcodeproj` file path + /// - Returns: user data path relative to the given path. + static func path(_ path: Path, userName: String) -> Path { + XCUserData.path(path) + "\(userName).xcuserdatad" + } +} diff --git a/Sources/XcodeProj/Project/Xcode.swift b/Sources/XcodeProj/Project/Xcode.swift index 47bd98832..44ab18058 100644 --- a/Sources/XcodeProj/Project/Xcode.swift +++ b/Sources/XcodeProj/Project/Xcode.swift @@ -1,60 +1,69 @@ import Foundation /// Class that contains Xcode constants. -public struct Xcode { +public enum Xcode { /// Last known constants. - public struct LastKnown { + public enum LastKnown { /// Last known SDKs. - public struct SDK { + public enum SDK { /// Last known SDK for iOS. - public static let ios: String = "12.0" + public static let ios: String = "14.0" /// Last known SDK for macOS. - public static let macos: String = "10.14" + public static let macos: String = "10.15" /// Last known SDK for tvOS. - public static let tvos: String = "12.0" + public static let tvos: String = "14.0" /// Last known SDK for watchos. - public static let watchos: String = "5.0" + public static let watchos: String = "7.0" } /// Last known archive version for Xcodeproj. public static let archiveVersion: UInt = 1 /// Last known Swift version (stable). - public static let swiftVersion = "4.2" + public static let swiftVersion = "5.4.2" /// Last known object version for Xcodeproj. - public static let objectVersion: UInt = 51 + public static let objectVersion: UInt = 55 /// Last known upgrade check. - public static let upgradeCheck = "1000" + public static let upgradeCheck = "1250" /// Last known Swift upgrade check. - public static let swiftUpgradeCheck = "1000" + public static let swiftUpgradeCheck = "1250" } /// Default values. - public struct Default { + public enum Default { /// The default object version for Xcodeproj. - public static let objectVersion: UInt = 52 // Xcode 11 + public static let objectVersion: UInt = 46 /// Default compatibility version. - public static let compatibilityVersion: String = "Xcode 9.3" + public static let compatibilityVersion: String = "Xcode 14.0" /// Default development region. public static let developmentRegion: String = "en" + + /// Default XCScheme format version + public static let xcschemeFormatVersion: String = "1.3" + + /// The last Swift upgrade check version + public static let lastSwiftUpgradeCheck: String = "1300" + + /// The last known Xcode upgrade check version. + public static let lastUpgradeCheck: String = "1300" } /// Inherited keywords used in build settings. public static let inheritedKeywords = ["${inherited}", "$(inherited)"] /// Header files extensions. - public static let headersExtensions = [".h", ".hh", ".hpp", ".ipp", ".tpp", ".hxx", ".def", ".inl", ".inc"] + public static let headersExtensions = [".h", ".hh", ".hpp", ".ipp", ".tpp", ".hxx", ".def", ".inl", ".inc", ".pch", ".apinotes"] /// Supported values. - public struct Supported { + public enum Supported { /// The version of `.xcscheme` files supported by Xcodeproj public static let xcschemeFormatVersion = "1.3" } @@ -64,7 +73,7 @@ public struct Xcode { /// - Parameter extension: file extension. /// - Returns: Xcode file type. public static func filetype(extension: String) -> String? { - return allExtensions[`extension`] + allExtensions[`extension`] } // Derived from Xcode3Core.ideplugin in Xcode 11.1 @@ -100,9 +109,11 @@ public struct Xcode { "atlas": "folder.skatlas", "au": "audio.au", "avi": "video.avi", + "bazel": "text.script.python", "bin": "archive.macbinary", "bmp": "image.bmp", - "bundle": "wrapper.plug-in", + "bundle": "wrapper.cfbundle", + "bzl": "text.script.python", "c": "sourcecode.c.c", "c++": "sourcecode.cpp.cpp", "cc": "sourcecode.cpp.cpp", @@ -124,6 +135,7 @@ public struct Xcode { "defs": "sourcecode.mig", "dext": "wrapper.driver-extension", "dict": "text.plist", + "docc": "folder.documentationcatalog", "dsym": "wrapper.dsym", "dtd": "text.xml", "dylan": "sourcecode.dylan", @@ -222,6 +234,7 @@ public struct Xcode { "nib~": "wrapper.nib", "nqc": "sourcecode.nqc", "o": "compiled.mach-o.objfile", + "otf": "file", "octest": "wrapper.cfbundle", "p": "sourcecode.pascal", "pas": "sourcecode.pascal", @@ -271,6 +284,7 @@ public struct Xcode { "sit": "archive.stuffit", "sks": "file.sks", "skybox": "file.skybox", + "sqlite": "file", "storyboard": "file.storyboard", "storyboardc": "wrapper.storyboardc", "strings": "text.plist.strings", @@ -283,6 +297,7 @@ public struct Xcode { "text": "net.daringfireball.markdown", "tif": "image.tiff", "tiff": "image.tiff", + "ttf": "file", "txt": "text", "uicatalog": "file.uicatalog", "usdz": "file.usdz", @@ -333,4 +348,10 @@ public struct Xcode { "yxx": "sourcecode.yacc", "zip": "archive.zip", ] + + /// Remote project reference dictionary keys. + public enum ProjectReference { + public static let projectReferenceKey = "ProjectRef" + public static let productGroupKey = "ProductGroup" + } } diff --git a/Sources/XcodeProj/Project/XcodeProj.swift b/Sources/XcodeProj/Project/XcodeProj.swift index 0a69104bb..57ac7889f 100644 --- a/Sources/XcodeProj/Project/XcodeProj.swift +++ b/Sources/XcodeProj/Project/XcodeProj.swift @@ -8,35 +8,31 @@ public final class XcodeProj: Equatable { /// Project workspace public var workspace: XCWorkspace - /// .pbxproj representatino + /// The path to the `.xcodeproj` directory. + public let path: Path? + + /// .pbxproj representation public var pbxproj: PBXProj /// Shared data. public var sharedData: XCSharedData? + /// User data. + public var userData: [XCUserData] + // MARK: - Init public init(path: Path) throws { var pbxproj: PBXProj! var workspace: XCWorkspace! var sharedData: XCSharedData? + var userData: [XCUserData] if !path.exists { throw XCodeProjError.notFound(path: path) } - let pbxprojPaths = path.glob("*.pbxproj") - if pbxprojPaths.isEmpty { + guard let pbxprojPath = path.glob("*.pbxproj").first else { throw XCodeProjError.pbxprojNotFound(path: path) } - let pbxprojPath = pbxprojPaths.first! - let (pbxProjData, pbxProjDictionary) = try XcodeProj.readPBXProj(path: pbxprojPath) - let context = ProjectDecodingContext( - pbxProjValueReader: { key in - pbxProjDictionary[key] - } - ) - - let plistDecoder = XcodeprojPropertyListDecoder(context: context) - pbxproj = try plistDecoder.decode(PBXProj.self, from: pbxProjData) - try pbxproj.updateProjectName(path: pbxprojPaths.first!) + pbxproj = try PBXProj(path: pbxprojPath) let xcworkspacePaths = path.glob("*.xcworkspace") if xcworkspacePaths.isEmpty { workspace = XCWorkspace() @@ -46,9 +42,15 @@ public final class XcodeProj: Equatable { let sharedDataPath = path + "xcshareddata" sharedData = try? XCSharedData(path: sharedDataPath) + userData = XCUserData.path(path) + .glob("*.xcuserdatad") + .compactMap { try? XCUserData(path: $0) } + + self.path = path self.pbxproj = pbxproj self.workspace = workspace self.sharedData = sharedData + self.userData = userData } public convenience init(pathString: String) throws { @@ -60,33 +62,28 @@ public final class XcodeProj: Equatable { /// - Parameters: /// - workspace: project internal workspace. /// - pbxproj: project .pbxproj. - public init(workspace: XCWorkspace, pbxproj: PBXProj, sharedData: XCSharedData? = nil) { + /// - sharedData: shared data + /// - userData: user data + public init(workspace: XCWorkspace, + pbxproj: PBXProj, + sharedData: XCSharedData? = nil, + userData: [XCUserData] = [], + path: Path? = nil) { self.workspace = workspace self.pbxproj = pbxproj self.sharedData = sharedData + self.userData = userData + self.path = path } // MARK: - Equatable public static func == (lhs: XcodeProj, rhs: XcodeProj) -> Bool { - return lhs.workspace == rhs.workspace && + lhs.workspace == rhs.workspace && lhs.pbxproj == rhs.pbxproj && - lhs.sharedData == rhs.sharedData - } - - // MARK: - Private - - private static func readPBXProj(path: Path) throws -> (Data, [String: Any]) { - let plistXML = try Data(contentsOf: path.url) - var propertyListFormat = PropertyListSerialization.PropertyListFormat.xml - let serialized = try PropertyListSerialization.propertyList( - from: plistXML, - options: .mutableContainersAndLeaves, - format: &propertyListFormat - ) - // swiftlint:disable:next force_cast - let pbxProjDictionary = serialized as! [String: Any] - return (plistXML, pbxProjDictionary) + lhs.sharedData == rhs.sharedData && + lhs.userData == rhs.userData && + lhs.path == rhs.path } } @@ -112,16 +109,16 @@ extension XcodeProj: Writable { try path.mkpath() try writeWorkspace(path: path, override: override) try writePBXProj(path: path, override: override, outputSettings: outputSettings) - try writeSchemes(path: path, override: override) - try writeBreakPoints(path: path, override: override) + try writeSharedData(path: path, override: override) + try writeUserData(path: path, override: override) } /// Returns workspace file path relative to the given path. /// /// - Parameter path: `.xcodeproj` file path - /// - Returns: worspace file path relative to the given path. + /// - Returns: workspace file path relative to the given path. public static func workspacePath(_ path: Path) -> Path { - return path + "project.xcworkspace" + path + "project.xcworkspace" } /// Writes workspace to the given path. @@ -138,7 +135,7 @@ extension XcodeProj: Writable { /// - Parameter path: `.xcodeproj` file path /// - Returns: project file path relative to the given path. public static func pbxprojPath(_ path: Path) -> Path { - return path + "project.pbxproj" + path + "project.pbxproj" } /// Writes project to the given path. @@ -156,24 +153,48 @@ extension XcodeProj: Writable { /// - Parameter path: `.xcodeproj` file path /// - Returns: shared data path relative to the given path. public static func sharedDataPath(_ path: Path) -> Path { - return path + "xcshareddata" + XCSharedData.path(path) + } + + /// Writes shared data to the given path. + /// + /// - Parameter path: path to `.xcodeproj` file. + /// - Parameter override: if shared data should be overridden. Default is true. + /// - Parameter outputSettings: Controls the writing of various files. + /// If false will throw error if shared data already exists at the given path. + public func writeSharedData(path: Path, override: Bool = true) throws { + try sharedData?.write(path: XCSharedData.path(path), override: override) } - /// Returns schemes folder path relative to the given path. + /// Writes user data to the given path. + /// + /// - Parameter path: path to `.xcodeproj` file. + /// - Parameter override: if user data should be overridden. Default is true. + /// - Parameter outputSettings: Controls the writing of various files. + /// If false will throw error if user data already exists at the given path. + public func writeUserData(path: Path, override: Bool = true) throws { + for userData in userData { + try userData.write(path: XCUserData.path(path, userName: userData.userName), override: override) + } + } + + /// Returns shared schemes folder path relative to the given path. /// /// - Parameter path: `.xcodeproj` file path /// - Returns: schemes folder path relative to the given path. + @available(*, deprecated, message: "use XCScheme.schemesPath(path:)") public static func schemesPath(_ path: Path) -> Path { - return XcodeProj.sharedDataPath(path) + "xcschemes" + XCScheme.schemesPath(sharedDataPath(path)) } - /// Returns scheme file path relative to the given path. + /// Returns shared scheme file path relative to the given path. /// /// - Parameter path: `.xcodeproj` file path /// - Parameter schemeName: scheme name /// - Returns: scheme file path relative to the given path. + @available(*, deprecated, message: "use XCScheme.path(path:schemeName)") public static func schemePath(_ path: Path, schemeName: String) -> Path { - return XcodeProj.schemesPath(path) + "\(schemeName).xcscheme" + XCScheme.path(schemesPath(path), schemeName: schemeName) } /// Writes all project schemes to the given path. @@ -183,34 +204,32 @@ extension XcodeProj: Writable { /// If true will remove all existing schemes before writing. /// If false will throw error if scheme already exists at the given path. public func writeSchemes(path: Path, override: Bool = true) throws { - guard let sharedData = sharedData else { return } + try sharedData?.writeSchemes(path: XCSharedData.path(path), override: override) - let schemesPath = XcodeProj.schemesPath(path) - if override, schemesPath.exists { - try schemesPath.delete() - } - try schemesPath.mkpath() - for scheme in sharedData.schemes { - try scheme.write(path: XcodeProj.schemePath(path, schemeName: scheme.name), override: override) + for userData in userData { + let userDataPath = XCUserData.path(path, userName: userData.userName) + try userData.writeSchemes(path: userDataPath, override: override) } } - /// Returns debugger folder path relative to the given path. + /// Returns shared debugger folder path relative to the given path. /// /// - Parameter path: `.xcodeproj` file path /// - Parameter schemeName: scheme name /// - Returns: debugger folder path relative to the given path. + @available(*, deprecated, message: "use XCDebugger.path(path:)") public static func debuggerPath(_ path: Path) -> Path { - return XcodeProj.sharedDataPath(path) + "xcdebugger" + XCDebugger.path(XCSharedData.path(path)) } - /// Returns breakpoints plist path relative to the given path. + /// Returns shared breakpoints plist path relative to the given path. /// /// - Parameter path: `.xcodeproj` file path /// - Parameter schemeName: scheme name /// - Returns: breakpoints plist path relative to the given path. + @available(*, deprecated, message: "use XCBreakpointList.path(path:)") public static func breakPointsPath(_ path: Path) -> Path { - return XcodeProj.debuggerPath(path) + "Breakpoints_v2.xcbkptlist" + XCBreakpointList.path(debuggerPath(path)) } /// Writes all project breakpoints to the given path. @@ -220,13 +239,12 @@ extension XcodeProj: Writable { /// If true will remove all existing debugger data before writing. /// If false will throw error if breakpoints file exists at the given path. public func writeBreakPoints(path: Path, override: Bool = true) throws { - guard let sharedData = sharedData else { return } + let sharedDataPath = XcodeProj.sharedDataPath(path) + try sharedData?.writeBreakpoints(path: sharedDataPath, override: override) - let debuggerPath = XcodeProj.debuggerPath(path) - if override, debuggerPath.exists { - try debuggerPath.delete() + for userData in userData { + let userDataPath = XCUserData.path(path, userName: userData.userName) + try userData.writeBreakpoints(path: userDataPath, override: override) } - try debuggerPath.mkpath() - try sharedData.breakpoints?.write(path: XcodeProj.breakPointsPath(path), override: override) } } diff --git a/Sources/XcodeProj/Protocols/Writable.swift b/Sources/XcodeProj/Protocols/Writable.swift index e6faa6a2a..f4a7016d3 100644 --- a/Sources/XcodeProj/Protocols/Writable.swift +++ b/Sources/XcodeProj/Protocols/Writable.swift @@ -1,26 +1,33 @@ import Foundation import PathKit -/// Protocol that defines how an entity can be writed into disk +/// Protocol that defines how an entity can be written to disk public protocol Writable { /// Writes the object that conforms the protocol. /// /// - Parameter path: The path to write to - /// - Parameter override: True if the content should be overriden if it already exists. + /// - Parameter override: True if the content should be overridden if it already exists. /// - Throws: writing error if something goes wrong. func write(path: Path, override: Bool) throws /// Writes the object that conforms the protocol. /// /// - Parameter pathString: The path string to write to - /// - Parameter override: True if the content should be overriden if it already exists. + /// - Parameter override: True if the content should be overridden if it already exists. /// - Throws: writing error if something goes wrong. func write(pathString: String, override: Bool) throws + + /// Gets the data representation of the object that conforms to the protocol. + /// + /// - Throws: error if encoding to Data fails. + func dataRepresentation() throws -> Data? } -extension Writable { - public func write(pathString: String, override: Bool) throws { +public extension Writable { + func write(pathString: String, override: Bool) throws { let path = Path(pathString) try write(path: path, override: override) } + + func dataRepresentation() throws -> Data? { nil } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+AditionalOption.swift b/Sources/XcodeProj/Scheme/XCScheme+AditionalOption.swift index e8c8f0125..3f4122099 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+AditionalOption.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+AditionalOption.swift @@ -2,8 +2,8 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class AdditionalOption: Equatable { +public extension XCScheme { + final class AdditionalOption: Equatable { // MARK: - Attributes public var key: String @@ -27,19 +27,19 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "AdditionalOption", - value: nil, - attributes: [ - "key": key, - "value": value, - "isEnabled": isEnabled.xmlString, - ]) + AEXMLElement(name: "AdditionalOption", + value: nil, + attributes: [ + "key": key, + "value": value, + "isEnabled": isEnabled.xmlString, + ]) } // MARK: - Equatable public static func == (lhs: AdditionalOption, rhs: AdditionalOption) -> Bool { - return lhs.key == rhs.key && + lhs.key == rhs.key && lhs.value == rhs.value && lhs.isEnabled == rhs.isEnabled } diff --git a/Sources/XcodeProj/Scheme/XCScheme+AnalyzeAction.swift b/Sources/XcodeProj/Scheme/XCScheme+AnalyzeAction.swift index 2b707e9f6..6dd25fb99 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+AnalyzeAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+AnalyzeAction.swift @@ -2,8 +2,8 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class AnalyzeAction: Equatable { +public extension XCScheme { + final class AnalyzeAction: Equatable { // MARK: - Static // Xcode disables PreActions and PostActions for Analyze actions, so this Action @@ -35,7 +35,7 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: AnalyzeAction, rhs: AnalyzeAction) -> Bool { - return lhs.buildConfiguration == rhs.buildConfiguration + lhs.buildConfiguration == rhs.buildConfiguration } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+ArchiveAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ArchiveAction.swift index 92ddab9c6..73cbae330 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ArchiveAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ArchiveAction.swift @@ -2,8 +2,8 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class ArchiveAction: SerialAction { +public extension XCScheme { + final class ArchiveAction: SerialAction { // MARK: - Static private static let defaultBuildConfiguration = "Release" diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift index 2ef963934..8cc9ed15a 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift @@ -2,14 +2,14 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class BuildAction: SerialAction { +public extension XCScheme { + final class BuildAction: SerialAction { public final class Entry: Equatable { - public enum BuildFor { + public enum BuildFor: Sendable { case running, testing, profiling, archiving, analyzing - public static var `default`: [BuildFor] = [.running, .testing, .archiving, .analyzing] - public static var indexing: [BuildFor] = [.testing, .analyzing, .archiving] - public static var testOnly: [BuildFor] = [.testing, .analyzing] + public static let `default`: [BuildFor] = [.running, .testing, .archiving, .analyzing] + public static let indexing: [BuildFor] = [.testing, .analyzing, .archiving] + public static let testOnly: [BuildFor] = [.testing, .analyzing] } // MARK: - Attributes @@ -65,16 +65,49 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: Entry, rhs: Entry) -> Bool { - return lhs.buildableReference == rhs.buildableReference && + lhs.buildableReference == rhs.buildableReference && lhs.buildFor == rhs.buildFor } } + public enum Architectures { + case matchRunDestination + case universal + case useTargetSettings + + fileprivate var xmlString: String? { + switch self { + case .matchRunDestination: + "Automatic" + case .universal: + "All" + case .useTargetSettings: + nil + } + } + + /// Creates a new instance from the given xml string. + /// + /// For undefined value, initialized as `useTargetSettings` since the XML element is removed. + fileprivate init(_ xmlString: String) { + switch xmlString { + case "Automatic": + self = .matchRunDestination + case "All": + self = .universal + default: + self = .useTargetSettings + } + } + } + // MARK: - Attributes public var buildActionEntries: [Entry] public var parallelizeBuild: Bool public var buildImplicitDependencies: Bool + public var runPostActionsOnFailure: Bool? + public var buildArchitectures: Architectures // MARK: - Init @@ -82,26 +115,32 @@ extension XCScheme { preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], parallelizeBuild: Bool = false, - buildImplicitDependencies: Bool = false) { + buildImplicitDependencies: Bool = false, + runPostActionsOnFailure: Bool? = nil, + buildArchitectures: Architectures = .useTargetSettings) { self.buildActionEntries = buildActionEntries self.parallelizeBuild = parallelizeBuild self.buildImplicitDependencies = buildImplicitDependencies + self.runPostActionsOnFailure = runPostActionsOnFailure + self.buildArchitectures = buildArchitectures super.init(preActions, postActions) } override init(element: AEXMLElement) throws { parallelizeBuild = element.attributes["parallelizeBuildables"].map { $0 == "YES" } ?? true buildImplicitDependencies = element.attributes["buildImplicitDependencies"].map { $0 == "YES" } ?? true + runPostActionsOnFailure = element.attributes["runPostActionsOnFailure"].map { $0 == "YES" } buildActionEntries = try element["BuildActionEntries"]["BuildActionEntry"] .all? .map(Entry.init) ?? [] + buildArchitectures = element.attributes["buildArchitectures"].map { Architectures($0) } ?? .useTargetSettings try super.init(element: element) } // MARK: - Helpers public func add(buildActionEntry: Entry) -> BuildAction { - var buildActionEntries = self.buildActionEntries + var buildActionEntries = buildActionEntries buildActionEntries.append(buildActionEntry) return BuildAction(buildActionEntries: buildActionEntries, parallelizeBuild: parallelizeBuild) @@ -110,15 +149,25 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { + var attributes = [ + "parallelizeBuildables": parallelizeBuild.xmlString, + "buildImplicitDependencies": buildImplicitDependencies.xmlString, + ] + + if let buildArchitecturesXMLString = buildArchitectures.xmlString { + attributes["buildArchitectures"] = buildArchitecturesXMLString + } + + if let runPostActionsOnFailure { + attributes["runPostActionsOnFailure"] = runPostActionsOnFailure.xmlString + } + let element = AEXMLElement(name: "BuildAction", value: nil, - attributes: [ - "parallelizeBuildables": parallelizeBuild.xmlString, - "buildImplicitDependencies": buildImplicitDependencies.xmlString, - ]) + attributes: attributes) super.writeXML(parent: element) let entries = element.addChild(name: "BuildActionEntries") - buildActionEntries.forEach { entry in + for entry in buildActionEntries { entries.addChild(entry.xmlElement()) } return element @@ -131,7 +180,9 @@ extension XCScheme { return super.isEqual(to: to) && buildActionEntries == rhs.buildActionEntries && parallelizeBuild == rhs.parallelizeBuild && - buildImplicitDependencies == rhs.buildImplicitDependencies + buildImplicitDependencies == rhs.buildImplicitDependencies && + runPostActionsOnFailure == rhs.runPostActionsOnFailure && + buildArchitectures == rhs.buildArchitectures } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildableProductRunnable.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildableProductRunnable.swift index 2dffa070b..e590e8f37 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildableProductRunnable.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildableProductRunnable.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class BuildableProductRunnable: Runnable { +public extension XCScheme { + final class BuildableProductRunnable: Runnable { // MARK: - XML override func xmlElement() -> AEXMLElement { diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift index 73720fe97..c44f3806e 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class BuildableReference: Equatable { +public extension XCScheme { + final class BuildableReference: Equatable, Hashable { // MARK: - Attributes public var referencedContainer: String @@ -13,8 +13,8 @@ extension XCScheme { var string: String { switch self { - case let .reference(object): return object.value - case let .string(string): return string + case let .reference(object): object.value + case let .string(string): string } } } @@ -23,9 +23,9 @@ extension XCScheme { blueprint = .reference(object.reference) } - private var blueprint: Blueprint - public var blueprintIdentifier: String { - return blueprint.string + private var blueprint: Blueprint? + public var blueprintIdentifier: String? { + blueprint?.string } public var buildableName: String @@ -35,12 +35,24 @@ extension XCScheme { // MARK: - Init public init(referencedContainer: String, - blueprint: PBXObject, + blueprint: PBXObject?, buildableName: String, blueprintName: String, buildableIdentifier: String = "primary") { self.referencedContainer = referencedContainer - self.blueprint = .reference(blueprint.reference) + self.blueprint = blueprint.map { Blueprint.reference($0.reference) } + self.buildableName = buildableName + self.buildableIdentifier = buildableIdentifier + self.blueprintName = blueprintName + } + + public init(referencedContainer: String, + blueprintIdentifier: String?, + buildableName: String, + blueprintName: String, + buildableIdentifier: String = "primary") { + self.referencedContainer = referencedContainer + blueprint = blueprintIdentifier.map(Blueprint.string) self.buildableName = buildableName self.buildableIdentifier = buildableIdentifier self.blueprintName = blueprintName @@ -52,9 +64,6 @@ extension XCScheme { guard let buildableIdentifier = element.attributes["BuildableIdentifier"] else { throw XCSchemeError.missing(property: "BuildableIdentifier") } - guard let blueprintIdentifier = element.attributes["BlueprintIdentifier"] else { - throw XCSchemeError.missing(property: "BlueprintIdentifier") - } guard let buildableName = element.attributes["BuildableName"] else { throw XCSchemeError.missing(property: "BuildableName") } @@ -65,32 +74,45 @@ extension XCScheme { throw XCSchemeError.missing(property: "ReferencedContainer") } self.buildableIdentifier = buildableIdentifier - blueprint = .string(blueprintIdentifier) + let blueprintIdentifier = element.attributes["BlueprintIdentifier"] + blueprint = blueprintIdentifier.map(Blueprint.string) self.buildableName = buildableName self.blueprintName = blueprintName self.referencedContainer = referencedContainer } func xmlElement() -> AEXMLElement { + var attributes: [String: String] = [ + "BuildableIdentifier": buildableIdentifier, + "BuildableName": buildableName, + "BlueprintName": blueprintName, + "ReferencedContainer": referencedContainer, + ] + if let blueprintIdentifier = blueprint?.string { + attributes["BlueprintIdentifier"] = blueprintIdentifier + } return AEXMLElement(name: "BuildableReference", value: nil, - attributes: [ - "BuildableIdentifier": buildableIdentifier, - "BlueprintIdentifier": blueprint.string, - "BuildableName": buildableName, - "BlueprintName": blueprintName, - "ReferencedContainer": referencedContainer, - ]) + attributes: attributes) } // MARK: - Equatable public static func == (lhs: BuildableReference, rhs: BuildableReference) -> Bool { - return lhs.referencedContainer == rhs.referencedContainer && + lhs.referencedContainer == rhs.referencedContainer && lhs.blueprintIdentifier == rhs.blueprintIdentifier && lhs.buildableName == rhs.buildableName && lhs.blueprint == rhs.blueprint && lhs.blueprintName == rhs.blueprintName } + + // MARK: - Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(referencedContainer) + hasher.combine(blueprintIdentifier) + hasher.combine(buildableName) + hasher.combine(blueprintName) + } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+CommandLineArguments.swift b/Sources/XcodeProj/Scheme/XCScheme+CommandLineArguments.swift index 2a37fb584..862299b33 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+CommandLineArguments.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+CommandLineArguments.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class CommandLineArguments: Equatable { +public extension XCScheme { + final class CommandLineArguments: Equatable { // MARK: - Attributes public let arguments: [CommandLineArgument] @@ -30,7 +30,7 @@ extension XCScheme { func xmlElement() -> AEXMLElement { let element = AEXMLElement(name: "CommandLineArguments", value: nil) - arguments.forEach { arg in + for arg in arguments { element.addChild(arg.xmlElement()) } return element @@ -39,13 +39,13 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: CommandLineArguments, rhs: CommandLineArguments) -> Bool { - return lhs.arguments == rhs.arguments + lhs.arguments == rhs.arguments } } } -extension XCScheme.CommandLineArguments { - public struct CommandLineArgument: Equatable { +public extension XCScheme.CommandLineArguments { + struct CommandLineArgument: Equatable { // MARK: - Attributes public let name: String @@ -61,15 +61,15 @@ extension XCScheme.CommandLineArguments { // MARK: - XML func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "CommandLineArgument", - value: nil, - attributes: ["argument": name, "isEnabled": enabled ? "YES" : "NO"]) + AEXMLElement(name: "CommandLineArgument", + value: nil, + attributes: ["argument": name, "isEnabled": enabled ? "YES" : "NO"]) } // MARK: - Equatable public static func == (lhs: CommandLineArgument, rhs: CommandLineArgument) -> Bool { - return lhs.name == rhs.name && + lhs.name == rhs.name && lhs.enabled == rhs.enabled } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+EnvironmentVariable.swift b/Sources/XcodeProj/Scheme/XCScheme+EnvironmentVariable.swift index 00f566108..7fe613ee0 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+EnvironmentVariable.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+EnvironmentVariable.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public struct EnvironmentVariable: Equatable { +public extension XCScheme { + struct EnvironmentVariable: Equatable { // MARK: - Attributes public let variable: String @@ -20,13 +20,13 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "EnvironmentVariable", - value: nil, - attributes: ["key": variable, "value": value, "isEnabled": enabled ? "YES" : "NO"]) + AEXMLElement(name: "EnvironmentVariable", + value: nil, + attributes: ["key": variable, "value": value, "isEnabled": enabled ? "YES" : "NO"]) } static func parseVariables(from element: AEXMLElement) throws -> [EnvironmentVariable] { - return try element.children.map { elt in + try element.children.map { elt in guard let variableKey = elt.attributes["key"] else { throw XCSchemeError.missing(property: "key") } @@ -44,7 +44,7 @@ extension XCScheme { static func xmlElement(from variables: [EnvironmentVariable]) -> AEXMLElement { let element = AEXMLElement(name: "EnvironmentVariables", value: nil) - variables.forEach { arg in + for arg in variables { element.addChild(arg.xmlElement()) } @@ -54,7 +54,7 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: EnvironmentVariable, rhs: EnvironmentVariable) -> Bool { - return lhs.variable == rhs.variable && + lhs.variable == rhs.variable && lhs.value == rhs.value && lhs.enabled == rhs.enabled } diff --git a/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift index 59837bec1..2a9aed87b 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift @@ -1,27 +1,37 @@ import AEXML import Foundation -extension XCScheme { - public final class ExecutionAction: Equatable { +public extension XCScheme { + final class ExecutionAction: Equatable { private static let ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction" // MARK: - Attributes public var title: String public var scriptText: String + public var shellToInvoke: String? public var environmentBuildable: BuildableReference? // MARK: - Init - public init(scriptText: String, title: String = "Run Script", environmentBuildable: BuildableReference? = nil) { + public init( + scriptText: String, + title: String = "Run Script", + shellToInvoke: String? = nil, + environmentBuildable: BuildableReference? = nil + ) { self.scriptText = scriptText self.title = title + self.shellToInvoke = shellToInvoke self.environmentBuildable = environmentBuildable } init(element: AEXMLElement) throws { scriptText = element["ActionContent"].attributes["scriptText"] ?? "" title = element["ActionContent"].attributes["title"] ?? "Run Script" + if let shellToInvoke = element["ActionContent"].attributes["shellToInvoke"] { + self.shellToInvoke = shellToInvoke + } environmentBuildable = try? BuildableReference(element: element["ActionContent"]["EnvironmentBuildable"]["BuildableReference"]) } @@ -31,14 +41,18 @@ extension XCScheme { let element = AEXMLElement(name: "ExecutionAction", value: nil, attributes: ["ActionType": ExecutionAction.ActionType]) + var attributes = [ + "title": title, + "scriptText": scriptText, + ] + if let shellToInvoke { + attributes["shellToInvoke"] = shellToInvoke + } let content = AEXMLElement(name: "ActionContent", value: nil, - attributes: [ - "title": title, - "scriptText": scriptText, - ]) + attributes: attributes) element.addChild(content) - if let environmentBuildable = environmentBuildable { + if let environmentBuildable { let environment = content.addChild(name: "EnvironmentBuildable") environment.addChild(environmentBuildable.xmlElement()) } @@ -48,7 +62,7 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: ExecutionAction, rhs: ExecutionAction) -> Bool { - return lhs.title == rhs.title && + lhs.title == rhs.title && lhs.scriptText == rhs.scriptText && lhs.environmentBuildable == rhs.environmentBuildable } diff --git a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift index afdcb6e76..5f73777bc 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift @@ -2,27 +2,29 @@ import AEXML import Foundation import PathKit -extension XCScheme { +public extension XCScheme { // swiftlint:disable:next type_body_length - public final class LaunchAction: SerialAction { - public enum Style: String { + final class LaunchAction: SerialAction { + public enum Style: String, Sendable { case auto = "0" case wait = "1" case custom = "2" } - public enum GPUFrameCaptureMode: String { + public enum GPUFrameCaptureMode: String, Sendable { case autoEnabled = "0" case metal = "1" case openGL = "2" case disabled = "3" } - public enum GPUValidationMode: String { - case enabled = "0" - case disabled = "1" - case extended = "2" - } + // The value used to disable 'API Validation'. + // If this feature is not being disabled, this value will not be present. + public let gpuValidationModeDisableValue = "1" + + // The value used to enable 'Shader Validation'. + // If this feature is not being enabled, this value will not be present. + public let gpuShaderValidationModeEnableValue = "2" // MARK: - Static @@ -30,7 +32,6 @@ extension XCScheme { public static let defaultDebugServiceExtension = "internal" private static let defaultLaunchStyle = Style.auto public static let defaultGPUFrameCaptureMode = GPUFrameCaptureMode.autoEnabled - public static let defaultGPUValidationMode = GPUValidationMode.enabled // MARK: - Attributes @@ -40,6 +41,18 @@ extension XCScheme { public var selectedLauncherIdentifier: String public var buildConfiguration: String public var launchStyle: Style + public var askForAppToLaunch: Bool? + public var pathRunnable: PathRunnable? { + // For backwards compatibility + get { + runnable as? PathRunnable + } + set { + runnable = newValue + } + } + + public var customWorkingDirectory: String? public var useCustomWorkingDirectory: Bool public var ignoresPersistentStateOnLaunch: Bool public var debugDocumentVersioning: Bool @@ -47,7 +60,10 @@ extension XCScheme { public var allowLocationSimulation: Bool public var locationScenarioReference: LocationScenarioReference? public var enableGPUFrameCaptureMode: GPUFrameCaptureMode - public var enableGPUValidationMode: GPUValidationMode + public var disableGPUValidationMode: Bool + public var enableGPUShaderValidationMode: Bool + public var showGraphicsOverview: Bool + public var logGraphicsOverview: Bool public var enableAddressSanitizer: Bool public var enableASanStackUseAfterReturn: Bool public var enableThreadSanitizer: Bool @@ -55,15 +71,21 @@ extension XCScheme { public var enableUBSanitizer: Bool public var stopOnEveryUBSanitizerIssue: Bool public var disableMainThreadChecker: Bool + public var disablePerformanceAntipatternChecker: Bool public var stopOnEveryMainThreadCheckerIssue: Bool public var additionalOptions: [AdditionalOption] public var commandlineArguments: CommandLineArguments? public var environmentVariables: [EnvironmentVariable]? public var language: String? public var region: String? + public var showNonLocalizedStrings: Bool public var launchAutomaticallySubstyle: String? + public var storeKitConfigurationFileReference: StoreKitConfigurationFileReference? // To enable the option in Xcode: defaults write com.apple.dt.Xcode IDEDebuggerFeatureSetting 12 public var customLaunchCommand: String? + public var customLLDBInitFile: String? + public var appClipInvocationURLString: String? + public var debugAsWhichUser: String? // MARK: - Init @@ -75,6 +97,8 @@ extension XCScheme { selectedDebuggerIdentifier: String = XCScheme.defaultDebugger, selectedLauncherIdentifier: String = XCScheme.defaultLauncher, launchStyle: Style = .auto, + askForAppToLaunch: Bool? = nil, + customWorkingDirectory: String? = nil, useCustomWorkingDirectory: Bool = false, ignoresPersistentStateOnLaunch: Bool = false, debugDocumentVersioning: Bool = true, @@ -82,7 +106,10 @@ extension XCScheme { allowLocationSimulation: Bool = true, locationScenarioReference: LocationScenarioReference? = nil, enableGPUFrameCaptureMode: GPUFrameCaptureMode = LaunchAction.defaultGPUFrameCaptureMode, - enableGPUValidationMode: GPUValidationMode = LaunchAction.defaultGPUValidationMode, + disableGPUValidationMode: Bool = false, + enableGPUShaderValidationMode: Bool = false, + showGraphicsOverview: Bool = false, + logGraphicsOverview: Bool = false, enableAddressSanitizer: Bool = false, enableASanStackUseAfterReturn: Bool = false, enableThreadSanitizer: Bool = false, @@ -90,20 +117,28 @@ extension XCScheme { enableUBSanitizer: Bool = false, stopOnEveryUBSanitizerIssue: Bool = false, disableMainThreadChecker: Bool = false, + disablePerformanceAntipatternChecker: Bool = false, stopOnEveryMainThreadCheckerIssue: Bool = false, additionalOptions: [AdditionalOption] = [], commandlineArguments: CommandLineArguments? = nil, environmentVariables: [EnvironmentVariable]? = nil, language: String? = nil, region: String? = nil, + showNonLocalizedStrings: Bool = false, launchAutomaticallySubstyle: String? = nil, - customLaunchCommand: String? = nil) { + storeKitConfigurationFileReference: StoreKitConfigurationFileReference? = nil, + customLaunchCommand: String? = nil, + customLLDBInitFile: String? = nil, + appClipInvocationURLString: String? = nil, + debugAsWhichUser: String? = nil) { self.runnable = runnable self.macroExpansion = macroExpansion self.buildConfiguration = buildConfiguration self.launchStyle = launchStyle self.selectedDebuggerIdentifier = selectedDebuggerIdentifier self.selectedLauncherIdentifier = selectedLauncherIdentifier + self.askForAppToLaunch = askForAppToLaunch + self.customWorkingDirectory = customWorkingDirectory self.useCustomWorkingDirectory = useCustomWorkingDirectory self.ignoresPersistentStateOnLaunch = ignoresPersistentStateOnLaunch self.debugDocumentVersioning = debugDocumentVersioning @@ -111,7 +146,10 @@ extension XCScheme { self.allowLocationSimulation = allowLocationSimulation self.locationScenarioReference = locationScenarioReference self.enableGPUFrameCaptureMode = enableGPUFrameCaptureMode - self.enableGPUValidationMode = enableGPUValidationMode + self.disableGPUValidationMode = disableGPUValidationMode + self.enableGPUShaderValidationMode = enableGPUShaderValidationMode + self.showGraphicsOverview = showGraphicsOverview + self.logGraphicsOverview = logGraphicsOverview self.enableAddressSanitizer = enableAddressSanitizer self.enableASanStackUseAfterReturn = enableASanStackUseAfterReturn self.enableThreadSanitizer = enableThreadSanitizer @@ -119,23 +157,122 @@ extension XCScheme { self.enableUBSanitizer = enableUBSanitizer self.stopOnEveryUBSanitizerIssue = stopOnEveryUBSanitizerIssue self.disableMainThreadChecker = disableMainThreadChecker + self.disablePerformanceAntipatternChecker = disablePerformanceAntipatternChecker self.stopOnEveryMainThreadCheckerIssue = stopOnEveryMainThreadCheckerIssue self.additionalOptions = additionalOptions self.commandlineArguments = commandlineArguments self.environmentVariables = environmentVariables self.language = language self.region = region + self.showNonLocalizedStrings = showNonLocalizedStrings self.launchAutomaticallySubstyle = launchAutomaticallySubstyle + self.storeKitConfigurationFileReference = storeKitConfigurationFileReference self.customLaunchCommand = customLaunchCommand + self.customLLDBInitFile = customLLDBInitFile + self.appClipInvocationURLString = appClipInvocationURLString + self.debugAsWhichUser = debugAsWhichUser super.init(preActions, postActions) } + @available(*, deprecated, message: "Use the init() that consolidates pathRunnable and runnable into a single parameter.") + public convenience init( + runnable: Runnable?, + buildConfiguration: String, + preActions: [ExecutionAction] = [], + postActions: [ExecutionAction] = [], + macroExpansion: BuildableReference? = nil, + selectedDebuggerIdentifier: String = XCScheme.defaultDebugger, + selectedLauncherIdentifier: String = XCScheme.defaultLauncher, + launchStyle: Style = .auto, + askForAppToLaunch: Bool? = nil, + pathRunnable: PathRunnable?, + customWorkingDirectory: String? = nil, + useCustomWorkingDirectory: Bool = false, + ignoresPersistentStateOnLaunch: Bool = false, + debugDocumentVersioning: Bool = true, + debugServiceExtension: String = LaunchAction.defaultDebugServiceExtension, + allowLocationSimulation: Bool = true, + locationScenarioReference: LocationScenarioReference? = nil, + enableGPUFrameCaptureMode: GPUFrameCaptureMode = LaunchAction.defaultGPUFrameCaptureMode, + disableGPUValidationMode: Bool = false, + enableGPUShaderValidationMode: Bool = false, + showGraphicsOverview: Bool = false, + logGraphicsOverview: Bool = false, + enableAddressSanitizer: Bool = false, + enableASanStackUseAfterReturn: Bool = false, + enableThreadSanitizer: Bool = false, + stopOnEveryThreadSanitizerIssue: Bool = false, + enableUBSanitizer: Bool = false, + stopOnEveryUBSanitizerIssue: Bool = false, + disableMainThreadChecker: Bool = false, + disablePerformanceAntipatternChecker: Bool = false, + stopOnEveryMainThreadCheckerIssue: Bool = false, + additionalOptions: [AdditionalOption] = [], + commandlineArguments: CommandLineArguments? = nil, + environmentVariables: [EnvironmentVariable]? = nil, + language: String? = nil, + region: String? = nil, + showNonLocalizedStrings: Bool = false, + launchAutomaticallySubstyle: String? = nil, + storeKitConfigurationFileReference: StoreKitConfigurationFileReference? = nil, + customLaunchCommand: String? = nil, + customLLDBInitFile: String? = nil, + appClipInvocationURLString: String? = nil, + debugAsWhichUser: String? = nil + ) { + self.init( + runnable: pathRunnable, + buildConfiguration: buildConfiguration, + preActions: preActions, + postActions: postActions, + macroExpansion: macroExpansion, + selectedDebuggerIdentifier: selectedDebuggerIdentifier, + selectedLauncherIdentifier: selectedLauncherIdentifier, + launchStyle: launchStyle, + askForAppToLaunch: askForAppToLaunch, + customWorkingDirectory: customWorkingDirectory, + useCustomWorkingDirectory: useCustomWorkingDirectory, + ignoresPersistentStateOnLaunch: ignoresPersistentStateOnLaunch, + debugDocumentVersioning: debugDocumentVersioning, + debugServiceExtension: debugServiceExtension, + allowLocationSimulation: allowLocationSimulation, + locationScenarioReference: locationScenarioReference, + enableGPUFrameCaptureMode: enableGPUFrameCaptureMode, + disableGPUValidationMode: disableGPUValidationMode, + enableGPUShaderValidationMode: enableGPUShaderValidationMode, + showGraphicsOverview: showGraphicsOverview, + logGraphicsOverview: logGraphicsOverview, + enableAddressSanitizer: enableAddressSanitizer, + enableASanStackUseAfterReturn: enableASanStackUseAfterReturn, + enableThreadSanitizer: enableThreadSanitizer, + stopOnEveryThreadSanitizerIssue: stopOnEveryThreadSanitizerIssue, + enableUBSanitizer: enableUBSanitizer, + stopOnEveryUBSanitizerIssue: stopOnEveryUBSanitizerIssue, + disableMainThreadChecker: disableMainThreadChecker, + disablePerformanceAntipatternChecker: disablePerformanceAntipatternChecker, + stopOnEveryMainThreadCheckerIssue: stopOnEveryMainThreadCheckerIssue, + additionalOptions: additionalOptions, + commandlineArguments: commandlineArguments, + environmentVariables: environmentVariables, + language: language, + region: region, + showNonLocalizedStrings: showNonLocalizedStrings, + launchAutomaticallySubstyle: launchAutomaticallySubstyle, + storeKitConfigurationFileReference: storeKitConfigurationFileReference, + customLaunchCommand: customLaunchCommand, + customLLDBInitFile: customLLDBInitFile, + appClipInvocationURLString: appClipInvocationURLString, + debugAsWhichUser: debugAsWhichUser + ) + } + // swiftlint:disable:next function_body_length override init(element: AEXMLElement) throws { buildConfiguration = element.attributes["buildConfiguration"] ?? LaunchAction.defaultBuildConfiguration selectedDebuggerIdentifier = element.attributes["selectedDebuggerIdentifier"] ?? XCScheme.defaultDebugger selectedLauncherIdentifier = element.attributes["selectedLauncherIdentifier"] ?? XCScheme.defaultLauncher launchStyle = element.attributes["launchStyle"].flatMap { Style(rawValue: $0) } ?? .auto + askForAppToLaunch = element.attributes["askForAppToLaunch"].map { $0 == "YES" || $0 == "Yes" } useCustomWorkingDirectory = element.attributes["useCustomWorkingDirectory"] == "YES" ignoresPersistentStateOnLaunch = element.attributes["ignoresPersistentStateOnLaunch"] == "YES" debugDocumentVersioning = element.attributes["debugDocumentVersioning"].map { $0 == "YES" } ?? true @@ -145,10 +282,13 @@ extension XCScheme { // Runnable let buildableProductRunnableElement = element["BuildableProductRunnable"] let remoteRunnableElement = element["RemoteRunnable"] + let pathRunnable = element["PathRunnable"] if buildableProductRunnableElement.error == nil { runnable = try BuildableProductRunnable(element: buildableProductRunnableElement) } else if remoteRunnableElement.error == nil { runnable = try RemoteRunnable(element: remoteRunnableElement) + } else if pathRunnable.error == nil { + runnable = try PathRunnable(element: pathRunnable) } let buildableReferenceElement = element["MacroExpansion"]["BuildableReference"] @@ -164,8 +304,10 @@ extension XCScheme { enableGPUFrameCaptureMode = element.attributes["enableGPUFrameCaptureMode"] .flatMap { GPUFrameCaptureMode(rawValue: $0) } ?? LaunchAction.defaultGPUFrameCaptureMode - enableGPUValidationMode = element.attributes["enableGPUValidationMode"] - .flatMap { GPUValidationMode(rawValue: $0) } ?? LaunchAction.defaultGPUValidationMode + disableGPUValidationMode = element.attributes["enableGPUValidationMode"] == gpuValidationModeDisableValue + enableGPUShaderValidationMode = element.attributes["enableGPUShaderValidationMode"] == gpuShaderValidationModeEnableValue + showGraphicsOverview = element.attributes["showGraphicsOverview"] == "Yes" + logGraphicsOverview = element.attributes["logGraphicsOverview"] == "Yes" enableAddressSanitizer = element.attributes["enableAddressSanitizer"] == "YES" enableASanStackUseAfterReturn = element.attributes["enableASanStackUseAfterReturn"] == "YES" enableThreadSanitizer = element.attributes["enableThreadSanitizer"] == "YES" @@ -173,6 +315,7 @@ extension XCScheme { enableUBSanitizer = element.attributes["enableUBSanitizer"] == "YES" stopOnEveryUBSanitizerIssue = element.attributes["stopOnEveryUBSanitizerIssue"] == "YES" disableMainThreadChecker = element.attributes["disableMainThreadChecker"] == "YES" + disablePerformanceAntipatternChecker = element.attributes["disablePerformanceAntipatternChecker"] == "YES" stopOnEveryMainThreadCheckerIssue = element.attributes["stopOnEveryMainThreadCheckerIssue"] == "YES" additionalOptions = try element["AdditionalOptions"]["AdditionalOption"] @@ -191,8 +334,22 @@ extension XCScheme { language = element.attributes["language"] region = element.attributes["region"] + showNonLocalizedStrings = element.attributes["showNonLocalizedStrings"] == "YES" launchAutomaticallySubstyle = element.attributes["launchAutomaticallySubstyle"] + + if element["StoreKitConfigurationFileReference"].all?.first != nil { + storeKitConfigurationFileReference = try StoreKitConfigurationFileReference(element: element["StoreKitConfigurationFileReference"]) + } else { + storeKitConfigurationFileReference = nil + } customLaunchCommand = element.attributes["customLaunchCommand"] + customLLDBInitFile = element.attributes["customLLDBInitFile"] + if let elementCustomWorkingDirectory: String = element.attributes["customWorkingDirectory"] { + customWorkingDirectory = elementCustomWorkingDirectory + } + + appClipInvocationURLString = element.attributes["appClipInvocationURLString"] + debugAsWhichUser = element.attributes["debugAsWhichUser"] try super.init(element: element) } @@ -212,11 +369,23 @@ extension XCScheme { "allowLocationSimulation": allowLocationSimulation.xmlString, ] + if let askForAppToLaunch { + attributes["askForAppToLaunch"] = askForAppToLaunch.xmlString + } if enableGPUFrameCaptureMode != LaunchAction.defaultGPUFrameCaptureMode { attributes["enableGPUFrameCaptureMode"] = enableGPUFrameCaptureMode.rawValue } - if enableGPUValidationMode != LaunchAction.defaultGPUValidationMode { - attributes["enableGPUValidationMode"] = enableGPUValidationMode.rawValue + if disableGPUValidationMode { + attributes["enableGPUValidationMode"] = gpuValidationModeDisableValue + } + if enableGPUShaderValidationMode { + attributes["enableGPUShaderValidationMode"] = gpuShaderValidationModeEnableValue + } + if showGraphicsOverview { + attributes["showGraphicsOverview"] = "Yes" + } + if logGraphicsOverview { + attributes["logGraphicsOverview"] = "Yes" } if enableAddressSanitizer { attributes["enableAddressSanitizer"] = enableAddressSanitizer.xmlString @@ -239,9 +408,21 @@ extension XCScheme { if disableMainThreadChecker { attributes["disableMainThreadChecker"] = disableMainThreadChecker.xmlString } + if disablePerformanceAntipatternChecker { + attributes["disablePerformanceAntipatternChecker"] = disablePerformanceAntipatternChecker.xmlString + } if stopOnEveryMainThreadCheckerIssue { attributes["stopOnEveryMainThreadCheckerIssue"] = stopOnEveryMainThreadCheckerIssue.xmlString } + if let customWorkingDirectory { + attributes["customWorkingDirectory"] = customWorkingDirectory + } + if let appClipInvocationURLString { + attributes["appClipInvocationURLString"] = appClipInvocationURLString + } + if let debugAsWhichUser { + attributes["debugAsWhichUser"] = debugAsWhichUser + } return attributes } @@ -251,46 +432,62 @@ extension XCScheme { value: nil, attributes: xmlAttributes) super.writeXML(parent: element) - if let runnable = runnable { + if let runnable { element.addChild(runnable.xmlElement()) } - if let locationScenarioReference = locationScenarioReference { + if let locationScenarioReference { element.addChild(locationScenarioReference.xmlElement()) } - if let macroExpansion = macroExpansion { + if let macroExpansion { let macro = element.addChild(name: "MacroExpansion") macro.addChild(macroExpansion.xmlElement()) } - if let commandlineArguments = commandlineArguments { + if let commandlineArguments { element.addChild(commandlineArguments.xmlElement()) } - if let environmentVariables = environmentVariables { + if let environmentVariables { element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) } - if let language = language { + if let language { element.attributes["language"] = language } - if let region = region { + if let region { element.attributes["region"] = region } - if let launchAutomaticallySubstyle = launchAutomaticallySubstyle { + + if showNonLocalizedStrings { + element.attributes["showNonLocalizedStrings"] = showNonLocalizedStrings.xmlString + } + + if let launchAutomaticallySubstyle { element.attributes["launchAutomaticallySubstyle"] = launchAutomaticallySubstyle } - if let customLaunchCommand = customLaunchCommand { + if let storeKitConfigurationFileReference { + element.addChild(storeKitConfigurationFileReference.xmlElement()) + } + + if let customLaunchCommand { element.attributes["customLaunchCommand"] = customLaunchCommand } - let additionalOptionsElement = element.addChild(AEXMLElement(name: "AdditionalOptions")) - additionalOptions.forEach { additionalOption in - additionalOptionsElement.addChild(additionalOption.xmlElement()) + if let customLLDBInitFile { + element.attributes["customLLDBInitFile"] = customLLDBInitFile } + + if !additionalOptions.isEmpty { + let additionalOptionsElement = element.addChild(AEXMLElement(name: "AdditionalOptions")) + for additionalOption in additionalOptions { + additionalOptionsElement.addChild(additionalOption.xmlElement()) + } + } + return element } @@ -305,6 +502,8 @@ extension XCScheme { selectedLauncherIdentifier == rhs.selectedLauncherIdentifier && buildConfiguration == rhs.buildConfiguration && launchStyle == rhs.launchStyle && + askForAppToLaunch == rhs.askForAppToLaunch && + customWorkingDirectory == rhs.customWorkingDirectory && useCustomWorkingDirectory == rhs.useCustomWorkingDirectory && ignoresPersistentStateOnLaunch == rhs.ignoresPersistentStateOnLaunch && debugDocumentVersioning == rhs.debugDocumentVersioning && @@ -312,7 +511,10 @@ extension XCScheme { allowLocationSimulation == rhs.allowLocationSimulation && locationScenarioReference == rhs.locationScenarioReference && enableGPUFrameCaptureMode == rhs.enableGPUFrameCaptureMode && - enableGPUValidationMode == rhs.enableGPUValidationMode && + disableGPUValidationMode == rhs.disableGPUValidationMode && + enableGPUShaderValidationMode == rhs.enableGPUShaderValidationMode && + showGraphicsOverview == rhs.showGraphicsOverview && + logGraphicsOverview == rhs.logGraphicsOverview && enableAddressSanitizer == rhs.enableAddressSanitizer && enableASanStackUseAfterReturn == rhs.enableASanStackUseAfterReturn && enableThreadSanitizer == rhs.enableThreadSanitizer && @@ -320,6 +522,7 @@ extension XCScheme { enableUBSanitizer == rhs.enableUBSanitizer && stopOnEveryUBSanitizerIssue == rhs.stopOnEveryUBSanitizerIssue && disableMainThreadChecker == rhs.disableMainThreadChecker && + disablePerformanceAntipatternChecker == rhs.disablePerformanceAntipatternChecker && stopOnEveryMainThreadCheckerIssue == rhs.stopOnEveryMainThreadCheckerIssue && additionalOptions == rhs.additionalOptions && commandlineArguments == rhs.commandlineArguments && @@ -327,7 +530,11 @@ extension XCScheme { language == rhs.language && region == rhs.region && launchAutomaticallySubstyle == rhs.launchAutomaticallySubstyle && - customLaunchCommand == rhs.customLaunchCommand + storeKitConfigurationFileReference == rhs.storeKitConfigurationFileReference && + customLaunchCommand == rhs.customLaunchCommand && + customLLDBInitFile == rhs.customLLDBInitFile && + appClipInvocationURLString == rhs.appClipInvocationURLString && + debugAsWhichUser == rhs.debugAsWhichUser } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+LocationScenarioReference.swift b/Sources/XcodeProj/Scheme/XCScheme+LocationScenarioReference.swift index eac6a4993..64bb69dbd 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+LocationScenarioReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+LocationScenarioReference.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class LocationScenarioReference: Equatable { +public extension XCScheme { + final class LocationScenarioReference: Equatable { // MARK: - Attributes public var identifier: String @@ -23,18 +23,18 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "LocationScenarioReference", - value: nil, - attributes: [ - "identifier": identifier, - "referenceType": referenceType, - ]) + AEXMLElement(name: "LocationScenarioReference", + value: nil, + attributes: [ + "identifier": identifier, + "referenceType": referenceType, + ]) } // MARK: - Equatable public static func == (lhs: LocationScenarioReference, rhs: LocationScenarioReference) -> Bool { - return lhs.identifier == rhs.identifier && + lhs.identifier == rhs.identifier && lhs.referenceType == rhs.referenceType } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+PathRunnable.swift b/Sources/XcodeProj/Scheme/XCScheme+PathRunnable.swift new file mode 100644 index 000000000..44296ea24 --- /dev/null +++ b/Sources/XcodeProj/Scheme/XCScheme+PathRunnable.swift @@ -0,0 +1,49 @@ +import AEXML +import Foundation +import PathKit + +public extension XCScheme { + class PathRunnable: Runnable { + // MARK: - Attributes + + public var filePath: String + + // MARK: - Init + + public init(filePath: String, + runnableDebuggingMode: String = "0") { + self.filePath = filePath + super.init(buildableReference: nil, + runnableDebuggingMode: runnableDebuggingMode) + } + + override init(element: AEXMLElement) throws { + filePath = element.attributes["FilePath"] ?? "" + try super.init(element: element) + } + + // MARK: - XML + + override func xmlElement() -> AEXMLElement { + let element = super.xmlElement() + element.name = "PathRunnable" + element.attributes["FilePath"] = filePath + return element + } + + // MARK: - Equatable + + override func isEqual(other: XCScheme.Runnable) -> Bool { + guard let other = other as? PathRunnable else { + return false + } + + return super.isEqual(other: other) && + filePath == other.filePath + } + + public static func == (lhs: PathRunnable, rhs: PathRunnable) -> Bool { + lhs.isEqual(other: rhs) + } + } +} diff --git a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift index 5b6ee2768..f972cf76a 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift @@ -2,29 +2,37 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class ProfileAction: SerialAction { +public extension XCScheme { + final class ProfileAction: SerialAction { // MARK: - Static private static let defaultBuildConfiguration = "Release" // MARK: - Attributes - public var buildableProductRunnable: BuildableProductRunnable? + public var runnable: Runnable? + public var buildableProductRunnable: BuildableProductRunnable? { + // For backwards compatibility - can be removed in the next major version + runnable as? BuildableProductRunnable + } + public var buildConfiguration: String public var shouldUseLaunchSchemeArgsEnv: Bool public var savedToolIdentifier: String public var ignoresPersistentStateOnLaunch: Bool + public var customWorkingDirectory: String? public var useCustomWorkingDirectory: Bool public var debugDocumentVersioning: Bool + public var askForAppToLaunch: Bool? public var commandlineArguments: CommandLineArguments? public var environmentVariables: [EnvironmentVariable]? public var macroExpansion: BuildableReference? public var enableTestabilityWhenProfilingTests: Bool + public var launchAutomaticallySubstyle: String? // MARK: - Init - public init(buildableProductRunnable: BuildableProductRunnable?, + public init(runnable: Runnable?, buildConfiguration: String, preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], @@ -32,37 +40,90 @@ extension XCScheme { shouldUseLaunchSchemeArgsEnv: Bool = true, savedToolIdentifier: String = "", ignoresPersistentStateOnLaunch: Bool = false, + customWorkingDirectory: String? = nil, useCustomWorkingDirectory: Bool = false, debugDocumentVersioning: Bool = true, + askForAppToLaunch: Bool? = nil, commandlineArguments: CommandLineArguments? = nil, environmentVariables: [EnvironmentVariable]? = nil, - enableTestabilityWhenProfilingTests: Bool = true) { - self.buildableProductRunnable = buildableProductRunnable + enableTestabilityWhenProfilingTests: Bool = true, + launchAutomaticallySubstyle: String? = nil) { + self.runnable = runnable self.buildConfiguration = buildConfiguration self.macroExpansion = macroExpansion self.shouldUseLaunchSchemeArgsEnv = shouldUseLaunchSchemeArgsEnv self.savedToolIdentifier = savedToolIdentifier + self.customWorkingDirectory = customWorkingDirectory self.useCustomWorkingDirectory = useCustomWorkingDirectory self.debugDocumentVersioning = debugDocumentVersioning + self.askForAppToLaunch = askForAppToLaunch self.commandlineArguments = commandlineArguments self.environmentVariables = environmentVariables self.ignoresPersistentStateOnLaunch = ignoresPersistentStateOnLaunch self.enableTestabilityWhenProfilingTests = enableTestabilityWhenProfilingTests + self.launchAutomaticallySubstyle = launchAutomaticallySubstyle super.init(preActions, postActions) } + public convenience init( + buildableProductRunnable: Runnable?, + buildConfiguration: String, + preActions: [ExecutionAction] = [], + postActions: [ExecutionAction] = [], + macroExpansion: BuildableReference? = nil, + shouldUseLaunchSchemeArgsEnv: Bool = true, + savedToolIdentifier: String = "", + ignoresPersistentStateOnLaunch: Bool = false, + customWorkingDirectory: String? = nil, + useCustomWorkingDirectory: Bool = false, + debugDocumentVersioning: Bool = true, + askForAppToLaunch: Bool? = nil, + commandlineArguments: CommandLineArguments? = nil, + environmentVariables: [EnvironmentVariable]? = nil, + enableTestabilityWhenProfilingTests: Bool = true, + launchAutomaticallySubstyle: String? = nil + ) { + self.init( + runnable: buildableProductRunnable, + buildConfiguration: buildConfiguration, + preActions: preActions, + postActions: postActions, + macroExpansion: macroExpansion, + shouldUseLaunchSchemeArgsEnv: shouldUseLaunchSchemeArgsEnv, + savedToolIdentifier: savedToolIdentifier, + ignoresPersistentStateOnLaunch: ignoresPersistentStateOnLaunch, + customWorkingDirectory: customWorkingDirectory, + useCustomWorkingDirectory: useCustomWorkingDirectory, + debugDocumentVersioning: debugDocumentVersioning, + askForAppToLaunch: askForAppToLaunch, + commandlineArguments: commandlineArguments, + environmentVariables: environmentVariables, + enableTestabilityWhenProfilingTests: enableTestabilityWhenProfilingTests, + launchAutomaticallySubstyle: launchAutomaticallySubstyle + ) + } + override init(element: AEXMLElement) throws { buildConfiguration = element.attributes["buildConfiguration"] ?? ProfileAction.defaultBuildConfiguration shouldUseLaunchSchemeArgsEnv = element.attributes["shouldUseLaunchSchemeArgsEnv"].map { $0 == "YES" } ?? true savedToolIdentifier = element.attributes["savedToolIdentifier"] ?? "" useCustomWorkingDirectory = element.attributes["useCustomWorkingDirectory"] == "YES" debugDocumentVersioning = element.attributes["debugDocumentVersioning"].map { $0 == "YES" } ?? true + askForAppToLaunch = element.attributes["askForAppToLaunch"].map { $0 == "YES" || $0 == "Yes" } ignoresPersistentStateOnLaunch = element.attributes["ignoresPersistentStateOnLaunch"].map { $0 == "YES" } ?? false + // Runnable let buildableProductRunnableElement = element["BuildableProductRunnable"] + let remoteRunnableElement = element["RemoteRunnable"] + let pathRunnableElement = element["PathRunnable"] if buildableProductRunnableElement.error == nil { - buildableProductRunnable = try BuildableProductRunnable(element: buildableProductRunnableElement) + runnable = try BuildableProductRunnable(element: buildableProductRunnableElement) + } else if remoteRunnableElement.error == nil { + runnable = try RemoteRunnable(element: remoteRunnableElement) + } else if pathRunnableElement.error == nil { + runnable = try PathRunnable(element: pathRunnableElement) } + let buildableReferenceElement = element["MacroExpansion"]["BuildableReference"] if buildableReferenceElement.error == nil { macroExpansion = try BuildableReference(element: buildableReferenceElement) @@ -76,6 +137,10 @@ extension XCScheme { self.environmentVariables = try EnvironmentVariable.parseVariables(from: environmentVariables) } enableTestabilityWhenProfilingTests = element.attributes["enableTestabilityWhenProfilingTests"].map { $0 != "No" } ?? true + launchAutomaticallySubstyle = element.attributes["launchAutomaticallySubstyle"] + if let elementCustomWorkingDirectory: String = element.attributes["customWorkingDirectory"] { + customWorkingDirectory = elementCustomWorkingDirectory + } try super.init(element: element) } @@ -92,23 +157,32 @@ extension XCScheme { "debugDocumentVersioning": debugDocumentVersioning.xmlString, ]) super.writeXML(parent: element) + if let runnable { + element.addChild(runnable.xmlElement()) + } + if let askForAppToLaunch { + element.attributes["askForAppToLaunch"] = askForAppToLaunch.xmlString + } if ignoresPersistentStateOnLaunch { element.attributes["ignoresPersistentStateOnLaunch"] = ignoresPersistentStateOnLaunch.xmlString } if !enableTestabilityWhenProfilingTests { element.attributes["enableTestabilityWhenProfilingTests"] = "No" } - if let buildableProductRunnable = buildableProductRunnable { - element.addChild(buildableProductRunnable.xmlElement()) - } - if let commandlineArguments = commandlineArguments { + if let commandlineArguments { element.addChild(commandlineArguments.xmlElement()) } - if let environmentVariables = environmentVariables { + if let environmentVariables { element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) } + if let launchAutomaticallySubstyle { + element.attributes["launchAutomaticallySubstyle"] = launchAutomaticallySubstyle + } + if let customWorkingDirectory { + element.attributes["customWorkingDirectory"] = customWorkingDirectory + } - if let macroExpansion = macroExpansion { + if let macroExpansion { let macro = element.addChild(name: "MacroExpansion") macro.addChild(macroExpansion.xmlElement()) } @@ -121,17 +195,20 @@ extension XCScheme { override func isEqual(to: Any?) -> Bool { guard let rhs = to as? ProfileAction else { return false } return super.isEqual(to: to) && - buildableProductRunnable == rhs.buildableProductRunnable && + runnable == rhs.runnable && buildConfiguration == rhs.buildConfiguration && shouldUseLaunchSchemeArgsEnv == rhs.shouldUseLaunchSchemeArgsEnv && savedToolIdentifier == rhs.savedToolIdentifier && ignoresPersistentStateOnLaunch == rhs.ignoresPersistentStateOnLaunch && + customWorkingDirectory == rhs.customWorkingDirectory && useCustomWorkingDirectory == rhs.useCustomWorkingDirectory && debugDocumentVersioning == rhs.debugDocumentVersioning && + askForAppToLaunch == rhs.askForAppToLaunch && commandlineArguments == rhs.commandlineArguments && environmentVariables == rhs.environmentVariables && macroExpansion == rhs.macroExpansion && - enableTestabilityWhenProfilingTests == rhs.enableTestabilityWhenProfilingTests + enableTestabilityWhenProfilingTests == rhs.enableTestabilityWhenProfilingTests && + launchAutomaticallySubstyle == rhs.launchAutomaticallySubstyle } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+RemoteRunnable.swift b/Sources/XcodeProj/Scheme/XCScheme+RemoteRunnable.swift index 6818c3215..b66cadd64 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+RemoteRunnable.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+RemoteRunnable.swift @@ -1,24 +1,28 @@ import AEXML import Foundation -extension XCScheme { - public final class RemoteRunnable: Runnable { +public extension XCScheme { + final class RemoteRunnable: Runnable { // MARK: - Attributes public var bundleIdentifier: String + public var remotePath: String? // MARK: - Init public init(buildableReference: BuildableReference, bundleIdentifier: String, - runnableDebuggingMode: String = "0") { + runnableDebuggingMode: String = "0", + remotePath: String? = nil) { self.bundleIdentifier = bundleIdentifier + self.remotePath = remotePath super.init(buildableReference: buildableReference, runnableDebuggingMode: runnableDebuggingMode) } override init(element: AEXMLElement) throws { bundleIdentifier = element.attributes["BundleIdentifier"] ?? "" + remotePath = element.attributes["RemotePath"] try super.init(element: element) } @@ -28,15 +32,24 @@ extension XCScheme { let element = super.xmlElement() element.name = "RemoteRunnable" element.attributes["BundleIdentifier"] = bundleIdentifier + element.attributes["RemotePath"] = remotePath return element } // MARK: - Equatable + override func isEqual(other: XCScheme.Runnable) -> Bool { + guard let other = other as? RemoteRunnable else { + return false + } + + return super.isEqual(other: other) && + bundleIdentifier == other.bundleIdentifier && + remotePath == other.remotePath + } + public static func == (lhs: RemoteRunnable, rhs: RemoteRunnable) -> Bool { - return lhs.runnableDebuggingMode == rhs.runnableDebuggingMode && - lhs.bundleIdentifier == rhs.bundleIdentifier && - lhs.buildableReference == rhs.buildableReference + lhs.isEqual(other: rhs) && rhs.isEqual(other: lhs) } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift b/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift index eaa96894b..4ada2d38c 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift @@ -1,16 +1,16 @@ import AEXML import Foundation -extension XCScheme { - public class Runnable: Equatable { +public extension XCScheme { + class Runnable: Equatable { // MARK: - Attributes public var runnableDebuggingMode: String - public var buildableReference: BuildableReference + public var buildableReference: BuildableReference? // MARK: - Init - public init(buildableReference: BuildableReference, + public init(buildableReference: BuildableReference?, runnableDebuggingMode: String = "0") { self.buildableReference = buildableReference self.runnableDebuggingMode = runnableDebuggingMode @@ -18,7 +18,7 @@ extension XCScheme { init(element: AEXMLElement) throws { runnableDebuggingMode = element.attributes["runnableDebuggingMode"] ?? "0" - buildableReference = try BuildableReference(element: element["BuildableReference"]) + buildableReference = try? BuildableReference(element: element["BuildableReference"]) } // MARK: - XML @@ -27,15 +27,21 @@ extension XCScheme { let element = AEXMLElement(name: "Runnable", value: nil, attributes: ["runnableDebuggingMode": runnableDebuggingMode]) - element.addChild(buildableReference.xmlElement()) + if let buildableReference { + element.addChild(buildableReference.xmlElement()) + } return element } // MARK: - Equatable + func isEqual(other: Runnable) -> Bool { + runnableDebuggingMode == other.runnableDebuggingMode && + buildableReference == other.buildableReference + } + public static func == (lhs: Runnable, rhs: Runnable) -> Bool { - return lhs.runnableDebuggingMode == rhs.runnableDebuggingMode && - lhs.buildableReference == rhs.buildableReference + lhs.isEqual(other: rhs) && rhs.isEqual(other: lhs) } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift b/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift index be8cf30b2..fb842834b 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public class SerialAction: Equatable { +public extension XCScheme { + class SerialAction: Equatable { // MARK: - Attributes public var preActions: [ExecutionAction] @@ -25,13 +25,13 @@ extension XCScheme { func writeXML(parent element: AEXMLElement) { if !preActions.isEmpty { let preActions = element.addChild(name: "PreActions") - self.preActions.forEach { preAction in + for preAction in self.preActions { preActions.addChild(preAction.xmlElement()) } } if !postActions.isEmpty { let postActions = element.addChild(name: "PostActions") - self.postActions.forEach { postAction in + for postAction in self.postActions { postActions.addChild(postAction.xmlElement()) } } @@ -39,14 +39,14 @@ extension XCScheme { // MARK: - Equatable - @objc dynamic func isEqual(to: Any?) -> Bool { + func isEqual(to: Any?) -> Bool { guard let rhs = to as? SerialAction else { return false } return preActions == rhs.preActions && postActions == rhs.postActions } public static func == (lhs: SerialAction, rhs: SerialAction) -> Bool { - return lhs.isEqual(to: rhs) + lhs.isEqual(to: rhs) } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+StoreKitConfigurationFileReference.swift b/Sources/XcodeProj/Scheme/XCScheme+StoreKitConfigurationFileReference.swift new file mode 100644 index 000000000..4edc620d1 --- /dev/null +++ b/Sources/XcodeProj/Scheme/XCScheme+StoreKitConfigurationFileReference.swift @@ -0,0 +1,36 @@ +import AEXML +import Foundation + +public extension XCScheme { + final class StoreKitConfigurationFileReference: Equatable { + // MARK: - Attributes + + public var identifier: String + + // MARK: - Init + + public init(identifier: String) { + self.identifier = identifier + } + + init(element: AEXMLElement) throws { + identifier = element.attributes["identifier"]! + } + + // MARK: - XML + + func xmlElement() -> AEXMLElement { + AEXMLElement(name: "StoreKitConfigurationFileReference", + value: nil, + attributes: [ + "identifier": identifier, + ]) + } + + // MARK: - Equatable + + public static func == (lhs: StoreKitConfigurationFileReference, rhs: StoreKitConfigurationFileReference) -> Bool { + lhs.identifier == rhs.identifier + } + } +} diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift index ffbc7aeab..735e699b1 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift @@ -2,12 +2,16 @@ import AEXML import Foundation import PathKit -extension XCScheme { - public final class TestAction: SerialAction { +public extension XCScheme { + final class TestAction: SerialAction { public enum AttachmentLifetime: String { case keepAlways, keepNever } + public enum ScreenCaptureFormat: String { + case screenshots, screenRecording + } + // MARK: - Static private static let defaultBuildConfiguration = "Debug" @@ -35,7 +39,9 @@ extension XCScheme { public var language: String? public var region: String? public var systemAttachmentLifetime: AttachmentLifetime? + public var preferredScreenCaptureFormat: ScreenCaptureFormat? public var userAttachmentLifetime: AttachmentLifetime? + public var customLLDBInitFile: String? // MARK: - Init @@ -62,7 +68,9 @@ extension XCScheme { language: String? = nil, region: String? = nil, systemAttachmentLifetime: AttachmentLifetime? = nil, - userAttachmentLifetime: AttachmentLifetime? = nil) { + preferredScreenCaptureFormat: ScreenCaptureFormat? = nil, + userAttachmentLifetime: AttachmentLifetime? = nil, + customLLDBInitFile: String? = nil) { self.buildConfiguration = buildConfiguration self.macroExpansion = macroExpansion self.testables = testables @@ -84,7 +92,9 @@ extension XCScheme { self.language = language self.region = region self.systemAttachmentLifetime = systemAttachmentLifetime + self.preferredScreenCaptureFormat = preferredScreenCaptureFormat self.userAttachmentLifetime = userAttachmentLifetime + self.customLLDBInitFile = customLLDBInitFile super.init(preActions, postActions) } @@ -134,8 +144,11 @@ extension XCScheme { systemAttachmentLifetime = element.attributes["systemAttachmentLifetime"] .flatMap(AttachmentLifetime.init(rawValue:)) + preferredScreenCaptureFormat = element.attributes["preferredScreenCaptureFormat"] + .flatMap(ScreenCaptureFormat.init(rawValue:)) userAttachmentLifetime = element.attributes["userAttachmentLifetime"] .flatMap(AttachmentLifetime.init(rawValue:)) + customLLDBInitFile = element.attributes["customLLDBInitFile"] try super.init(element: element) } @@ -146,7 +159,7 @@ extension XCScheme { attributes["buildConfiguration"] = buildConfiguration attributes["selectedDebuggerIdentifier"] = selectedDebuggerIdentifier attributes["selectedLauncherIdentifier"] = selectedLauncherIdentifier - if let language = language { + if let language { attributes["language"] = language } attributes["region"] = region @@ -154,7 +167,7 @@ extension XCScheme { if codeCoverageEnabled { attributes["codeCoverageEnabled"] = codeCoverageEnabled.xmlString } - if let onlyGenerateCoverageForSpecifiedTargets = onlyGenerateCoverageForSpecifiedTargets { + if let onlyGenerateCoverageForSpecifiedTargets { attributes["onlyGenerateCoverageForSpecifiedTargets"] = onlyGenerateCoverageForSpecifiedTargets.xmlString } if enableAddressSanitizer { @@ -173,45 +186,61 @@ extension XCScheme { attributes["disableMainThreadChecker"] = disableMainThreadChecker.xmlString } attributes["systemAttachmentLifetime"] = systemAttachmentLifetime?.rawValue + + switch preferredScreenCaptureFormat { + case .screenshots: + attributes["preferredScreenCaptureFormat"] = preferredScreenCaptureFormat?.rawValue + case .none, .screenRecording: + break + } + if case .keepAlways? = userAttachmentLifetime { attributes["userAttachmentLifetime"] = userAttachmentLifetime?.rawValue } + if let customLLDBInitFile { + attributes["customLLDBInitFile"] = customLLDBInitFile + } let element = AEXMLElement(name: "TestAction", value: nil, attributes: attributes) super.writeXML(parent: element) - if let testPlans = testPlans { + if let testPlans { let testPlansElement = element.addChild(name: "TestPlans") - testPlans.forEach { testPlan in + for testPlan in testPlans { testPlansElement.addChild(testPlan.xmlElement()) } } - let testablesElement = element.addChild(name: "Testables") - testables.forEach { testable in - testablesElement.addChild(testable.xmlElement()) - } - if let macroExpansion = macroExpansion { + if let macroExpansion { let macro = element.addChild(name: "MacroExpansion") macro.addChild(macroExpansion.xmlElement()) } - if let commandlineArguments = commandlineArguments { + let testablesElement = element.addChild(name: "Testables") + for testable in testables { + testablesElement.addChild(testable.xmlElement()) + } + + if let commandlineArguments { element.addChild(commandlineArguments.xmlElement()) } - if let environmentVariables = environmentVariables { + if let environmentVariables { element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) } - let additionalOptionsElement = element.addChild(AEXMLElement(name: "AdditionalOptions")) - additionalOptions.forEach { additionalOption in - additionalOptionsElement.addChild(additionalOption.xmlElement()) + if !additionalOptions.isEmpty { + let additionalOptionsElement = element.addChild(AEXMLElement(name: "AdditionalOptions")) + for additionalOption in additionalOptions { + additionalOptionsElement.addChild(additionalOption.xmlElement()) + } } - let codeCoverageTargetsElement = element.addChild(AEXMLElement(name: "CodeCoverageTargets")) - codeCoverageTargets.forEach { target in - codeCoverageTargetsElement.addChild(target.xmlElement()) + if !codeCoverageTargets.isEmpty { + let codeCoverageTargetsElement = element.addChild(AEXMLElement(name: "CodeCoverageTargets")) + for target in codeCoverageTargets { + codeCoverageTargetsElement.addChild(target.xmlElement()) + } } return element @@ -241,8 +270,10 @@ extension XCScheme { language == rhs.language && region == rhs.region && systemAttachmentLifetime == rhs.systemAttachmentLifetime && + preferredScreenCaptureFormat == rhs.preferredScreenCaptureFormat && userAttachmentLifetime == rhs.userAttachmentLifetime && - codeCoverageTargets == rhs.codeCoverageTargets + codeCoverageTargets == rhs.codeCoverageTargets && + customLLDBInitFile == rhs.customLLDBInitFile } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+SkippedTests.swift b/Sources/XcodeProj/Scheme/XCScheme+TestItem.swift similarity index 56% rename from Sources/XcodeProj/Scheme/XCScheme+SkippedTests.swift rename to Sources/XcodeProj/Scheme/XCScheme+TestItem.swift index 5e5e77e0b..9ede59f03 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+SkippedTests.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestItem.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class SkippedTest: Equatable { +public extension XCScheme { + final class TestItem: Equatable { // MARK: - Attributes public var identifier: String @@ -20,15 +20,15 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "Test", - value: nil, - attributes: ["Identifier": identifier]) + AEXMLElement(name: "Test", + value: nil, + attributes: ["Identifier": identifier]) } // MARK: - Equatable - public static func == (lhs: SkippedTest, rhs: SkippedTest) -> Bool { - return lhs.identifier == rhs.identifier + public static func == (lhs: TestItem, rhs: TestItem) -> Bool { + lhs.identifier == rhs.identifier } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestParallelization.swift b/Sources/XcodeProj/Scheme/XCScheme+TestParallelization.swift new file mode 100644 index 000000000..dda224b1b --- /dev/null +++ b/Sources/XcodeProj/Scheme/XCScheme+TestParallelization.swift @@ -0,0 +1,12 @@ +import Foundation + +public extension XCScheme { + /// 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. + enum TestParallelization: String { + case all + case swiftTestingOnly + case none + } +} diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestPlanReference.swift b/Sources/XcodeProj/Scheme/XCScheme+TestPlanReference.swift index 4ce2b5726..0c8e3f19f 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestPlanReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestPlanReference.swift @@ -1,8 +1,8 @@ import AEXML import Foundation -extension XCScheme { - public final class TestPlanReference: Equatable { +public extension XCScheme { + final class TestPlanReference: Equatable { // MARK: - Attributes public var reference: String @@ -39,7 +39,7 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: TestPlanReference, rhs: TestPlanReference) -> Bool { - return lhs.reference == rhs.reference && + lhs.reference == rhs.reference && lhs.default == rhs.default } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift index 9230a2433..f6b6a4b42 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift @@ -1,37 +1,65 @@ import AEXML import Foundation -extension XCScheme { - public final class TestableReference: Equatable { +public extension XCScheme { + final class TestableReference: Equatable { // MARK: - Attributes public var skipped: Bool - public var parallelizable: Bool + public var parallelization: TestParallelization public var randomExecutionOrdering: Bool + public var useTestSelectionWhitelist: Bool? public var buildableReference: BuildableReference - public var skippedTests: [SkippedTest] + public var locationScenarioReference: LocationScenarioReference? + public var skippedTests: [TestItem] + public var selectedTests: [TestItem] // MARK: - Init public init(skipped: Bool, - parallelizable: Bool = false, + parallelization: TestParallelization = .none, randomExecutionOrdering: Bool = false, buildableReference: BuildableReference, - skippedTests: [SkippedTest] = []) { + locationScenarioReference: LocationScenarioReference? = nil, + skippedTests: [TestItem] = [], + selectedTests: [TestItem] = [], + useTestSelectionWhitelist: Bool? = nil) { self.skipped = skipped - self.parallelizable = parallelizable + self.parallelization = parallelization self.randomExecutionOrdering = randomExecutionOrdering self.buildableReference = buildableReference + self.locationScenarioReference = locationScenarioReference + self.useTestSelectionWhitelist = useTestSelectionWhitelist + self.selectedTests = selectedTests self.skippedTests = skippedTests } init(element: AEXMLElement) throws { skipped = element.attributes["skipped"] == "YES" - parallelizable = element.attributes["parallelizable"] == "YES" + + if let parallelizableValue = element.attributes["parallelizable"] { + parallelization = parallelizableValue == "YES" ? .all : .none + } else { + parallelization = .swiftTestingOnly + } + + useTestSelectionWhitelist = element.attributes["useTestSelectionWhitelist"] == "YES" randomExecutionOrdering = element.attributes["testExecutionOrdering"] == "random" buildableReference = try BuildableReference(element: element["BuildableReference"]) - if let skippedTests = element["SkippedTests"]["Test"].all, !skippedTests.isEmpty { - self.skippedTests = try skippedTests.map(SkippedTest.init) + + if element["LocationScenarioReference"].all?.first != nil { + locationScenarioReference = try LocationScenarioReference(element: element["LocationScenarioReference"]) + } else { + locationScenarioReference = nil + } + + if let selectedTests = element["SelectedTests"]["Test"].all { + self.selectedTests = try selectedTests.map(TestItem.init) + } else { + selectedTests = [] + } + if let skippedTests = element["SkippedTests"]["Test"].all { + self.skippedTests = try skippedTests.map(TestItem.init) } else { skippedTests = [] } @@ -41,16 +69,42 @@ extension XCScheme { func xmlElement() -> AEXMLElement { var attributes: [String: String] = ["skipped": skipped.xmlString] - attributes["parallelizable"] = parallelizable ? parallelizable.xmlString : nil + + switch parallelization { + case .all: + attributes["parallelizable"] = "YES" + case .none: + attributes["parallelizable"] = "NO" + case .swiftTestingOnly: + break // SwiftTesting is inferred by the lack of a value + } + + if let useTestSelectionWhitelist { + attributes["useTestSelectionWhitelist"] = useTestSelectionWhitelist.xmlString + } attributes["testExecutionOrdering"] = randomExecutionOrdering ? "random" : nil let element = AEXMLElement(name: "TestableReference", value: nil, attributes: attributes) element.addChild(buildableReference.xmlElement()) - if !skippedTests.isEmpty { - let skippedTestsElement = element.addChild(name: "SkippedTests") - skippedTests.forEach { skippedTest in - skippedTestsElement.addChild(skippedTest.xmlElement()) + + if let locationScenarioReference { + element.addChild(locationScenarioReference.xmlElement()) + } + + if useTestSelectionWhitelist == true { + if !selectedTests.isEmpty { + let selectedTestsElement = element.addChild(name: "SelectedTests") + for selectedTest in selectedTests { + selectedTestsElement.addChild(selectedTest.xmlElement()) + } + } + } else { + if !skippedTests.isEmpty { + let skippedTestsElement = element.addChild(name: "SkippedTests") + for skippedTest in skippedTests { + skippedTestsElement.addChild(skippedTest.xmlElement()) + } } } return element @@ -59,11 +113,14 @@ extension XCScheme { // MARK: - Equatable public static func == (lhs: TestableReference, rhs: TestableReference) -> Bool { - return lhs.skipped == rhs.skipped && - lhs.parallelizable == rhs.parallelizable && + lhs.skipped == rhs.skipped && + lhs.parallelization == rhs.parallelization && lhs.randomExecutionOrdering == rhs.randomExecutionOrdering && lhs.buildableReference == rhs.buildableReference && - lhs.skippedTests == rhs.skippedTests + lhs.locationScenarioReference == rhs.locationScenarioReference && + lhs.useTestSelectionWhitelist == rhs.useTestSelectionWhitelist && + lhs.skippedTests == rhs.skippedTests && + lhs.selectedTests == rhs.selectedTests } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme.swift b/Sources/XcodeProj/Scheme/XCScheme.swift index ee2cb7596..41013fb62 100644 --- a/Sources/XcodeProj/Scheme/XCScheme.swift +++ b/Sources/XcodeProj/Scheme/XCScheme.swift @@ -1,17 +1,17 @@ import AEXML import Foundation -import PathKit +@preconcurrency import PathKit -public enum XCSchemeError: Error, CustomStringConvertible { +public enum XCSchemeError: Error, CustomStringConvertible, Sendable { case notFound(path: Path) case missing(property: String) public var description: String { switch self { case let .notFound(path): - return ".xcscheme couldn't be found at path \(path.string)" + ".xcscheme couldn't be found at path \(path.string)" case let .missing(property): - return "Property \(property) missing" + "Property \(property) missing" } } } @@ -42,7 +42,7 @@ public final class XCScheme: Writable, Equatable { throw XCSchemeError.notFound(path: path) } name = path.lastComponentWithoutExtension - let document = try AEXMLDocument(xml: try path.read()) + let document = try AEXMLDocument(xml: path.read()) let scheme = document["Scheme"] lastUpgradeVersion = scheme.attributes["LastUpgradeVersion"] version = scheme.attributes["version"] @@ -80,45 +80,58 @@ public final class XCScheme: Writable, Equatable { self.wasCreatedForAppExtension = wasCreatedForAppExtension } + public convenience init(pathString: String) throws { + try self.init(path: Path(pathString)) + } + // MARK: - Writable public func write(path: Path, override: Bool) throws { + let document = getAEXMLDocument() + if override, path.exists { + try path.delete() + } + try path.write(document.xmlXcodeFormat) + } + + public func dataRepresentation() throws -> Data? { + getAEXMLDocument().xmlXcodeFormat.data(using: .utf8) + } + + private func getAEXMLDocument() -> AEXMLDocument { let document = AEXMLDocument() var schemeAttributes: [String: String] = [:] schemeAttributes["LastUpgradeVersion"] = lastUpgradeVersion schemeAttributes["version"] = version let scheme = document.addChild(name: "Scheme", value: nil, attributes: schemeAttributes) - if let buildAction = buildAction { + if let buildAction { scheme.addChild(buildAction.xmlElement()) } - if let testAction = testAction { + if let testAction { scheme.addChild(testAction.xmlElement()) } - if let launchAction = launchAction { + if let launchAction { scheme.addChild(launchAction.xmlElement()) } - if let profileAction = profileAction { + if let profileAction { scheme.addChild(profileAction.xmlElement()) } - if let analyzeAction = analyzeAction { + if let analyzeAction { scheme.addChild(analyzeAction.xmlElement()) } - if let archiveAction = archiveAction { + if let archiveAction { scheme.addChild(archiveAction.xmlElement()) } - if let wasCreatedForAppExtension = wasCreatedForAppExtension { + if let wasCreatedForAppExtension { scheme.attributes["wasCreatedForAppExtension"] = wasCreatedForAppExtension.xmlString } - if override, path.exists { - try path.delete() - } - try path.write(document.xmlXcodeFormat) + return document } // MARK: - Equatable public static func == (lhs: XCScheme, rhs: XCScheme) -> Bool { - return lhs.buildAction == rhs.buildAction && + lhs.buildAction == rhs.buildAction && lhs.testAction == rhs.testAction && lhs.launchAction == rhs.launchAction && lhs.profileAction == rhs.profileAction && @@ -130,3 +143,22 @@ public final class XCScheme: Writable, Equatable { lhs.wasCreatedForAppExtension == rhs.wasCreatedForAppExtension } } + +public extension XCScheme { + /// Returns schemes folder path relative to the given path. + /// + /// - Parameter path: parent folder of schemes folder (xcshareddata or xcuserdata) + /// - Returns: schemes folder path relative to the given path. + static func schemesPath(_ path: Path) -> Path { + path + "xcschemes" + } + + /// Returns scheme file path relative to the given path. + /// + /// - Parameter path: parent folder of schemes folder (xcshareddata or xcuserdata) + /// - Parameter schemeName: scheme name + /// - Returns: scheme file path relative to the given path. + static func path(_ path: Path, schemeName: String) -> Path { + XCScheme.schemesPath(path) + "\(schemeName).xcscheme" + } +} diff --git a/Sources/XcodeProj/Scheme/XCSchemeManagement.swift b/Sources/XcodeProj/Scheme/XCSchemeManagement.swift new file mode 100644 index 000000000..dce47e271 --- /dev/null +++ b/Sources/XcodeProj/Scheme/XCSchemeManagement.swift @@ -0,0 +1,206 @@ +import Foundation +@preconcurrency import PathKit + +public enum XCSchemeManagementError: Error, Equatable, LocalizedError, CustomStringConvertible, Sendable { + /// Thrown when the user tries to initialize a XCSchemeManagement instace passing a path to a file that doesn't exist. + case notFound(path: Path) + + public var description: String { + switch self { + case let .notFound(path): + "Couldn't initialize XCSchemeManagement because the file at path \(path.string) was not found." + } + } + + public var errorDescription: String? { + description + } +} + +/// This struct represents the xcschememanagement.plist file that is generated by Xcode +/// to attach metdata to schemes such as the order of schemes orwhether a scheme is shared or no. +/// The file is formatted as a property list file. +public struct XCSchemeManagement: Codable, Equatable, Writable { + public struct AutocreationBuildable: Equatable, Codable { + var primary: Bool + } + + /// Scheme configuration object. + public struct UserStateScheme: Equatable, Codable { + /// Coding keys + public enum CodingKeys: String, CodingKey { + case shared + case orderHint + case isShown + case name + } + + /// Name of the scheme (with the .xcscheme extension) + public var name: String + + /// True if the scheme should be shared. + public var shared: Bool + + /// Attribute used by Xcode to sort the schemes. + public var orderHint: Int? + + /// True if the scheme should be shown in the list of schemes. + public var isShown: Bool? + + /// The key that should be used when encoding the scheme configuration. + var key: String { + var key = name + if shared { + key.append("_^#shared#^_") + } + return key + } + + /// It initializes the scheme configuration with its attributes. + /// - Parameters: + /// - name: Name of the scheme (with the .xcscheme extension) + /// - shared: True if the scheme should be shared. + /// - orderHint: Attribute used by Xcode to sort the schemes. + /// - isShown: True if the scheme should be shown in the list of schemes. + public init(name: String, + shared: Bool = false, + orderHint: Int? = nil, + isShown: Bool? = nil) { + self.name = name + self.shared = shared + self.orderHint = orderHint + self.isShown = isShown + } + + // MARK: - Codable + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + orderHint = try container.decodeIfPresent(.orderHint) + isShown = try container.decodeIfPresent(.isShown) + shared = try container.decodeIfPresent(.shared) ?? false + name = try container.decode(.name) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if let orderHint { + try container.encode(orderHint, forKey: .orderHint) + } + if let isShown { + try container.encode(isShown, forKey: .isShown) + } + } + } + + /// Coding keys. + public enum CodingKeys: String, CodingKey { + case schemeUserState = "SchemeUserState" + case suppressBuildableAutocreation = "SuppressBuildableAutocreation" + } + + /// An array that contains the configuration of the schemes. + public var schemeUserState: [XCSchemeManagement.UserStateScheme]? + + /// A dictionary where the key is the object reference of the target, and the value the configuration for auto-creating schemes. + public var suppressBuildableAutocreation: [String: XCSchemeManagement.AutocreationBuildable]? + + /// Default constructor. + /// - Parameters: + /// - schemeUserState: An array that contains the configuration of the schemes. + /// - suppressBuildableAutocreation: A dictionary where the key is the object reference of the target, and the value the configuration for auto-creating schemes. + public init(schemeUserState: [XCSchemeManagement.UserStateScheme]? = nil, + suppressBuildableAutocreation: [String: XCSchemeManagement.AutocreationBuildable]? = nil) { + self.schemeUserState = schemeUserState + self.suppressBuildableAutocreation = suppressBuildableAutocreation + } + + /// Initializes the XCSchemeManagement instance by parsing an existing xcschememanagement.plist + /// - Parameter path: Path to the xcschememanagement.plist file. + /// - Throws: An error if the file is malformated. + public init(path: Path) throws { + if !path.exists { + throw XCSchemeManagementError.notFound(path: path) + } + let decoder = XcodeprojPropertyListDecoder() + self = try decoder.decode(XCSchemeManagement.self, from: path.read()) + } + + /// Converts the object into a property list and writes it at the given path. + /// - Parameter path: Path to the file where it should be written. + /// - Parameter override: if project should be overridden. Default is false. + /// If true will remove all existing data before writing. + /// If false will throw error iff file exists at the given path. + /// - Throws: An error if the write fails. + public func write(path: Path, override: Bool = false) throws { + if override, path.exists { + try path.delete() + } + + let encoder = getEncoder() + try encoder.encode(self).write(to: path.url) + } + + /// Gets the data representation of the property list representation of the object. + /// + /// - Throws: Error if encoding fails. + public func dataRepresentation() throws -> Data? { + try getEncoder().encode(self) + } + + private func getEncoder() -> PropertyListEncoder { + let encoder = PropertyListEncoder() + encoder.outputFormat = .xml + return encoder + } + + // MARK: - Codable + + public init(from decoder: Decoder) throws { + let plistDecoder = XcodeprojPropertyListDecoder() + let container = try decoder.container(keyedBy: CodingKeys.self) + suppressBuildableAutocreation = try container.decodeIfPresent(.suppressBuildableAutocreation) + if let schemeUserStateDictionary = try container.decodeIfPresent([String: Any].self, forKey: .schemeUserState) { + schemeUserState = try schemeUserStateDictionary + .sorted(by: { $0.key < $1.key }) + .compactMap { key, value -> XCSchemeManagement.UserStateScheme? in + var name = key + guard var valueDictionary = value as? [String: Any] else { return nil } + if key.contains("_^#shared#^_") { + valueDictionary["shared"] = true + name = key.replacingOccurrences(of: "_^#shared#^_", with: "") + } + valueDictionary["name"] = name + + let data = try PropertyListSerialization.data(fromPropertyList: valueDictionary, format: .xml, options: 0) + return try plistDecoder.decode(XCSchemeManagement.UserStateScheme.self, from: data) + } + } else { + suppressBuildableAutocreation = nil + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + if let suppressBuildableAutocreation { + try container.encode(suppressBuildableAutocreation, forKey: .suppressBuildableAutocreation) + } + + if let schemeUserState { + let encodableSchemeUserState = schemeUserState + .reduce(into: [String: XCSchemeManagement.UserStateScheme]()) { $0[$1.key] = $1 } + try container.encode(encodableSchemeUserState, forKey: .schemeUserState) + } + } +} + +extension XCSchemeManagement { + /// Returns scheme management file path relative to the given path. + /// + /// - Parameter path: schemes folder + /// - Returns: scheme management plist path relative to the given path. + static func path(_ path: Path) -> Path { + path + "xcschememanagement.plist" + } +} diff --git a/Sources/XcodeProj/Utils/BuildSettingsProvider.swift b/Sources/XcodeProj/Utils/BuildSettingsProvider.swift index 80f6e6469..615b77111 100644 --- a/Sources/XcodeProj/Utils/BuildSettingsProvider.swift +++ b/Sources/XcodeProj/Utils/BuildSettingsProvider.swift @@ -19,7 +19,7 @@ public class BuildSettingsProvider { /// - tvOS: tvOS. /// - watchOS: watchOS. public enum Platform { - case iOS, macOS, tvOS, watchOS + case iOS, macOS, tvOS, watchOS, visionOS } /// Target product type. @@ -29,8 +29,10 @@ public class BuildSettingsProvider { /// - dynamicLibrary: dynamic library. /// - application: application. /// - bundle: bundle. - /// - appExtension: application extension - /// - watchExtension: watch extension + /// - appExtension: application extension. + /// - watchExtension: watch extension. + /// - unitTests: unit tests. + /// - uiTests: ui tests. public enum Product { case framework, staticLibrary, dynamicLibrary, application, bundle, appExtension, watchExtension, unitTests, uiTests } @@ -44,50 +46,47 @@ public class BuildSettingsProvider { /// - swift: true if the target contains Swift code. /// - Returns: build settings. public static func targetDefault(variant: Variant? = nil, platform: Platform?, product: Product?, swift: Bool? = nil) -> BuildSettings { - var buildSettings: [String: Any] = [:] - - if let platform = platform { + var buildSettings: BuildSettings = [:] + + if let platform { buildSettings.merge(targetSettings(platform: platform), uniquingKeysWith: { $1 }) } - - if let product = product { + + if let product { buildSettings.merge(targetSettings(product: product), uniquingKeysWith: { $1 }) } - - if let platform = platform, let product = product { + + if let platform, let product { buildSettings.merge(targetSettings(platform: platform, product: product), uniquingKeysWith: { $1 }) } - - if let platform = platform, let variant = variant { + + if let platform, let variant { buildSettings.merge(targetSettings(variant: variant, platform: platform), uniquingKeysWith: { $1 }) } - - if let variant = variant, let swift = swift, swift == true { + + if let variant, let swift, swift == true { buildSettings.merge(targetSwiftSettings(variant: variant), uniquingKeysWith: { $1 }) } - - if let product = product, let swift = swift, swift == true { + + if let product, let swift, swift == true { buildSettings.merge(targetSwiftSettings(product: product), uniquingKeysWith: { $1 }) } - - if let platform = platform, let product = product, let swift = swift, swift == true { - buildSettings.merge(targetSwiftSettings(platform: platform, product: product), uniquingKeysWith: { $1 }) - } - + return buildSettings } /// Returns default build settings that Xcode sets in new projects. /// + /// - Parameters variant: build settings variant. /// - Returns: build settings. public static func projectDefault(variant: Variant) -> BuildSettings { switch variant { case .all: - return projectAll() + projectAll() case .debug: - return projectDebug() + projectDebug() case .release: - return projectRelease() + projectRelease() } } @@ -95,8 +94,9 @@ public class BuildSettingsProvider { // swiftlint:disable:next function_body_length private static func projectAll() -> BuildSettings { - return [ + [ "ALWAYS_SEARCH_USER_PATHS": "NO", + "CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED": "YES", "CLANG_ANALYZER_NONNULL": "YES", "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE", "CLANG_CXX_LANGUAGE_STANDARD": "gnu++14", @@ -120,12 +120,14 @@ public class BuildSettingsProvider { "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF": "YES", "CLANG_WARN_OBJC_LITERAL_CONVERSION": "YES", "CLANG_WARN_OBJC_ROOT_CLASS": "YES_ERROR", + "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "YES", "CLANG_WARN_RANGE_LOOP_ANALYSIS": "YES", "CLANG_WARN_STRICT_PROTOTYPES": "YES", "CLANG_WARN_SUSPICIOUS_MOVE": "YES", "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE", "CLANG_WARN_UNREACHABLE_CODE": "YES", "COPY_PHASE_STRIP": "NO", + "DEAD_CODE_STRIPPING": "YES", "ENABLE_STRICT_OBJC_MSGSEND": "YES", "GCC_C_LANGUAGE_STANDARD": "gnu11", "GCC_NO_COMMON_BLOCKS": "YES", @@ -140,7 +142,7 @@ public class BuildSettingsProvider { } private static func projectDebug() -> BuildSettings { - return [ + [ "DEBUG_INFORMATION_FORMAT": "dwarf", "ENABLE_TESTABILITY": "YES", "GCC_DYNAMIC_NO_PIC": "NO", @@ -152,48 +154,56 @@ public class BuildSettingsProvider { } private static func projectRelease() -> BuildSettings { - return [ + [ "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", "ENABLE_NS_ASSERTIONS": "NO", "MTL_ENABLE_DEBUG_INFO": "NO", - "VALIDATE_PRODUCT": "YES" + "VALIDATE_PRODUCT": "YES", ] } private static func targetSettings(platform: Platform) -> BuildSettings { switch platform { case .iOS: - return [ + [ "SDKROOT": "iphoneos", "CODE_SIGN_IDENTITY": "iPhone Developer", - "TARGETED_DEVICE_FAMILY": "1,2" + "TARGETED_DEVICE_FAMILY": "1,2", ] case .macOS: - return [ + [ "SDKROOT": "macosx", "CODE_SIGN_IDENTITY": "-", ] case .tvOS: - return [ + [ "SDKROOT": "appletvos", - "TARGETED_DEVICE_FAMILY": "3" + "TARGETED_DEVICE_FAMILY": "3", ] case .watchOS: - return [ + [ "SDKROOT": "watchos", - "TARGETED_DEVICE_FAMILY": "4" + "TARGETED_DEVICE_FAMILY": "4", + ] + case .visionOS: + [ + "SDKROOT": "xros", + "CODE_SIGN_IDENTITY": "iPhone Developer", + "TARGETED_DEVICE_FAMILY": "1,2,7", ] } } - + private static func targetSettings(product: Product) -> BuildSettings { switch product { case .application: - return [ - "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon" + [ + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", ] case .framework: - return [ + [ "CODE_SIGN_IDENTITY": "", "CURRENT_PROJECT_VERSION": "1", "DEFINES_MODULE": "YES", @@ -204,15 +214,15 @@ public class BuildSettingsProvider { "PRODUCT_NAME": "$(TARGET_NAME:c99extidentifier)", "SKIP_INSTALL": "YES", "VERSION_INFO_PREFIX": "", - "VERSIONING_SYSTEM": "apple-generic" + "VERSIONING_SYSTEM": "apple-generic", ] case .bundle: - return [ + [ "WRAPPER_EXTENSION": "bundle", - "SKIP_INSTALL": "YES" + "SKIP_INSTALL": "YES", ] default: - return [:] + [:] } } @@ -220,152 +230,150 @@ public class BuildSettingsProvider { private static func targetSettings(platform: Platform, product: Product) -> BuildSettings { switch (platform, product) { case (.iOS, .application): - return [ - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"] + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], ] case (.macOS, .application): - return [ + [ "COMBINE_HIDPI_IMAGES": "YES", - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks"] + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks"], ] case (.tvOS, .application): - return [ + [ "ASSETCATALOG_COMPILER_APPICON_NAME": "App Icon & Top Shelf Image", "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME": "LaunchImage", - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"] + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], ] case (.watchOS, .application): - return [ - "SKIP_INSTALL": "YES" + [ + "SKIP_INSTALL": "YES", + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], + ] + case (.visionOS, .application): + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], ] case (.iOS, .framework): - return [ + [ "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], ] case (.macOS, .framework): - return [ + [ "COMBINE_HIDPI_IMAGES": "YES", "FRAMEWORK_VERSION": "A", - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"] + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"], ] case (.tvOS, .framework): - return [ - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"] + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], ] case (.watchOS, .framework): - return [ + [ "APPLICATION_EXTENSION_API_ONLY": "YES", "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], ] - case ([.iOS, .tvOS, .watchOS], .staticLibrary): - return [ + case (.visionOS, .framework): + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], + ] + case ([.iOS, .tvOS, .watchOS, .visionOS], .staticLibrary): + [ "OTHER_LDFLAGS": "-ObjC", - "SKIP_INSTALL": "YES" + "SKIP_INSTALL": "YES", ] case (.macOS, .staticLibrary): - return [ + [ "EXECUTABLE_PREFIX": "lib", - "SKIP_INSTALL": "YES" + "SKIP_INSTALL": "YES", ] case (.macOS, .dynamicLibrary): - return [ + [ "DYLIB_COMPATIBILITY_VERSION": "1", "DYLIB_CURRENT_VERSION": "1", "EXECUTABLE_PREFIX": "lib", - "SKIP_INSTALL": "YES" + "SKIP_INSTALL": "YES", ] case (.macOS, .bundle): - return [ + [ "COMBINE_HIDPI_IMAGES": "YES", "INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Bundles", ] - case ([.iOS, .tvOS], .appExtension): - return [ - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"] + case ([.iOS, .tvOS, .visionOS], .appExtension): + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"], ] case (.macOS, .appExtension): - return [ + [ "LD_RUNPATH_SEARCH_PATHS": [ "$(inherited)", "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks" - ] + "@executable_path/../../../../Frameworks", + ], ] case (.watchOS, .watchExtension): - return [ + [ "LD_RUNPATH_SEARCH_PATHS": [ "$(inherited)", "@executable_path/Frameworks", - "@executable_path/../../Frameworks" - ] + "@executable_path/../../Frameworks", + ], ] case (.watchOS, .appExtension): - return [ + [ "LD_RUNPATH_SEARCH_PATHS": [ "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", - "@executable_path/../../../../Frameworks" - ] + "@executable_path/../../../../Frameworks", + ], + ] + case ([.iOS, .tvOS, .visionOS], [.unitTests, .uiTests]): + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], ] - case ([.iOS, .tvOS], [.unitTests, .uiTests]): - return [ - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"], - ] case (.macOS, [.unitTests, .uiTests]): - return [ - "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"] - ] + [ + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"], + ] default: - return [:] + [:] } } - - static private func targetSettings(variant: Variant, + + private static func targetSettings(variant: Variant, platform: Platform) -> BuildSettings { switch (variant, platform) { default: - return [:] + [:] } } - - static private func targetSwiftSettings(variant: Variant) -> BuildSettings { + + private static func targetSwiftSettings(variant: Variant) -> BuildSettings { switch variant { case .debug: - return [ + [ "SWIFT_OPTIMIZATION_LEVEL": "-Onone", - "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], "SWIFT_COMPILATION_MODE": "singlefile", ] case .release: - return [ - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "SWIFT_COMPILATION_MODE": "wholemodule" + [ + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "SWIFT_COMPILATION_MODE": "wholemodule", ] default: - return [:] + [:] } } - - static private func targetSwiftSettings(product: Product) -> BuildSettings { + + private static func targetSwiftSettings(product: Product) -> BuildSettings { switch product { case .framework: - return [ - "DEFINES_MODULE": "YES" - ] - default: - return [:] - } - } - - static private func targetSwiftSettings(platform: Platform, product: Product) -> BuildSettings { - switch (platform, product) { - case (.watchOS, .application): - return [ - "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES" + [ + "DEFINES_MODULE": "YES", ] default: - return [:] + [:] } } } @@ -374,12 +382,12 @@ public class BuildSettingsProvider { // // - reference: https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#ID426 -private func ~= (lhs: [BuildSettingsProvider.Product], +private func ~= (lhs: [BuildSettingsProvider.Product], rhs: BuildSettingsProvider.Product) -> Bool { - return lhs.contains(rhs) + lhs.contains(rhs) } -private func ~= (lhs: [BuildSettingsProvider.Platform], +private func ~= (lhs: [BuildSettingsProvider.Platform], rhs: BuildSettingsProvider.Platform) -> Bool { - return lhs.contains(rhs) + lhs.contains(rhs) } diff --git a/Sources/XcodeProj/Utils/Collection+Extras.swift b/Sources/XcodeProj/Utils/Collection+Extras.swift new file mode 100644 index 000000000..c332d2ce3 --- /dev/null +++ b/Sources/XcodeProj/Utils/Collection+Extras.swift @@ -0,0 +1,34 @@ +extension Collection where Element: BinaryInteger, Index == Int { + @inlinable + @inline(__always) + func containsCString(_ cString: T) -> Bool where T.Element: BinaryInteger, T.Index == Int { + guard !cString.isEmpty else { return true } + + // Drop null terminator if present + let subarrayCount = cString.last == 0 + ? cString.count - 1 + : cString.count + + guard subarrayCount <= count else { return false } + + let lastSubarrayStartingPos = count - subarrayCount + var i = 0 + while i <= lastSubarrayStartingPos { + var match = true + var j = 0 + while j < subarrayCount { + if self[i + j] != cString[j] { + match = false + break + } + j += 1 + } + if match { + return true + } + + i += 1 + } + return false + } +} diff --git a/Sources/XcodeProj/Utils/CommentedString.swift b/Sources/XcodeProj/Utils/CommentedString.swift index a77b7b93a..278214517 100644 --- a/Sources/XcodeProj/Utils/CommentedString.swift +++ b/Sources/XcodeProj/Utils/CommentedString.swift @@ -1,5 +1,31 @@ import Foundation +private extension UInt8 { + static let tab: UInt8 = 9 // '\t' + static let newline: UInt8 = 10 // '\n' + static let backslash: UInt8 = 92 // '\' + static let underscore: UInt8 = 95 // '_' + static let doubleQuotes: UInt8 = 34 // '"' + static let dollar: UInt8 = 36 // '$' + static let slash: UInt8 = 47 // '/' + + static let dot: UInt8 = 46 // '.' + static let nine: UInt8 = 57 // '9' + + static let capitalA: UInt8 = 65 // 'A' + static let capitalZ: UInt8 = 90 // 'Z' + + static let smallA: UInt8 = 97 // 'a' + static let smallN: UInt8 = 110 // 'n' + static let smallT: UInt8 = 116 // 't' + static let smallZ: UInt8 = 122 // 'z' +} + +private extension ContiguousArray { + static let slashesUTF8CString = "//".utf8CString + static let threeUnderscoresUTF8CString = "___".utf8CString +} + /// String that includes a comment struct CommentedString { /// Entity string value. @@ -18,56 +44,40 @@ struct CommentedString { self.comment = comment } - /// Set of characters that are invalid. - private static var invalidCharacters: CharacterSet = { - var invalidSet = CharacterSet(charactersIn: "_$") - invalidSet.insert(charactersIn: UnicodeScalar(".") ... UnicodeScalar("9")) - invalidSet.insert(charactersIn: UnicodeScalar("A") ... UnicodeScalar("Z")) - invalidSet.insert(charactersIn: UnicodeScalar("a") ... UnicodeScalar("z")) - invalidSet.invert() - return invalidSet - }() - - /// Substrings that cause Xcode to quote the string content. - private let invalidStrings = [ - "___", - "//", - ] - /// Returns a valid string for Xcode projects. var validString: String { switch string { - case "": return "".quoted + case "": return "\"\"" case "false": return "NO" case "true": return "YES" default: break } - var escaped = string - // escape escape - if escaped.contains("\\" as Character) { - escaped = escaped.replacingOccurrences(of: "\\", with: "\\\\") - } - // escape quotes - if escaped.contains("\"" as Character) { - escaped = escaped.replacingOccurrences(of: "\"", with: "\\\"") - } - // escape tab - if escaped.contains("\t" as Character) { - escaped = escaped.replacingOccurrences(of: "\t", with: "\\t") - } - // escape newlines - if escaped.contains("\n" as Character) { - escaped = escaped.replacingOccurrences(of: "\n", with: "\\n") - } + var str = string + return str.withUTF8 { buffer -> String in + let containsInvalidCharacters = buffer.containsInvalidCharacters - if !escaped.isQuoted, - escaped.rangeOfCharacter(from: CommentedString.invalidCharacters) != nil || - invalidStrings.contains(where: { escaped.range(of: $0) != nil }) { - escaped = escaped.quoted - } + if !containsInvalidCharacters() { + let containsSpecialCheckCharacters = buffer.containsSpecialCheckCharacters() + + if !containsSpecialCheckCharacters { + return string + } else if !buffer.containsCString(ContiguousArray.slashesUTF8CString), + !buffer.containsCString(ContiguousArray.threeUnderscoresUTF8CString) { + return string + } + } + + // calculate exact size + let escapedCapacity = buffer.escapedCommentCapacity() - return escaped + // write directly into String storage + return String(unsafeUninitializedCapacity: escapedCapacity) { stringBuffer in + stringBuffer.fillValidString(from: buffer) + + return escapedCapacity + } + } } } @@ -79,7 +89,7 @@ extension CommentedString: Hashable { } static func == (lhs: CommentedString, rhs: CommentedString) -> Bool { - return lhs.string == rhs.string && lhs.comment == rhs.comment + lhs.string == rhs.string && lhs.comment == rhs.comment } } @@ -98,3 +108,107 @@ extension CommentedString: ExpressibleByStringLiteral { self.init(value) } } + +// MARK: - Private + +private extension UnsafeMutableBufferPointer { + /// Fills preallocated `UnsafeBufferPointer` + func fillValidString(from buffer: UnsafeBufferPointer) { + var outIndex = 0 + + self[outIndex] = .doubleQuotes + outIndex += 1 + + for character in buffer { + switch character { + case .backslash: + self[outIndex] = .backslash + self[outIndex + 1] = .backslash + outIndex += 2 + + case .doubleQuotes: + self[outIndex] = .backslash + self[outIndex + 1] = .doubleQuotes + outIndex += 2 + + case .tab: + self[outIndex] = .backslash + self[outIndex + 1] = .smallT + outIndex += 2 + + case .newline: + self[outIndex] = .backslash + self[outIndex + 1] = .smallN + outIndex += 2 + + default: + self[outIndex] = character + outIndex += 1 + } + } + + self[outIndex] = .doubleQuotes + } +} + +private extension UnsafeBufferPointer { + /// Valid characters are: + /// 1. `_` and `$` + /// 2. `.`...`9` + /// 3. `A`...`Z` + /// 4. `a`...`z` + func containsInvalidCharacters() -> Bool { + for character in self { + // character == '_' || character == '$' + if character == .underscore || character == .dollar { + continue + } + // character >= '.' && character <= '9' + if character >= .dot, character <= .nine { + continue + } + // character >= 'A' && character <= 'Z' + if character >= .capitalA, character <= .capitalZ { + continue + } + // character >= 'a' && character <= 'z' + if character >= .smallA, character <= .smallZ { + continue + } + + return true + } + + return false + } + + /// Special check characters are `_` and `/` + func containsSpecialCheckCharacters() -> Bool { + for character in self { + if character == .underscore || character == .slash { + return true + } + } + + return false + } + + /// Calculates escaped string size + /// Basically, `count + count(where: { [.backslash, .doubleQuotes, .tab, .newline].contains($0) }` + func escapedCommentCapacity() -> Int { + var escapeCount = 0 + + for character in self { + switch character { + case .backslash, .doubleQuotes, .tab, .newline: + escapeCount += 1 // each adds one extra byte + default: + break + } + } + + return count // original bytes + + escapeCount // extra escape bytes + + 2 // surrounding quotes + } +} diff --git a/Sources/XcodeProj/Utils/Decoders.swift b/Sources/XcodeProj/Utils/Decoders.swift index 610287280..31b610065 100644 --- a/Sources/XcodeProj/Utils/Decoders.swift +++ b/Sources/XcodeProj/Utils/Decoders.swift @@ -13,7 +13,7 @@ class PBXObjectReferenceRepository { /// - objects: objects. /// - Returns: object reference. func getOrCreate(reference: String, objects: PBXObjects) -> PBXObjectReference { - return lock.whileLocked { + lock.whileLocked { if let objectReference = references[reference] { return objectReference } @@ -31,12 +31,10 @@ class ProjectDecodingContext { /// Objects. let objects: PBXObjects - let pbxProjValueReader: ((String) -> Any?)? - init(pbxProjValueReader: ((String) -> Any?)? = nil) { + init() { objectReferenceRepository = PBXObjectReferenceRepository() objects = PBXObjects(objects: []) - self.pbxProjValueReader = pbxProjValueReader } } @@ -44,11 +42,11 @@ class ProjectDecodingContext { extension CodingUserInfoKey { /// Context user info key. - static var context: CodingUserInfoKey = CodingUserInfoKey(rawValue: "context")! + static let context: CodingUserInfoKey = .init(rawValue: "context")! } /// Xcodeproj JSON decoder. -class XcodeprojJSONDecoder: JSONDecoder { +final class XcodeprojJSONDecoder: JSONDecoder, @unchecked Sendable { /// Default init. init(context: ProjectDecodingContext = ProjectDecodingContext()) { super.init() @@ -57,7 +55,7 @@ class XcodeprojJSONDecoder: JSONDecoder { } /// Xcodeproj property list decoder. -class XcodeprojPropertyListDecoder: PropertyListDecoder { +final class XcodeprojPropertyListDecoder: PropertyListDecoder, @unchecked Sendable { /// Default init. init(context: ProjectDecodingContext = ProjectDecodingContext()) { super.init() @@ -71,6 +69,6 @@ extension Decoder { /// Returns the decoding context. var context: ProjectDecodingContext { // swiftlint:disable:next force_cast - return userInfo[.context] as! ProjectDecodingContext + userInfo[.context] as! ProjectDecodingContext } } diff --git a/Sources/XcodeProj/Utils/JSONDecoding.swift b/Sources/XcodeProj/Utils/JSONDecoding.swift index ac3311694..1478f4199 100644 --- a/Sources/XcodeProj/Utils/JSONDecoding.swift +++ b/Sources/XcodeProj/Utils/JSONDecoding.swift @@ -19,12 +19,6 @@ struct JSONCodingKeys: CodingKey { extension KeyedDecodingContainer { func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] { - // Optimization for root dictionary decoding - if let decoder = try? superDecoder().context, - let pbxProjValueReader = decoder.pbxProjValueReader, - let result = pbxProjValueReader(key.stringValue) as? [String: Any] { - return result - } let container = try nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key) return try container.decode(type) } @@ -95,7 +89,7 @@ extension UnkeyedDecodingContainer { } mutating func decode(_ type: [String: Any].Type) throws -> [String: Any] { - let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self) + let nestedContainer = try nestedContainer(keyedBy: JSONCodingKeys.self) return try nestedContainer.decode(type) } } diff --git a/Sources/XcodeProj/Utils/PBXBatchUpdater.swift b/Sources/XcodeProj/Utils/PBXBatchUpdater.swift index 69674ffab..2e127d5e9 100644 --- a/Sources/XcodeProj/Utils/PBXBatchUpdater.swift +++ b/Sources/XcodeProj/Utils/PBXBatchUpdater.swift @@ -75,16 +75,15 @@ public final class PBXBatchUpdater { return existing } - let path: String? - switch sourceTree { + let path: String? = switch sourceTree { case .group: - path = filePath.relative(to: groupPath).string + filePath.relative(to: groupPath).string case .sourceRoot: - path = filePath.relative(to: sourceRoot).string + filePath.relative(to: sourceRoot).string case .absolute: - path = filePath.string + filePath.string default: - path = nil + nil } let fileReference = PBXFileReference( sourceTree: sourceTree, @@ -105,7 +104,7 @@ public final class PBXBatchUpdater { private func existingFileReference(at filePath: Path, in group: PBXGroup) throws -> PBXFileReference? { let objectReferences = try lazilyInstantiateObjectReferences() if let existingObjectReference = objectReferences[filePath], - let existingFileReference = objects.fileReferences[existingObjectReference] { + let existingFileReference = objects.fileReferences[existingObjectReference] { if !group.childrenReferences.contains(existingObjectReference) { group.childrenReferences.append(existingObjectReference) } @@ -167,14 +166,14 @@ public final class PBXBatchUpdater { private func lazilyInstantiateObjectReferences() throws -> [Path: PBXObjectReference] { let objectReferences: [Path: PBXObjectReference] - if let references = self.references { + if let references { objectReferences = references } else { - objectReferences = Dictionary(uniqueKeysWithValues: - try objects.fileReferences.compactMap { + objectReferences = try Dictionary(uniqueKeysWithValues: + objects.fileReferences.compactMap { let fullPath = try $0.value.fullPath(sourceRoot: sourceRoot)! return (fullPath, $0.key) - }) + }) references = objectReferences } return objectReferences @@ -182,14 +181,14 @@ public final class PBXBatchUpdater { private func lazilyInstantiateGroups() throws -> [Path: PBXGroup] { let unwrappedGroups: [Path: PBXGroup] - if let groups = self.groups { + if let groups { unwrappedGroups = groups } else { - unwrappedGroups = Dictionary(uniqueKeysWithValues: - try objects.groups.compactMap { + unwrappedGroups = try Dictionary(uniqueKeysWithValues: + objects.groups.compactMap { let fullPath = try $0.value.fullPath(sourceRoot: sourceRoot)! return (fullPath, $0.value) - }) + }) groups = unwrappedGroups } return unwrappedGroups diff --git a/Sources/XcodeProj/Utils/PlistDecoding.swift b/Sources/XcodeProj/Utils/PlistDecoding.swift new file mode 100644 index 000000000..4e65da68a --- /dev/null +++ b/Sources/XcodeProj/Utils/PlistDecoding.swift @@ -0,0 +1,37 @@ +import Foundation + +indirect enum PlistObject: Sendable, Equatable { + case string(String) + case array([String]) + case dictionary([String: PlistObject]) +} + +extension PlistObject: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + do { + let string = try container.decode(String.self) + self = .string(string) + } catch { + do { + let array = try container.decode([String].self) + self = .array(array) + } catch { + let dictionary = try container.decode([String: PlistObject].self) + self = .dictionary(dictionary) + } + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .string(string): + try container.encode(string) + case let .array(array): + try container.encode(array) + case let .dictionary(dictionary): + try container.encode(dictionary) + } + } +} diff --git a/Sources/XcodeProj/Utils/PlistValue.swift b/Sources/XcodeProj/Utils/PlistValue.swift index cd48273ff..432a04b29 100644 --- a/Sources/XcodeProj/Utils/PlistValue.swift +++ b/Sources/XcodeProj/Utils/PlistValue.swift @@ -12,22 +12,22 @@ indirect enum PlistValue { var string: CommentedString? { switch self { - case let .string(string): return string - default: return nil + case let .string(string): string + default: nil } } var array: [PlistValue]? { switch self { - case let .array(array): return array - default: return nil + case let .array(array): array + default: nil } } var dictionary: [CommentedString: PlistValue]? { switch self { - case let .dictionary(dictionary): return dictionary - default: return nil + case let .dictionary(dictionary): dictionary + default: nil } } } @@ -72,19 +72,68 @@ extension PlistValue: Equatable { static func == (lhs: PlistValue, rhs: PlistValue) -> Bool { switch (lhs, rhs) { case let (.string(lhsString), .string(rhsString)): - return lhsString == rhsString + lhsString == rhsString case let (.array(lhsArray), .array(rhsArray)): - return lhsArray == rhsArray + lhsArray == rhsArray case let (.dictionary(lhsDictionary), .dictionary(rhsDictionary)): - return lhsDictionary == rhsDictionary + lhsDictionary == rhsDictionary default: - return false + false } } } // MARK: - Dictionary Extension (PlistValue) +extension [String: BuildSetting] { + func plist() -> PlistValue { + var dictionary: [CommentedString: PlistValue] = [:] + forEach { key, value in + switch value { + case let .string(stringValue): + dictionary[CommentedString(key)] = PlistValue.string(CommentedString(stringValue)) + case let .array(arrayValue): + dictionary[CommentedString(key)] = arrayValue.plist() + } + } + return .dictionary(dictionary) + } +} + +extension [String: BuildFileSetting] { + func plist() -> PlistValue { + var dictionary: [CommentedString: PlistValue] = [:] + forEach { key, value in + switch value { + case let .string(stringValue): + dictionary[CommentedString(key)] = PlistValue.string(CommentedString(stringValue)) + case let .array(arrayValue): + dictionary[CommentedString(key)] = arrayValue.plist() + } + } + return .dictionary(dictionary) + } +} + +extension [String: ProjectAttribute] { + func plist() -> PlistValue { + var dictionary: [CommentedString: PlistValue] = [:] + forEach { key, value in + switch value { + case let .string(stringValue): + dictionary[CommentedString(key)] = PlistValue.string(CommentedString(stringValue)) + case let .array(arrayValue): + dictionary[CommentedString(key)] = arrayValue.plist() + case let .attributeDictionary(attributes): + dictionary[CommentedString(key)] = attributes.mapValues { $0.plist() }.plist() + case let .targetReference(object): + dictionary[CommentedString(key)] = .string(CommentedString(object.reference.value)) + } + } + return .dictionary(dictionary) + } +} + extension Dictionary where Key == String { func plist() -> PlistValue { var dictionary: [CommentedString: PlistValue] = [:] @@ -95,6 +144,8 @@ extension Dictionary where Key == String { dictionary[CommentedString(key)] = subDictionary.plist() } else if let string = value as? CustomStringConvertible { dictionary[CommentedString(key)] = .string(CommentedString(string.description)) + } else if let plistValue = value as? PlistValue { + dictionary[CommentedString(key)] = plistValue } } return .dictionary(dictionary) @@ -105,7 +156,7 @@ extension Dictionary where Key == String { extension Array { func plist() -> PlistValue { - return .array(compactMap { (element) -> PlistValue? in + .array(compactMap { element -> PlistValue? in if let array = element as? [Any] { return array.plist() } else if let dictionary = element as? [String: Any] { diff --git a/Sources/XcodeProj/Utils/ReferenceGenerator.swift b/Sources/XcodeProj/Utils/ReferenceGenerator.swift index e5e24a6a2..b197c55f4 100644 --- a/Sources/XcodeProj/Utils/ReferenceGenerator.swift +++ b/Sources/XcodeProj/Utils/ReferenceGenerator.swift @@ -31,6 +31,7 @@ final class ReferenceGenerator: ReferenceGenerating { // cache current reference values var references: Set = [] + // swiftformat:disable:next preferForLoop proj.objects.forEach { object in if !object.reference.temporary { references.insert(object.reference.value) @@ -50,19 +51,18 @@ final class ReferenceGenerator: ReferenceGenerating { try generateGroupReferences(productsGroup, identifiers: identifiers) } + // Project references + try project.projectReferences.forEach { objectReferenceDict in + guard let projectReference = objectReferenceDict[Xcode.ProjectReference.projectReferenceKey]?.getObject() as? PBXFileReference, + let productsGroup = objectReferenceDict[Xcode.ProjectReference.productGroupKey]?.getObject() as? PBXGroup else { return } + try generateFileReference(projectReference, identifiers: identifiers) + try generateGroupReferences(productsGroup, identifiers: identifiers + [projectReference.name ?? projectReference.path ?? ""]) + } + // Targets let targets: [PBXTarget] = project.targets try targets.forEach { try generateTargetReferences($0, identifiers: identifiers) } - // Project references - try project.projectReferences.flatMap { $0.values }.forEach { objectReference in - if let fileReference = objectReference.getObject() as? PBXFileReference { - try generateFileReference(fileReference, identifiers: identifiers) - } else if let group = objectReference.getObject() as? PBXGroup { - try generateGroupReferences(group, identifiers: identifiers) - } - } - /// Configuration list if let configurationList: XCConfigurationList = project.buildConfigurationListReference.getObject() { try generateConfigurationListReferences(configurationList, identifiers: identifiers) @@ -80,24 +80,39 @@ final class ReferenceGenerator: ReferenceGenerating { fixReference(for: project, identifiers: identifiers) // Packages - project.packages.forEach { + for remotePackage in project.remotePackages { var identifiers = identifiers - identifiers.append($0.repositoryURL ?? $0.name ?? "") - fixReference(for: $0, identifiers: identifiers) + identifiers.append(remotePackage.repositoryURL ?? remotePackage.name ?? "") + fixReference(for: remotePackage, identifiers: identifiers) + } + + // Packages + for localPackage in project.localPackages { + var identifiers = identifiers + identifiers.append(localPackage.relativePath) + fixReference(for: localPackage, identifiers: identifiers) } // Targets let targets: [PBXTarget] = project.targetReferences.objects() - targets.forEach { target in - + for target in targets { var identifiers = identifiers identifiers.append(target.name) // Packages - target.packageProductDependencies.forEach { + target.packageProductDependencies?.forEach { packageProductDependency in var identifiers = identifiers - identifiers.append($0.productName) - fixReference(for: $0, identifiers: identifiers) + identifiers.append(packageProductDependency.productName) + fixReference(for: packageProductDependency, identifiers: identifiers) + } + + // Build Tool Plug-ins + target.dependencies.forEach { + guard let product = $0.product, product.isPlugin else { return } + + var identifiers = identifiers + identifiers.append(product.productName) + fixReference(for: product, identifiers: identifiers) } fixReference(for: target, identifiers: identifiers) @@ -124,6 +139,8 @@ final class ReferenceGenerator: ReferenceGenerating { guard let childFileElement: PBXFileElement = child.getObject() else { return } if let childGroup = childFileElement as? PBXGroup { try generateGroupReferences(childGroup, identifiers: identifiers) + } else if let childSynchronizedRootGroup = childFileElement as? PBXFileSystemSynchronizedRootGroup { + try generateSynchronizedRootGroupReferences(childSynchronizedRootGroup, identifiers: identifiers) } else if let childFileReference = childFileElement as? PBXFileReference { try generateFileReference(childFileReference, identifiers: identifiers) } else if let childReferenceProxy = childFileElement as? PBXReferenceProxy { @@ -146,6 +163,50 @@ final class ReferenceGenerator: ReferenceGenerating { fixReference(for: fileReference, identifiers: identifiers) } + /// Generates the reference for a synchronized root group object. + /// + /// - Parameters: + /// - synchronizedRootGroup: synchronized root group instance. + /// - identifiers: list of identifiers. + private func generateSynchronizedRootGroupReferences(_ synchronizedRootGroup: PBXFileSystemSynchronizedRootGroup, + identifiers: [String]) throws { + var identifiers = identifiers + if let groupName = synchronizedRootGroup.fileName() { + identifiers.append(groupName) + } + + fixReference(for: synchronizedRootGroup, identifiers: identifiers) + + // Generate references for exception sets + if let exceptions = synchronizedRootGroup.exceptions { + try exceptions.forEach { exception in + try generateExceptionSetReferences(exception, identifiers: identifiers) + } + } + } + + /// Generates the reference for an exception set object. + /// + /// - Parameters: + /// - exceptionSet: exception set instance. + /// - identifiers: list of identifiers. + private func generateExceptionSetReferences(_ exceptionSet: PBXFileSystemSynchronizedExceptionSet, + identifiers: [String]) throws { + var identifiers = identifiers + + if let buildFileException = exceptionSet as? PBXFileSystemSynchronizedBuildFileExceptionSet { + if let target = buildFileException.target { + identifiers.append(target.reference.value) + } + fixReference(for: buildFileException, identifiers: identifiers) + } else if let buildPhaseException = exceptionSet as? PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet { + if let buildPhase = buildPhaseException.buildPhase { + identifiers.append(buildPhase.reference.value) + } + fixReference(for: buildPhaseException, identifiers: identifiers) + } + } + /// Generates the reference for a configuration list object. /// /// - Parameters: @@ -208,9 +269,9 @@ final class ReferenceGenerator: ReferenceGenerating { // Target proxy if let targetProxyReference = targetDependency.targetProxyReference, - targetProxyReference.temporary, - let targetProxy = targetDependency.targetProxy, - let remoteGlobalIDString = targetProxy.remoteGlobalID?.uuid { + targetProxyReference.temporary, + let targetProxy = targetDependency.targetProxy, + let remoteGlobalIDString = targetProxy.remoteGlobalID?.uuid { var identifiers = identifiers identifiers.append(remoteGlobalIDString) fixReference(for: targetProxy, identifiers: identifiers) @@ -287,7 +348,7 @@ final class ReferenceGenerator: ReferenceGenerating { var identifiers = identifiers if let fileReference = buildFile.fileReference, - let fileReferenceObject: PBXObject = fileReference.getObject() { + let fileReferenceObject: PBXObject = fileReference.getObject() { identifiers.append(fileReferenceObject.reference.value) } @@ -318,8 +379,8 @@ extension ReferenceGenerator { /// - Parameters: /// - object: The object to generate a reference for /// - identifiers: list of identifiers used to generate the reference of the object. - func fixReference(for object: T, - identifiers: [String]) { + func fixReference(for object: some PBXObject, + identifiers: [String]) { if object.reference.temporary { var identifiers = identifiers if let context = object.context { diff --git a/Sources/XcodeProj/Utils/XCConfig.swift b/Sources/XcodeProj/Utils/XCConfig.swift index 92ee297dd..639f6c1b0 100644 --- a/Sources/XcodeProj/Utils/XCConfig.swift +++ b/Sources/XcodeProj/Utils/XCConfig.swift @@ -35,15 +35,15 @@ public final class XCConfig { let fileLines = try path.read().components(separatedBy: "\n") includes = fileLines .compactMap(XCConfigParser.configFrom(path: path, projectPath: projectPath)) - var buildSettings: [String: String] = [:] + var buildSettings: BuildSettings = [:] fileLines .compactMap(XCConfigParser.settingFrom) - .forEach { buildSettings[$0.key] = $0.value } + .forEach { buildSettings[$0.key] = .string($0.value) } self.buildSettings = buildSettings } } -final class XCConfigParser { +enum XCConfigParser { /// Given the path the line is being parsed from, it returns a function that parses a line, /// and returns the include path and the config that the include is pointing to. /// @@ -51,24 +51,24 @@ final class XCConfigParser { /// - Parameter projectPath: path where the .xcodeproj is, for resolving project-relative includes. /// - Returns: function that parses the line. static func configFrom(path: Path, projectPath: Path?) -> (String) -> (include: Path, config: XCConfig)? { - return { line in + { line in includeRegex.matches(in: line, options: [], range: NSRange(location: 0, length: line.count)) - .compactMap { (match) -> String? in + .compactMap { match -> String? in if match.numberOfRanges == 2 { return NSString(string: line).substring(with: match.range(at: 1)) } return nil } - .compactMap { pathString in - let includePath: Path = Path(pathString) + .compactMap { pathString -> (include: Path, config: XCConfig)? in + let includePath: Path = .init(pathString) var config: XCConfig? do { // first try to load the included xcconfig relative to the current xcconfig config = try XCConfig(path: path.parent() + includePath, projectPath: projectPath) - } catch (XCConfigError.notFound(_)) where projectPath != nil { + } catch XCConfigError.notFound(_) where projectPath != nil { // if that fails, try to load the included xcconfig relative to the project config = try? XCConfig(path: projectPath!.parent() + includePath, projectPath: projectPath) } catch { @@ -81,11 +81,11 @@ final class XCConfigParser { } static func settingFrom(line: String) -> (key: String, value: String)? { - return settingRegex.matches(in: line, - options: [], - range: NSRange(location: 0, - length: line.count)) - .compactMap { (match) -> (key: String, value: String)? in + settingRegex.matches(in: line, + options: [], + range: NSRange(location: 0, + length: line.count)) + .compactMap { match -> (key: String, value: String)? in if match.numberOfRanges == 3 { let key: String = NSString(string: line).substring(with: match.range(at: 1)) let value: String = NSString(string: line).substring(with: match.range(at: 2)) @@ -97,9 +97,9 @@ final class XCConfigParser { } // swiftlint:disable:next force_try - private static var includeRegex = try! NSRegularExpression(pattern: "#include\\s+\"(.+\\.xcconfig)\"", options: .caseInsensitive) + private static let includeRegex = try! NSRegularExpression(pattern: "#include\\s+\"(.+\\.xcconfig)\"", options: .caseInsensitive) // swiftlint:disable:next force_try - private static var settingRegex = try! NSRegularExpression(pattern: "^([a-zA-Z0-9_\\[\\]=\\*~]+)\\s*=\\s*(\"?.*?\"?)\\s*(?:;\\s*)?$", options: []) + private static let settingRegex = try! NSRegularExpression(pattern: "^([a-zA-Z0-9_\\[\\]=\\*~]+)\\s*=\\s*(\"?.*?\"?)\\s*(?:;\\s*)?(?=$|\\/\\/)", options: []) } // MARK: - XCConfig Extension (Equatable) @@ -114,24 +114,24 @@ extension XCConfig: Equatable { return false } } - return NSDictionary(dictionary: lhs.buildSettings).isEqual(to: rhs.buildSettings) + return lhs.buildSettings == rhs.buildSettings } } // MARK: - XCConfig Extension (Helpers) -extension XCConfig { +public extension XCConfig { /// It returns the build settings after flattening all the includes. /// /// - Returns: build settings flattening all the includes. - public func flattenedBuildSettings() -> [String: Any] { - var content: [String: Any] = buildSettings + func flattenedBuildSettings() -> [String: BuildSetting] { + var content: [String: BuildSetting] = buildSettings includes - .map { $0.1 } + .map(\.1) .flattened() - .map { $0.buildSettings } + .map(\.buildSettings) .forEach { configDictionary in - configDictionary.forEach { key, value in + for (key, value) in configDictionary { if content[key] == nil { content[key] = value } } } @@ -143,19 +143,28 @@ extension XCConfig { extension XCConfig: Writable { public func write(path: Path, override: Bool) throws { - var content = "" - content.append(writeIncludes()) - content.append("\n") - content.append(writeBuildSettings()) + let content = getContent() if override, path.exists { try path.delete() } try path.write(content) } + public func dataRepresentation() throws -> Data? { + getContent().data(using: .utf8) + } + + private func getContent() -> String { + var content = "" + content.append(writeIncludes()) + content.append("\n") + content.append(writeBuildSettings()) + return content + } + private func writeIncludes() -> String { var content = "" - includes.forEach { include in + for include in includes { content.append("#include \"\(include.0.string)\"\n") } content.append("\n") @@ -164,7 +173,7 @@ extension XCConfig: Writable { private func writeBuildSettings() -> String { var content = "" - buildSettings.forEach { key, value in + for (key, value) in buildSettings { content.append("\(key) = \(value)\n") } content.append("\n") @@ -174,15 +183,15 @@ extension XCConfig: Writable { // MARK: - Array Extension (XCConfig) -extension Array where Element == XCConfig { +extension [XCConfig] { /// It returns an array with the XCConfig reversely flattened. It's useful for resolving the build settings. /// /// - Returns: flattened configurations array. func flattened() -> [XCConfig] { - let reversed = self.reversed() - .flatMap { (config) -> [XCConfig] in + let reversed = reversed() + .flatMap { config -> [XCConfig] in var configs = [XCConfig(includes: [], buildSettings: config.buildSettings)] - configs.append(contentsOf: config.includes.map { $0.1 }.flattened()) + configs.append(contentsOf: config.includes.map(\.1).flattened()) return configs } return reversed diff --git a/Sources/XcodeProj/Workspace/XCWorkspace.swift b/Sources/XcodeProj/Workspace/XCWorkspace.swift index 6bba2c04b..afefcf44f 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspace.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspace.swift @@ -6,6 +6,9 @@ public final class XCWorkspace: Writable, Equatable { /// Workspace data public var data: XCWorkspaceData + /// The path to the `.xcworkspace` directory. + public let path: Path? + // MARK: - Init /// Initializes the workspace with the path where the workspace is. @@ -15,20 +18,20 @@ public final class XCWorkspace: Writable, Equatable { /// - Parameter path: .xcworkspace path. /// - Throws: throws an error if the workspace cannot be initialized. public convenience init(path: Path) throws { - if !path.exists { + guard path.exists else { throw XCWorkspaceError.notFound(path: path) } let xcworkspaceDataPaths = path.glob("*.xcworkspacedata") - if xcworkspaceDataPaths.isEmpty { - self.init() + if let xcworkspaceDataPath = xcworkspaceDataPaths.first { + try self.init(data: XCWorkspaceData(path: xcworkspaceDataPath), path: path) } else { - try self.init(data: XCWorkspaceData(path: xcworkspaceDataPaths.first!)) + self.init() } } /// Initializes a default workspace with a single reference that points to self: public convenience init() { - let data = XCWorkspaceData(children: [.file(.init(location: .self("")))]) + let data = XCWorkspaceData(children: [.file(.init(location: .current("")))]) self.init(data: data) } @@ -44,8 +47,10 @@ public final class XCWorkspace: Writable, Equatable { /// /// - Parameters: /// - data: workspace data. - public init(data: XCWorkspaceData) { + /// - path: .xcworkspace path. + public init(data: XCWorkspaceData, path: Path? = nil) { self.data = data + self.path = path } // MARK: - Writable @@ -53,15 +58,28 @@ public final class XCWorkspace: Writable, Equatable { public func write(path: Path, override: Bool = true) throws { let dataPath = path + "contents.xcworkspacedata" if override, dataPath.exists { + if let existingContent: String = try? dataPath.read(), + existingContent == data.rawContents() { + // Raw data matches what's on disk + // there's no need to overwrite the contents + // this mitigates Xcode forcing users to + // close and re-open projects/workspaces + // on regeneration. + return + } try dataPath.delete() } try dataPath.mkpath() try data.write(path: dataPath) } + public func dataRepresentation() throws -> Data? { + data.rawContents().data(using: .utf8) + } + // MARK: - Equatable - public static func == (_: XCWorkspace, rhs: XCWorkspace) -> Bool { - return rhs.data == rhs.data + public static func == (lhs: XCWorkspace, rhs: XCWorkspace) -> Bool { + lhs.data == rhs.data && lhs.path == rhs.path } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceData.swift b/Sources/XcodeProj/Workspace/XCWorkspaceData.swift index d67120ab6..0a5601bee 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceData.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceData.swift @@ -11,8 +11,8 @@ public final class XCWorkspaceData { } extension XCWorkspaceData: Equatable { - public static func == (_: XCWorkspaceData, rhs: XCWorkspaceData) -> Bool { - return rhs.children == rhs.children + public static func == (lhs: XCWorkspaceData, rhs: XCWorkspaceData) -> Bool { + lhs.children == rhs.children } } @@ -37,19 +37,27 @@ extension XCWorkspaceData: Writable { self.init(children: children) } - // MARK: - - - public func write(path: Path, override: Bool = true) throws { + func rawContents() -> String { let document = AEXMLDocument() let workspace = document.addChild(name: "Workspace", value: nil, attributes: ["version": "1.0"]) _ = children .map { $0.xmlElement() } .map(workspace.addChild) + return document.xmlXcodeFormat + } + // MARK: - + + public func write(path: Path, override: Bool = true) throws { + let rawXml = rawContents() if override, path.exists { try path.delete() } - try path.write(document.xmlXcodeFormat) + try path.write(rawXml) + } + + public func dataRepresentation() throws -> Data? { + rawContents().data(using: .utf8) } } @@ -70,9 +78,9 @@ private extension XCWorkspaceDataElement { func xmlElement() -> AEXMLElement { switch self { case let .file(fileRef): - return fileRef.xmlElement() + fileRef.xmlElement() case let .group(group): - return group.xmlElement() + group.xmlElement() } } } @@ -126,12 +134,12 @@ private extension XCWorkspaceDataFileRef { guard let location = element.attributes["location"] else { throw Error.missingLocationAttribute } - self.init(location: try XCWorkspaceDataElementLocationType(string: location)) + try self.init(location: XCWorkspaceDataElementLocationType(string: location)) } func xmlElement() -> AEXMLElement { - return AEXMLElement(name: "FileRef", - value: nil, - attributes: ["location": location.description]) + AEXMLElement(name: "FileRef", + value: nil, + attributes: ["location": location.description]) } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceDataElement.swift b/Sources/XcodeProj/Workspace/XCWorkspaceDataElement.swift index 439a8f092..78467a716 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceDataElement.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceDataElement.swift @@ -12,9 +12,9 @@ public enum XCWorkspaceDataElement: Equatable { public var location: XCWorkspaceDataElementLocationType { switch self { case let .file(ref): - return ref.location + ref.location case let .group(ref): - return ref.location + ref.location } } @@ -23,11 +23,11 @@ public enum XCWorkspaceDataElement: Equatable { public static func == (lhs: XCWorkspaceDataElement, rhs: XCWorkspaceDataElement) -> Bool { switch (lhs, rhs) { case let (.file(lhs), .file(rhs)): - return lhs == rhs + lhs == rhs case let (.group(lhs), .group(rhs)): - return lhs == rhs + lhs == rhs default: - return false + false } } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift b/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift index 7cd890906..53d1966e0 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift @@ -9,40 +9,40 @@ public enum XCWorkspaceDataElementLocationType { case container(String) // "Relative to container" case developer(String) // "Relative to Developer Directory" case group(String) // "Relative to group" - case `self`(String) // Single project workspace in xcodeproj directory + case current(String) // Single project workspace in xcodeproj directory case other(String, String) public var schema: String { switch self { case .absolute: - return "absolute" + "absolute" case .container: - return "container" + "container" case .developer: - return "developer" + "developer" case .group: - return "group" - case .self: - return "self" + "group" + case .current: + "self" case let .other(schema, _): - return schema + schema } } public var path: String { switch self { case let .absolute(path): - return path + path case let .container(path): - return path + path case let .developer(path): - return path + path case let .group(path): - return path - case let .self(path): - return path + path + case let .current(path): + path case let .other(_, path): - return path + path } } @@ -62,7 +62,7 @@ public enum XCWorkspaceDataElementLocationType { case "group": self = .group(path) case "self": - self = .self(path) + self = .current(path) default: self = .other(schema, path) } @@ -71,7 +71,7 @@ public enum XCWorkspaceDataElementLocationType { extension XCWorkspaceDataElementLocationType: CustomStringConvertible { public var description: String { - return "\(schema):\(path)" + "\(schema):\(path)" } } @@ -79,18 +79,18 @@ extension XCWorkspaceDataElementLocationType: Equatable { public static func == (lhs: XCWorkspaceDataElementLocationType, rhs: XCWorkspaceDataElementLocationType) -> Bool { switch (lhs, rhs) { case let (.absolute(lhs), .absolute(rhs)): - return lhs == rhs + lhs == rhs case let (.container(lhs), .container(rhs)): - return lhs == rhs + lhs == rhs case let (.developer(lhs), .developer(rhs)): - return lhs == rhs + lhs == rhs case let (.group(lhs), .group(rhs)): - return lhs == rhs - case let (.self(lhs), .self(rhs)): - return lhs == rhs - case let (.other(lhs), .other(rhs)): - return lhs.0 == rhs.0 && lhs.1 == rhs.1 - default: return false + lhs == rhs + case let (.current(lhs), .current(rhs)): + lhs == rhs + case let (.other(lhs0, lhs1), .other(rhs0, rhs1)): + lhs0 == rhs0 && lhs1 == rhs1 + default: false } } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceDataFileRef.swift b/Sources/XcodeProj/Workspace/XCWorkspaceDataFileRef.swift index 101fd8c0d..03b59dbd1 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceDataFileRef.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceDataFileRef.swift @@ -10,6 +10,6 @@ public final class XCWorkspaceDataFileRef { extension XCWorkspaceDataFileRef: Equatable { public static func == (lhs: XCWorkspaceDataFileRef, rhs: XCWorkspaceDataFileRef) -> Bool { - return lhs.location == rhs.location + lhs.location == rhs.location } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceDataGroup.swift b/Sources/XcodeProj/Workspace/XCWorkspaceDataGroup.swift index ff18f9b41..5d281d89f 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceDataGroup.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceDataGroup.swift @@ -14,7 +14,7 @@ public final class XCWorkspaceDataGroup { extension XCWorkspaceDataGroup: Equatable { public static func == (lhs: XCWorkspaceDataGroup, rhs: XCWorkspaceDataGroup) -> Bool { - return lhs.location == rhs.location && + lhs.location == rhs.location && lhs.name == rhs.name && lhs.children == rhs.children } diff --git a/Tapestries/Package.resolved b/Tapestries/Package.resolved deleted file mode 100644 index 17b034539..000000000 --- a/Tapestries/Package.resolved +++ /dev/null @@ -1,97 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "acho", - "repositoryURL": "https://github.com/fortmarek/acho", - "state": { - "branch": "spm_bump", - "revision": "7fb6330df1a1e647e43a15f9ff0f03e02e9f46e8", - "version": null - } - }, - { - "package": "AEXML", - "repositoryURL": "https://github.com/tadija/AEXML", - "state": { - "branch": null, - "revision": "e4d517844dd03dac557e35d77a8e9ab438de91a6", - "version": "4.4.0" - } - }, - { - "package": "ColorizeSwift", - "repositoryURL": "https://github.com/mtynior/ColorizeSwift.git", - "state": { - "branch": "master", - "revision": "7492d039d594daccc39cacea343eed6cc9d1ed5a", - "version": null - } - }, - { - "package": "PathKit", - "repositoryURL": "https://github.com/kylef/PathKit", - "state": { - "branch": null, - "revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511", - "version": "1.0.0" - } - }, - { - "package": "Spectre", - "repositoryURL": "https://github.com/kylef/Spectre.git", - "state": { - "branch": null, - "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5", - "version": "0.9.0" - } - }, - { - "package": "llbuild", - "repositoryURL": "https://github.com/apple/swift-llbuild.git", - "state": { - "branch": "master", - "revision": "c0aafa63fb42311c3754bd50530e0f7b3c6db357", - "version": null - } - }, - { - "package": "SwiftPM", - "repositoryURL": "https://github.com/apple/swift-package-manager", - "state": { - "branch": "swift-5.0-RELEASE", - "revision": "3a57975e10be0b1a8b87992ddf3a49701036f96c", - "version": null - } - }, - { - "package": "tapestry", - "repositoryURL": "https://github.com/AckeeCZ/tapestry.git", - "state": { - "branch": "master", - "revision": "3e11df7e2efa03b6fb71c2e093edc035ed52066a", - "version": null - } - }, - { - "package": "tuist", - "repositoryURL": "https://github.com/fortmarek/tuist.git", - "state": { - "branch": "master", - "revision": "4644c388abf3545bc6fe2c3862964463f54e1fb7", - "version": null - } - }, - { - "package": "XcodeProj", - "repositoryURL": "https://github.com/tuist/XcodeProj", - "state": { - "branch": "master", - "revision": "cdc932551da8f464d1dbd9e940e162f2f7da2ab6", - "version": null - } - } - ] - }, - "version": 1 -} diff --git a/Tapestries/Package.swift b/Tapestries/Package.swift deleted file mode 100644 index a7051c7cf..000000000 --- a/Tapestries/Package.swift +++ /dev/null @@ -1,21 +0,0 @@ -// swift-tools-version:5.1 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "Tapestries", - products: [ - .library(name: "TapestryConfig", targets: ["TapestryConfig"]), - ], - dependencies: [ - // Tapestry - .package(url: "https://github.com/AckeeCZ/tapestry.git", .branch("master")), - ], - targets: [ - .target(name: "TapestryConfig", - dependencies: [ - "PackageDescription", - ]), - ] -) diff --git a/Tapestries/Sources/TapestryConfig/TapestryConfig.swift b/Tapestries/Sources/TapestryConfig/TapestryConfig.swift deleted file mode 100644 index 5054f6e3a..000000000 --- a/Tapestries/Sources/TapestryConfig/TapestryConfig.swift +++ /dev/null @@ -1,14 +0,0 @@ -import PackageDescription - -let config = TapestryConfig(release: Release(actions: [.pre(.dependenciesCompatibility([.cocoapods, .spm(.all)])), - .pre(tool: "tuist", arguments: ["generate"]), - .pre(tool: "bundle", arguments: ["exec", "rake", "carthage_update_dependencies"]), - .pre(tool: "bundle", arguments: ["exec", "rake", "release_check"]), - .pre(.docsUpdate), - .post(tool: "bundle", arguments: ["exec", "pod", "trunk", "push", "--allow-warnings", "--verbose"]), - .post(tool: "bundle", arguments: ["exec", "rake", "archive_carthage"])], - add: ["README.md", - "xcodeproj.podspec", - "CHANGELOG.md"], - commitMessage: "Version \(Argument.version)", - push: true)) diff --git a/Templates/Equality.stencil b/Templates/Equality.stencil index 43df267d7..4d149477d 100644 --- a/Templates/Equality.stencil +++ b/Templates/Equality.stencil @@ -3,18 +3,18 @@ import Foundation {% for type in types.implementing.AutoEquatable|class|!annotated:"skipEquality" %} extension {{ type.name }} { /// :nodoc: - @objc override {{ type.accessLevel }} func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? {{ type.name }} else { return false } - {% for variable in type.storedVariables %} - {% if variable.actualTypeName.name == "[String: Any]" or variable.actualTypeName.name == "[String: Any]?" or variable.actualTypeName.name == "BuildSettings"%} - if !NSDictionary(dictionary: self.{{ variable.name}} {% if variable.typeName.isOptional %}?? [:]{% endif %}).isEqual(to: rhs.{{ variable.name }} {% if variable.typeName.isOptional %}?? [:]{% endif %}) { return false } - {% elif variable|!annotated:"skipEquality" %} - if self.{{ variable.name }} != rhs.{{ variable.name }} { return false } - {% endif %} - {% endfor %} - {% for variable in type.computedVariables|annotated:"forceEquality" %}if self.{{ variable.name }} != rhs.{{ variable.name }} { return false } - {% endfor %} - {% if type.inheritedTypes.first == "NSObject" %}return true{% else %}return super.isEqual(to: rhs){% endif %} + func isEqual(to rhs: {{ type.name }}) -> Bool { + {% for variable in type.storedVariables %} + {% if variable.typeName.dictionary %} + if !NSDictionary(dictionary: {{ variable.name}}{% if variable.typeName.isOptional %} ?? [:]{% endif %}).isEqual(NSDictionary(dictionary: rhs.{{ variable.name }}{% if variable.typeName.isOptional %} ?? [:]{% endif %})) { return false } + {% elif variable|!annotated:"skipEquality" %} + if {{ variable.name }} != rhs.{{ variable.name }} { return false } + {% endif %} + {% endfor %} + {% for variable in type.computedVariables|annotated:"forceEquality" %}if self.{{ variable.name }} != rhs.{{ variable.name }} { return false } + {% endfor %} + {% if type.inheritedTypes.first == "NSObject" %}return true{% else %}return super.isEqual(to: rhs){% endif %} } } -{% endfor %} \ No newline at end of file + +{% endfor %} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 938594b30..000000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,4 +0,0 @@ -import XCTest -@testable import XcodeProjTests - -// testDictionaryExtras() diff --git a/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift b/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift index d97c1b764..d7836dffc 100644 --- a/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift +++ b/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift @@ -5,39 +5,203 @@ import XCTest extension String { var cleaned: String { - return replacingOccurrences(of: " ", with: "").components(separatedBy: "\n").filter { !$0.isEmpty }.joined(separator: " ") + replacingOccurrences(of: " ", with: "").components(separatedBy: "\n").filter { !$0.isEmpty }.joined(separator: " ") } } class AEXML_XcodeFormatTests: XCTestCase { - private let expectedXml = + private let expectedBuildActionXml = """ + buildImplicitDependencies = "NO" + buildArchitectures = "Automatic" + runPostActionsOnFailure = "YES"> """ + private let expectedLaunchActionXml = + """ + + + + """ + + private let expectedTestActionXml = + """ + + + + """ + + private let expectedSchemeXml = + """ + + + + """ + + private let expectedRemoteRunnableXml = + """ + + + + """ + func test_BuildAction_attributes_sorted_when_original_sorted() { - validateAttributes(attributes: [ - "parallelizeBuildables": "YES", - "buildImplicitDependencies": "NO", - ]) + validateAttributes( + expectedXML: expectedBuildActionXml.cleaned, + childName: "BuildAction", + attributes: [ + "parallelizeBuildables": "YES", + "buildImplicitDependencies": "NO", + "buildArchitectures": "Automatic", + "runPostActionsOnFailure": "YES", + ] + ) } func test_BuildAction_attributes_sorted_when_original_unsorted() { - validateAttributes(attributes: [ - "buildImplicitDependencies": "NO", - "parallelizeBuildables": "YES", - ]) + validateAttributes( + expectedXML: expectedBuildActionXml.cleaned, + childName: "BuildAction", + attributes: [ + "buildImplicitDependencies": "NO", + "parallelizeBuildables": "YES", + "runPostActionsOnFailure": "YES", + "buildArchitectures": "Automatic", + ] + ) + } + + func test_LaunchAction_attributes_sorted_when_original_sorted() { + validateAttributes( + expectedXML: expectedLaunchActionXml.cleaned, + childName: "LaunchAction", + attributes: [ + "buildConfiguration": "Debug", + "selectedLauncherIdentifier": "Xcode.DebuggerFoundation.Launcher.LLDB", + "customLLDBInitFile": "$(BAZEL_LLDB_INIT)", + "launchStyle": "0", + "askForAppToLaunch": "YES", + "allowLocationSimulation": "YES", + "disableMainThreadChecker": "YES", + "disablePerformanceAntipatternChecker": "YES", + ] + ) + } + + func test_LaunchAction_attributes_sorted_when_original_unsorted() { + validateAttributes( + expectedXML: expectedLaunchActionXml.cleaned, + childName: "LaunchAction", + attributes: [ + "disableMainThreadChecker": "YES", + "disablePerformanceAntipatternChecker": "YES", + "customLLDBInitFile": "$(BAZEL_LLDB_INIT)", + "allowLocationSimulation": "YES", + "buildConfiguration": "Debug", + "selectedLauncherIdentifier": "Xcode.DebuggerFoundation.Launcher.LLDB", + "launchStyle": "0", + "askForAppToLaunch": "YES", + ] + ) + } + + func test_TestAction_attributes_sorted_when_original_sorted() { + validateAttributes( + expectedXML: expectedTestActionXml.cleaned, + childName: "TestAction", + attributes: [ + "buildConfiguration": "Debug", + "customLLDBInitFile": "$(BAZEL_LLDB_INIT)", + "selectedLauncherIdentifier": "Xcode.DebuggerFoundation.Launcher.LLDB", + "shouldUseLaunchSchemeArgsEnv": "YES", + "disableMainThreadChecker": "YES", + ] + ) + } + + func test_TestAction_attributes_sorted_when_original_unsorted() { + validateAttributes( + expectedXML: expectedTestActionXml.cleaned, + childName: "TestAction", + attributes: [ + "disableMainThreadChecker": "YES", + "shouldUseLaunchSchemeArgsEnv": "YES", + "buildConfiguration": "Debug", + "customLLDBInitFile": "$(BAZEL_LLDB_INIT)", + "selectedLauncherIdentifier": "Xcode.DebuggerFoundation.Launcher.LLDB", + ] + ) + } + + func test_Scheme_attributes_sorted_when_original_sorted() { + validateAttributes( + expectedXML: expectedSchemeXml.cleaned, + childName: "Scheme", + attributes: [ + "LastUpgradeVersion": "1320", + "wasCreatedForAppExtension": "YES", + "version": "1.7", + ] + ) + } + + func test_Scheme_attributes_sorted_when_original_unsorted() { + validateAttributes( + expectedXML: expectedSchemeXml.cleaned, + childName: "Scheme", + attributes: [ + "wasCreatedForAppExtension": "YES", + "LastUpgradeVersion": "1320", + "version": "1.7", + ] + ) + } + + func test_RemoteRunnable() { + validateAttributes( + expectedXML: expectedRemoteRunnableXml.cleaned, + childName: "RemoteRunnable", + attributes: [ + "BundleIdentifier": "BundleID", + "RemotePath": "REMOTE_PATH", + "runnableDebuggingMode": "2", + ] + ) } - func validateAttributes(attributes: [String: String], line: UInt = #line) { + func validateAttributes( + expectedXML: String, + childName: String, + attributes: [String: String], + line: UInt = #line + ) { let document = AEXMLDocument() - let child = document.addChild(name: "BuildAction") + let child = document.addChild(name: childName) child.attributes = attributes let result = document.xmlXcodeFormat - XCTAssertEqual(expectedXml.cleaned, result.cleaned, line: line) + XCTAssertEqual(result.cleaned, expectedXML, line: line) } } diff --git a/Tests/XcodeProjTests/Extensions/Decodable+Dictionary.swift b/Tests/XcodeProjTests/Extensions/Decodable+Dictionary.swift deleted file mode 100644 index 00303ef53..000000000 --- a/Tests/XcodeProjTests/Extensions/Decodable+Dictionary.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -@testable import XcodeProj - -extension Decodable { - /// Initialies the Decodable object with a JSON dictionary. - /// - /// - Parameter jsonDictionary: json dictionary. - /// - Throws: throws an error if the initialization fails. - init(jsonDictionary: [String: Any]) throws { - let data = try JSONSerialization.data(withJSONObject: jsonDictionary, options: []) - let decoder = XcodeprojJSONDecoder( - context: ProjectDecodingContext(pbxProjValueReader: { key in - jsonDictionary[key] - }) - ) - self = try decoder.decode(Self.self, from: data) - } -} diff --git a/Tests/XcodeProjTests/Extensions/Dictionary+ExtrasTests.swift b/Tests/XcodeProjTests/Extensions/Dictionary+ExtrasTests.swift deleted file mode 100644 index 5ff3c4037..000000000 --- a/Tests/XcodeProjTests/Extensions/Dictionary+ExtrasTests.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation -import PathKit -import XcodeProj -import XCTest - -class DictionaryExtrasTests: XCTestCase { - var fixtures: Path! - - override func setUp() { - super.setUp() - fixtures = Path(#file).parent().parent().parent().parent() + "Fixtures" - } - - func test_loadPlist_returnsANilValue_whenTheFileDoesntExist() { - XCTAssertNil(loadPlist(path: "test")) - } - - func test_loadPlist_returnsTheDictionary_whenTheFileDoesExist() { - let iosProject = fixtures + "iOS/Project.xcodeproj/project.pbxproj" - XCTAssertNotNil(loadPlist(path: iosProject.string)) - } -} diff --git a/Tests/XcodeProjTests/Extensions/XCTestCase+Assertions.swift b/Tests/XcodeProjTests/Extensions/XCTestCase+Assertions.swift index 67df9c183..a9b989716 100644 --- a/Tests/XcodeProjTests/Extensions/XCTestCase+Assertions.swift +++ b/Tests/XcodeProjTests/Extensions/XCTestCase+Assertions.swift @@ -2,7 +2,7 @@ import Foundation import XCTest extension XCTestCase { - typealias EquatableError = Error & Equatable + typealias EquatableError = Equatable & Error func XCTAssertNotNilAndUnwrap(_ obj: T?, message: String = "") -> T { guard let unwrappedObj = obj else { @@ -12,7 +12,7 @@ extension XCTestCase { return unwrappedObj } - func XCTAssertThrowsSpecificError(_ expression: @autoclosure () throws -> T, _ error: E, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { + func XCTAssertThrowsSpecificError(_ expression: @autoclosure () throws -> some Any, _ error: E, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, line: UInt = #line) { XCTAssertThrowsError(try expression(), message(), file: file, line: line) { actualError in let message = "Expected \(error) got \(actualError)" diff --git a/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift b/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift new file mode 100644 index 000000000..95d8c881c --- /dev/null +++ b/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift @@ -0,0 +1,29 @@ +import Foundation + +/// Returns the output of running `executable` with `args`. Throws an error if the process exits indicating failure. +@discardableResult +func checkedOutput(_ executable: String, _ args: [String]) throws -> String? { + #if os(iOS) + return nil + #else + let process = Process() + let output = Pipe() + + if executable.contains("/") { + process.launchPath = executable + } else { + process.launchPath = try checkedOutput("/usr/bin/which", [executable])?.trimmingCharacters(in: .newlines) + } + + process.arguments = args + process.standardOutput = output + process.launch() + process.waitUntilExit() + + guard process.terminationStatus == 0 else { + throw NSError(domain: NSPOSIXErrorDomain, code: Int(process.terminationStatus)) + } + + return String(data: output.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + #endif +} diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift index 517e2dec9..63686a756 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift @@ -6,4 +6,27 @@ final class PBXBuildFileTests: XCTestCase { func test_isa_returnsTheCorrectValue() { XCTAssertEqual(PBXBuildFile.isa, "PBXBuildFile") } + + func test_platformFilterIsSet() { + let pbxBuildFile = PBXBuildFile( + platformFilter: "platformFilter" + ) + XCTAssertEqual(pbxBuildFile.platformFilter, "platformFilter") + } + + func test_platformCompilerFlagsIsSet() { + let expected = "flagValue" + let pbxBuildFile = PBXBuildFile( + settings: ["COMPILER_FLAGS": .string(expected)] + ) + XCTAssertEqual(pbxBuildFile.compilerFlags, expected) + } + + func test_platformAttributesIsSet() { + let expected = ["Public"] + let pbxBuildFile = PBXBuildFile( + settings: ["ATTRIBUTES": .array(expected)] + ) + XCTAssertEqual(pbxBuildFile.attributes, expected) + } } diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildRuleTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildRuleTests.swift index c682a3b35..da81bdbc2 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildRuleTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildRuleTests.swift @@ -14,7 +14,8 @@ final class PBXBuildRuleTests: XCTestCase { name: "rule", outputFiles: ["a", "b"], outputFilesCompilerFlags: ["-1", "-2"], - script: "script") + script: "script", + runOncePerArchitecture: false) } func test_init_initializesTheBuildRuleWithTheRightAttributes() { @@ -26,6 +27,7 @@ final class PBXBuildRuleTests: XCTestCase { XCTAssertEqual(subject.outputFiles, ["a", "b"]) XCTAssertEqual(subject.outputFilesCompilerFlags ?? [], ["-1", "-2"]) XCTAssertEqual(subject.script, "script") + XCTAssertEqual(subject.runOncePerArchitecture, false) } func test_isa_returnsTheCorrectValue() { @@ -40,7 +42,8 @@ final class PBXBuildRuleTests: XCTestCase { name: "rule", outputFiles: ["a", "b"], outputFilesCompilerFlags: ["-1", "-2"], - script: "script") + script: "script", + runOncePerArchitecture: false) XCTAssertEqual(subject, another) } } diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXCopyFilesBuildPhaseTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXCopyFilesBuildPhaseTests.swift index bff3259bf..e809f2952 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXCopyFilesBuildPhaseTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXCopyFilesBuildPhaseTests.swift @@ -49,8 +49,8 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertNil(phase.dstPath, "Expected dstPath to be nil but it's present") } catch {} } @@ -60,8 +60,8 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertEqual(phase.buildActionMask, PBXBuildPhase.defaultBuildActionMask, "Expected buildActionMask to be equal to \(PBXBuildPhase.defaultBuildActionMask) but it's \(phase.buildActionMask)") } catch {} } @@ -71,8 +71,31 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertNil(phase.dstSubfolderSpec, "Expected dstSubfolderSpec to be nil but it's present") + } catch {} + } + + func test_init_decodesDstSubfolder() { + var dictionary = testDictionary() + dictionary["dstSubfolder"] = "Frameworks" + let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) + let decoder = XcodeprojJSONDecoder() + do { + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertEqual(phase.dstSubfolder, .frameworks) + } catch {} + } + + func test_init_decodesUnknownDstSubfolder() { + var dictionary = testDictionary() + dictionary["dstSubfolder"] = "InvalidSubfolder" + let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) + let decoder = XcodeprojJSONDecoder() + do { + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertEqual(phase.dstSubfolder, .unknown("InvalidSubfolder")) + XCTAssertEqual(phase.dstSubfolder?.rawValue, "InvalidSubfolder") } catch {} } @@ -82,8 +105,8 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertNil(phase.files, "Expected files to be nil but it's present") } catch {} } @@ -93,8 +116,8 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertFalse(phase.runOnlyForDeploymentPostprocessing, "Expected runOnlyForDeploymentPostprocessing to default to false") } catch {} } @@ -102,8 +125,28 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase { XCTAssertEqual(PBXCopyFilesBuildPhase.isa, "PBXCopyFilesBuildPhase") } + func test_equal_whenDstSubfolderIsDifferent_returnsFalse() { + let lhs = PBXCopyFilesBuildPhase(dstPath: "dstPath", + dstSubfolderSpec: .frameworks, + dstSubfolder: .frameworks, + name: "Copy") + let rhs = PBXCopyFilesBuildPhase(dstPath: "dstPath", + dstSubfolderSpec: .frameworks, + dstSubfolder: .resources, + name: "Copy") + XCTAssertNotEqual(lhs, rhs) + } + + func test_write_preservesUnknownDstSubfolderRawValue() throws { + let subject = PBXCopyFilesBuildPhase(dstSubfolder: .unknown("InvalidSubfolder")) + let proj = PBXProj.fixture() + let (_, plistValue) = try subject.plistKeyAndValue(proj: proj, reference: "ref") + + XCTAssertEqual(plistValue.dictionary?["dstSubfolder"]?.string, "InvalidSubfolder") + } + func testDictionary() -> [String: Any] { - return [ + [ "dstPath": "dstPath", "buildActionMask": 0, "dstSubfolderSpec": 12, diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXFrameworksBuildPhaseTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXFrameworksBuildPhaseTests.swift index 9c4e105c5..362faf786 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXFrameworksBuildPhaseTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXFrameworksBuildPhaseTests.swift @@ -13,13 +13,13 @@ final class PBXFrameworksBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXFrameworksBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertNil(phase.files, "Expected files to be nil but it's present") } catch {} } private func testDictionary() -> [String: Any] { - return [ + [ "files": ["file1"], "runOnlyForDeploymentPostprocessing": 0, "reference": "reference", diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXHeadersBuildPhaseTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXHeadersBuildPhaseTests.swift index d81845815..1b6cc38fe 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXHeadersBuildPhaseTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXHeadersBuildPhaseTests.swift @@ -13,8 +13,8 @@ final class PBXHeadersBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXHeadersBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertEqual(phase.buildActionMask, PBXBuildPhase.defaultBuildActionMask, "Expected buildActionMask to be equal to \(PBXBuildPhase.defaultBuildActionMask) but it's \(phase.buildActionMask)") } catch {} } @@ -24,8 +24,8 @@ final class PBXHeadersBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXHeadersBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertNil(phase.files, "Expected files to be nil but it's present") } catch {} } @@ -35,13 +35,13 @@ final class PBXHeadersBuildPhaseTests: XCTestCase { let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) let decoder = XcodeprojJSONDecoder() do { - _ = try decoder.decode(PBXHeadersBuildPhase.self, from: data) - XCTAssertTrue(false, "Expected to throw an error but it didn't") + let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data) + XCTAssertFalse(phase.runOnlyForDeploymentPostprocessing, "Expected runOnlyForDeploymentPostprocessing to default to false") } catch {} } private func testDictionary() -> [String: Any] { - return [ + [ "buildActionMask": 3, "files": ["file"], "runOnlyForDeploymentPostprocessing": 2, diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXResourcesBuildPhaseTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXResourcesBuildPhaseTests.swift index 83b1c8280..223c9cc34 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXResourcesBuildPhaseTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXResourcesBuildPhaseTests.swift @@ -8,7 +8,7 @@ final class PBXResourcesBuildPhaseTests: XCTestCase { } private func testDictionary() -> [String: Any] { - return [ + [ "files": ["file1"], "buildActionMask": "333", "runOnlyForDeploymentPostprocessing": "3", diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXShellScriptBuildPhaseTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXShellScriptBuildPhaseTests.swift index 6850b00ff..8a9282387 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXShellScriptBuildPhaseTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXShellScriptBuildPhaseTests.swift @@ -16,7 +16,8 @@ final class PBXShellScriptBuildPhaseTests: XCTestCase { let (_, doNotShowPlistValue) = try doNotShow.plistKeyAndValue(proj: proj, reference: "ref") if case let PlistValue.dictionary(showDictionary) = showPlistValue, - case let PlistValue.dictionary(doNotShowDictionary) = doNotShowPlistValue { + case let PlistValue.dictionary(doNotShowDictionary) = doNotShowPlistValue + { XCTAssertNil(showDictionary["showEnvVarsInLog"]) XCTAssertEqual(doNotShowDictionary["showEnvVarsInLog"]?.string, "0") } else { @@ -24,13 +25,52 @@ final class PBXShellScriptBuildPhaseTests: XCTestCase { } } - private func testDictionary() -> [String: Any] { - return [ - "files": ["files"], - "inputPaths": ["input"], - "outputPaths": ["output"], - "shellPath": "shellPath", - "shellScript": "shellScript", - ] + func test_write_alwaysOutOfDate() throws { + // Given + let alwaysOutOfDateNotPresent = PBXShellScriptBuildPhase() + let alwaysOutOfDateFalse = PBXShellScriptBuildPhase(alwaysOutOfDate: false) + let alwaysOutOfDateTrue = PBXShellScriptBuildPhase(alwaysOutOfDate: true) + let proj = PBXProj.fixture() + + // When + guard + case let .dictionary(valuesWhenNotPresent) = try alwaysOutOfDateNotPresent.plistKeyAndValue(proj: proj, reference: "ref").value, + case let .dictionary(valuesWhenFalse) = try alwaysOutOfDateFalse.plistKeyAndValue(proj: proj, reference: "ref").value, + case let .dictionary(valuesWhenTrue) = try alwaysOutOfDateTrue.plistKeyAndValue(proj: proj, reference: "ref").value + else { + XCTFail("Plist should contain dictionary") + return + } + + // Then + XCTAssertFalse(valuesWhenNotPresent.keys.contains("alwaysOutOfDate")) + XCTAssertFalse(valuesWhenFalse.keys.contains("alwaysOutOfDate")) + XCTAssertEqual(valuesWhenTrue["alwaysOutOfDate"], "1") + } + + func test_write_dependencyFile() throws { + let discoveryPath = "$(DERIVED_FILE_DIR)/target.d" + let discovery = PBXShellScriptBuildPhase(dependencyFile: discoveryPath) + let proj = PBXProj.fixture() + + let (_, discoveryPlistValue) = try discovery.plistKeyAndValue(proj: proj, reference: "ref") + + XCTAssertEqual(discoveryPlistValue.dictionary?["dependencyFile"]?.string, CommentedString(discoveryPath)) + } + + func test_skips_nilDependencyFile() throws { + let noDiscovery = PBXShellScriptBuildPhase(dependencyFile: nil) + let proj = PBXProj.fixture() + + let (_, noDiscoveryPlistValue) = try noDiscovery.plistKeyAndValue(proj: proj, reference: "ref") + + XCTAssertNil(noDiscoveryPlistValue.dictionary?["dependencyFile"]) + } + + func test_notequal_differentDependencyFile() throws { + let noDiscovery = PBXShellScriptBuildPhase(dependencyFile: nil) + let discovery = PBXShellScriptBuildPhase(dependencyFile: "$(DERIVED_FILE_DIR)/target.d") + + XCTAssertNotEqual(noDiscovery, discovery) } } diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhase+Fixtures.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhase+Fixtures.swift index 403ab751a..77c0de9c1 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhase+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhase+Fixtures.swift @@ -4,10 +4,10 @@ import Foundation extension PBXSourcesBuildPhase { static func fixture(files: [PBXBuildFile] = []) -> PBXSourcesBuildPhase { - return PBXSourcesBuildPhase(files: files, - inputFileListPaths: nil, - outputFileListPaths: nil, - buildActionMask: PBXBuildPhase.defaultBuildActionMask, - runOnlyForDeploymentPostprocessing: false) + PBXSourcesBuildPhase(files: files, + inputFileListPaths: nil, + outputFileListPaths: nil, + buildActionMask: PBXBuildPhase.defaultBuildActionMask, + runOnlyForDeploymentPostprocessing: false) } } diff --git a/Tests/XcodeProjTests/Objects/Configuration/BuildFileSettingTests.swift b/Tests/XcodeProjTests/Objects/Configuration/BuildFileSettingTests.swift new file mode 100644 index 000000000..5064f56e2 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Configuration/BuildFileSettingTests.swift @@ -0,0 +1,34 @@ +import Foundation +import Testing +@testable import XcodeProj + +struct BuildFileSettingTests { + @Test func test_BuildSettings_encodes_to_JSON() async throws { + let expectedJSON = #"{"one":"one","two":["two","two"]}"# + + let settings: [String: BuildFileSetting] = [ + "one": .string("one"), + "two": .array(["two", "two"]), + ] + + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + + let result = try encoder.encode(settings) + + #expect(result == expectedJSON.data(using: .utf8)) + } + + @Test func test_buildSettings_decodes_from_JSON() async throws { + let json = #"{"one":"one","two":["two","two"]}"# + + let expectedSettings: [String: BuildFileSetting] = [ + "one": .string("one"), + "two": .array(["two", "two"]), + ] + + let result = try JSONDecoder().decode([String: BuildFileSetting].self, from: json.data(using: .utf8)!) + + #expect(result == expectedSettings) + } +} diff --git a/Tests/XcodeProjTests/Objects/Configuration/BuildSettingTests.swift b/Tests/XcodeProjTests/Objects/Configuration/BuildSettingTests.swift new file mode 100644 index 000000000..5842a0a7c --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Configuration/BuildSettingTests.swift @@ -0,0 +1,49 @@ +import Foundation +import Testing +@testable import XcodeProj + +struct BuildSettingTests { + @Test func test_BuildSettings_encode_to_string() async throws { + #expect(BuildSetting.string("one").description == "one") + #expect(BuildSetting.array(["one", "two"]).description == "one two") + } + + @Test func test_buildSettings_decodes_from_JSON() async throws { + let json = #"{"one":"one","two":["two","two"]}"# + + let expectedSettings: BuildSettings = [ + "one": .string("one"), + "two": .array(["two", "two"]), + ] + + let result = try JSONDecoder().decode(BuildSettings.self, from: json.data(using: .utf8)!) + + #expect(result == expectedSettings) + } + + @Test func test_buildSettings_bool_conversion() async throws { + let settings: [BuildSetting] = [ + BuildSetting.string("YES"), + BuildSetting.string("NO"), + BuildSetting.string("tuist"), + BuildSetting.string("No"), + BuildSetting.string("yES"), + BuildSetting.array(["YES", "yES"]), + true, + false, + ] + + let expected: [Bool?] = [ + true, + false, + nil, + nil, + nil, + nil, + true, + false, + ] + + #expect(settings.map(\.boolValue) == expected) + } +} diff --git a/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfiguration+Fixtures.swift b/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfiguration+Fixtures.swift index 94567b6ef..6c2bafdfd 100644 --- a/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfiguration+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfiguration+Fixtures.swift @@ -3,6 +3,6 @@ import Foundation extension XCBuildConfiguration { static func fixture(name: String = "Debug") -> XCBuildConfiguration { - return XCBuildConfiguration(name: name) + XCBuildConfiguration(name: name) } } diff --git a/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfigurationTests.swift b/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfigurationTests.swift index 1483ebf6b..64d357f8a 100644 --- a/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfigurationTests.swift +++ b/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfigurationTests.swift @@ -28,7 +28,7 @@ final class XCBuildConfigurationTests: XCTestCase { subject.append(setting: "PRODUCT_NAME", value: "$(TARGET_NAME:c99extidentifier)") // Then - XCTAssertEqual(subject.buildSettings["PRODUCT_NAME"] as? String, "$(inherited) $(TARGET_NAME:c99extidentifier)") + XCTAssertEqual(subject.buildSettings["PRODUCT_NAME"], "$(inherited) $(TARGET_NAME:c99extidentifier)") } func test_append_when_theSettingExists() { @@ -41,7 +41,7 @@ final class XCBuildConfigurationTests: XCTestCase { subject.append(setting: "OTHER_LDFLAGS", value: "flag2") // Then - XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? String, "flag1 flag2") + XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"], "flag1 flag2") } func test_append_when_duplicateSettingExists() { @@ -54,7 +54,7 @@ final class XCBuildConfigurationTests: XCTestCase { subject.append(setting: "OTHER_LDFLAGS", value: "flag1") // Then - XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? String, "flag1") + XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"], "flag1") } func test_append_removesDuplicates_when_theSettingIsAnArray() { @@ -69,7 +69,7 @@ final class XCBuildConfigurationTests: XCTestCase { subject.append(setting: "OTHER_LDFLAGS", value: "flag1") // Then - XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? [String], ["flag1", "flag2"]) + XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"], ["flag1", "flag2"]) } func test_append_when_theSettingExistsAsAnArray() { @@ -82,11 +82,26 @@ final class XCBuildConfigurationTests: XCTestCase { subject.append(setting: "OTHER_LDFLAGS", value: "flag3") // Then - XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? [String], ["flag1", "flag2", "flag3"]) + XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"], ["flag1", "flag2", "flag3"]) + } + + func test_configuration_files_in_synchronized_group() { + let synchronizedGroup = PBXFileSystemSynchronizedRootGroup.fixture(sourceTree: .group, + path: "Xcode16BuildConfigurations", + explicitFileTypes: [:], + exceptions: [], + explicitFolders: []) + let subject = XCBuildConfiguration(name: "Debug", + baseConfigurationAnchor: synchronizedGroup, + baseConfigurationRelativePath: "Configs/DevConfig.xcconfig", + buildSettings: [:]) + XCTAssertNil(subject.baseConfigurationReference) + XCTAssertNotNil(subject.baseConfigurationReferenceAnchor) + XCTAssertNotNil(subject.baseConfigurationReferenceRelativePath) } private func testDictionary() -> [String: Any] { - return [ + [ "baseConfigurationReference": "baseConfigurationReference", "buildSettings": [:], "name": "name", diff --git a/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationList+Fixtures.swift b/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationList+Fixtures.swift index 6bd1dac14..975838b02 100644 --- a/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationList+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationList+Fixtures.swift @@ -5,8 +5,9 @@ extension XCConfigurationList { static func fixture(buildConfigurations: [XCBuildConfiguration] = [XCBuildConfiguration.fixture(name: "Debug"), XCBuildConfiguration.fixture(name: "Release")], defaultConfigurationName: String? = "Debug", - defaultConfigurationIsVisible _: Bool = true) -> XCConfigurationList { - return XCConfigurationList(buildConfigurations: buildConfigurations, - defaultConfigurationName: defaultConfigurationName) + defaultConfigurationIsVisible _: Bool = true) -> XCConfigurationList + { + XCConfigurationList(buildConfigurations: buildConfigurations, + defaultConfigurationName: defaultConfigurationName) } } diff --git a/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationListTests.swift b/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationListTests.swift index 447ed7e5d..1a3824506 100644 --- a/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationListTests.swift +++ b/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationListTests.swift @@ -12,7 +12,7 @@ final class XCConfigurationListTests: XCTestCase { let configurationList = XCConfigurationList(buildConfigurations: []) objects.add(object: configurationList) let configurations = try configurationList.addDefaultConfigurations() - let names = configurations.map { $0.name } + let names = configurations.map(\.name) XCTAssertEqual(configurations.count, 2) XCTAssertTrue(names.contains("Debug")) diff --git a/Tests/XcodeProjTests/Objects/Files/PBXContainerItemProxyTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXContainerItemProxyTests.swift index fb3c0d6ee..e5f660a78 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXContainerItemProxyTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXContainerItemProxyTests.swift @@ -20,14 +20,14 @@ final class PBXContainerItemProxyTests: XCTestCase { func test_maintains_remoteID() { let target = PBXNativeTarget(name: "") - let project = PBXProject(name: "", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "", mainGroup: PBXGroup()) + let project = PBXProject(name: "", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "", preferredProjectObjectVersion: nil, minimizedProjectReferenceProxies: nil, mainGroup: PBXGroup()) let containerProxy = PBXContainerItemProxy(containerPortal: .project(project), remoteGlobalID: .object(target)) XCTAssertEqual(target.uuid, containerProxy.remoteGlobalID?.uuid) } private func testDictionary() -> [String: Any] { - return [ + [ "containerPortal": "containerPortal", "remoteGlobalIDString": "remoteGlobalIDString", "remoteInfo": "remoteInfo", diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileElementTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileElementTests.swift index a73d44f27..b7b5acd81 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileElementTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileElementTests.swift @@ -54,6 +54,8 @@ final class PBXFileElementTests: XCTestCase { let project = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup) let objects = PBXObjects(objects: [project, mainGroup, fileref, group]) @@ -109,6 +111,8 @@ final class PBXFileElementTests: XCTestCase { let project = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: rootGroup) let objects = PBXObjects(objects: [fileref, nestedGroup, rootGroup, project]) @@ -120,7 +124,7 @@ final class PBXFileElementTests: XCTestCase { } private func testDictionary() -> [String: Any] { - return [ + [ "sourceTree": "absolute", "path": "path", "name": "name", diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileReference+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileReference+Fixtures.swift index 760e097bb..58c650873 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileReference+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileReference+Fixtures.swift @@ -4,7 +4,8 @@ import Foundation extension PBXFileReference { static func fixture(sourceTree _: PBXSourceTree = .group, - name: String? = "Test") -> PBXFileReference { - return PBXFileReference(sourceTree: .group, name: name) + name: String? = "Test") -> PBXFileReference + { + PBXFileReference(sourceTree: .group, name: name) } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileReferenceTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileReferenceTests.swift index 1b850a702..1ab99a3b6 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileReferenceTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileReferenceTests.swift @@ -39,7 +39,7 @@ final class PBXFileReferenceTests: XCTestCase { } private func testDictionary() -> [String: Any] { - return [ + [ "name": "name", "sourceTree": "group", ] diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift new file mode 100644 index 000000000..bda5f78ea --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift @@ -0,0 +1,19 @@ +import XcodeProj + +extension PBXFileSystemSynchronizedBuildFileExceptionSet { + static func fixture(target: PBXTarget = .fixture(), + membershipExceptions: [String]? = [], + publicHeaders: [String]? = [], + privateHeaders: [String]? = [], + additionalCompilerFlagsByRelativePath: [String: String]? = nil, + attributesByRelativePath: [String: [String]]? = nil, + platformFiltersByRelativePath: [String: [String]]? = nil) -> PBXFileSystemSynchronizedBuildFileExceptionSet { + PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, + membershipExceptions: membershipExceptions, + publicHeaders: publicHeaders, + privateHeaders: privateHeaders, + additionalCompilerFlagsByRelativePath: additionalCompilerFlagsByRelativePath, + attributesByRelativePath: attributesByRelativePath, + platformFiltersByRelativePath: platformFiltersByRelativePath) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift new file mode 100644 index 000000000..0ef9fda58 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift @@ -0,0 +1,78 @@ +import Foundation +import XCTest +@testable import XcodeProj + +final class PBXFileSystemSynchronizedBuildFileExceptionSetTests: XCTestCase { + var target: PBXTarget! + var subject: PBXFileSystemSynchronizedBuildFileExceptionSet! + + override func setUp() { + super.setUp() + target = PBXTarget.fixture() + subject = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + } + + override func tearDown() { + target = nil + subject = nil + super.tearDown() + } + + func test_itHasTheCorrectIsa() { + XCTAssertEqual(PBXFileSystemSynchronizedBuildFileExceptionSet.isa, "PBXFileSystemSynchronizedBuildFileExceptionSet") + } + + func test_equal_returnsTheCorrectValue() { + let another = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + XCTAssertEqual(subject, another) + } + + func test_equal_withDifferentPlatformFilters_returnsNotEqual() { + let one = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture( + target: target, + platformFiltersByRelativePath: ["file.swift": ["ios"]] + ) + let another = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture( + target: target, + platformFiltersByRelativePath: ["file.swift": ["tvos"]] + ) + XCTAssertNotEqual(one, another) + } + + func test_plistKeyAndValue_platformFiltersByRelativePath_serializesCorrectly() throws { + let proj = PBXProj() + let exceptionSet = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture( + target: target, + platformFiltersByRelativePath: [ + "Resources/ios_only.mp4": ["ios"], + "Resources/multi.mp4": ["ios", "tvos"], + ] + ) + proj.add(object: exceptionSet) + + let (_, plistValue) = try exceptionSet.plistKeyAndValue(proj: proj, reference: "ref") + + let dict = try XCTUnwrap(plistValue.dictionary?[CommentedString("platformFiltersByRelativePath")]?.dictionary) + XCTAssertEqual( + dict[CommentedString("Resources/ios_only.mp4")], + .array([.string(CommentedString("ios"))]) + ) + XCTAssertEqual( + dict[CommentedString("Resources/multi.mp4")], + .array([.string(CommentedString("ios")), .string(CommentedString("tvos"))]) + ) + } + + func test_plistKeyAndValue_platformFiltersByRelativePath_omittedWhenNil() throws { + let proj = PBXProj() + let exceptionSet = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture( + target: target, + platformFiltersByRelativePath: nil + ) + proj.add(object: exceptionSet) + + let (_, plistValue) = try exceptionSet.plistKeyAndValue(proj: proj, reference: "ref") + + XCTAssertNil(plistValue.dictionary?[CommentedString("platformFiltersByRelativePath")]) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift new file mode 100644 index 000000000..c4eabf4f7 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift @@ -0,0 +1,27 @@ +import XcodeProj + +extension PBXFileSystemSynchronizedRootGroup { + static func fixture(sourceTree: PBXSourceTree? = nil, + path: String? = nil, + name: String? = nil, + includeInIndex: Bool? = nil, + usesTabs: Bool? = nil, + indentWidth: UInt? = nil, + tabWidth: UInt? = nil, + wrapsLines: Bool? = nil, + explicitFileTypes: [String: String] = [:], + exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], + explicitFolders: [String] = []) -> PBXFileSystemSynchronizedRootGroup { + PBXFileSystemSynchronizedRootGroup(sourceTree: sourceTree, + path: path, + name: name, + includeInIndex: includeInIndex, + usesTabs: usesTabs, + indentWidth: indentWidth, + tabWidth: tabWidth, + wrapsLines: wrapsLines, + explicitFileTypes: explicitFileTypes, + exceptions: exceptions, + explicitFolders: explicitFolders) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift new file mode 100644 index 000000000..374156104 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift @@ -0,0 +1,51 @@ +import Foundation +import XCTest +@testable import XcodeProj + +final class PBXFileSystemSynchronizedRootGroupTests: XCTestCase { + var subject: PBXFileSystemSynchronizedRootGroup! + var target: PBXTarget! + var exception: PBXFileSystemSynchronizedBuildFileExceptionSet! + + override func setUp() { + super.setUp() + target = PBXTarget.fixture() + exception = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + subject = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, + path: "synchronized", + name: "synchronized", + includeInIndex: true, + usesTabs: true, + indentWidth: 2, + tabWidth: 2, + wrapsLines: true, + explicitFileTypes: ["Source.m": "sourcecode.c.objc"], + exceptions: [exception], + explicitFolders: []) + } + + override func tearDown() { + exception = nil + subject = nil + super.tearDown() + } + + func test_itHasTheCorrectIsa() { + XCTAssertEqual(PBXFileSystemSynchronizedRootGroup.isa, "PBXFileSystemSynchronizedRootGroup") + } + + func test_equal_returnsTheCorrectValue() { + let another = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, + path: "synchronized", + name: "synchronized", + includeInIndex: true, + usesTabs: true, + indentWidth: 2, + tabWidth: 2, + wrapsLines: true, + explicitFileTypes: ["Source.m": "sourcecode.c.objc"], + exceptions: [exception], + explicitFolders: []) + XCTAssertEqual(subject, another) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXGroup+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXGroup+Fixtures.swift index 14ab6aa27..fb617bd00 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXGroup+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXGroup+Fixtures.swift @@ -4,9 +4,10 @@ import Foundation extension PBXGroup { static func fixture(children _: [PBXFileElement] = [], sourceTree: PBXSourceTree = .group, - name: String = "test") -> PBXGroup { - return PBXGroup(children: [], - sourceTree: sourceTree, - name: name) + name: String = "test") -> PBXGroup + { + PBXGroup(children: [], + sourceTree: sourceTree, + name: name) } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXGroupTests+Path.swift b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests+Path.swift new file mode 100644 index 000000000..eb30de385 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests+Path.swift @@ -0,0 +1,30 @@ +#if os(macOS) || (os(Linux) && compiler(>=6.1)) + + import Foundation + import PathKit + import XcodeProj + import XCTest + + extension PBXGroupTests { + func test_takingGroupWithSpecificPath() throws { + let proj = try PBXProj(data: iosProjectWithExtensionsData()) + let mainGroup = proj.rootObject?.mainGroup + XCTAssertNotNil(mainGroup) + + let mainAppGroup = mainGroup?.group(with: "AppWithExtensions") + XCTAssertNotNil(mainAppGroup) + + let nilGroup = mainGroup?.group(with: "Not_Existing") + XCTAssertNil(nilGroup) + } + + func test_takingCreatedGroupWithSpecificPath() throws { + let proj = try PBXProj(data: iosProjectData()) + let mainGroup = proj.rootObject?.mainGroup + XCTAssertNil(mainGroup?.group(with: "group")) + + try mainGroup?.addGroup(named: "group") + XCTAssertNotNil(mainGroup?.group(with: "group")) + } + } +#endif diff --git a/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift index 33fe3b825..fae0e530d 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift @@ -18,6 +18,8 @@ final class PBXGroupTests: XCTestCase { let pbxProject = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: group) project.add(object: pbxProject) @@ -39,6 +41,22 @@ final class PBXGroupTests: XCTestCase { XCTAssertNotNil(childGroup?.parent) } + func test_addGroup_assignAllParents() { + let project = makeEmptyPBXProj() + let group = PBXGroup(children: [], + sourceTree: .group, + name: "group") + project.add(object: group) + + let expectGroups = ["foo", "bar", "baz"] + let createdGroups = try? group.addGroup(named: expectGroups.joined(separator: "/")) + + XCTAssertEqual(3, createdGroups?.count) + XCTAssertEqual(createdGroups?[0].parent?.name, "group") + XCTAssertEqual(createdGroups?[1].parent?.name, "foo") + XCTAssertEqual(createdGroups?[2].parent?.name, "bar") + } + func test_addVariantGroup() { let project = makeEmptyPBXProj() let group = PBXGroup(children: [], @@ -55,7 +73,7 @@ final class PBXGroupTests: XCTestCase { XCTAssertEqual(childVariantGroups.count, expectedGroupNames.count) - childVariantGroups.enumerated().forEach { index, variantGroup in + for (index, variantGroup) in childVariantGroups.enumerated() { let parentGroup = (index == 0) ? group : childVariantGroups[index - 1] if index == childVariantGroups.count - 1 { @@ -123,6 +141,8 @@ final class PBXGroupTests: XCTestCase { let pbxProject = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: group) project.add(object: pbxProject) @@ -147,6 +167,8 @@ final class PBXGroupTests: XCTestCase { let pbxProject = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: group) project.add(object: pbxProject) @@ -170,6 +192,8 @@ final class PBXGroupTests: XCTestCase { let pbxProject = PBXProject(name: "ProjectName", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: group) project.add(object: pbxProject) @@ -184,7 +208,7 @@ final class PBXGroupTests: XCTestCase { } private func makeEmptyPBXProj() -> PBXProj { - return PBXProj( + PBXProj( rootObject: nil, objectVersion: 0, archiveVersion: 0, @@ -194,7 +218,7 @@ final class PBXGroupTests: XCTestCase { } private func testDictionary() -> [String: Any] { - return [ + [ "children": ["child"], "name": "name", "sourceTree": "absolute", diff --git a/Tests/XcodeProjTests/Objects/Files/XCVersionGroup+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/XCVersionGroup+Fixtures.swift index 1fb87a83e..310a5da4f 100644 --- a/Tests/XcodeProjTests/Objects/Files/XCVersionGroup+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/XCVersionGroup+Fixtures.swift @@ -8,17 +8,18 @@ extension XCVersionGroup { name: String? = "name", sourceTree: PBXSourceTree = .group, versionGroupType: String = "versionGroupType", - children: [PBXFileReference] = [PBXFileReference(name: "currentVersion")]) -> XCVersionGroup { + children: [PBXFileReference] = [PBXFileReference(name: "currentVersion")]) -> XCVersionGroup + { let group = XCVersionGroup(currentVersion: currentVersion, path: path, name: name, sourceTree: sourceTree, versionGroupType: versionGroupType, children: children) - if let currentVersion = currentVersion { + if let currentVersion { objects.add(object: currentVersion) } - children.forEach({ objects.add(object: $0) }) + children.forEach { objects.add(object: $0) } objects.add(object: group) return group } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXOutputSettingsTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXOutputSettingsTests.swift index 3f20341ce..dfc935bed 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXOutputSettingsTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXOutputSettingsTests.swift @@ -1,190 +1,278 @@ +#if os(macOS) || (os(Linux) && compiler(>=6.1)) -import XCTest -@testable import XcodeProj + import XCTest + @testable import XcodeProj -class PBXOutputSettingsTests: XCTestCase { - var proj: PBXProj! - var buildFileAssets: PBXBuildFile! - var buildFileMain: PBXBuildFile! + class PBXOutputSettingsTests: XCTestCase { + // MARK: - PBXFileOrder - PBXBuldFile - var objectBuildFileAssets: (PBXObjectReference, PBXBuildFile)! - var objectBuildFileMain: (PBXObjectReference, PBXBuildFile)! + func test_PBXFileOrder_PBXBuildFile_by_uuid_when_iosProject() throws { + let iosProject = try iosProject() - var objectBuildPhaseFileAssets: (PBXObjectReference, PBXBuildPhaseFile)! - var objectBuildPhaseFileMain: (PBXObjectReference, PBXBuildPhaseFile)! + XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: iosProject.objectBuildFileAssets, rhs: iosProject.objectBuildFileMain)) + XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: iosProject.objectBuildFileMain, rhs: iosProject.objectBuildFileAssets)) + } - var fileReferenceAssets: PBXFileReference! - var fileReferenceCoreData: PBXFileReference! + func test_PBXFileOrder_PBXBuildFile_by_filename_when_iosProject() throws { + let iosProject = try iosProject() - var objectFileReferenceAssets: (PBXObjectReference, PBXFileReference)! - var objectFileReferenceCoreData: (PBXObjectReference, PBXFileReference)! + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildFileAssets, rhs: iosProject.objectBuildFileMain)) + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildFileMain, rhs: iosProject.objectBuildFileAssets)) + } - var groupFrameworks: PBXGroup! - var groupProducts: PBXGroup! + func test_PBXFileOrder_PBXBuildFile_by_filename_when_fileSharedAcrossTargetsProject() throws { + let fileSharedAcrossTargetsProject = try fileSharedAcrossTargetsProject() - var objectGroupFrameworks: (PBXObjectReference, PBXGroup)! - var objectGroupProducts: (PBXObjectReference, PBXGroup)! + let sameNameByFilename = fileSharedAcrossTargetsProject.objectBuildFileSameName.sorted(by: PBXFileOrder.byFilename.sort) + XCTAssertLessThan(sameNameByFilename.first!.1.uuid, sameNameByFilename.last!.1.uuid) + } - var navigatorFileGroup: PBXGroup! + func test_PBXFileOrder_PBXBuildFile_by_filename_when_nil_name_and_path_when_iosProject() throws { + let iosProject = try iosProject() - override func setUp() { - let dic = iosProjectDictionary() - do { - proj = try PBXProj(jsonDictionary: dic.1) - } catch { - XCTFail("Failed to load project from file: \(error)") + iosProject.buildFileAssets.file?.name = nil + iosProject.buildFileMain.file?.name = nil + iosProject.buildFileAssets.file?.path = nil + iosProject.buildFileMain.file?.path = nil + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildFileAssets, rhs: iosProject.objectBuildFileMain)) + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildFileMain, rhs: iosProject.objectBuildFileAssets)) } -// proj.buildFiles.forEach { print("\(type(of: $0.file!)) \($0.file!.fileName()!)") } - buildFileAssets = proj.buildFiles.first { $0.file?.fileName() == "Assets.xcassets" }! - buildFileMain = proj.buildFiles.first { $0.file?.fileName() == "Main.storyboard" }! + func test_PBXFileOrder_PBXBuildFile_by_filename_when_no_file_when_iosProject() throws { + let iosProject = try iosProject() - objectBuildFileAssets = (buildFileAssets.reference, buildFileAssets) - objectBuildFileMain = (buildFileMain.reference, buildFileMain) + let ref1 = iosProject.buildFileAssets.reference + let ref2 = iosProject.buildFileMain.reference + iosProject.buildFileAssets.file = nil + iosProject.buildFileMain.file = nil + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: (ref1, iosProject.buildFileAssets), rhs: (ref2, iosProject.buildFileMain))) + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: (ref2, iosProject.buildFileMain), rhs: (ref1, iosProject.buildFileAssets))) + } - objectBuildPhaseFileAssets = proj.objects.buildPhaseFile.first { $0.value.buildFile.file?.fileName() == "Assets.xcassets" }! - objectBuildPhaseFileMain = proj.objects.buildPhaseFile.first { $0.value.buildFile.file?.fileName() == "Main.storyboard" }! + // MARK: - PBXFileOrder - PBXBuildPhaseFile - fileReferenceAssets = proj.fileReferences.first { $0.fileName() == "Assets.xcassets" }! - fileReferenceCoreData = proj.fileReferences.first { $0.fileName() == "CoreData.framework" }! + func test_PBXFileOrder_PBXBuildPhaseFile_by_uuid_when_iosProject() throws { + let iosProject = try iosProject() - objectFileReferenceAssets = (buildFileAssets.reference, fileReferenceAssets) - objectFileReferenceCoreData = (buildFileMain.reference, fileReferenceCoreData) + XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: iosProject.objectBuildPhaseFileAssets, rhs: iosProject.objectBuildPhaseFileMain)) + XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: iosProject.objectBuildPhaseFileMain, rhs: iosProject.objectBuildPhaseFileAssets)) + } - groupFrameworks = proj.groups.first { $0.fileName() == "Frameworks" }! - groupProducts = proj.groups.first { $0.fileName() == "Products" }! + func test_PBXFileOrder_PBXBuildPhaseFile_by_filename_when_iosProject() throws { + let iosProject = try iosProject() - objectGroupFrameworks = (groupFrameworks.reference, groupFrameworks) - objectGroupProducts = (groupProducts.reference, groupProducts) + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildPhaseFileAssets, rhs: iosProject.objectBuildPhaseFileMain)) + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectBuildPhaseFileMain, rhs: iosProject.objectBuildPhaseFileAssets)) + } - navigatorFileGroup = proj.groups.first { $0.fileName() == "iOS" }! - } + // MARK: - PBXFileOrder - PBXFileReference - // MARK: - PBXFileOrder - PBXBuldFile + func test_PBXFileOrder_PBXFileReference_by_uuid_when_iosProject() throws { + let iosProject = try iosProject() - func test_PBXFileOrder_PBXBuildFile_by_uuid() { - XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: objectBuildFileAssets, rhs: objectBuildFileMain)) - XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: objectBuildFileMain, rhs: objectBuildFileAssets)) - } + XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: iosProject.objectFileReferenceAssets, rhs: iosProject.objectFileReferenceCoreData)) + XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: iosProject.objectFileReferenceCoreData, rhs: iosProject.objectFileReferenceAssets)) + } - func test_PBXFileOrder_PBXBuildFile_by_filename() { - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectBuildFileAssets, rhs: objectBuildFileMain)) - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectBuildFileMain, rhs: objectBuildFileAssets)) - } + func test_PBXFileOrder_PBXFileReference_by_filename_when_iosProject() throws { + let iosProject = try iosProject() - func test_PBXFileOrder_PBXBuildFile_by_filename_when_nil_name_and_path() { - buildFileAssets.file?.name = nil - buildFileMain.file?.name = nil - buildFileAssets.file?.path = nil - buildFileMain.file?.path = nil - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectBuildFileAssets, rhs: objectBuildFileMain)) - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectBuildFileMain, rhs: objectBuildFileAssets)) - } + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectFileReferenceAssets, rhs: iosProject.objectFileReferenceCoreData)) + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectFileReferenceCoreData, rhs: iosProject.objectFileReferenceAssets)) + } - func test_PBXFileOrder_PBXBuildFile_by_filename_when_no_file() { - let ref1 = buildFileAssets.reference - let ref2 = buildFileMain.reference - buildFileAssets.file = nil - buildFileMain.file = nil - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: (ref1, buildFileAssets), rhs: (ref2, buildFileMain))) - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: (ref2, buildFileMain), rhs: (ref1, buildFileAssets))) - } + func test_PBXFileOrder_PBXFileReference_by_filename_when_fileSharedAcrossTargetsProject() throws { + let fileSharedAcrossTargetsProject = try fileSharedAcrossTargetsProject() - // MARK: - PBXFileOrder - PBXBuildPhaseFile + let sameNameByFilename = fileSharedAcrossTargetsProject.objectFileReferenceSameName.sorted(by: PBXFileOrder.byFilename.sort) + XCTAssertLessThan(sameNameByFilename.first!.1.uuid, sameNameByFilename.last!.1.uuid) + } - func test_PBXFileOrder_PBXBuildPhaseFile_by_uuid() { - XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: objectBuildPhaseFileAssets, rhs: objectBuildPhaseFileMain)) - XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: objectBuildPhaseFileMain, rhs: objectBuildPhaseFileAssets)) - } + func test_PBXFileOrder_PBXFileReference_by_filename_when_nil_name_and_path_when_iosProject() throws { + let iosProject = try iosProject() - func test_PBXFileOrder_PBXBuildPhaseFile_by_filename() { - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectBuildPhaseFileAssets, rhs: objectBuildPhaseFileMain)) - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectBuildPhaseFileMain, rhs: objectBuildPhaseFileAssets)) - } + iosProject.fileReferenceAssets.name = nil + iosProject.fileReferenceCoreData.name = nil + iosProject.fileReferenceAssets.path = nil + iosProject.fileReferenceCoreData.path = nil + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectFileReferenceAssets, rhs: iosProject.objectFileReferenceCoreData)) + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectFileReferenceCoreData, rhs: iosProject.objectFileReferenceAssets)) + } - // MARK: - PBXFileOrder - PBXFileReference + // MARK: - PBXFileOrder - Other - func test_PBXFileOrder_PBXFileReference_by_uuid() { - XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: objectFileReferenceAssets, rhs: objectFileReferenceCoreData)) - XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: objectFileReferenceCoreData, rhs: objectFileReferenceAssets)) - } + func test_PBXFileOrder_Other_by_uuid_when_iosProject() throws { + let iosProject = try iosProject() - func test_PBXFileOrder_PBXFileReference_by_filename() { - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectFileReferenceAssets, rhs: objectFileReferenceCoreData)) - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectFileReferenceCoreData, rhs: objectFileReferenceAssets)) - } + XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: iosProject.objectGroupFrameworks, rhs: iosProject.objectGroupProducts)) + XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: iosProject.objectGroupProducts, rhs: iosProject.objectGroupFrameworks)) + } - func test_PBXFileOrder_PBXFileReference_by_filename_when_nil_name_and_path() { - fileReferenceAssets.name = nil - fileReferenceCoreData.name = nil - fileReferenceAssets.path = nil - fileReferenceCoreData.path = nil - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectFileReferenceAssets, rhs: objectFileReferenceCoreData)) - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectFileReferenceCoreData, rhs: objectFileReferenceAssets)) - } + func test_PBXFileOrder_Other_by_filename_when_iosProject() throws { + let iosProject = try iosProject() - // MARK: - PBXFileOrder - Other + XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: iosProject.objectGroupFrameworks, rhs: iosProject.objectGroupProducts)) + XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: iosProject.objectGroupProducts, rhs: iosProject.objectGroupFrameworks)) + } - func test_PBXFileOrder_Other_by_uuid() { - XCTAssertTrue(PBXFileOrder.byUUID.sort(lhs: objectGroupFrameworks, rhs: objectGroupProducts)) - XCTAssertFalse(PBXFileOrder.byUUID.sort(lhs: objectGroupProducts, rhs: objectGroupFrameworks)) - } + // MARK: - PBXNavigatorFileOrder - func test_PBXFileOrder_Other_by_filename() { - XCTAssertTrue(PBXFileOrder.byFilename.sort(lhs: objectGroupFrameworks, rhs: objectGroupProducts)) - XCTAssertFalse(PBXFileOrder.byFilename.sort(lhs: objectGroupProducts, rhs: objectGroupFrameworks)) - } + func test_PBXNavigatorFileOrder_unsorted_when_iosProject() { + XCTAssertNil(PBXNavigatorFileOrder.unsorted.sort) + } - // MARK: - PBXNavigatorFileOrder + func test_PBXNavigatorFileOrder_by_filename_when_iosProject() throws { + let iosProject = try iosProject() + + let sort: (PBXFileElement, PBXFileElement) -> Bool = PBXNavigatorFileOrder.byFilename.sort! + let sorted = iosProject.navigatorFileGroup.children.sorted(by: sort).map { $0.fileName()! } + XCTAssertEqual([ + "AppDelegate.swift", + "Assets.xcassets", + "GroupWithoutFolder", + "Info.plist", + "LaunchScreen.storyboard", + "Main.storyboard", + "Model.xcdatamodeld", + "Private.h", + "Protected.h", + "Public.h", + "ViewController.swift", + ], sorted) + } - func test_PBXNavigatorFileOrder_unsorted() { - XCTAssertNil(PBXNavigatorFileOrder.unsorted.sort) - } + func test_PBXNavigatorFileOrder_by_filename_groups_first_when_iosProject() throws { + let iosProject = try iosProject() + + let sort: (PBXFileElement, PBXFileElement) -> Bool = PBXNavigatorFileOrder.byFilenameGroupsFirst.sort! + let sorted = iosProject.navigatorFileGroup.children.sorted(by: sort).map { $0.fileName()! } + XCTAssertEqual([ + "GroupWithoutFolder", + "AppDelegate.swift", + "Assets.xcassets", + "Info.plist", + "LaunchScreen.storyboard", + "Main.storyboard", + "Model.xcdatamodeld", + "Private.h", + "Protected.h", + "Public.h", + "ViewController.swift", + ], sorted) + } - func test_PBXNavigatorFileOrder_by_filename() { - let sort: (PBXFileElement, PBXFileElement) -> Bool = PBXNavigatorFileOrder.byFilename.sort! - let sorted = navigatorFileGroup.children.sorted(by: sort).map { $0.fileName()! } - XCTAssertEqual([ - "AppDelegate.swift", - "Assets.xcassets", - "GroupWithoutFolder", - "Info.plist", - "LaunchScreen.storyboard", - "Main.storyboard", - "Model.xcdatamodeld", - "Private.h", - "Protected.h", - "Public.h", - "ViewController.swift", - ], sorted) - } + // MARK: - PBXBuildPhaseFileOrder - func test_PBXNavigatorFileOrder_by_filename_groups_first() { - let sort: (PBXFileElement, PBXFileElement) -> Bool = PBXNavigatorFileOrder.byFilenameGroupsFirst.sort! - let sorted = navigatorFileGroup.children.sorted(by: sort).map { $0.fileName()! } - XCTAssertEqual([ - "GroupWithoutFolder", - "AppDelegate.swift", - "Assets.xcassets", - "Info.plist", - "LaunchScreen.storyboard", - "Main.storyboard", - "Model.xcdatamodeld", - "Private.h", - "Protected.h", - "Public.h", - "ViewController.swift", - ], sorted) - } + func test_PBXBuildPhaseFileOrder_unsorted() { + XCTAssertNil(PBXBuildPhaseFileOrder.unsorted.sort) + } - // MARK: - PBXBuildPhaseFileOrder + func test_PBXBuildPhaseFileOrder_by_filename_when_iosProject() throws { + let iosProject = try iosProject() - func test_PBXBuildPhaseFileOrder_unsorted() { - XCTAssertNil(PBXBuildPhaseFileOrder.unsorted.sort) - } + XCTAssertTrue(PBXBuildPhaseFileOrder.byFilename.sort!(iosProject.buildFileAssets, iosProject.buildFileMain)) + XCTAssertFalse(PBXBuildPhaseFileOrder.byFilename.sort!(iosProject.buildFileMain, iosProject.buildFileAssets)) + } + + // MARK: - Private + + struct iOSProject { + var proj: PBXProj! + var buildFileAssets: PBXBuildFile! + var buildFileMain: PBXBuildFile! + + var objectBuildFileAssets: (PBXObjectReference, PBXBuildFile)! + var objectBuildFileMain: (PBXObjectReference, PBXBuildFile)! - func test_PBXBuildPhaseFileOrder_by_filename() { - XCTAssertTrue(PBXBuildPhaseFileOrder.byFilename.sort!(buildFileAssets, buildFileMain)) - XCTAssertFalse(PBXBuildPhaseFileOrder.byFilename.sort!(buildFileMain, buildFileAssets)) + var objectBuildPhaseFileAssets: (PBXObjectReference, PBXBuildPhaseFile)! + var objectBuildPhaseFileMain: (PBXObjectReference, PBXBuildPhaseFile)! + + var fileReferenceAssets: PBXFileReference! + var fileReferenceCoreData: PBXFileReference! + + var objectFileReferenceAssets: (PBXObjectReference, PBXFileReference)! + var objectFileReferenceCoreData: (PBXObjectReference, PBXFileReference)! + + var groupFrameworks: PBXGroup! + var groupProducts: PBXGroup! + + var objectGroupFrameworks: (PBXObjectReference, PBXGroup)! + var objectGroupProducts: (PBXObjectReference, PBXGroup)! + + var navigatorFileGroup: PBXGroup! + } + + private func iosProject() throws -> iOSProject { + let data = try XCTUnwrap(iosProjectData()) + let proj = try XCTUnwrap(PBXProj(data: data)) + + let buildFileAssets = proj.buildFiles.first { $0.file?.fileName() == "Assets.xcassets" }! + let buildFileMain = proj.buildFiles.first { $0.file?.fileName() == "Main.storyboard" }! + + let objectBuildFileAssets = (buildFileAssets.reference, buildFileAssets) + let objectBuildFileMain = (buildFileMain.reference, buildFileMain) + + let objectBuildPhaseFileAssets = proj.objects.buildPhaseFile.first { $0.value.buildFile.file?.fileName() == "Assets.xcassets" }! + let objectBuildPhaseFileMain = proj.objects.buildPhaseFile.first { $0.value.buildFile.file?.fileName() == "Main.storyboard" }! + + let fileReferenceAssets = proj.fileReferences.first { $0.fileName() == "Assets.xcassets" }! + let fileReferenceCoreData = proj.fileReferences.first { $0.fileName() == "CoreData.framework" }! + + let objectFileReferenceAssets = (buildFileAssets.reference, fileReferenceAssets) + let objectFileReferenceCoreData = (buildFileMain.reference, fileReferenceCoreData) + + let groupFrameworks = proj.groups.first { $0.fileName() == "Frameworks" }! + let groupProducts = proj.groups.first { $0.fileName() == "Products" }! + + let objectGroupFrameworks = (groupFrameworks.reference, groupFrameworks) + let objectGroupProducts = (groupProducts.reference, groupProducts) + + let navigatorFileGroup = proj.groups.first { $0.fileName() == "iOS" }! + + return iOSProject( + proj: proj, + buildFileAssets: buildFileAssets, + buildFileMain: buildFileMain, + objectBuildFileAssets: objectBuildFileAssets, + objectBuildFileMain: objectBuildFileMain, + objectBuildPhaseFileAssets: objectBuildPhaseFileAssets, + objectBuildPhaseFileMain: objectBuildPhaseFileMain, + fileReferenceAssets: fileReferenceAssets, + fileReferenceCoreData: fileReferenceCoreData, + objectFileReferenceAssets: objectFileReferenceAssets, + objectFileReferenceCoreData: objectFileReferenceCoreData, + groupFrameworks: groupFrameworks, + groupProducts: groupProducts, + objectGroupFrameworks: objectGroupFrameworks, + objectGroupProducts: objectGroupProducts, + navigatorFileGroup: navigatorFileGroup + ) + } + + struct FileSharedAcrossTargetsProject { + var proj: PBXProj! + var buildFileSameName: [PBXBuildFile]! + var objectBuildFileSameName: [(PBXObjectReference, PBXBuildFile)]! + var fileReferenceSameName: [PBXFileReference]! + var objectFileReferenceSameName: [(PBXObjectReference, PBXFileReference)]! + } + + func fileSharedAcrossTargetsProject() throws -> FileSharedAcrossTargetsProject { + let dic = try XCTUnwrap(fileSharedAcrossTargetsData()) + let proj = try XCTUnwrap(PBXProj(data: dic)) + + let buildFileSameName = proj.buildFiles.filter { $0.file?.fileName() == "SameName.h" } + let objectBuildFileSameName = proj.buildFiles.map { ($0.reference, $0) } + let fileReferenceSameName = proj.fileReferences.filter { $0.fileName() == "FileSharedAcrossTargetsTests.swift" } + let objectFileReferenceSameName = fileReferenceSameName.map { ($0.reference, $0) } + + return FileSharedAcrossTargetsProject( + proj: proj, + buildFileSameName: buildFileSameName, + objectBuildFileSameName: objectBuildFileSameName, + fileReferenceSameName: fileReferenceSameName, + objectFileReferenceSameName: objectFileReferenceSameName + ) + } } -} +#endif diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProj+Fixtures.swift b/Tests/XcodeProjTests/Objects/Project/PBXProj+Fixtures.swift index 69752cdb4..8488d0fb7 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProj+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProj+Fixtures.swift @@ -5,12 +5,13 @@ extension PBXProj { static func fixture(rootObject: PBXProject? = PBXProject.fixture(), objectVersion: UInt = Xcode.LastKnown.objectVersion, archiveVersion: UInt = Xcode.LastKnown.archiveVersion, - classes: [String: Any] = [:], - objects: [PBXObject] = []) -> PBXProj { - return PBXProj(rootObject: rootObject, - objectVersion: objectVersion, - archiveVersion: archiveVersion, - classes: classes, - objects: objects) + classes: [String: [String]] = [:], + objects: [PBXObject] = []) -> PBXProj + { + PBXProj(rootObject: rootObject, + objectVersion: objectVersion, + archiveVersion: archiveVersion, + classes: classes, + objects: objects) } } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift index 7d31493f4..aa7424c5e 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift @@ -1,405 +1,675 @@ +#if os(macOS) || (os(Linux) && compiler(>=6.1)) + import Foundation + import XCTest + @testable import XcodeProj -import Foundation -import XCTest -@testable import XcodeProj + class PBXProjEncoderTests: XCTestCase { + var proj: PBXProj! -class PBXProjEncoderTests: XCTestCase { - var proj: PBXProj! + // MARK: - Header - override func setUp() { - super.setUp() - let dic = iosProjectDictionary() - do { - proj = try PBXProj(jsonDictionary: dic.1) - } catch { - XCTFail("Failed to load project from file \(error)") + func test_writeHeaders_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + XCTAssertEqual(583, lines.count) + XCTAssertEqual("// !$*UTF8*$!", lines[0]) } - } - // MARK: - Header + // MARK: - Internal file lists + + func test_buildFiles_in_default_uuid_order_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + var line = lines.validate(line: "/* Begin PBXBuildFile section */") + line = lines.validate(lineContaining: "04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A31F153924008A2F98 /* Public.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A51F153924008A2F98 /* Private.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1A22AAF48100428760 /* MyLocalPackage in Frameworks */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */", onLineAfter: line) + lines.validate(line: "/* End PBXBuildFile section */", onLineAfter: line) + } - func test_writeHeaders() throws { - let lines = self.lines(fromFile: encodeProject()) - XCTAssertEqual(583, lines.count) - XCTAssertEqual("// !$*UTF8*$!", lines[0]) - } + func test_buildFiles_in_filename_order_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projFileListOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + var line = lines.validate(line: "/* Begin PBXBuildFile section */") + line = lines.validate(lineContaining: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */", onLineAfter: line) + line = lines.validate(lineContaining: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A51F153924008A2F98 /* Private.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A31F153924008A2F98 /* Public.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1A22AAF48100428760 /* MyLocalPackage in Frameworks */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */", onLineAfter: line) + lines.validate(line: "/* End PBXBuildFile section */", onLineAfter: line) + } - // MARK: - Internal file lists - - func test_buildFiles_in_default_uuid_order() { - let lines = self.lines(fromFile: encodeProject()) - var line = lines.validate(line: "/* Begin PBXBuildFile section */") - line = lines.validate(lineContaining: "04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A31F153924008A2F98 /* Public.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A51F153924008A2F98 /* Private.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1A22AAF48100428760 /* MyLocalPackage in Frameworks */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */", onLineAfter: line) - lines.validate(line: "/* End PBXBuildFile section */", onLineAfter: line) - } + func test_buildFiles_in_filename_order_when_fileSharedAcrossTargetsProject() throws { + try loadFileSharedAcrossTargetsProject() - func test_buildFiles_in_filename_order() { - let settings = PBXOutputSettings(projFileListOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - var line = lines.validate(line: "/* Begin PBXBuildFile section */") - line = lines.validate(lineContaining: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C09F1F153824008A2F98 /* CoreData.framework in Frameworks */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */", onLineAfter: line) - line = lines.validate(lineContaining: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A51F153924008A2F98 /* Private.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A31F153924008A2F98 /* Public.h in Headers */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2B1EAA3484007A9026 /* iOSTests.swift in Sources */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1A22AAF48100428760 /* MyLocalPackage in Frameworks */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1C22AAF48100428760 /* RxSwift in Frameworks */", onLineAfter: line) - lines.validate(line: "/* End PBXBuildFile section */", onLineAfter: line) - } + let settings = PBXOutputSettings(projFileListOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + var line = lines.validate(line: "/* Begin PBXBuildFile section */") - func test_file_references_in_default_uuid_order() { - let lines = self.lines(fromFile: encodeProject()) - var line = lines.validate(line: "/* Begin PBXFileReference section */") - line = lines.validate(lineContaining: "04D5C09E1F153824008A2F98 /* CoreData.framework */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A01F153915008A2F98 /* Public.h */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A11F15391B008A2F98 /* Protected.h */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A21F153921008A2F98 /* Private.h */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C121EAA3484007A9026 /* iOS.app */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C151EAA3484007A9026 /* AppDelegate.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C171EAA3484007A9026 /* ViewController.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1A1EAA3484007A9026 /* Base */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1C1EAA3484007A9026 /* Assets.xcassets */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1F1EAA3484007A9026 /* Base */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C211EAA3484007A9026 /* Info.plist */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C261EAA3484007A9026 /* iOSTests.xctest */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2A1EAA3484007A9026 /* iOSTests.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2C1EAA3484007A9026 /* Info.plist */", onLineAfter: line) - line = lines.validate(lineContaining: "23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */", onLineAfter: line) - line = lines.validate(lineContaining: "3CD1EADC205763E400DAEECB /* Model.xcdatamodel */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1822AAF41000428760 /* MyLocalPackage */", onLineAfter: line) - lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) - } + line = lines.validate(lineContaining: "6C103C032A49CC5400D7EFE4 /* FileSharedAcrossTargets.framework in Frameworks */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C092A49CC5400D7EFE4 /* FileSharedAcrossTargets.h in Headers */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C082A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift in Sources */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C132A49CC7300D7EFE4 /* SharedHeader.h in Headers */", onLineAfter: line) - func test_file_references_in_filename_order() { - let settings = PBXOutputSettings(projFileListOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - var line = lines.validate(line: "/* Begin PBXFileReference section */") - line = lines.validate(lineContaining: "23766C151EAA3484007A9026 /* AppDelegate.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1C1EAA3484007A9026 /* Assets.xcassets */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1A1EAA3484007A9026 /* Base */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C1F1EAA3484007A9026 /* Base */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C09E1F153824008A2F98 /* CoreData.framework */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C211EAA3484007A9026 /* Info.plist */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2C1EAA3484007A9026 /* Info.plist */", onLineAfter: line) - line = lines.validate(lineContaining: "3CD1EADC205763E400DAEECB /* Model.xcdatamodel */", onLineAfter: line) - line = lines.validate(lineContaining: "42AA1A1822AAF41000428760 /* MyLocalPackage */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A21F153921008A2F98 /* Private.h */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A11F15391B008A2F98 /* Protected.h */", onLineAfter: line) - line = lines.validate(lineContaining: "04D5C0A01F153915008A2F98 /* Public.h */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C171EAA3484007A9026 /* ViewController.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C121EAA3484007A9026 /* iOS.app */", onLineAfter: line) - line = lines.validate(lineContaining: "23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C2A1EAA3484007A9026 /* iOSTests.swift */", onLineAfter: line) - line = lines.validate(lineContaining: "23766C261EAA3484007A9026 /* iOSTests.xctest */", onLineAfter: line) - lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) - } + lines.validate(line: "/* End PBXBuildFile section */", onLineAfter: line) + } - // MARK: - Navigator - - func test_navigator_groups_in_default_order() { - let lines = self.lines(fromFile: encodeProject()) - - let beginGroup = lines.findLine("/* Begin PBXGroup section */") - - // Root - let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) - let rootChildrenStart = lines.findLine("children = (", after: rootGroup) - let rootChildrenEnd = lines.findLine(");", after: rootChildrenStart) - - lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) - lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) - lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) - lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) - - // iOS - let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) - let iosChildrenStart = lines.findLine("children = (", after: iosGroup) - let iosChildrenEnd = lines.findLine(");", after: iosChildrenStart) - - lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) - - // iOS Tests - let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) - let iosTestsChildrenStart = lines.findLine("children = (", after: iosTestsGroup) - let iosTestsChildrenEnd = lines.findLine(");", after: iosTestsChildrenStart) - - lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", betweenLine: iosTestsChildrenStart, andLine: iosTestsChildrenEnd) - lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", betweenLine: iosTestsChildrenStart, andLine: iosTestsChildrenEnd) - } + func test_file_references_in_default_uuid_order_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + var line = lines.validate(line: "/* Begin PBXFileReference section */") + line = lines.validate(lineContaining: "04D5C09E1F153824008A2F98 /* CoreData.framework */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A01F153915008A2F98 /* Public.h */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A11F15391B008A2F98 /* Protected.h */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A21F153921008A2F98 /* Private.h */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C121EAA3484007A9026 /* iOS.app */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C151EAA3484007A9026 /* AppDelegate.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C171EAA3484007A9026 /* ViewController.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1A1EAA3484007A9026 /* Base */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1C1EAA3484007A9026 /* Assets.xcassets */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1F1EAA3484007A9026 /* Base */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C211EAA3484007A9026 /* Info.plist */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C261EAA3484007A9026 /* iOSTests.xctest */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2A1EAA3484007A9026 /* iOSTests.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2C1EAA3484007A9026 /* Info.plist */", onLineAfter: line) + line = lines.validate(lineContaining: "23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */", onLineAfter: line) + line = lines.validate(lineContaining: "3CD1EADC205763E400DAEECB /* Model.xcdatamodel */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1822AAF41000428760 /* MyLocalPackage */", onLineAfter: line) + + lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) + } - func test_navigator_groups_in_filename_order() { - let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - - let beginGroup = lines.findLine("/* Begin PBXGroup section */") - - // Root - let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) - var line = lines.findLine("children = (", after: rootGroup) - line = lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", after: line) - line = lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", after: line) - line = lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", after: line) - line = lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", after: line) - lines.validate(line: ");", after: line) - - // iOS - let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) - line = lines.findLine("children = (", after: iosGroup) - line = lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", after: line) - line = lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", after: line) - line = lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", after: line) - line = lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", after: line) - line = lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", after: line) - line = lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", after: line) - line = lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", after: line) - line = lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", after: line) - line = lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", after: line) - line = lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", after: line) - line = lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", after: line) - lines.validate(line: ");", after: line) - - // iOS Tests - let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) - line = lines.findLine("children = (", after: iosTestsGroup) - line = lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", after: line) - line = lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", after: line) - lines.validate(line: ");", after: line) - } + func test_file_references_in_default_uuid_order_when_fileSharedAcrossTargetsProject() throws { + try loadFileSharedAcrossTargetsProject() - func test_navigator_groups_in_filename_groups_first_order() { - let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - - let beginGroup = lines.findLine("/* Begin PBXGroup section */") - - // Root - let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) - var line = lines.findLine("children = (", after: rootGroup) - line = lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", after: line) - line = lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", after: line) - line = lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", after: line) - line = lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", after: line) - lines.validate(line: ");", after: line) - - // iOS - let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) - line = lines.findLine("children = (", after: iosGroup) - line = lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", after: line) - line = lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", after: line) - line = lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", after: line) - line = lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", after: line) - line = lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", after: line) - line = lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", after: line) - line = lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", after: line) - line = lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", after: line) - line = lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", after: line) - line = lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", after: line) - line = lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", after: line) - lines.validate(line: ");", after: line) - - // iOS Tests - let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) - line = lines.findLine("children = (", after: iosTestsGroup) - line = lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", after: line) - line = lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", after: line) - lines.validate(line: ");", after: line) - } + let lines = lines(fromFile: encodeProject()) + var line = lines.validate(line: "/* Begin PBXFileReference section */") + line = lines.validate(lineContaining: "6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103BFD2A49CC5300D7EFE4 /* FileSharedAcrossTargets.h */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C022A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.xctest */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C072A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C122A49CC7300D7EFE4 /* SharedHeader.h */", onLineAfter: line) + line = lines.validate(lineContaining: "6CB965012A49DC1F009186C6 /* FileSharedAcrossTargetsTests.swift */", onLineAfter: line) + + lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) + } - // MARK: - Build phases + func test_file_references_in_filename_order_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projFileListOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + var line = lines.validate(line: "/* Begin PBXFileReference section */") + line = lines.validate(lineContaining: "23766C151EAA3484007A9026 /* AppDelegate.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1C1EAA3484007A9026 /* Assets.xcassets */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1A1EAA3484007A9026 /* Base */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C1F1EAA3484007A9026 /* Base */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C09E1F153824008A2F98 /* CoreData.framework */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C211EAA3484007A9026 /* Info.plist */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2C1EAA3484007A9026 /* Info.plist */", onLineAfter: line) + line = lines.validate(lineContaining: "3CD1EADC205763E400DAEECB /* Model.xcdatamodel */", onLineAfter: line) + line = lines.validate(lineContaining: "42AA1A1822AAF41000428760 /* MyLocalPackage */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A21F153921008A2F98 /* Private.h */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A11F15391B008A2F98 /* Protected.h */", onLineAfter: line) + line = lines.validate(lineContaining: "04D5C0A01F153915008A2F98 /* Public.h */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C171EAA3484007A9026 /* ViewController.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C121EAA3484007A9026 /* iOS.app */", onLineAfter: line) + line = lines.validate(lineContaining: "23C1E0AF23657FB500B8D1EF /* iOS.xctestplan */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C2A1EAA3484007A9026 /* iOSTests.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "23766C261EAA3484007A9026 /* iOSTests.xctest */", onLineAfter: line) + lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) + } - func test_build_phase_sources_unsorted() { - let lines = self.lines(fromFile: encodeProject()) - let beginGroup = lines.findLine("/* Begin PBXSourcesBuildPhase section */") - let files = lines.findLine("files = (", after: beginGroup) - let endGroup = lines.findLine("/* End PBXSourcesBuildPhase section */") - lines.validate(line: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */,", betweenLine: files, andLine: endGroup) - } + func test_file_references_in_filename_order_when_fileSharedAcrossTargetsProject() throws { + try loadFileSharedAcrossTargetsProject() + + let settings = PBXOutputSettings(projFileListOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + var line = lines.validate(line: "/* Begin PBXFileReference section */") + line = lines.validate(lineContaining: "6C103BFA2A49CC5300D7EFE4 /* FileSharedAcrossTargets.framework */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103BFD2A49CC5300D7EFE4 /* FileSharedAcrossTargets.h */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C072A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "6CB965012A49DC1F009186C6 /* FileSharedAcrossTargetsTests.swift */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C022A49CC5400D7EFE4 /* FileSharedAcrossTargetsTests.xctest */", onLineAfter: line) + line = lines.validate(lineContaining: "6C103C122A49CC7300D7EFE4 /* SharedHeader.h */", onLineAfter: line) + lines.validate(line: "/* End PBXFileReference section */", onLineAfter: line) + } - func test_build_phase_sources_sorted() { - let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - let beginGroup = lines.findLine("/* Begin PBXSourcesBuildPhase section */") - var line = lines.findLine("files = (", after: beginGroup) - line = lines.validate(line: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */,", after: line) - line = lines.validate(line: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */,", after: line) - line = lines.validate(line: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */,", after: line) - line = lines.validate(line: "/* End PBXSourcesBuildPhase section */", after: line) - } + // MARK: - Navigator + + func test_navigator_groups_in_default_order_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + + let beginGroup = lines.findLine("/* Begin PBXGroup section */") + + // Root + let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) + let rootChildrenStart = lines.findLine("children = (", after: rootGroup) + let rootChildrenEnd = lines.findLine(");", after: rootChildrenStart) + + lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) + lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) + lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) + lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", betweenLine: rootChildrenStart, andLine: rootChildrenEnd) + + // iOS + let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) + let iosChildrenStart = lines.findLine("children = (", after: iosGroup) + let iosChildrenEnd = lines.findLine(");", after: iosChildrenStart) + + lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", betweenLine: iosChildrenStart, andLine: iosChildrenEnd) + + // iOS Tests + let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) + let iosTestsChildrenStart = lines.findLine("children = (", after: iosTestsGroup) + let iosTestsChildrenEnd = lines.findLine(");", after: iosTestsChildrenStart) + + lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", betweenLine: iosTestsChildrenStart, andLine: iosTestsChildrenEnd) + lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", betweenLine: iosTestsChildrenStart, andLine: iosTestsChildrenEnd) + } - func test_build_phase_headers_unsorted() { - let lines = self.lines(fromFile: encodeProject()) - let beginGroup = lines.findLine("/* Begin PBXHeadersBuildPhase section */") - let files = lines.findLine("files = (", after: beginGroup) - let endGroup = lines.findLine("/* End PBXHeadersBuildPhase section */") - lines.validate(line: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "04D5C0A51F153924008A2F98 /* Private.h in Headers */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "04D5C0A31F153924008A2F98 /* Public.h in Headers */,", betweenLine: files, andLine: endGroup) - } + func test_navigator_groups_in_filename_order_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXGroup section */") + + // Root + let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) + var line = lines.findLine("children = (", after: rootGroup) + line = lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", after: line) + line = lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", after: line) + line = lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", after: line) + line = lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", after: line) + lines.validate(line: ");", after: line) + + // iOS + let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) + line = lines.findLine("children = (", after: iosGroup) + line = lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", after: line) + line = lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", after: line) + line = lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", after: line) + line = lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", after: line) + line = lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", after: line) + line = lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", after: line) + line = lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", after: line) + line = lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", after: line) + line = lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", after: line) + line = lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", after: line) + line = lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", after: line) + lines.validate(line: ");", after: line) + + // iOS Tests + let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) + line = lines.findLine("children = (", after: iosTestsGroup) + line = lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", after: line) + line = lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", after: line) + lines.validate(line: ");", after: line) + } - func test_build_phase_headers_sorted() { - let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - let beginGroup = lines.findLine("/* Begin PBXHeadersBuildPhase section */") - var line = lines.findLine("files = (", after: beginGroup) - line = lines.validate(line: "04D5C0A51F153924008A2F98 /* Private.h in Headers */,", after: line) - line = lines.validate(line: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */,", after: line) - line = lines.validate(line: "04D5C0A31F153924008A2F98 /* Public.h in Headers */,", after: line) - line = lines.validate(line: "/* End PBXHeadersBuildPhase section */", after: line) - } + func test_navigator_groups_in_filename_groups_first_order_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXGroup section */") + + // Root + let rootGroup = lines.findLine("23766C091EAA3484007A9026 = {", after: beginGroup) + var line = lines.findLine("children = (", after: rootGroup) + line = lines.validate(line: "04D5C09D1F153824008A2F98 /* Frameworks */,", after: line) + line = lines.validate(line: "23766C131EAA3484007A9026 /* Products */,", after: line) + line = lines.validate(line: "23766C141EAA3484007A9026 /* iOS */,", after: line) + line = lines.validate(line: "23766C291EAA3484007A9026 /* iOSTests */,", after: line) + lines.validate(line: ");", after: line) + + // iOS + let iosGroup = lines.findLine("23766C141EAA3484007A9026 /* iOS */ = {", after: beginGroup) + line = lines.findLine("children = (", after: iosGroup) + line = lines.validate(line: "3CD1EAD92057638200DAEECB /* GroupWithoutFolder */,", after: line) + line = lines.validate(line: "23766C151EAA3484007A9026 /* AppDelegate.swift */,", after: line) + line = lines.validate(line: "23766C1C1EAA3484007A9026 /* Assets.xcassets */,", after: line) + line = lines.validate(line: "23766C211EAA3484007A9026 /* Info.plist */,", after: line) + line = lines.validate(line: "23766C1E1EAA3484007A9026 /* LaunchScreen.storyboard */,", after: line) + line = lines.validate(line: "23766C191EAA3484007A9026 /* Main.storyboard */,", after: line) + line = lines.validate(line: "3CD1EADB205763E400DAEECB /* Model.xcdatamodeld */,", after: line) + line = lines.validate(line: "04D5C0A21F153921008A2F98 /* Private.h */,", after: line) + line = lines.validate(line: "04D5C0A11F15391B008A2F98 /* Protected.h */,", after: line) + line = lines.validate(line: "04D5C0A01F153915008A2F98 /* Public.h */,", after: line) + line = lines.validate(line: "23766C171EAA3484007A9026 /* ViewController.swift */,", after: line) + lines.validate(line: ");", after: line) + + // iOS Tests + let iosTestsGroup = lines.findLine("23766C291EAA3484007A9026 /* iOSTests */ = {", after: beginGroup) + line = lines.findLine("children = (", after: iosTestsGroup) + line = lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", after: line) + line = lines.validate(line: "23766C2A1EAA3484007A9026 /* iOSTests.swift */,", after: line) + lines.validate(line: ");", after: line) + } - func test_build_phase_resources_unsorted() { - let lines = self.lines(fromFile: encodeProject()) - let beginGroup = lines.findLine("/* Begin PBXResourcesBuildPhase section */") - let files = lines.findLine("files = (", after: beginGroup) - let endGroup = lines.findLine("/* End PBXResourcesBuildPhase section */") - lines.validate(line: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */,", betweenLine: files, andLine: endGroup) - lines.validate(line: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */,", betweenLine: files, andLine: endGroup) - } + // MARK: - File system synchronized root groups - func test_build_phase_resources_sorted() { - let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) - let lines = self.lines(fromFile: encodeProject(settings: settings)) - let beginGroup = lines.findLine("/* Begin PBXResourcesBuildPhase section */") - var line = lines.findLine("files = (", after: beginGroup) - line = lines.validate(line: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */,", after: line) - line = lines.validate(line: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */,", after: line) - line = lines.validate(line: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */,", after: line) - line = lines.validate(line: "/* End PBXResourcesBuildPhase section */", after: line) - } + func test_fileSystemSynchronizedRootGroups_when_projectWithFileSystemSynchronizedRootGroups() throws { + // Given + try loadSynchronizedRootGroups() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) - // MARK: - Test internals + let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedRootGroup section */") + var line = lines.validate(line: "6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = \"\"; };", after: beginGroup) + line = lines.validate(line: "/* End PBXFileSystemSynchronizedRootGroup section */", after: line) + } - private func encodeProject(settings: PBXOutputSettings = PBXOutputSettings(), line: UInt = #line) -> String { - do { - return try PBXProjEncoder(outputSettings: settings).encode(proj: proj) - } catch { - XCTFail("Unexpected error encoding project: \(error)", line: line) - return "" + // MARK: - File system synchronized build file exception set + + func test_fileSystemSynchronizedBuildFileExceptionSets_when_projectWithFileSystemSynchronizedRootGroups() throws { + // Given + try loadSynchronizedRootGroups() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */") + var line = lines.validate(line: "6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {", after: beginGroup) + line = lines.validate(line: "isa = PBXFileSystemSynchronizedBuildFileExceptionSet;", after: line) + line = lines.validate(line: "membershipExceptions = (", after: line) + line = lines.validate(line: "Exception/Exception.swift,", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "target = 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */;", after: line) + line = lines.validate(line: "};", after: line) + line = lines.validate(line: "/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */", after: line) } - } - private func encodeProjectThrows(error expectedError: E, line: UInt = #line) where E: Error { - do { - _ = try PBXProjEncoder(outputSettings: PBXOutputSettings()).encode(proj: proj) - XCTFail("Expected '\(expectedError)' to be thrown", line: line) - } catch { - if type(of: expectedError) != type(of: error) { - XCTFail("Expected '\(expectedError)' to be thrown, but got \(error)", line: line) + // MARK: - File system synchronized group build phase membership exception set + + func test_fileSystemSynchronizedGroupBuildPhaseMembershipExceptionSets_when_projectWithFileSystemSynchronizedRootGroups() throws { + // Given + try loadSynchronizedRootGroups() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */") + var line = lines.validate(line: "F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {", after: beginGroup) + line = lines.validate(line: "isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;", after: line) + line = lines.validate(line: "attributesByRelativePath = {", after: line) + line = lines.validate(line: "XPCService.xpc = (", after: line) + line = lines.validate(line: "RemoveHeadersOnCopy,", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "};", after: line) + line = lines.validate(line: "buildPhase = F841A9CA2D63AFBB00059ED6 /* CopyFiles */;", after: line) + line = lines.validate(line: "membershipExceptions = (", after: line) + line = lines.validate(line: "XPCService.xpc,", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "};", after: line) + line = lines.validate(line: "/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */", after: line) + } + + // MARK: File system synchronized build configuration files + + func test_fileSystemSynchronizedBuildConfigurationFiles() throws { + try loadProjectWithConfigurationFilesInSynchronizedGroup() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin XCBuildConfiguration section */") + var line = lines.validate(line: "9183098E2EFB16F800EE08DF /* Debug */ = {", after: beginGroup) + line = lines.validate(line: "isa = XCBuildConfiguration;", after: line) + line = lines.validate(line: "baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */;", after: line) + line = lines.validate(line: "baseConfigurationReferenceRelativePath = Configs/DevConfig.xcconfig;", after: line) + + line = lines.validate(line: "9183098F2EFB16F800EE08DF /* Release */ = {", after: line) + line = lines.validate(line: "isa = XCBuildConfiguration;", after: line) + line = lines.validate(line: "baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */;", after: line) + line = lines.validate(line: "baseConfigurationReferenceRelativePath = Configs/ReleaseConfig.xcconfig;", after: line) + + line = lines.validate(line: "918309912EFB16F800EE08DF /* Debug */ = {", after: line) + line = lines.validate(line: "isa = XCBuildConfiguration;", after: line) + line = lines.validate(line: "baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */;", after: line) + line = lines.validate(line: "baseConfigurationReferenceRelativePath = Configs/TargetConfig.xcconfig;", after: line) + + line = lines.validate(line: "918309922EFB16F800EE08DF /* Release */ = {", after: line) + line = lines.validate(line: "isa = XCBuildConfiguration;", after: line) + line = lines.validate(line: "baseConfigurationReferenceAnchor = 918309872EFB16F800EE08DF /* Xcode16BuildConfigurations */;", after: line) + line = lines.validate(line: "baseConfigurationReferenceRelativePath = Configs/TargetConfig.xcconfig;", after: line) + } + + // MARK: - Projects + + func test_ProjectReferenceOrder() throws { + try loadProjectWithWrongProjectReferencesOrder() + + let lines = lines(fromFile: encodeProject()) + + let beginGroup = lines.findLine("/* Begin PBXProject section */") + let beginReferences = lines.findLine("projectReferences = (", after: beginGroup) + let endReferences = lines.findLine(");", after: beginReferences) + let firstReferenceLine = lines.validate(line: "ProjectRef = 87A3E8A0727A99EE88ED4E64 /* Framework1.xcodeproj */;", betweenLine: beginReferences, andLine: endReferences) + lines.validate(line: "ProjectRef = 08931D1475E84509040F7FEA /* Framework2.xcodeproj */;", betweenLine: firstReferenceLine, andLine: endReferences) + } + + // MARK: - Build phases + + func test_build_phase_sources_unsorted_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + let beginGroup = lines.findLine("/* Begin PBXSourcesBuildPhase section */") + let files = lines.findLine("files = (", after: beginGroup) + let endGroup = lines.findLine("/* End PBXSourcesBuildPhase section */") + lines.validate(line: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */,", betweenLine: files, andLine: endGroup) + } + + func test_build_phase_sources_sorted_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("/* Begin PBXSourcesBuildPhase section */") + var line = lines.findLine("files = (", after: beginGroup) + line = lines.validate(line: "23766C161EAA3484007A9026 /* AppDelegate.swift in Sources */,", after: line) + line = lines.validate(line: "3CD1EADD205763E400DAEECB /* Model.xcdatamodeld in Sources */,", after: line) + line = lines.validate(line: "23766C181EAA3484007A9026 /* ViewController.swift in Sources */,", after: line) + line = lines.validate(line: "/* End PBXSourcesBuildPhase section */", after: line) + } + + func test_build_phase_headers_unsorted_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + let beginGroup = lines.findLine("/* Begin PBXHeadersBuildPhase section */") + let files = lines.findLine("files = (", after: beginGroup) + let endGroup = lines.findLine("/* End PBXHeadersBuildPhase section */") + lines.validate(line: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "04D5C0A51F153924008A2F98 /* Private.h in Headers */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "04D5C0A31F153924008A2F98 /* Public.h in Headers */,", betweenLine: files, andLine: endGroup) + } + + func test_build_phase_headers_sorted_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("/* Begin PBXHeadersBuildPhase section */") + var line = lines.findLine("files = (", after: beginGroup) + line = lines.validate(line: "04D5C0A51F153924008A2F98 /* Private.h in Headers */,", after: line) + line = lines.validate(line: "04D5C0A41F153924008A2F98 /* Protected.h in Headers */,", after: line) + line = lines.validate(line: "04D5C0A31F153924008A2F98 /* Public.h in Headers */,", after: line) + line = lines.validate(line: "/* End PBXHeadersBuildPhase section */", after: line) + } + + func test_build_phase_resources_unsorted_when_iOSProject() throws { + try loadiOSProject() + + let lines = lines(fromFile: encodeProject()) + let beginGroup = lines.findLine("/* Begin PBXResourcesBuildPhase section */") + let files = lines.findLine("files = (", after: beginGroup) + let endGroup = lines.findLine("/* End PBXResourcesBuildPhase section */") + lines.validate(line: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */,", betweenLine: files, andLine: endGroup) + lines.validate(line: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */,", betweenLine: files, andLine: endGroup) + } + + func test_build_phase_resources_sorted_when_iOSProject() throws { + try loadiOSProject() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("/* Begin PBXResourcesBuildPhase section */") + var line = lines.findLine("files = (", after: beginGroup) + line = lines.validate(line: "23766C1D1EAA3484007A9026 /* Assets.xcassets in Resources */,", after: line) + line = lines.validate(line: "23766C201EAA3484007A9026 /* LaunchScreen.storyboard in Resources */,", after: line) + line = lines.validate(line: "23766C1B1EAA3484007A9026 /* Main.storyboard in Resources */,", after: line) + line = lines.validate(line: "/* End PBXResourcesBuildPhase section */", after: line) + } + + func test_build_rules_when_targetWithCustomBuildRulesProject() throws { + try loadTargetWithCustomBuildRulesProject() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("6CAD68202A56E31400662D8A /* PBXBuildRule */ = {") + var line = lines.validate(line: "isa = PBXBuildRule;", after: beginGroup) + line = lines.validate(line: "compilerSpec = com.apple.compilers.proxy.script;", after: line) + line = lines.validate(line: "dependencyFile = \"$(DERIVED_FILES_DIR)/$(INPUT_FILE_PATH).d\";", after: line) + line = lines.validate(line: "fileType = pattern.proxy;", after: line) + line = lines.validate(line: "inputFiles = (", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "isEditable = 1;", after: line) + line = lines.validate(line: "name = \"Custom 2 with dependency file\";", after: line) + line = lines.validate(line: "outputFiles = (", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "script = \"# Type a script or drag a script file from your workspace to insert its path.\\n\";", after: line) + line = lines.validate(line: "};", after: line) + } + + func test_package_section_when_projectWithXCLocalSwiftPackageReference() throws { + try loadProjectWithXCLocalSwiftPackageReference() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("/* Begin XCLocalSwiftPackageReference section */") + var line = lines.validate(line: "C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference \"MyLocalPackage\" */ = {", after: beginGroup) + line = lines.validate(line: "isa = XCLocalSwiftPackageReference;", after: line) + line = lines.validate(line: "relativePath = MyLocalPackage;", after: line) + line = lines.validate(line: "};", after: line) + line = lines.validate(line: "/* End XCLocalSwiftPackageReference section */", after: line) + } + + func test_package_references_when_projectWithXCLocalSwiftPackageReference() throws { + try loadProjectWithXCLocalSwiftPackageReference() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("packageReferences = (") + var line = lines.validate(line: "42AA19FF22AAF0D600428760 /* XCRemoteSwiftPackageReference \"RxSwift\" */,", after: beginGroup) + line = lines.validate(line: "C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference \"MyLocalPackage\" */,", after: line) + line = lines.validate(line: ");", after: line) + } + + func test_package_references_when_projectWithRelativePathForXCLocalSwiftPackageReference() throws { + try loadProjectWithRelativeXCLocalSwiftPackageReference() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("packageReferences = (") + var line = lines.validate(line: "C9FDF5C82AD8AE400096A37A /* XCLocalSwiftPackageReference \"../MyLocalPackage\" */,", after: beginGroup) + line = lines.validate(line: ");", after: line) + } + + func test_package_references_when_projectWithXCLocalSwiftPackageReferences() throws { + try loadProjectWithXCLocalSwiftPackageReferences() + + let settings = PBXOutputSettings(projBuildPhaseFileOrder: .byFilename) + let lines = lines(fromFile: encodeProject(settings: settings)) + let beginGroup = lines.findLine("packageReferences = (") + var line = lines.validate(line: "C9FDF5C52AD604310096A37A /* XCLocalSwiftPackageReference \"MyLocalPackage\" */,", after: beginGroup) + line = lines.validate(line: "C9FDF5CB2AD8B3B50096A37A /* XCLocalSwiftPackageReference \"MyOtherLocalPackage/MyOtherLocalPackage\" */,", after: line) + line = lines.validate(line: ");", after: line) + } + + // MARK: - Test internals + + private func encodeProject(settings: PBXOutputSettings = PBXOutputSettings(), line: UInt = #line) -> String { + do { + return try PBXProjEncoder(outputSettings: settings).encode(proj: proj) + } catch { + XCTFail("Unexpected error encoding project: \(error)", line: line) + return "" } } - } - private func lines(fromFile file: String) -> [String] { - return file.replacingOccurrences(of: "\t", with: "").components(separatedBy: "\n") - } -} + private func encodeProjectThrows(error expectedError: some Error, line: UInt = #line) { + do { + _ = try PBXProjEncoder(outputSettings: PBXOutputSettings()).encode(proj: proj) + XCTFail("Expected '\(expectedError)' to be thrown", line: line) + } catch { + if type(of: expectedError) != type(of: error) { + XCTFail("Expected '\(expectedError)' to be thrown, but got \(error)", line: line) + } + } + } -// MARK: - Line validations + private func lines(fromFile file: String) -> [String] { + file.replacingOccurrences(of: "\t", with: "").components(separatedBy: "\n") + } -private extension Array where Element == String { - @discardableResult func validate(line string: String, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt = #line) -> Int { - return validate(string, using: { $0 == $1 }, betweenLine: lineAbove, andLine: lineBelow, line: line) - } + private func loadiOSProject() throws { + proj = try PBXProj(data: iosProjectData()) + } - @discardableResult func validate(lineContaining string: String, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt = #line) -> Int { - return validate(string, using: { $0.contains($1) }, betweenLine: lineAbove, andLine: lineBelow, line: line) - } + private func loadSynchronizedRootGroups() throws { + proj = try PBXProj(data: synchronizedRootGroupsFixture()) + } - func validate(_ string: String, using: (String, String) -> Bool, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt) -> Int { - let lineNumber = validate(string, using: using, after: lineAbove, line: line) - if lineNumber >= lineBelow { - XCTFail("Expected to find line between lines \(lineAbove) and \(lineBelow), but was found after \(lineBelow).", line: line) + private func loadFileSharedAcrossTargetsProject() throws { + proj = try PBXProj(data: fileSharedAcrossTargetsData()) } - return lineNumber - } - @discardableResult func validate(line string: String, onLineAfter: Int, line: UInt = #line) -> Int { - return validate(string, using: { $0 == $1 }, onLineAfter: onLineAfter, line: line) - } + private func loadTargetWithCustomBuildRulesProject() throws { + proj = try PBXProj(data: targetWithCustomBuildRulesData()) + } - @discardableResult func validate(lineContaining string: String, onLineAfter: Int, line: UInt = #line) -> Int { - return validate(string, using: { $0.contains($1) }, onLineAfter: onLineAfter, line: line) - } + private func loadProjectWithXCLocalSwiftPackageReference() throws { + proj = try PBXProj(data: iosProjectWithXCLocalSwiftPackageReference()) + } - func validate(_ string: String, using: (String, String) -> Bool, onLineAfter: Int, line: UInt) -> Int { - let lineNumber = validate(string, using: using, after: onLineAfter, line: line) - if lineNumber != onLineAfter + 1 { - XCTFail("Expected to find at line \(onLineAfter + 1), but was found on line \(lineNumber).", line: line) + private func loadProjectWithXCLocalSwiftPackageReferences() throws { + proj = try PBXProj(data: iosProjectWithXCLocalSwiftPackageReferences()) } - return lineNumber - } - @discardableResult func validate(line string: String, after: Int = 0, line: UInt = #line) -> Int { - return validate(string, using: { $0 == $1 }, after: after, line: line) - } + private func loadProjectWithRelativeXCLocalSwiftPackageReference() throws { + proj = try PBXProj(data: iosProjectWithRelativeXCLocalSwiftPackageReferences()) + } - @discardableResult func validate(lineContaining string: String, after: Int = 0, line: UInt = #line) -> Int { - return validate(string, using: { $0.contains($1) }, after: after, line: line) - } + private func loadProjectWithWrongProjectReferencesOrder() throws { + proj = try PBXProj(data: projectWithWrongProjectReferencesOrder()) + } - func validate(_ string: String, using: (String, String) -> Bool, after: Int, line: UInt) -> Int { - let lineNumber = findLine(string, matcher: using, after: after) - if lineNumber == endIndex { - XCTFail("Line not found after line \(after)", line: line) + private func loadProjectWithShellScriptBuildPhase() throws { + proj = try PBXProj(data: projectWithCustomShellScript()) } - return lineNumber - } - func findLine(_ string: String, after: Int = 0) -> Int { - return findLine(string, matcher: { $0 == $1 }, after: after) + private func loadProjectWithConfigurationFilesInSynchronizedGroup() throws { + proj = try PBXProj(data: projectWithConfigurationFilesInSynchronizedGroup()) + } } - func findLine(containing string: String, after: Int = 0) -> Int { - return findLine(string, matcher: { $0.contains($1) }, after: after) - } + // MARK: - Line validations + + private extension [String] { + @discardableResult func validate(line string: String, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt = #line) -> Int { + validate(string, using: { $0 == $1 }, betweenLine: lineAbove, andLine: lineBelow, line: line) + } + + @discardableResult func validate(lineContaining string: String, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt = #line) -> Int { + validate(string, using: { $0.contains($1) }, betweenLine: lineAbove, andLine: lineBelow, line: line) + } - func findLine(_ string: String, matcher: (String, String) -> Bool, after: Int) -> Int { - for i in after ..< endIndex { - if matcher(self[i], string) { - return i + func validate(_ string: String, using: (String, String) -> Bool, betweenLine lineAbove: Int, andLine lineBelow: Int, line: UInt) -> Int { + let lineNumber = validate(string, using: using, after: lineAbove, line: line) + if lineNumber >= lineBelow { + XCTFail("Expected to find line between lines \(lineAbove) and \(lineBelow), but was found after \(lineBelow).", line: line) } + return lineNumber + } + + @discardableResult func validate(line string: String, onLineAfter: Int, line: UInt = #line) -> Int { + validate(string, using: { $0 == $1 }, onLineAfter: onLineAfter, line: line) + } + + @discardableResult func validate(lineContaining string: String, onLineAfter: Int, line: UInt = #line) -> Int { + validate(string, using: { $0.contains($1) }, onLineAfter: onLineAfter, line: line) + } + + func validate(_ string: String, using: (String, String) -> Bool, onLineAfter: Int, line: UInt) -> Int { + let lineNumber = validate(string, using: using, after: onLineAfter, line: line) + if lineNumber != onLineAfter + 1 { + XCTFail("Expected to find at line \(onLineAfter + 1), but was found on line \(lineNumber).", line: line) + } + return lineNumber + } + + @discardableResult func validate(line string: String, after: Int = 0, line: UInt = #line) -> Int { + validate(string, using: { $0 == $1 }, after: after, line: line) } - return endIndex - } - func log() { - var line: Int = 0 - forEach { - let lineStr = "\(line)" - let lineNo = lineStr + String(repeating: " ", count: 5 - lineStr.count) - print(lineNo, "|", $0) - line += 1 + @discardableResult func validate(lineContaining string: String, after: Int = 0, line: UInt = #line) -> Int { + validate(string, using: { $0.contains($1) }, after: after, line: line) + } + + func validate(_ string: String, using: (String, String) -> Bool, after: Int, line: UInt) -> Int { + let lineNumber = findLine(string, matcher: using, after: after) + if lineNumber == endIndex { + XCTFail("Line not found after line \(after)", line: line) + } + return lineNumber + } + + func findLine(_ string: String, after: Int = 0) -> Int { + findLine(string, matcher: { $0 == $1 }, after: after) + } + + func findLine(containing string: String, after: Int = 0) -> Int { + findLine(string, matcher: { $0.contains($1) }, after: after) + } + + func findLine(_ string: String, matcher: (String, String) -> Bool, after: Int) -> Int { + for i in after ..< endIndex { + if matcher(self[i], string) { + return i + } + } + return endIndex + } + + func log() { + var line = 0 + forEach { + let lineStr = "\(line)" + let lineNo = lineStr + String(repeating: " ", count: 5 - lineStr.count) + print(lineNo, "|", $0) + line += 1 + } } } -} + +#endif diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift index 3bcd920a7..570c89e8f 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift @@ -1,109 +1,89 @@ -import Foundation -import PathKit -import XCTest -@testable import XcodeProj +#if os(macOS) || (os(Linux) && compiler(>=6.1)) + import Foundation + import PathKit + import XCTest + @testable import XcodeProj -import Darwin - -final class PBXProjIntegrationTests: XCTestCase { - func test_init_initializesTheProjCorrectly() { - let data = try! Data(contentsOf: fixturePath().url) - let decoder = XcodeprojPropertyListDecoder() - let proj = try? decoder.decode(PBXProj.self, from: data) - XCTAssertNotNil(proj) - if let proj = proj { - assert(proj: proj) + final class PBXProjIntegrationTests: XCTestCase { + func test_init_initializesTheProjCorrectly() throws { + let data = try XCTUnwrap(Data(contentsOf: fixturePath().url)) + let decoder = XcodeprojPropertyListDecoder() + let proj = try? decoder.decode(PBXProj.self, from: data) + XCTAssertNotNil(proj) + if let proj { + assert(proj: proj) + } } - } - - func test_write() { - testWrite(from: fixturePath(), - initModel: { path -> PBXProj? in - let data = try! Data(contentsOf: path.url) - let decoder = XcodeprojPropertyListDecoder() - return try? decoder.decode(PBXProj.self, from: data) - }, - modify: { $0 }) - } - func test_write_produces_no_diff() throws { - let tmpDir = try Path.uniqueTemporary() - defer { - try? tmpDir.delete() + func test_write() throws { + try testWrite(from: fixturePath(), + initModel: { path -> PBXProj? in + let data = try XCTUnwrap(Data(contentsOf: path.url)) + let decoder = XcodeprojPropertyListDecoder() + return try? decoder.decode(PBXProj.self, from: data) + }, + modify: { $0 }) } - let fixturePath = self.fixturePath().parent() - let xcodeprojPath = tmpDir + "Project.xcodeproj" - try fixturePath.copy(xcodeprojPath) + func test_write_produces_no_diff() throws { + let tmpDir = try Path.uniqueTemporary() + defer { + try? tmpDir.delete() + } - try tmpDir.chdir { - // Create a commit - try checkedOutput("git", ["init"]) - try checkedOutput("git", ["add", "."]) - try checkedOutput("git", ["commit", "-m", "test"]) + let fixturePath = fixturePath().parent() + let xcodeprojPath = tmpDir + "Project.xcodeproj" + try fixturePath.copy(xcodeprojPath) - // Read/write the project - let project = try XcodeProj(path: xcodeprojPath) - try project.writePBXProj(path: xcodeprojPath, outputSettings: PBXOutputSettings()) + try tmpDir.chdir { + // Create a commit + try checkedOutput("git", ["init"]) + try checkedOutput("git", ["add", "."]) + try checkedOutput("git", [ + "-c", "user.email=test@example.com", "-c", "user.name=Test User", + "commit", "-m", "test", + ]) - let got = try checkedOutput("git", ["status"]) - XCTAssertTrue(got?.contains("nothing to commit") ?? false) - } - } - - private func fixturePath() -> Path { - let path = fixturesPath() + "iOS/Project.xcodeproj/project.pbxproj" - return path - } + // Read/write the project + let project = try XcodeProj(path: xcodeprojPath) + try project.writePBXProj(path: xcodeprojPath, outputSettings: PBXOutputSettings()) - private func assert(proj: PBXProj) { - XCTAssertEqual(proj.archiveVersion, 1) - XCTAssertEqual(proj.objectVersion, 52) - XCTAssertEqual(proj.classes.count, 0) - XCTAssertEqual(proj.objects.buildFiles.count, 13) - XCTAssertEqual(proj.objects.aggregateTargets.count, 0) - XCTAssertEqual(proj.objects.containerItemProxies.count, 1) - XCTAssertEqual(proj.objects.copyFilesBuildPhases.count, 1) - XCTAssertEqual(proj.objects.groups.count, 6) - XCTAssertEqual(proj.objects.configurationLists.count, 3) - XCTAssertEqual(proj.objects.buildConfigurations.count, 6) - XCTAssertEqual(proj.objects.variantGroups.count, 2) - XCTAssertEqual(proj.objects.targetDependencies.count, 1) - XCTAssertEqual(proj.objects.sourcesBuildPhases.count, 2) - XCTAssertEqual(proj.objects.shellScriptBuildPhases.count, 1) - XCTAssertEqual(proj.objects.resourcesBuildPhases.count, 2) - XCTAssertEqual(proj.objects.frameworksBuildPhases.count, 2) - XCTAssertEqual(proj.objects.headersBuildPhases.count, 1) - XCTAssertEqual(proj.objects.nativeTargets.count, 2) - XCTAssertEqual(proj.objects.fileReferences.count, 17) - XCTAssertEqual(proj.objects.buildRules.count, 1) - XCTAssertEqual(proj.objects.versionGroups.count, 1) - XCTAssertEqual(proj.objects.projects.count, 1) - XCTAssertEqual(proj.objects.swiftPackageProductDependencies.count, 2) - XCTAssertEqual(proj.objects.remoteSwiftPackageReferences.count, 1) - } -} - -/// Returns the output of running `executable` with `args`. Throws an error if the process exits indicating failure. -@discardableResult -private func checkedOutput(_ executable: String, _ args: [String]) throws -> String? { - let process = Process() - let output = Pipe() - - if executable.contains("/") { - process.launchPath = executable - } else { - process.launchPath = try checkedOutput("/usr/bin/which", [executable])?.trimmingCharacters(in: .newlines) - } + let got = try checkedOutput("git", ["status"]) + XCTAssertTrue(got?.contains("nothing to commit") ?? false) + } + } - process.arguments = args - process.standardOutput = output - process.launch() - process.waitUntilExit() + private func fixturePath() -> Path { + let path = fixturesPath() + "iOS/Project.xcodeproj/project.pbxproj" + return path + } - guard process.terminationStatus == 0 else { - throw NSError(domain: NSPOSIXErrorDomain, code: Int(process.terminationStatus)) + private func assert(proj: PBXProj) { + XCTAssertEqual(proj.archiveVersion, 1) + XCTAssertEqual(proj.objectVersion, 52) + XCTAssertEqual(proj.classes.count, 0) + XCTAssertEqual(proj.objects.buildFiles.count, 13) + XCTAssertEqual(proj.objects.aggregateTargets.count, 0) + XCTAssertEqual(proj.objects.containerItemProxies.count, 1) + XCTAssertEqual(proj.objects.copyFilesBuildPhases.count, 1) + XCTAssertEqual(proj.objects.groups.count, 6) + XCTAssertEqual(proj.objects.configurationLists.count, 3) + XCTAssertEqual(proj.objects.buildConfigurations.count, 6) + XCTAssertEqual(proj.objects.variantGroups.count, 2) + XCTAssertEqual(proj.objects.targetDependencies.count, 1) + XCTAssertEqual(proj.objects.sourcesBuildPhases.count, 2) + XCTAssertEqual(proj.objects.shellScriptBuildPhases.count, 1) + XCTAssertEqual(proj.objects.resourcesBuildPhases.count, 2) + XCTAssertEqual(proj.objects.frameworksBuildPhases.count, 2) + XCTAssertEqual(proj.objects.headersBuildPhases.count, 1) + XCTAssertEqual(proj.objects.nativeTargets.count, 2) + XCTAssertEqual(proj.objects.fileReferences.count, 17) + XCTAssertEqual(proj.objects.buildRules.count, 1) + XCTAssertEqual(proj.objects.versionGroups.count, 1) + XCTAssertEqual(proj.objects.projects.count, 1) + XCTAssertEqual(proj.objects.swiftPackageProductDependencies.count, 2) + XCTAssertEqual(proj.objects.remoteSwiftPackageReferences.count, 1) + } } - return String(data: output.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) -} +#endif diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProject+Fixtures.swift b/Tests/XcodeProjTests/Objects/Project/PBXProject+Fixtures.swift index 5edc67534..dd51d4143 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProject+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProject+Fixtures.swift @@ -5,10 +5,13 @@ extension PBXProject { static func fixture(name: String = "test", buildConfigurationList: XCConfigurationList = XCConfigurationList.fixture(), compatibilityVersion: String = Xcode.Default.compatibilityVersion, - mainGroup: PBXGroup = PBXGroup.fixture()) -> PBXProject { - return PBXProject(name: name, - buildConfigurationList: buildConfigurationList, - compatibilityVersion: compatibilityVersion, - mainGroup: mainGroup) + mainGroup: PBXGroup = PBXGroup.fixture()) -> PBXProject + { + PBXProject(name: name, + buildConfigurationList: buildConfigurationList, + compatibilityVersion: compatibilityVersion, + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, + mainGroup: mainGroup) } } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift index b82db6adf..1d1b8cf20 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift @@ -12,11 +12,13 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: PBXGroup(), attributes: ["LastUpgradeCheck": "0940"], targetAttributes: [target: ["TestTargetID": "123"]]) - project.setTargetAttributes(["custom": "abc", "TestTargetID": testTarget], target: target) + project.setTargetAttributes(["custom": "abc", "TestTargetID": .targetReference(testTarget)], target: target) let plist = try project.plistKeyAndValue(proj: PBXProj(), reference: "") let attributes = plist.value.dictionary?["attributes"]?.dictionary ?? [:] @@ -31,6 +33,62 @@ final class PBXProjectTests: XCTestCase { XCTAssertEqual(attributes, expectedAttributes) } + func test_attributes_writes_fixed_value_correctly() throws { + let target = PBXTarget(name: "") + target.reference.fix("app") + + let testTarget = PBXTarget(name: "") + + let project = PBXProject(name: "", + buildConfigurationList: XCConfigurationList(), + compatibilityVersion: "", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, + mainGroup: PBXGroup(), + attributes: ["LastUpgradeCheck": "0940"], + targetAttributes: [target: ["TestTargetID": "123"]]) + + project.setTargetAttributes(["custom": "abc", "TestTargetID": .targetReference(testTarget)], target: target) + + // When writing the project we need to account for any mutation of the object that may have occurred after being added to the project. + testTarget.reference.fix("test") + + let plist = try project.plistKeyAndValue(proj: PBXProj(), reference: "") + let attributes = plist.value.dictionary?["attributes"]?.dictionary ?? [:] + + let expectedAttributes: [CommentedString: PlistValue] = [ + "LastUpgradeCheck": "0940", + "TargetAttributes": ["app": [ + "custom": "abc", + "TestTargetID": "test", + ]], + ] + XCTAssertEqual(attributes, expectedAttributes) + } + + func test_plistKeyAndValue_returnsEmptyTargetAttributes_when_itsEmpty() throws { + // Given + let target = PBXTarget(name: "") + target.reference.fix("app") + + let testTarget = PBXTarget(name: "") + testTarget.reference.fix("test") + + let project = PBXProject(name: "Project", + buildConfigurationList: XCConfigurationList(), + compatibilityVersion: nil, + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, + mainGroup: PBXGroup()) + + // When + let plist = try project.plistKeyAndValue(proj: PBXProj(), reference: "") + + // Then + let attributes = plist.value.dictionary?["attributes"]?.dictionary?["TargetAttributes"]?.dictionary + XCTAssertEqual(attributes, [:]) + } + func test_addLocalSwiftPackage() throws { // Given let objects = PBXObjects(objects: []) @@ -54,6 +112,8 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup, targets: [target]) @@ -67,7 +127,7 @@ final class PBXProjectTests: XCTestCase { // Then XCTAssertEqual(packageProduct, objects.buildFiles.first?.value.product) XCTAssertEqual(packageProduct, objects.swiftPackageProductDependencies.first?.value) - XCTAssertEqual(packageProduct, target.packageProductDependencies.first) + XCTAssertEqual(packageProduct, target.packageProductDependencies?.first) XCTAssertEqual(objects.fileReferences.first?.value.name, "Product") @@ -89,6 +149,8 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup, targets: [target]) @@ -102,6 +164,48 @@ final class PBXProjectTests: XCTestCase { PBXProjError.frameworksBuildPhaseNotFound(targetName: target.name)) } + func test_removeRemotePackage() throws { + // Given + let objects = PBXObjects(objects: []) + + let buildPhase = PBXFrameworksBuildPhase( + files: [], + inputFileListPaths: nil, + outputFileListPaths: nil, buildActionMask: PBXBuildPhase.defaultBuildActionMask, + runOnlyForDeploymentPostprocessing: true + ) + let target = PBXNativeTarget(name: "Target", + buildConfigurationList: nil, + buildPhases: [buildPhase]) + objects.add(object: target) + + let configurationList = XCConfigurationList.fixture() + let mainGroup = PBXGroup.fixture() + objects.add(object: configurationList) + objects.add(object: mainGroup) + + let project = PBXProject(name: "Project", + buildConfigurationList: configurationList, + compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, + mainGroup: mainGroup, + targets: [target]) + + objects.add(object: project) + + let _ = try project.addSwiftPackage(repositoryURL: "url", + productName: "Product", + versionRequirement: .branch("main"), + targetName: "Target") + + // When + project.remotePackages.removeFirst() + + // Then + XCTAssert(project.remotePackages.isEmpty) + } + func test_addSwiftPackage() throws { // Given let objects = PBXObjects(objects: []) @@ -125,6 +229,8 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup, targets: [target]) @@ -133,11 +239,11 @@ final class PBXProjectTests: XCTestCase { // When let remoteReference = try project.addSwiftPackage(repositoryURL: "url", productName: "Product", - versionRequirement: .branch("master"), + versionRequirement: .branch("main"), targetName: "Target") // Then - XCTAssertEqual(remoteReference, project.packages.first) + XCTAssertEqual(remoteReference, project.remotePackages.first) XCTAssertEqual(remoteReference, objects.remoteSwiftPackageReferences.first?.value) XCTAssertEqual(remoteReference, objects.buildFiles.first?.value.product?.package) @@ -178,6 +284,8 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup, targets: [target, secondTarget]) @@ -186,25 +294,36 @@ final class PBXProjectTests: XCTestCase { // When let packageProduct = try project.addSwiftPackage(repositoryURL: "url", productName: "Product", - versionRequirement: .branch("master"), + versionRequirement: .branch("main"), targetName: target.name) let secondPackageProduct = try project.addSwiftPackage(repositoryURL: "url", productName: "Product", - versionRequirement: .branch("master"), + versionRequirement: .branch("main"), targetName: secondTarget.name) - + let thirdPackageProduct = try project.addSwiftPackage(repositoryURL: "url", + productName: "Product2", + versionRequirement: .branch("main"), + targetName: target.name) // Then XCTAssertEqual(packageProduct, secondPackageProduct) - XCTAssertEqual(project.packages.count, 1) - XCTAssertEqual(target.packageProductDependencies, secondTarget.packageProductDependencies) + XCTAssertEqual(packageProduct, thirdPackageProduct) + XCTAssertEqual(project.remotePackages.count, 1) + XCTAssertEqual(target.packageProductDependencies?.count, 2) + XCTAssertEqual(secondTarget.packageProductDependencies?.count, 1) XCTAssertNotEqual(buildPhase.files?.first?.hashValue, secondBuildPhase.files?.first?.hashValue) - XCTAssertEqual(objects.swiftPackageProductDependencies.count, 1) + XCTAssertEqual(objects.swiftPackageProductDependencies.count, 2) XCTAssertThrowsSpecificError(try project.addSwiftPackage(repositoryURL: "url", productName: "Product", - versionRequirement: .branch("second-master"), + versionRequirement: .branch("second-main"), targetName: secondTarget.name), PBXProjError.multipleRemotePackages(productName: "Product")) + + XCTAssertThrowsSpecificError(try project.addSwiftPackage(repositoryURL: "url", + productName: "Product2", + versionRequirement: .branch("second-main"), + targetName: target.name), + PBXProjError.multipleRemotePackages(productName: "Product2")) } func test_addLocalSwiftPackage_duplication() throws { @@ -241,6 +360,8 @@ final class PBXProjectTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup, targets: [target, secondTarget]) diff --git a/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift b/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift new file mode 100644 index 000000000..1ad9d94f4 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift @@ -0,0 +1,124 @@ +import Foundation +import XCTest + +@testable import XcodeProj + +final class XCLocalSwiftPackageReferenceTests: XCTestCase { + func test_init() throws { + // Given + let decoder = XcodeprojPropertyListDecoder() + let plist: [String: [String: Any]] = ["ref": ["relativePath": "path"]] + + let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) + + // When + let decoded = try decoder.decode([String: XCLocalSwiftPackageReference].self, from: data) + let got = try XCTUnwrap(decoded["ref"]) + + // Then + XCTAssertEqual(got.reference.value, "ref") + XCTAssertEqual(got.relativePath, "path") + } + + func test_plistValues() throws { + // When + let proj = PBXProj() + let subject = XCLocalSwiftPackageReference(relativePath: "repository") + + // Given + let got = try subject.plistKeyAndValue(proj: proj, reference: "ref") + + // Then + XCTAssertEqual(got.value, .dictionary([ + "isa": "XCLocalSwiftPackageReference", + "relativePath": "repository", + ])) + } + + func test_equal() { + // When + let first = XCLocalSwiftPackageReference(relativePath: "repository") + let second = XCLocalSwiftPackageReference(relativePath: "repository") + + // Then + XCTAssertEqual(first, second) + } + + func test_name() { + // When + let subject = XCLocalSwiftPackageReference(relativePath: "tuist/xcodeproj") + + // Then + XCTAssertEqual(subject.name, "tuist/xcodeproj") + } + + // MARK: - Add/Delete Tests + + func test_add_addsObjectToPBXProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage = XCLocalSwiftPackageReference(relativePath: "../MyPackage") + + // When + proj.add(object: localPackage) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + XCTAssertEqual(proj.objects.localSwiftPackageReferences.values.first?.relativePath, "../MyPackage") + } + + func test_delete_removesObjectFromPBXProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage = XCLocalSwiftPackageReference(relativePath: "../MyPackage") + proj.add(object: localPackage) + + // Verify it was added + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + + // When + proj.delete(object: localPackage) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 0) + } + + func test_delete_removesCorrectObject_whenMultipleExist() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage1 = XCLocalSwiftPackageReference(relativePath: "../Package1") + let localPackage2 = XCLocalSwiftPackageReference(relativePath: "../Package2") + let localPackage3 = XCLocalSwiftPackageReference(relativePath: "../Package3") + proj.add(object: localPackage1) + proj.add(object: localPackage2) + proj.add(object: localPackage3) + + // Verify all were added + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 3) + + // When - delete the middle one + proj.delete(object: localPackage2) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 2) + let remainingPaths = proj.objects.localSwiftPackageReferences.values.map(\.relativePath) + XCTAssertTrue(remainingPaths.contains("../Package1")) + XCTAssertTrue(remainingPaths.contains("../Package3")) + XCTAssertFalse(remainingPaths.contains("../Package2")) + } + + func test_delete_doesNothing_whenObjectNotInProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let addedPackage = XCLocalSwiftPackageReference(relativePath: "../AddedPackage") + let notAddedPackage = XCLocalSwiftPackageReference(relativePath: "../NotAddedPackage") + proj.add(object: addedPackage) + + // When - try to delete an object that was never added + proj.delete(object: notAddedPackage) + + // Then - the added package should still be there + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + XCTAssertEqual(proj.objects.localSwiftPackageReferences.values.first?.relativePath, "../AddedPackage") + } +} diff --git a/Tests/XcodeProjTests/Objects/SwiftPackage/XCRemoteSwiftPackageReferenceTests.swift b/Tests/XcodeProjTests/Objects/SwiftPackage/XCRemoteSwiftPackageReferenceTests.swift index 0f33a9c4a..a06008726 100644 --- a/Tests/XcodeProjTests/Objects/SwiftPackage/XCRemoteSwiftPackageReferenceTests.swift +++ b/Tests/XcodeProjTests/Objects/SwiftPackage/XCRemoteSwiftPackageReferenceTests.swift @@ -7,16 +7,17 @@ final class XCRemoteSwiftPackageReferenceTests: XCTestCase { func test_init() throws { // Given let decoder = XcodeprojPropertyListDecoder() - let plist: [String: Any] = ["reference": "ref", - "repositoryURL": "url", - "requirement": [ - "kind": "revision", - "revision": "abc", - ]] + let plist: [String: [String: Any]] = ["ref": [ + "repositoryURL": "url", + "requirement": [ + "kind": "revision", + "revision": "abc", + ]]] let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) // When - let got = try decoder.decode(XCRemoteSwiftPackageReference.self, from: data) + let decoded = try decoder.decode([String: XCRemoteSwiftPackageReference].self, from: data) + let got = try XCTUnwrap(decoded["ref"]) // Then XCTAssertEqual(got.reference.value, "ref") @@ -40,7 +41,7 @@ final class XCRemoteSwiftPackageReferenceTests: XCTestCase { func test_versionRequirement_returnsTheRightPlistValues_when_branch() throws { // When - let subject = XCRemoteSwiftPackageReference.VersionRequirement.branch("master") + let subject = XCRemoteSwiftPackageReference.VersionRequirement.branch("main") // Given let got = subject.plistValues() @@ -48,7 +49,7 @@ final class XCRemoteSwiftPackageReferenceTests: XCTestCase { // Then XCTAssertEqual(got, [ "kind": "branch", - "branch": .string(.init("master")), + "branch": .string(.init("main")), ]) } diff --git a/Tests/XcodeProjTests/Objects/SwiftPackage/XCSwiftPackageProductDependencyTests.swift b/Tests/XcodeProjTests/Objects/SwiftPackage/XCSwiftPackageProductDependencyTests.swift index 475712a2b..60823f04b 100644 --- a/Tests/XcodeProjTests/Objects/SwiftPackage/XCSwiftPackageProductDependencyTests.swift +++ b/Tests/XcodeProjTests/Objects/SwiftPackage/XCSwiftPackageProductDependencyTests.swift @@ -8,17 +8,38 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase { // Given let decoder = XcodeprojPropertyListDecoder() let encoder = PropertyListEncoder() - let plist = ["reference": "reference", - "productName": "xcodeproj", - "package": "packageReference"] + let plist = ["reference": [ + "productName": "xcodeproj", + "package": "packageReference"]] let data = try encoder.encode(plist) // When - let got = try decoder.decode(XCSwiftPackageProductDependency.self, from: data) + let decoded = try decoder.decode([String: XCSwiftPackageProductDependency].self, from: data) + let got = try XCTUnwrap(decoded["reference"]) // Then XCTAssertEqual(got.productName, "xcodeproj") XCTAssertEqual(got.packageReference?.value, "packageReference") + XCTAssertEqual(got.isPlugin, false) + } + + func test_initAsPlugin() throws { + // Given + let decoder = XcodeprojPropertyListDecoder() + let encoder = PropertyListEncoder() + let plist = ["reference": [ + "productName": "plugin:xcodeproj", + "package": "packageReference"]] + let data = try encoder.encode(plist) + + // When + let decoded = try decoder.decode([String: XCSwiftPackageProductDependency].self, from: data) + let got = try XCTUnwrap(decoded["reference"]) + + // Then + XCTAssertEqual(got.productName, "xcodeproj") + XCTAssertEqual(got.packageReference?.value, "packageReference") + XCTAssertEqual(got.isPlugin, true) } func test_plistValues() throws { @@ -39,6 +60,25 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase { ])) } + func test_plistValuesAsPlugin() throws { + // Given + let proj = PBXProj() + let package = XCRemoteSwiftPackageReference(repositoryURL: "repository") + let subject = XCSwiftPackageProductDependency(productName: "product", + package: package, + isPlugin: true) + + // When + let got = try subject.plistKeyAndValue(proj: proj, reference: "reference") + + // Then + XCTAssertEqual(got.value, .dictionary([ + "isa": "XCSwiftPackageProductDependency", + "productName": "plugin:product", + "package": .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\"")), + ])) + } + func test_equal() { // Given let package = XCRemoteSwiftPackageReference(repositoryURL: "repository") @@ -50,4 +90,20 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase { // Then XCTAssertEqual(first, second) } + + func test_isPlugin() { + // Given + let plugin = XCSwiftPackageProductDependency(productName: "product", isPlugin: true) + + // Then + XCTAssertTrue(plugin.isPlugin) + } + + func test_isNotPlugin() { + // Given + let plugin = XCSwiftPackageProductDependency(productName: "product") + + // Then + XCTAssertFalse(plugin.isPlugin) + } } diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift index d6f9278c4..09947e76c 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift @@ -19,7 +19,7 @@ final class PBXAggregateTargetTests: XCTestCase { } func testDictionary() -> [String: Any] { - return [ + [ "buildConfigurationList": "buildConfigurationList", "buildPhases": ["phase"], "buildRules": ["rule"], @@ -27,4 +27,36 @@ final class PBXAggregateTargetTests: XCTestCase { "name": "name", ] } + + func test_addDependency() throws { + let objects = PBXObjects(objects: []) + + let configurationList = XCConfigurationList.fixture() + let mainGroup = PBXGroup.fixture() + objects.add(object: configurationList) + objects.add(object: mainGroup) + + let project = PBXProject(name: "Project", + buildConfigurationList: configurationList, + compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, + mainGroup: mainGroup) + + objects.add(object: project) + let target = PBXAggregateTarget(name: "Target", buildConfigurationList: nil) + let dependency = PBXAggregateTarget(name: "Dependency", buildConfigurationList: nil) + objects.add(object: target) + objects.add(object: dependency) + _ = try target.addDependency(target: dependency) + let targetDependency: PBXTargetDependency? = target.dependencyReferences.first?.getObject() + + XCTAssertEqual(targetDependency?.name, "Dependency") + XCTAssertEqual(targetDependency?.targetReference, dependency.reference) + let containerItemProxy: PBXContainerItemProxy? = targetDependency?.targetProxyReference?.getObject() + XCTAssertEqual(containerItemProxy?.containerPortalReference, project.reference) + XCTAssertEqual(containerItemProxy?.remoteGlobalID?.uuid, dependency.reference.value) + XCTAssertEqual(containerItemProxy?.proxyType, .nativeTarget) + XCTAssertEqual(containerItemProxy?.remoteInfo, "Dependency") + } } diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXNativeTargetTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXNativeTargetTests.swift index 7f82ff482..11aa27581 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXNativeTargetTests.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXNativeTargetTests.swift @@ -19,7 +19,7 @@ final class PBXNativeTargetTests: XCTestCase { } private func testDictionary() -> [String: Any] { - return [ + [ "buildConfigurationList": "test", "buildPhases": ["phase"], "buildRules": ["rule"], @@ -40,6 +40,8 @@ final class PBXNativeTargetTests: XCTestCase { let project = PBXProject(name: "Project", buildConfigurationList: configurationList, compatibilityVersion: "0", + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup) objects.add(object: project) diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXProductTypeTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXProductTypeTests.swift index bb51cfb29..83d62f3d0 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXProductTypeTests.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXProductTypeTests.swift @@ -39,6 +39,10 @@ final class PBXProductTypeTests: XCTestCase { XCTAssertEqual(PBXProductType.appExtension.rawValue, "com.apple.product-type.app-extension") } + func test_extensionKitExtension_hasTheRightValue() { + XCTAssertEqual(PBXProductType.extensionKitExtension.rawValue, "com.apple.product-type.extensionkit-extension") + } + func test_commandLineTool_hasTheRightValue() { XCTAssertEqual(PBXProductType.commandLineTool.rawValue, "com.apple.product-type.tool") } @@ -90,4 +94,16 @@ final class PBXProductTypeTests: XCTestCase { func test_intentsServiceExtension_hasTheRightValue() { XCTAssertEqual(PBXProductType.intentsServiceExtension.rawValue, "com.apple.product-type.app-extension.intents-service") } + + func test_appClip_hasTheRightValue() { + XCTAssertEqual(PBXProductType.onDemandInstallCapableApplication.rawValue, "com.apple.product-type.application.on-demand-install-capable") + } + + func test_driverExtension_hasTheRightValue() { + XCTAssertEqual(PBXProductType.driverExtension.rawValue, "com.apple.product-type.driver-extension") + } + + func test_systemExtension_hasTheRightValue() { + XCTAssertEqual(PBXProductType.systemExtension.rawValue, "com.apple.product-type.system-extension") + } } diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXTarget+Fixtures.swift b/Tests/XcodeProjTests/Objects/Targets/PBXTarget+Fixtures.swift index 2cbbf25b2..07da826aa 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXTarget+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXTarget+Fixtures.swift @@ -10,14 +10,15 @@ extension PBXTarget { dependencies: [PBXTargetDependency] = [], productName: String? = "Test", product: PBXFileReference = PBXFileReference.fixture(name: "Test.app"), - productType: PBXProductType = PBXProductType.application) -> PBXTarget { - return PBXTarget(name: name, - buildConfigurationList: buildConfigurationList, - buildPhases: buildPhases, - buildRules: buildRules, - dependencies: dependencies, - productName: productName, - product: product, - productType: productType) + productType: PBXProductType = PBXProductType.application) -> PBXTarget + { + PBXTarget(name: name, + buildConfigurationList: buildConfigurationList, + buildPhases: buildPhases, + buildRules: buildRules, + dependencies: dependencies, + productName: productName, + product: product, + productType: productType) } } diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXTargetTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXTargetTests.swift index a0fe6b00e..a47242a3c 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXTargetTests.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXTargetTests.swift @@ -85,4 +85,48 @@ final class PBXTargetTests: XCTestCase { XCTAssertTrue(embedFrameworkBuildPhases.contains(embedFrameworkBuildPhase1)) XCTAssertTrue(embedFrameworkBuildPhases.contains(embedFrameworkBuildPhase2)) } + + func test_runScriptBuildPhases_returnsEmptyIfNoRunScriptBuildPhases() { + let notShellScriptBuildPhase1 = PBXFrameworksBuildPhase( + files: [], + inputFileListPaths: nil, + outputFileListPaths: nil, buildActionMask: PBXBuildPhase.defaultBuildActionMask, + runOnlyForDeploymentPostprocessing: true + ) + let notShellScriptBuildPhase2 = PBXCopyFilesBuildPhase( + dstPath: nil, + dstSubfolderSpec: .resources, + name: "Embed Frameworks", + buildActionMask: PBXBuildPhase.defaultBuildActionMask, + files: [], + runOnlyForDeploymentPostprocessing: true + ) + + subject.buildPhases.append(notShellScriptBuildPhase1) + subject.buildPhases.append(notShellScriptBuildPhase2) + + let runScriptBuildPhases = subject.runScriptBuildPhases() + XCTAssertTrue(runScriptBuildPhases.isEmpty) + } + + func test_runScriptBuildPhases_returnsRunScriptBuildPhasesIfPresent() { + let otherScriptBuildPhase1 = PBXFrameworksBuildPhase() + let runScriptBuildPhase1 = PBXShellScriptBuildPhase( + name: "Run Script 1" + ) + let runScriptBuildPhase2 = PBXShellScriptBuildPhase( + name: "Run Script 2" + ) + let otherScriptBuildPhase2 = PBXCopyFilesBuildPhase() + + subject.buildPhases.append(otherScriptBuildPhase1) + subject.buildPhases.append(runScriptBuildPhase1) + subject.buildPhases.append(runScriptBuildPhase2) + subject.buildPhases.append(otherScriptBuildPhase2) + + let runScriptBuildPhases = subject.runScriptBuildPhases() + XCTAssertEqual(runScriptBuildPhases.count, 2) + XCTAssertTrue(runScriptBuildPhases.contains(runScriptBuildPhase1)) + XCTAssertTrue(runScriptBuildPhases.contains(runScriptBuildPhase2)) + } } diff --git a/Tests/XcodeProjTests/Project/WorkspaceSettingsTests.swift b/Tests/XcodeProjTests/Project/WorkspaceSettingsTests.swift index 8ae3e1816..7c70e40d6 100644 --- a/Tests/XcodeProjTests/Project/WorkspaceSettingsTests.swift +++ b/Tests/XcodeProjTests/Project/WorkspaceSettingsTests.swift @@ -23,9 +23,23 @@ final class WorkspaceSettingsTests: XCTestCase { XCTAssertTrue(got.autoCreateSchemes == true) } + func test_init_when_relative_derivedData_is_enabled() throws { + let path = fixturesPath() + "WorkspaceSettings/OriginalRelativeDerivedData.xcsettings" + let got = try WorkspaceSettings.at(path: path) + XCTAssertTrue(got.derivedDataCustomLocation == "CustomizedDerivedData") + XCTAssertTrue(got.derivedDataLocationStyle == .workspaceRelativePath) + } + + func test_init_when_absolute_derivedData_is_enabled() throws { + let path = fixturesPath() + "WorkspaceSettings/OriginalAbsoluteDerivedData.xcsettings" + let got = try WorkspaceSettings.at(path: path) + XCTAssertTrue(got.derivedDataCustomLocation == "/User/xcodeproj/DerivedData") + XCTAssertTrue(got.derivedDataLocationStyle == .absolutePath) + } + func test_equals() { - let lhs = WorkspaceSettings(buildSystem: .new) - let rhs = WorkspaceSettings(buildSystem: .original) + let lhs = WorkspaceSettings(buildSystem: .new, autoCreateSchemes: true) + let rhs = WorkspaceSettings(buildSystem: .original, autoCreateSchemes: true) XCTAssertNotEqual(lhs, rhs) } @@ -36,10 +50,14 @@ final class WorkspaceSettingsTests: XCTestCase { var settings = try WorkspaceSettings.at(path: path) settings.buildSystem = .original + settings.derivedDataLocationStyle = .workspaceRelativePath + settings.derivedDataCustomLocation = "DerivedData" try settings.write(path: copyPath, override: true) settings = try WorkspaceSettings.at(path: copyPath) XCTAssertEqual(settings.buildSystem, .original) + XCTAssertEqual(settings.derivedDataLocationStyle, .workspaceRelativePath) + XCTAssertEqual(settings.derivedDataCustomLocation, "DerivedData") } } } diff --git a/Tests/XcodeProjTests/Project/XCBreakpointListTests.swift b/Tests/XcodeProjTests/Project/XCBreakpointListTests.swift index c3e95ca35..5def85b4e 100644 --- a/Tests/XcodeProjTests/Project/XCBreakpointListTests.swift +++ b/Tests/XcodeProjTests/Project/XCBreakpointListTests.swift @@ -12,16 +12,16 @@ final class XCBreakpointListIntegrationTests: XCTestCase { func test_init_initializesTheBreakpointListCorrectly() { XCTAssertNotNil(subject) - if let subject = subject { + if let subject { assert(breakpointList: subject) } } - func test_write() { - testWrite(from: fixturePath(), - initModel: { try? XCBreakpointList(path: $0) }, - modify: { $0 }, - assertion: { assert(breakpointList: $1) }) + func test_write() throws { + try testWrite(from: fixturePath(), + initModel: { try? XCBreakpointList(path: $0) }, + modify: { $0 }, + assertion: { assert(breakpointList: $1) }) } // MARK: - Private @@ -104,9 +104,20 @@ final class XCBreakpointListIntegrationTests: XCTestCase { let ideTestFailureAppleScriptAction = ideTestFailureContent.actions[0] XCTAssertEqual(ideTestFailureAppleScriptAction.actionExtensionID, .appleScript) XCTAssertNotNil(ideTestFailureAppleScriptAction.actionContent) + + // Runtime exception failure + let runtimeIssue = breakpointList.breakpoints[6] + XCTAssertEqual(runtimeIssue.breakpointExtensionID, .runtimeIssue) + let runtimeIssueContent = runtimeIssue.breakpointContent + XCTAssertEqual(runtimeIssueContent.enabled, true) + XCTAssertEqual(runtimeIssueContent.ignoreCount, "0") + XCTAssertEqual(runtimeIssueContent.continueAfterRunningActions, false) + let runtimeExceptionAppleScriptAction = runtimeIssueContent.actions[0] + XCTAssertEqual(runtimeExceptionAppleScriptAction.actionExtensionID, .appleScript) + XCTAssertNotNil(runtimeExceptionAppleScriptAction.actionContent) } private func fixturePath() -> Path { - return fixturesPath() + "iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist" + fixturesPath() + "iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist" } } diff --git a/Tests/XcodeProjTests/Project/XCUserDataTests.swift b/Tests/XcodeProjTests/Project/XCUserDataTests.swift new file mode 100644 index 000000000..36ab2db26 --- /dev/null +++ b/Tests/XcodeProjTests/Project/XCUserDataTests.swift @@ -0,0 +1,41 @@ +import Foundation +import PathKit +import XCTest +@testable import XcodeProj + +final class XCUserDataTests: XCTestCase { + func test_read_userData() throws { + let subject = try XCUserData(path: userDataPath) + assert(userData: subject, userName: "username1") + } + + func test_write_userData() throws { + try testWrite(from: userDataPath, + initModel: { try? XCUserData(path: $0) }, + modify: { userData in + // XCScheme's that are already in place (the removed element) should not be removed by a write + userData.schemes = userData.schemes.filter { $0.name != "iOS-other" } + return userData + }, + assertion: { + assert(userData: $1, userName: "copy") + }) + } + + func test_read_write_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: userDataPath, initModel: XCUserData.init(path:)) + } + + // MARK: - Private + + private func assert(userData: XCUserData, userName: String) { + XCTAssertEqual(userData.userName, userName) + XCTAssertEqual(userData.schemes.count, 3) + XCTAssertEqual(userData.breakpoints?.breakpoints.count, 2) + XCTAssertEqual(userData.schemeManagement?.schemeUserState?.count, 6) + } + + private var userDataPath: Path { + fixturesPath() + "iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad" + } +} diff --git a/Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift b/Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift new file mode 100644 index 000000000..f3bd72571 --- /dev/null +++ b/Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift @@ -0,0 +1,114 @@ +#if os(macOS) || (os(Linux) && compiler(>=6.1)) + import Foundation + import PathKit + import XCTest + @testable import XcodeProj + + final class XcodeProjIntegrationTests: XCTestCase { + func test_write_xcode16Project() throws { + try testReadWriteProducesNoDiff(from: xcode16ProjectPath, + initModel: XcodeProj.init(path:)) + } + + func test_read_iosXcodeProj() throws { + let subject = try XcodeProj(path: iosProjectPath) + assert(project: subject) + } + + func test_write_iosXcodeProj() throws { + try testWrite(from: iosProjectPath, + initModel: { try? XcodeProj(path: $0) }, + modify: { project in + // XCUserData that is already in place (the removed element) should not be removed by a write + _ = project.userData.removeLast() + return project + }, + assertion: { assert(project: $1) }) + } + + func test_read_write_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: iosProjectPath, + initModel: XcodeProj.init(path:)) + } + + func test_read_write_produces_no_diff_when_synchronizedRootGroupsFixture() throws { + try testReadWriteProducesNoDiff(from: synchronizedRootGroupsFixturePath, + initModel: XcodeProj.init(path:)) + } + + func test_initialize_PBXProj_with_data() throws { + // Given + let pbxprojPath = iosProjectPath + "project.pbxproj" + let pbxprojFromDisk = try PBXProj(path: pbxprojPath) + let pbxprojData = try Data(contentsOf: pbxprojPath.url) + + // When + let pbxprojFromData = try PBXProj(data: pbxprojData) + try pbxprojFromData.updateProjectName(path: pbxprojPath) + + // Then + XCTAssertEqual(pbxprojFromData, pbxprojFromDisk) + } + + func test_write_includes_workspace_settings() throws { + // Define workspace settings that should be written + let workspaceSettings = WorkspaceSettings(buildSystem: .new, derivedDataLocationStyle: .default, autoCreateSchemes: false) + + try testWrite(from: iosProjectPath, + initModel: { try? XcodeProj(path: $0) }, + modify: { project in + project.sharedData?.workspaceSettings = workspaceSettings + return project + }, + assertion: { + /** + * Expect that the workspace settings read from file are equal to the + * workspace settings we expected to write. + */ + XCTAssertEqual($1.sharedData?.workspaceSettings, workspaceSettings) + }) + } + + // MARK: - Private + + private func assert(project: XcodeProj) { + // Workspace + XCTAssertEqual(project.workspace.data.children.count, 1) + + // Project + XCTAssertEqual(project.pbxproj.objects.buildFiles.count, 13) + + // Shared Data + XCTAssertNotNil(project.sharedData) + XCTAssertEqual(project.sharedData?.schemes.count, 1) + XCTAssertNotNil(project.sharedData?.breakpoints) + XCTAssertNil(project.sharedData?.workspaceSettings) + + // User Data + XCTAssertEqual(project.userData.count, 3) + + XCTAssertEqual(project.userData[0].userName, "username1") + XCTAssertEqual(project.userData[0].schemes.count, 3) + XCTAssertEqual(project.userData[0].breakpoints?.breakpoints.count, 2) + XCTAssertNotNil(project.userData[0].schemeManagement) + + XCTAssertEqual(project.userData[1].userName, "username2") + XCTAssertEqual(project.userData[1].schemes.count, 1) + XCTAssertNil(project.userData[1].breakpoints?.breakpoints) + XCTAssertNil(project.userData[1].schemeManagement) + } + + private var iosProjectPath: Path { + fixturesPath() + "iOS/Project.xcodeproj" + } + + private var xcode16ProjectPath: Path { + fixturesPath() + "Xcode16/Test.xcodeproj" + } + + private var synchronizedRootGroupsFixturePath: Path { + fixturesPath() + "SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj" + } + } + +#endif diff --git a/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift b/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift new file mode 100644 index 000000000..776803adb --- /dev/null +++ b/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift @@ -0,0 +1,24 @@ +import XCTest +@testable import XcodeProj + +final class XCSchemeBuildableReferenceTests: XCTestCase { + func test_hash() throws { + // Values that are equal must generate the same hash value + let aBuildRef = XCScheme.BuildableReference( + referencedContainer: "container ref", + blueprint: nil, + buildableName: "buildable name", + blueprintName: "blueprint name" + ) + let bBuildRef = XCScheme.BuildableReference( + referencedContainer: "container ref", + blueprint: nil, + buildableName: "buildable name", + blueprintName: "blueprint name" + ) + XCTAssertEqual(aBuildRef, bBuildRef) + + let buildRefs: Set = [aBuildRef] + XCTAssertTrue(buildRefs.contains(bBuildRef)) + } +} diff --git a/Tests/XcodeProjTests/Scheme/XCSchemeManagementTests.swift b/Tests/XcodeProjTests/Scheme/XCSchemeManagementTests.swift new file mode 100644 index 000000000..9a5f719d7 --- /dev/null +++ b/Tests/XcodeProjTests/Scheme/XCSchemeManagementTests.swift @@ -0,0 +1,89 @@ +import AEXML +import Foundation +import PathKit +import XCTest +@testable import XcodeProj + +final class XCSchemeManagementTests: XCTestCase { + func test_init_from_path() throws { + // Given + let path = xcschememanagementPath + + // When + let got = try XCSchemeManagement(path: path) + + // Then + XCTAssertEqual(got.suppressBuildableAutocreation, [ + "E525238B16245A900012E2BA": .init(primary: true), + ]) + + XCTAssertEqual(got.schemeUserState, [ + .init(name: "App.xcscheme", shared: false, orderHint: 0, isShown: false), + .init(name: "Test 0.xcscheme", shared: false, orderHint: 3, isShown: nil), + .init(name: "Test 1.xcscheme", shared: false, orderHint: 4, isShown: false), + .init(name: "Tuist.xcscheme", shared: true, orderHint: 1, isShown: true), + .init(name: "XcodeProj.xcscheme", shared: false, orderHint: 2, isShown: nil), + ]) + } + + func test_read_write_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: xcschememanagementPath, initModel: XCSchemeManagement.init(path:)) + } + + func test_read_is_stable() throws { + // Given + let path = xcschememanagementPath + + // When + let reads = try (0 ..< 10).map { _ in + try XCSchemeManagement(path: path) + } + + // Then + let unstableReads = reads.dropFirst().filter { $0 != reads.first } + XCTAssertTrue(unstableReads.isEmpty) + } + + func test_write_produces_no_diff() throws { + #if os(iOS) + throw XCTSkip("'Process' API is unavailable on iOS platform") + #else + let tmpDir = try Path.uniqueTemporary() + defer { + try? tmpDir.delete() + } + + try tmpDir.chdir { + // Write + let plistPath = tmpDir + "xcschememanagement.plist" + let subject = XCSchemeManagement( + schemeUserState: [ + .init(name: "Test 0.xcscheme", shared: true, orderHint: 0, isShown: true), + .init(name: "Test 1.xcscheme", shared: true, orderHint: 1, isShown: true), + .init(name: "Test 2.xcscheme", shared: true, orderHint: 2, isShown: false), + .init(name: "Test 3.xcscheme", shared: true, orderHint: 3, isShown: true), + ], + suppressBuildableAutocreation: [ + "E525238B16245A900012E2BA": .init(primary: true), + ] + ) + try subject.write(path: plistPath, override: true) + + // Create a commit + try checkedOutput("git", ["init"]) + try checkedOutput("git", ["add", "."]) + try checkedOutput("git", ["commit", "-m", "test"]) + + // Write again + try subject.write(path: plistPath, override: true) + + let got = try checkedOutput("git", ["status"]) + XCTAssertTrue(got?.contains("nothing to commit") ?? false) + } + #endif + } + + private var xcschememanagementPath: Path { + fixturesPath() + "Schemes/xcschememanagement.plist" + } +} diff --git a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift index 5727680de..160d1f78c 100644 --- a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift +++ b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift @@ -8,34 +8,96 @@ final class XCSchemeIntegrationTests: XCTestCase { let subject = try XCScheme(path: iosSchemePath) assert(scheme: subject) } - - func test_write_iosScheme() { - testWrite(from: iosSchemePath, - initModel: { try? XCScheme(path: $0) }, - modify: { $0 }, - assertion: { assert(scheme: $1) }) + + func test_write_iosScheme() throws { + try testWrite(from: iosSchemePath, + initModel: { try? XCScheme(path: $0) }, + modify: { $0 }, + assertion: { assert(scheme: $1) }) + } + + func test_read_write_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: iosSchemePath, + initModel: XCScheme.init(path:)) + } + + func test_read_appClipScheme() { + let subject = try? XCScheme(path: appClipScheme) + XCTAssertNotNil(subject) + if let subject { + assert(appClip: subject) + } + } + + func test_read_runnableWithoutBuildableReferenceScheme() { + let subject = try? XCScheme(path: runnableWithoutBuildableReferenceSchemePath) + + XCTAssertNotNil(subject) + if let subject { + assert(runnableWithoutBuildableReferenceScheme: subject) + } + } + + func test_remoteRunnable_runnableWithoutBuildableReferenceScheme() throws { + // Given / When + let subject = try XCScheme(path: runnableWithoutBuildableReferenceSchemePath) + + // Then + let launchAction = try XCTUnwrap(subject.launchAction) + let remoteRunnable = try XCTUnwrap(launchAction.runnable as? XCScheme.RemoteRunnable) + XCTAssertEqual(remoteRunnable.bundleIdentifier, "me.ava.Ava-Staging") + XCTAssertEqual(remoteRunnable.runnableDebuggingMode, "1") + XCTAssertEqual(remoteRunnable.remotePath, "/var/containers/Bundle/Application/018F0933-05E8-4359-9955-39E0523C4246/Ava.app") + } + + func test_write_runnableWithoutBuildableReferenceScheme() throws { + try testWrite(from: runnableWithoutBuildableReferenceSchemePath, + initModel: { try? XCScheme(path: $0) }, + modify: { $0 }, + assertion: { assert(runnableWithoutBuildableReferenceScheme: $1) }) } func test_read_minimalScheme() { let subject = try? XCScheme(path: minimalSchemePath) XCTAssertNotNil(subject) - if let subject = subject { + if let subject { assert(minimalScheme: subject) } } - func test_write_minimalScheme() { - testWrite(from: minimalSchemePath, - initModel: { try? XCScheme(path: $0) }, - modify: { $0 }, - assertion: { assert(minimalScheme: $1) }) + func test_write_minimalScheme() throws { + try testWrite(from: minimalSchemePath, + initModel: { try? XCScheme(path: $0) }, + modify: { $0 }, + assertion: { assert(minimalScheme: $1) }) + } + + func test_read_debugAsRootScheme() throws { + let subject = try XCScheme(path: debugAsRootSchemePath) + + XCTAssertNotNil(subject.launchAction) + XCTAssertEqual(subject.launchAction?.debugAsWhichUser, "root") + } + + func test_write_debugAsRootScheme() throws { + try testWrite(from: debugAsRootSchemePath, + initModel: { try? XCScheme(path: $0) }, + modify: { $0 }, + assertion: { _, scheme in + XCTAssertEqual(scheme.launchAction?.debugAsWhichUser, "root") + }) + } + + func test_read_write_debugAsRootScheme_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: debugAsRootSchemePath, + initModel: XCScheme.init(path:)) } func test_write_testableReferenceDefaultAttributesValuesAreOmitted() { let reference = XCScheme.TestableReference( skipped: false, - parallelizable: false, + parallelization: .swiftTestingOnly, randomExecutionOrdering: false, buildableReference: XCScheme.BuildableReference( referencedContainer: "", @@ -43,17 +105,19 @@ final class XCSchemeIntegrationTests: XCTestCase { buildableName: "", blueprintName: "" ), - skippedTests: [] + skippedTests: [], + selectedTests: [] ) let subject = reference.xmlElement() XCTAssertNil(subject.attributes["parallelizable"]) XCTAssertNil(subject.attributes["testExecutionOrdering"]) + XCTAssertNil(subject.attributes["useTestSelectionWhitelist"]) } - func test_write_testableReferenceAttributesValues() { + func test_write_testableReferenceAttributesValues_allParallelization() { let reference = XCScheme.TestableReference( skipped: false, - parallelizable: true, + parallelization: .all, randomExecutionOrdering: true, buildableReference: XCScheme.BuildableReference( referencedContainer: "", @@ -61,14 +125,112 @@ final class XCSchemeIntegrationTests: XCTestCase { buildableName: "", blueprintName: "" ), - skippedTests: [] + skippedTests: [], + selectedTests: [], + useTestSelectionWhitelist: true ) let subject = reference.xmlElement() XCTAssertEqual(subject.attributes["skipped"], "NO") XCTAssertEqual(subject.attributes["parallelizable"], "YES") + XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES") + XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random") + } + + func test_write_testableReferenceAttributesValues_noneParallelization() { + let reference = XCScheme.TestableReference( + skipped: false, + parallelization: .none, + randomExecutionOrdering: true, + buildableReference: XCScheme.BuildableReference( + referencedContainer: "", + blueprint: PBXObject(), + buildableName: "", + blueprintName: "" + ), + skippedTests: [], + selectedTests: [], + useTestSelectionWhitelist: true + ) + let subject = reference.xmlElement() + XCTAssertEqual(subject.attributes["skipped"], "NO") + XCTAssertEqual(subject.attributes["parallelizable"], "NO") + XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES") XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random") } + func test_write_testableReferenceAttributesValues_swiftTestingOnlyParallelization() { + let reference = XCScheme.TestableReference( + skipped: false, + parallelization: .swiftTestingOnly, + randomExecutionOrdering: true, + buildableReference: XCScheme.BuildableReference( + referencedContainer: "", + blueprint: PBXObject(), + buildableName: "", + blueprintName: "" + ), + skippedTests: [], + selectedTests: [], + useTestSelectionWhitelist: true + ) + let subject = reference.xmlElement() + XCTAssertEqual(subject.attributes["skipped"], "NO") + XCTAssertEqual(subject.attributes["parallelizable"], nil) + XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES") + XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random") + } + + func test_computed_parallelization_testableReference_none() { + let reference = XCScheme.TestableReference( + skipped: false, + parallelization: .none, + randomExecutionOrdering: true, + buildableReference: XCScheme.BuildableReference( + referencedContainer: "", + blueprint: PBXObject(), + buildableName: "", + blueprintName: "" + ), + skippedTests: [], + selectedTests: [], + useTestSelectionWhitelist: true + ) + + XCTAssertEqual(reference.parallelization, .none) + } + + func test_write_testableReferenceSelectedTests() { + // Given + let reference = XCScheme.TestableReference( + skipped: false, + parallelization: .all, + randomExecutionOrdering: true, + buildableReference: XCScheme.BuildableReference( + referencedContainer: "", + blueprint: PBXObject(), + buildableName: "", + blueprintName: "" + ), + skippedTests: [], + selectedTests: [ + .init(identifier: "foo"), + ], + useTestSelectionWhitelist: true + ) + let subject = reference.xmlElement() + + // When + let selectedTests = subject.children.first { $0.name == "SelectedTests" } + let skippedTests = subject.children.first { $0.name == "SkippedTests" } + let firstSelectedTest = selectedTests?.children.first { $0.name == "Test" } + + // Then + XCTAssertNil(skippedTests) + XCTAssertNotNil(selectedTests) + XCTAssertNotNil(firstSelectedTest) + XCTAssertEqual(firstSelectedTest?.attributes["Identifier"], "foo") + } + func test_write_testPlanReferenceDefaultAttributesValuesAreOmitted() { let reference = XCScheme.TestPlanReference(reference: "to_some_path") let subject = reference.xmlElement() @@ -84,19 +246,215 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(subject.attributes["reference"], "to_some_paht") XCTAssertEqual(subject.attributes["default"], "YES") } - + + func test_testAction_pathRunnable_serializingAndDeserializing() throws { + // Given + let filePath = "/usr/bin/foo" + let pathRunnable = XCScheme.PathRunnable(filePath: filePath, runnableDebuggingMode: "0") + let subject = XCScheme.LaunchAction(runnable: pathRunnable, buildConfiguration: "Debug") + + // When + let element = subject.xmlElement() + let reconstructedSubject = try XCScheme.LaunchAction(element: element) + + // Then + XCTAssertEqual(subject, reconstructedSubject) + } + func test_testAction_serializingAndDeserializing() throws { // Given let subject = XCScheme.TestAction(buildConfiguration: "Debug", macroExpansion: nil) - + + // When + let element = subject.xmlElement() + let reconstructedSubject = try XCScheme.TestAction(element: element) + + // Then + XCTAssertEqual(subject, reconstructedSubject) + } + + func test_launchAction_customLLDBInitFile_serializingAndDeserializing() throws { + // Given + let lldbInitPath = "/Users/user/custom/.lldbinit" + let subject = XCScheme.LaunchAction(runnable: nil, buildConfiguration: "Debug", customLLDBInitFile: lldbInitPath) + + // When + let element = subject.xmlElement() + let reconstructedSubject = try XCScheme.LaunchAction(element: element) + + // Then + XCTAssertEqual(subject, reconstructedSubject) + } + + func test_testAction_customLLDBInitFile_serializingAndDeserializing() throws { + // Given + let lldbInitPath = "/Users/user/custom/.lldbinit" + let subject = XCScheme.TestAction(buildConfiguration: "Debug", macroExpansion: nil, customLLDBInitFile: lldbInitPath) + // When let element = subject.xmlElement() let reconstructedSubject = try XCScheme.TestAction(element: element) - + // Then XCTAssertEqual(subject, reconstructedSubject) } + func test_scheme_remoteRunnable() throws { + // Given / When + let subject = try XCScheme(path: watchAppSchemePath) + + // Then + let launchAction = try XCTUnwrap(subject.launchAction) + let remoteRunnable = try XCTUnwrap(launchAction.runnable as? XCScheme.RemoteRunnable) + XCTAssertEqual(remoteRunnable.bundleIdentifier, "com.apple.Carousel") + XCTAssertEqual(remoteRunnable.runnableDebuggingMode, "2") + XCTAssertEqual(remoteRunnable.remotePath, "/AppWithExtensions") + } + + func test_launchAction_remoteRunnable_serializingAndDeserializing() throws { + // Given + let buildableReference = try buildableReferenceWithStringBluePrint() + let remoteRunnable = XCScheme.RemoteRunnable(buildableReference: buildableReference, + bundleIdentifier: "io.tuist", + remotePath: "/Some/Path") + let subject = XCScheme.LaunchAction(runnable: remoteRunnable, + buildConfiguration: "Debug") + + // When + let element = subject.xmlElement() + let reconstructedSubject = try XCScheme.LaunchAction(element: element) + + // Then + XCTAssertEqual(reconstructedSubject, subject) + } + + func test_launchAction_remoteRunnable_showNonLocalizedStrings() throws { + // Given + let buildableReference = try buildableReferenceWithStringBluePrint() + let remoteRunnable = XCScheme.RemoteRunnable(buildableReference: buildableReference, + bundleIdentifier: "io.tuist", + remotePath: "/Some/Path") + let subject = XCScheme.LaunchAction(runnable: remoteRunnable, + buildConfiguration: "Debug", + showNonLocalizedStrings: true) + + // When + let element = subject.xmlElement() + let reconstructedSubject = try XCScheme.LaunchAction(element: element) + + // Then + XCTAssertEqual(reconstructedSubject, subject) + XCTAssertEqual(reconstructedSubject.showNonLocalizedStrings, true) + } + + func test_runnable_equtable() throws { + // Given + let buildableReferenceA = try buildableReferenceWithStringBluePrint(name: "A") + let buildableReferenceB = try buildableReferenceWithStringBluePrint(name: "B") + let remoteRunnableA1 = XCScheme.RemoteRunnable(buildableReference: buildableReferenceA, + bundleIdentifier: "io.tuist", + runnableDebuggingMode: "0", + remotePath: "/Some/Path") + let remoteRunnableA2 = XCScheme.RemoteRunnable(buildableReference: buildableReferenceA, + bundleIdentifier: "io.tuist", + runnableDebuggingMode: "0", + remotePath: "/Some/Path") + let remoteRunnableA3 = XCScheme.RemoteRunnable(buildableReference: buildableReferenceA, + bundleIdentifier: "io.another.tuist", + runnableDebuggingMode: "2", + remotePath: "/Some/Other/Path") + let remoteRunnableB = XCScheme.RemoteRunnable(buildableReference: buildableReferenceB, + bundleIdentifier: "io.tuist", + runnableDebuggingMode: "0", + remotePath: "/Some/Path") + + let runnableA1 = XCScheme.Runnable(buildableReference: buildableReferenceA, + runnableDebuggingMode: "0") + let runnableA2 = XCScheme.Runnable(buildableReference: buildableReferenceA, + runnableDebuggingMode: "0") + let runnableA3 = XCScheme.Runnable(buildableReference: buildableReferenceA, + runnableDebuggingMode: "2") + let runnableB = XCScheme.Runnable(buildableReference: buildableReferenceB, + runnableDebuggingMode: "0") + + // When / Then + XCTAssertEqual(remoteRunnableA1, remoteRunnableA2) + XCTAssertNotEqual(remoteRunnableA1, remoteRunnableA3) + XCTAssertNotEqual(remoteRunnableA1, remoteRunnableB) + XCTAssertNotEqual(remoteRunnableA1, runnableA1) + + XCTAssertEqual(runnableA1, runnableA2) + XCTAssertNotEqual(runnableA1, runnableA3) + XCTAssertNotEqual(runnableA1, runnableB) + XCTAssertNotEqual(runnableA1, remoteRunnableA1) + } + + func test_schemeWithoutBlueprintIdentifier_canBeCreated() { + let subject = try? XCScheme(path: noBlueprintIDPath) + XCTAssertNotNil(subject) + } + + func test_schemeWithoutBlueprintIdentifier_serializesWithoutBlueprintIdentifier() throws { + let subject = try XCScheme(path: noBlueprintIDPath) + let buildable = try XCTUnwrap(subject.buildAction?.buildActionEntries.first?.buildableReference) + let buildableXML = buildable.xmlElement() + XCTAssertNotNil(buildableXML.attributes["BlueprintName"]) + XCTAssertNil(buildableXML.attributes["BlueprintIdentifier"]) + } + + func test_buildAction_runPostActionsOnFailure() throws { + // Given / When + let subject = try XCScheme(path: runPostActionsOnFailureSchemePath) + + // Then + let buildAction = try XCTUnwrap(subject.buildAction) + XCTAssertTrue(buildAction.runPostActionsOnFailure == true) + } + + func test_buildAction_runPostActionsOnFailure_serializingAndDeserializing() throws { + // Given + let scheme = try XCScheme(path: runPostActionsOnFailureSchemePath) + let subject = try XCTUnwrap(scheme.buildAction) + + // When + let xml = subject.xmlElement() + let reconstructedSubject = try XCScheme.BuildAction(element: xml) + + // Then + XCTAssertEqual(reconstructedSubject, subject) + } + + func test_buildAction_buildArchitectures() throws { + // Given / When + let subject = try XCScheme(path: buildArchitecturesSchemePath) + + // Then + let buildAction = try XCTUnwrap(subject.buildAction) + XCTAssertTrue(buildAction.buildArchitectures == .matchRunDestination) + } + + func test_buildAction_buildArchitectures_whenXMLElementDoesNotExist() throws { + // Given / When + let subject = try XCScheme(path: minimalSchemePath) + + // Then + let buildAction = try XCTUnwrap(subject.buildAction) + XCTAssertTrue(buildAction.buildArchitectures == .useTargetSettings) + } + + func test_buildAction_buildArchitectures_serializingAndDeserializing() throws { + // Given + let scheme = try XCScheme(path: buildArchitecturesSchemePath) + let subject = try XCTUnwrap(scheme.buildAction) + + // When + let xml = subject.xmlElement() + let reconstructedSubject = try XCScheme.BuildAction(element: xml) + + // Then + XCTAssertEqual(reconstructedSubject, subject) + } + // MARK: - Private private func assert(scheme: XCScheme) { @@ -105,6 +463,7 @@ final class XCSchemeIntegrationTests: XCTestCase { // Build action XCTAssertTrue(scheme.buildAction?.parallelizeBuild == true) XCTAssertTrue(scheme.buildAction?.buildImplicitDependencies == true) + XCTAssertNil(scheme.buildAction?.runPostActionsOnFailure) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.testing) == true) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.running) == true) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.profiling) == true) @@ -117,18 +476,22 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.buildAction?.buildActionEntries.first?.buildableReference.referencedContainer, "container:Project.xcodeproj") XCTAssertEqual(scheme.buildAction?.preActions.first?.title, "Build Pre-action") XCTAssertEqual(scheme.buildAction?.preActions.first?.scriptText, "echo prebuild") + XCTAssertNil(scheme.buildAction?.preActions.first?.shellToInvoke) XCTAssertEqual(scheme.buildAction?.postActions.first?.title, "Build Post-action") XCTAssertEqual(scheme.buildAction?.postActions.first?.scriptText, "echo postbuild") + XCTAssertEqual(scheme.buildAction?.postActions.first?.shellToInvoke, "/bin/sh") // Test action XCTAssertEqual(scheme.testAction?.buildConfiguration, "Debug") XCTAssertEqual(scheme.testAction?.selectedDebuggerIdentifier, "Xcode.DebuggerFoundation.Debugger.LLDB") XCTAssertEqual(scheme.testAction?.selectedDebuggerIdentifier, "Xcode.DebuggerFoundation.Debugger.LLDB") XCTAssertEqual(scheme.testAction?.shouldUseLaunchSchemeArgsEnv, true) + XCTAssertEqual(scheme.testAction?.preferredScreenCaptureFormat, .screenshots) XCTAssertEqual(scheme.testAction?.codeCoverageEnabled, true) XCTAssertEqual(scheme.testAction?.onlyGenerateCoverageForSpecifiedTargets, true) XCTAssertEqual(scheme.testAction?.testables.first?.skipped, false) - XCTAssertEqual(scheme.testAction?.testables.first?.parallelizable, false) + XCTAssertEqual(scheme.testAction?.testables.first?.parallelization, .swiftTestingOnly) XCTAssertEqual(scheme.testAction?.testables.first?.randomExecutionOrdering, false) + XCTAssertEqual(scheme.testAction?.testables.first?.useTestSelectionWhitelist, false) XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.buildableIdentifier, "primary") XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.blueprintIdentifier, "23766C251EAA3484007A9026") XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.buildableName, "iOSTests.xctest") @@ -156,6 +519,7 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.testAction?.enableUBSanitizer, false) XCTAssertEqual(scheme.testAction?.disableMainThreadChecker, false) XCTAssertEqual(scheme.testAction?.additionalOptions.isEmpty, true) + XCTAssertEqual(scheme.launchAction?.storeKitConfigurationFileReference?.identifier, "../../Configuration.storekit") let testEnvironmentVariables = XCTAssertNotNilAndUnwrap(scheme.testAction?.environmentVariables) XCTAssertEqual(testEnvironmentVariables.count, 1) @@ -182,14 +546,16 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.profileAction?.buildConfiguration, "Release") XCTAssertEqual(scheme.profileAction?.shouldUseLaunchSchemeArgsEnv, true) XCTAssertEqual(scheme.profileAction?.savedToolIdentifier, "") + XCTAssertNil(scheme.profileAction?.customWorkingDirectory) XCTAssertEqual(scheme.profileAction?.useCustomWorkingDirectory, false) XCTAssertEqual(scheme.profileAction?.debugDocumentVersioning, true) - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.runnableDebuggingMode, "0") - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.buildableReference.buildableIdentifier, "primary") - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.buildableReference.blueprintIdentifier, "23766C111EAA3484007A9026") - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.buildableReference.buildableName, "iOS.app") - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.buildableReference.blueprintName, "iOS") - XCTAssertEqual(scheme.profileAction?.buildableProductRunnable?.buildableReference.referencedContainer, "container:Project.xcodeproj") + XCTAssertNil(scheme.profileAction?.askForAppToLaunch) + XCTAssertEqual(scheme.profileAction?.runnable?.runnableDebuggingMode, "0") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.buildableIdentifier, "primary") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.blueprintIdentifier, "23766C111EAA3484007A9026") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.buildableName, "iOS.app") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.blueprintName, "iOS") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.referencedContainer, "container:Project.xcodeproj") XCTAssertEqual(scheme.profileAction?.preActions.isEmpty, true) XCTAssertEqual(scheme.profileAction?.postActions.first?.title, "Run Script") XCTAssertEqual(scheme.profileAction?.postActions.first?.scriptText, "echo analysis done") @@ -210,17 +576,19 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.launchAction?.selectedDebuggerIdentifier, "Xcode.DebuggerFoundation.Debugger.LLDB") XCTAssertEqual(scheme.launchAction?.selectedLauncherIdentifier, "Xcode.DebuggerFoundation.Launcher.LLDB") XCTAssertEqual(scheme.launchAction?.launchStyle, .custom) + XCTAssertNil(scheme.launchAction?.askForAppToLaunch) + XCTAssertNil(scheme.launchAction?.customWorkingDirectory) XCTAssertEqual(scheme.launchAction?.useCustomWorkingDirectory, false) XCTAssertEqual(scheme.launchAction?.ignoresPersistentStateOnLaunch, false) XCTAssertEqual(scheme.launchAction?.debugDocumentVersioning, true) XCTAssertEqual(scheme.launchAction?.debugServiceExtension, "internal") XCTAssertEqual(scheme.launchAction?.allowLocationSimulation, true) XCTAssertEqual(scheme.launchAction?.runnable?.runnableDebuggingMode, "0") - XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference.buildableIdentifier, "primary") - XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference.blueprintIdentifier, "23766C111EAA3484007A9026") - XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference.buildableName, "iOS.app") - XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference.blueprintName, "iOS") - XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference.referencedContainer, "container:Project.xcodeproj") + XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference?.buildableIdentifier, "primary") + XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference?.blueprintIdentifier, "23766C111EAA3484007A9026") + XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference?.buildableName, "iOS.app") + XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference?.blueprintName, "iOS") + XCTAssertEqual(scheme.launchAction?.runnable?.buildableReference?.referencedContainer, "container:Project.xcodeproj") XCTAssertEqual(scheme.launchAction?.locationScenarioReference?.identifier, "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier") XCTAssertEqual(scheme.launchAction?.locationScenarioReference?.referenceType, "1") XCTAssertEqual(scheme.launchAction?.preActions.first?.title, "") @@ -244,8 +612,10 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.launchAction?.enableUBSanitizer, false) XCTAssertEqual(scheme.launchAction?.stopOnEveryUBSanitizerIssue, false) XCTAssertEqual(scheme.launchAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.launchAction?.disablePerformanceAntipatternChecker, false) XCTAssertEqual(scheme.launchAction?.stopOnEveryMainThreadCheckerIssue, false) XCTAssertEqual(scheme.launchAction?.additionalOptions.isEmpty, true) + XCTAssertEqual(scheme.launchAction?.appClipInvocationURLString, nil) let launchEnvironmentVariables = XCTAssertNotNilAndUnwrap(scheme.launchAction?.environmentVariables) XCTAssertEqual(launchEnvironmentVariables.count, 1) @@ -257,6 +627,115 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertTrue(!launchCLIArgs.arguments.isEmpty) XCTAssertEqual(launchCLIArgs.arguments[0].name, "MyLaunchArgument") XCTAssertTrue(launchCLIArgs.arguments[0].enabled) + XCTAssertNil(scheme.launchAction?.customLLDBInitFile) + } + + private func assert(runnableWithoutBuildableReferenceScheme scheme: XCScheme) { + XCTAssertEqual(scheme.version, "2.0") + XCTAssertEqual(scheme.lastUpgradeVersion, "1230", "\(scheme.lastUpgradeVersion!) not equals 1230") + + // Build action + XCTAssertTrue(scheme.buildAction?.parallelizeBuild == true) + XCTAssertTrue(scheme.buildAction?.buildImplicitDependencies == true) + XCTAssertNil(scheme.buildAction?.runPostActionsOnFailure) + XCTAssertEqual(scheme.buildAction?.buildActionEntries.count, 2) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.testing) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.running) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.profiling) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.archiving) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.analyzing) == true) + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.buildableIdentifier, "primary") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.blueprintIdentifier, "FE7C11D21B6DB70D0041DF02") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.buildableName, "Ava.app") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.blueprintName, "core-ava") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.referencedContainer, "container:core-ava.xcodeproj") + + XCTAssertTrue(scheme.buildAction?.buildActionEntries[1].buildFor.contains(.testing) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[1].buildFor.contains(.running) == false) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[1].buildFor.contains(.profiling) == false) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[1].buildFor.contains(.archiving) == false) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[1].buildFor.contains(.analyzing) == false) + XCTAssertEqual(scheme.buildAction?.buildActionEntries[1].buildableReference.buildableIdentifier, "primary") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[1].buildableReference.blueprintIdentifier, "9942115E25C4D3B7000711CE") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[1].buildableReference.buildableName, "AvaTests.xctest") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[1].buildableReference.blueprintName, "AvaTests") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[1].buildableReference.referencedContainer, "container:core-ava.xcodeproj") + + // Test action + XCTAssertEqual(scheme.testAction?.buildConfiguration, "Debug") + XCTAssertEqual(scheme.testAction?.selectedDebuggerIdentifier, "Xcode.DebuggerFoundation.Debugger.LLDB") + XCTAssertEqual(scheme.testAction?.selectedLauncherIdentifier, "Xcode.DebuggerFoundation.Launcher.LLDB") + XCTAssertTrue(scheme.testAction?.shouldUseLaunchSchemeArgsEnv == true) + XCTAssertTrue(scheme.testAction?.codeCoverageEnabled == false) + XCTAssertEqual(scheme.testAction?.onlyGenerateCoverageForSpecifiedTargets, nil) + XCTAssertNil(scheme.testAction?.macroExpansion) + XCTAssertEqual(scheme.testAction?.enableAddressSanitizer, false) + XCTAssertEqual(scheme.testAction?.enableASanStackUseAfterReturn, false) + XCTAssertEqual(scheme.testAction?.enableThreadSanitizer, false) + XCTAssertEqual(scheme.testAction?.enableUBSanitizer, false) + XCTAssertEqual(scheme.testAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.testAction?.additionalOptions.isEmpty, true) + XCTAssertNil(scheme.testAction?.commandlineArguments) + XCTAssertNil(scheme.testAction?.environmentVariables) + + // Launch action + XCTAssertEqual(scheme.launchAction?.selectedDebuggerIdentifier, XCScheme.defaultDebugger) + XCTAssertEqual(scheme.launchAction?.selectedLauncherIdentifier, XCScheme.defaultLauncher) + XCTAssertEqual(scheme.launchAction?.buildConfiguration, "Staging") + XCTAssertEqual(scheme.launchAction?.launchStyle, XCScheme.LaunchAction.Style.auto) + XCTAssertTrue(scheme.launchAction?.askForAppToLaunch == true) + XCTAssertEqual(scheme.launchAction?.customWorkingDirectory, "/customWorkingDirectory") + XCTAssertTrue(scheme.launchAction?.useCustomWorkingDirectory == false) + XCTAssertTrue(scheme.launchAction?.ignoresPersistentStateOnLaunch == false) + XCTAssertTrue(scheme.launchAction?.debugDocumentVersioning == true) + XCTAssertEqual(scheme.launchAction?.debugServiceExtension, XCScheme.LaunchAction.defaultDebugServiceExtension) + XCTAssertTrue(scheme.launchAction?.allowLocationSimulation == true) + XCTAssertNil(scheme.launchAction?.locationScenarioReference) + XCTAssertNil(scheme.launchAction?.commandlineArguments) + XCTAssertEqual(scheme.launchAction?.enableAddressSanitizer, false) + XCTAssertEqual(scheme.launchAction?.enableASanStackUseAfterReturn, false) + XCTAssertEqual(scheme.launchAction?.enableThreadSanitizer, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryThreadSanitizerIssue, false) + XCTAssertEqual(scheme.launchAction?.enableUBSanitizer, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryUBSanitizerIssue, false) + XCTAssertEqual(scheme.launchAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.launchAction?.disablePerformanceAntipatternChecker, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryMainThreadCheckerIssue, false) + XCTAssertEqual(scheme.launchAction?.additionalOptions.isEmpty, true) + XCTAssertNil(scheme.launchAction?.storeKitConfigurationFileReference) + XCTAssertEqual(scheme.launchAction?.macroExpansion?.buildableIdentifier, "primary") + XCTAssertEqual(scheme.launchAction?.macroExpansion?.blueprintIdentifier, "FE7C11D21B6DB70D0041DF02") + XCTAssertEqual(scheme.launchAction?.macroExpansion?.buildableName, "Ava.app") + XCTAssertEqual(scheme.launchAction?.macroExpansion?.blueprintName, "core-ava") + XCTAssertEqual(scheme.launchAction?.macroExpansion?.referencedContainer, "container:core-ava.xcodeproj") + XCTAssertNil(scheme.launchAction?.environmentVariables) + XCTAssertNil(scheme.launchAction?.appClipInvocationURLString) + + // Profile action + XCTAssertEqual(scheme.profileAction?.runnable?.runnableDebuggingMode, "0") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.blueprintIdentifier, "FE7C11D21B6DB70D0041DF02") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.buildableIdentifier, "primary") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.blueprintName, "core-ava") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.buildableName, "Ava.app") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.referencedContainer, "container:core-ava.xcodeproj") + XCTAssertEqual(scheme.profileAction?.buildConfiguration, "Release") + XCTAssertTrue(scheme.profileAction?.shouldUseLaunchSchemeArgsEnv == true) + XCTAssertEqual(scheme.profileAction?.savedToolIdentifier, "") + XCTAssertNil(scheme.profileAction?.customWorkingDirectory) + XCTAssertTrue(scheme.profileAction?.useCustomWorkingDirectory == false) + XCTAssertTrue(scheme.profileAction?.debugDocumentVersioning == true) + XCTAssertTrue(scheme.profileAction?.askForAppToLaunch == true) + XCTAssertNil(scheme.profileAction?.commandlineArguments) + XCTAssertNil(scheme.profileAction?.environmentVariables) + XCTAssertEqual(scheme.profileAction?.launchAutomaticallySubstyle, "2") + + // Analyze action + XCTAssertEqual(scheme.analyzeAction?.buildConfiguration, "Debug") + + // Archive action + XCTAssertEqual(scheme.archiveAction?.buildConfiguration, "Release") + XCTAssertTrue(scheme.archiveAction?.revealArchiveInOrganizer == true) + XCTAssertNil(scheme.archiveAction?.customArchiveName) } private func assert(minimalScheme scheme: XCScheme) { @@ -266,6 +745,7 @@ final class XCSchemeIntegrationTests: XCTestCase { // Build action XCTAssertTrue(scheme.buildAction?.parallelizeBuild == true) XCTAssertTrue(scheme.buildAction?.buildImplicitDependencies == true) + XCTAssertNil(scheme.buildAction?.runPostActionsOnFailure) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.testing) == true) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.running) == false) XCTAssertTrue(scheme.buildAction?.buildActionEntries.first?.buildFor.contains(.profiling) == true) @@ -300,6 +780,8 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.launchAction?.selectedLauncherIdentifier, XCScheme.defaultLauncher) XCTAssertEqual(scheme.launchAction?.buildConfiguration, "Debug") XCTAssertEqual(scheme.launchAction?.launchStyle, XCScheme.LaunchAction.Style.auto) + XCTAssertNil(scheme.launchAction?.askForAppToLaunch) + XCTAssertNil(scheme.launchAction?.customWorkingDirectory) XCTAssertTrue(scheme.launchAction?.useCustomWorkingDirectory == false) XCTAssertTrue(scheme.launchAction?.ignoresPersistentStateOnLaunch == false) XCTAssertTrue(scheme.launchAction?.debugDocumentVersioning == true) @@ -314,8 +796,11 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.launchAction?.enableUBSanitizer, false) XCTAssertEqual(scheme.launchAction?.stopOnEveryUBSanitizerIssue, false) XCTAssertEqual(scheme.launchAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.launchAction?.disablePerformanceAntipatternChecker, false) XCTAssertEqual(scheme.launchAction?.stopOnEveryMainThreadCheckerIssue, false) XCTAssertEqual(scheme.launchAction?.additionalOptions.isEmpty, true) + XCTAssertNil(scheme.launchAction?.storeKitConfigurationFileReference) + XCTAssertNil(scheme.launchAction?.appClipInvocationURLString) let launchEnvironmentVariables = XCTAssertNotNilAndUnwrap(scheme.launchAction?.environmentVariables) XCTAssertEqual(launchEnvironmentVariables.count, 1) @@ -324,12 +809,14 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertTrue(launchEnvironmentVariables[0].enabled) // Profile action - XCTAssertNil(scheme.profileAction?.buildableProductRunnable) + XCTAssertNil(scheme.profileAction?.runnable) XCTAssertEqual(scheme.profileAction?.buildConfiguration, "Release") XCTAssertTrue(scheme.profileAction?.shouldUseLaunchSchemeArgsEnv == true) XCTAssertEqual(scheme.profileAction?.savedToolIdentifier, "") + XCTAssertNil(scheme.profileAction?.customWorkingDirectory) XCTAssertTrue(scheme.profileAction?.useCustomWorkingDirectory == false) XCTAssertTrue(scheme.profileAction?.debugDocumentVersioning == true) + XCTAssertNil(scheme.profileAction?.askForAppToLaunch) XCTAssertNil(scheme.profileAction?.commandlineArguments) XCTAssertNil(scheme.profileAction?.environmentVariables) @@ -342,13 +829,146 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertNil(scheme.archiveAction?.customArchiveName) } + private func assert(appClip scheme: XCScheme) { + XCTAssertEqual(scheme.version, "1.7") + XCTAssertEqual(scheme.lastUpgradeVersion, "1600", "\(scheme.lastUpgradeVersion!) not equals 1600") + + // Build action + XCTAssertTrue(scheme.buildAction?.parallelizeBuild == true) + XCTAssertTrue(scheme.buildAction?.buildImplicitDependencies == true) + XCTAssertNil(scheme.buildAction?.runPostActionsOnFailure) + XCTAssertEqual(scheme.buildAction?.buildActionEntries.count, 1) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.testing) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.running) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.profiling) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.archiving) == true) + XCTAssertTrue(scheme.buildAction?.buildActionEntries[0].buildFor.contains(.analyzing) == true) + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.buildableIdentifier, "primary") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.blueprintIdentifier, "5CC725022DA91FB6004D43D4") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.buildableName, "app_clip.app") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.blueprintName, "app_clip") + XCTAssertEqual(scheme.buildAction?.buildActionEntries[0].buildableReference.referencedContainer, "container:example.xcodeproj") + + // Test action + XCTAssertEqual(scheme.testAction?.buildConfiguration, "Debug") + XCTAssertEqual(scheme.testAction?.selectedDebuggerIdentifier, "Xcode.DebuggerFoundation.Debugger.LLDB") + XCTAssertEqual(scheme.testAction?.selectedLauncherIdentifier, "Xcode.DebuggerFoundation.Launcher.LLDB") + XCTAssertTrue(scheme.testAction?.shouldUseLaunchSchemeArgsEnv == true) + XCTAssertTrue(scheme.testAction?.codeCoverageEnabled == false) + XCTAssertEqual(scheme.testAction?.onlyGenerateCoverageForSpecifiedTargets, nil) + XCTAssertNil(scheme.testAction?.macroExpansion) + XCTAssertEqual(scheme.testAction?.enableAddressSanitizer, false) + XCTAssertEqual(scheme.testAction?.enableASanStackUseAfterReturn, false) + XCTAssertEqual(scheme.testAction?.enableThreadSanitizer, false) + XCTAssertEqual(scheme.testAction?.enableUBSanitizer, false) + XCTAssertEqual(scheme.testAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.testAction?.additionalOptions.isEmpty, true) + XCTAssertNil(scheme.testAction?.commandlineArguments) + XCTAssertNil(scheme.testAction?.environmentVariables) + + // Launch action + XCTAssertEqual(scheme.launchAction?.selectedDebuggerIdentifier, XCScheme.defaultDebugger) + XCTAssertEqual(scheme.launchAction?.selectedLauncherIdentifier, XCScheme.defaultLauncher) + XCTAssertEqual(scheme.launchAction?.buildConfiguration, "Debug") + XCTAssertEqual(scheme.launchAction?.launchStyle, XCScheme.LaunchAction.Style.auto) + XCTAssertNil(scheme.launchAction?.askForAppToLaunch) + XCTAssertNil(scheme.launchAction?.customWorkingDirectory) + XCTAssertTrue(scheme.launchAction?.useCustomWorkingDirectory == false) + XCTAssertTrue(scheme.launchAction?.ignoresPersistentStateOnLaunch == false) + XCTAssertTrue(scheme.launchAction?.debugDocumentVersioning == true) + XCTAssertEqual(scheme.launchAction?.debugServiceExtension, XCScheme.LaunchAction.defaultDebugServiceExtension) + XCTAssertTrue(scheme.launchAction?.allowLocationSimulation == true) + XCTAssertNil(scheme.launchAction?.locationScenarioReference) + XCTAssertNil(scheme.launchAction?.commandlineArguments) + XCTAssertEqual(scheme.launchAction?.enableAddressSanitizer, false) + XCTAssertEqual(scheme.launchAction?.enableASanStackUseAfterReturn, false) + XCTAssertEqual(scheme.launchAction?.enableThreadSanitizer, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryThreadSanitizerIssue, false) + XCTAssertEqual(scheme.launchAction?.enableUBSanitizer, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryUBSanitizerIssue, false) + XCTAssertEqual(scheme.launchAction?.disableMainThreadChecker, false) + XCTAssertEqual(scheme.launchAction?.disablePerformanceAntipatternChecker, false) + XCTAssertEqual(scheme.launchAction?.stopOnEveryMainThreadCheckerIssue, false) + XCTAssertEqual(scheme.launchAction?.additionalOptions.isEmpty, true) + XCTAssertNil(scheme.launchAction?.storeKitConfigurationFileReference) + XCTAssertNil(scheme.launchAction?.macroExpansion?.referencedContainer) + XCTAssertNil(scheme.launchAction?.environmentVariables) + XCTAssertEqual(scheme.launchAction?.appClipInvocationURLString, "https://example.com/") + + // Profile action + XCTAssertEqual(scheme.profileAction?.runnable?.runnableDebuggingMode, "0") + XCTAssertEqual(scheme.profileAction?.runnable?.buildableReference?.referencedContainer, "container:example.xcodeproj") + XCTAssertEqual(scheme.profileAction?.buildConfiguration, "Release") + XCTAssertTrue(scheme.profileAction?.shouldUseLaunchSchemeArgsEnv == true) + XCTAssertEqual(scheme.profileAction?.savedToolIdentifier, "") + XCTAssertNil(scheme.profileAction?.customWorkingDirectory) + XCTAssertTrue(scheme.profileAction?.useCustomWorkingDirectory == false) + XCTAssertTrue(scheme.profileAction?.debugDocumentVersioning == true) + XCTAssertNil(scheme.profileAction?.askForAppToLaunch) + XCTAssertNil(scheme.profileAction?.commandlineArguments) + XCTAssertNil(scheme.profileAction?.environmentVariables) + XCTAssertNil(scheme.profileAction?.launchAutomaticallySubstyle) + + // Analyze action + XCTAssertEqual(scheme.analyzeAction?.buildConfiguration, "Debug") + + // Archive action + XCTAssertEqual(scheme.archiveAction?.buildConfiguration, "Release") + XCTAssertTrue(scheme.archiveAction?.revealArchiveInOrganizer == true) + XCTAssertNil(scheme.archiveAction?.customArchiveName) + } + + private func buildableReferenceWithStringBluePrint(name: String = "App") throws -> XCScheme.BuildableReference { + // To allow performing comparisons when serializing / deserializing we need a + // buildable reference that contains a `.string` blue print + let buildableReference = XCScheme.BuildableReference( + referencedContainer: "\(name).xcodeproj", + blueprint: PBXObject(), + buildableName: name, + blueprintName: name + ) + let xmlElement = buildableReference.xmlElement() + return try XCScheme.BuildableReference(element: xmlElement) + } + private var iosSchemePath: Path { - return fixturesPath() + "iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme" + fixturesPath() + "iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme" } private var minimalSchemePath: Path { // Not strictly minimal in the sense that it specifies the least amount of information to be valid, // but minimal in the sense it doesn't have most of the standard elements and attributes. - return fixturesPath() + "Schemes/MinimalInformation.xcscheme" + fixturesPath() + "Schemes/MinimalInformation.xcscheme" + } + + private var runnableWithoutBuildableReferenceSchemePath: Path { + fixturesPath() + "Schemes/RunnableWithoutBuildableReference.xcscheme" + } + + /// Path to a scheme with a buildable reference that contains no blueprint identifier + private var noBlueprintIDPath: Path { + fixturesPath() + "Schemes/NoBlueprintID.xcscheme" + } + + private var watchAppSchemePath: Path { + fixturesPath() + "iOS/AppWithExtensions/AppWithExtensions.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme" + } + + private var runPostActionsOnFailureSchemePath: Path { + // A scheme with the `runPostActionsOnFailure` enabled + fixturesPath() + "Schemes/RunPostActionsOnFailure.xcscheme" + } + + /// A scheme that `buildArchitectures` is specified "Automatic". + private var buildArchitecturesSchemePath: Path { + fixturesPath() + "Schemes/BuildArchitectures.xcscheme" + } + + private var appClipScheme: Path { + fixturesPath() + "Schemes/AppClip.xcscheme" + } + + private var debugAsRootSchemePath: Path { + fixturesPath() + "Schemes/DebugAsRoot.xcscheme" } } diff --git a/Tests/XcodeProjTests/Tests/Fixtures.swift b/Tests/XcodeProjTests/Tests/Fixtures.swift index 10511fa03..045a7ce49 100644 --- a/Tests/XcodeProjTests/Tests/Fixtures.swift +++ b/Tests/XcodeProjTests/Tests/Fixtures.swift @@ -1,12 +1,62 @@ import Foundation import PathKit -import XcodeProj +@testable import XcodeProj func fixturesPath() -> Path { - return Path(#file).parent().parent().parent().parent() + "Fixtures" + Path(#filePath).parent().parent().parent().parent() + "Fixtures" } -func iosProjectDictionary() -> (Path, [String: Any]) { +func synchronizedRootGroupsFixture() throws -> Data { + let synchronizedRootGroups = fixturesPath() + "SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj" + return try Data(contentsOf: synchronizedRootGroups.url) +} + +func iosProjectData() throws -> Data { let iosProject = fixturesPath() + "iOS/Project.xcodeproj/project.pbxproj" - return (iosProject, loadPlist(path: iosProject.string)!) + return try Data(contentsOf: iosProject.url) +} + +func iosProjectWithExtensionsData() throws -> Data { + let iosProjectWithExtensions = fixturesPath() + "iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithExtensions.url) +} + +func fileSharedAcrossTargetsData() throws -> Data { + let fileSharedAcrossTargetsProject = fixturesPath() + "FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.pbxproj" + return try Data(contentsOf: fileSharedAcrossTargetsProject.url) +} + +func targetWithCustomBuildRulesData() throws -> Data { + let targetWithCustomBuildRulesProject = fixturesPath() + "TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.pbxproj" + return try Data(contentsOf: targetWithCustomBuildRulesProject.url) +} + +func iosProjectWithXCLocalSwiftPackageReference() throws -> Data { + let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithXCLocalSwiftPackageReference.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url) +} + +func iosProjectWithRelativeXCLocalSwiftPackageReferences() throws -> Data { + let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithRelativeXCLocalSwiftPackageReference/ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url) +} + +func iosProjectWithXCLocalSwiftPackageReferences() throws -> Data { + let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithXCLocalSwiftPackageReferences.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url) +} + +func projectWithWrongProjectReferencesOrder() throws -> Data { + let iosProjectWithProjectReferences = fixturesPath() + "Xcode16ProjectReferenceOrder/Wrong.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithProjectReferences.url) +} + +func projectWithCustomShellScript() throws -> Data { + let iosProjectWithShellScript = fixturesPath() + "Xcode16ProjectReferenceOrder/Test.xcodeproj/project.pbxproj" + return try Data(contentsOf: iosProjectWithShellScript.url) +} + +func projectWithConfigurationFilesInSynchronizedGroup() throws -> Data { + let projectWithConfigurationFilesInSynchronizedGroup = fixturesPath() + "Xcode16BuildConfigurations/Xcode16BuildConfigurations.xcodeproj/project.pbxproj" + return try Data(contentsOf: projectWithConfigurationFilesInSynchronizedGroup.url) } diff --git a/Tests/XcodeProjTests/Tests/testWrite.swift b/Tests/XcodeProjTests/Tests/testWrite.swift index 47bb10142..56b45dd61 100644 --- a/Tests/XcodeProjTests/Tests/testWrite.swift +++ b/Tests/XcodeProjTests/Tests/testWrite.swift @@ -6,29 +6,31 @@ import XCTest func testWrite(file _: StaticString = #file, line _: UInt = #line, from path: Path, - initModel: (Path) -> T?, - modify: (T) -> T) { - testWrite(from: path, initModel: initModel, modify: modify, assertion: { XCTAssertEqual($0, $1) }) + initModel: (Path) throws -> T?, + modify: (T) -> T) throws +{ + try testWrite(from: path, initModel: initModel, modify: modify, assertion: { XCTAssertEqual($0, $1) }) } -func testWrite(file: StaticString = #file, +func testWrite(file: StaticString = #filePath, line: UInt = #line, from path: Path, - initModel: (Path) -> T?, + initModel: (Path) throws -> T?, modify: (T) -> T, - assertion: (_ before: T, _ after: T) -> Void) { + assertion: (_ before: T, _ after: T) -> Void) throws +{ let copyPath = path.parent() + "copy.\(path.extension!)" try? copyPath.delete() try? path.copy(copyPath) - let got = initModel(copyPath) + let got = try initModel(copyPath) XCTAssertNotNil(got, file: file, line: line) - if let got = got { + if let got { let modified = modify(got) do { try modified.write(path: copyPath, override: true) - let gotAfterWriting = initModel(copyPath) + let gotAfterWriting = try initModel(copyPath) XCTAssertNotNil(gotAfterWriting, file: file, line: line) - if let gotAfterWriting = gotAfterWriting { + if let gotAfterWriting { assertion(got, gotAfterWriting) } } catch { @@ -37,3 +39,38 @@ func testWrite(file: StaticString = #file, } try? copyPath.delete() } + +func testReadWriteProducesNoDiff(file _: StaticString = #file, + line _: UInt = #line, + from path: Path, + initModel: (Path) throws -> some Writable) throws +{ + #if os(iOS) + throw XCTSkip("'Process' API is unavailable on iOS platform") + #else + let tmpDir = try Path.uniqueTemporary() + defer { + try? tmpDir.delete() + } + + let fileName = path.lastComponent + let tmpPath = tmpDir + fileName + try path.copy(tmpPath) + + try tmpDir.chdir { + // Create a commit + try checkedOutput("git", ["init"]) + try checkedOutput("git", ["add", "."]) + try checkedOutput("git", [ + "-c", "user.email=test@example.com", "-c", "user.name=Test User", + "commit", "-m", "test", + ]) + + let object = try initModel(tmpPath) + try object.write(path: tmpPath, override: true) + + let diff = try XCTUnwrap(checkedOutput("git", ["diff"])) + XCTAssertEqual(diff, "") + } + #endif +} diff --git a/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift b/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift index ed5e1c277..9c0bcb857 100644 --- a/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift +++ b/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift @@ -1,244 +1,343 @@ import Foundation -import XCTest +import Testing @testable import XcodeProj -class BuildSettingProviderTests: XCTestCase { - - func test_targetSettings_iosAplication() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .iOS, - product: .application, - swift: true) - - // Then - assertEqualSettings(results, [ - "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", - "CODE_SIGN_IDENTITY": "iPhone Developer", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks" - ], - "SDKROOT": "iphoneos", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "TARGETED_DEVICE_FAMILY": "1,2" - ]) - } - - func test_targetSettings_iosFramework() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .iOS, - product: .framework, - swift: true) - - // Then - assertEqualSettings(results, [ - "CODE_SIGN_IDENTITY": "", - "CURRENT_PROJECT_VERSION": "1", - "DEFINES_MODULE": "YES", - "DYLIB_COMPATIBILITY_VERSION": "1", - "DYLIB_CURRENT_VERSION": "1", - "DYLIB_INSTALL_NAME_BASE": "@rpath", - "INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Frameworks", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks" - ], - "PRODUCT_NAME": "$(TARGET_NAME:c99extidentifier)", - "SDKROOT": "iphoneos", - "SKIP_INSTALL": "YES", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "TARGETED_DEVICE_FAMILY": "1,2", - "VERSIONING_SYSTEM": "apple-generic", - "VERSION_INFO_PREFIX": "" - ]) - } - - func test_targetSettings_iosExtension() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .iOS, - product: .appExtension, - swift: true) - - // Then - assertEqualSettings(results, [ - "CODE_SIGN_IDENTITY": "iPhone Developer", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ], - "SDKROOT": "iphoneos", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "TARGETED_DEVICE_FAMILY": "1,2", - ]) - } - - func test_targetSettings_macOSAplication() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .macOS, - product: .application, - swift: true) - - // Then - assertEqualSettings(results, [ - "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", - "CODE_SIGN_IDENTITY": "-", - "COMBINE_HIDPI_IMAGES": "YES", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/../Frameworks" - ], - "SDKROOT": "macosx", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule" - ]) - } - - func test_targetSettings_tvOSAplication() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .tvOS, - product: .application, - swift: true) - - // Then - assertEqualSettings(results, [ - "ASSETCATALOG_COMPILER_APPICON_NAME": "App Icon & Top Shelf Image", - "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME": "LaunchImage", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks" - ], - "SDKROOT": "appletvos", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "TARGETED_DEVICE_FAMILY": "3" - ]) - } - - func test_targetSettings_watchOSAplication() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .release, - platform: .watchOS, - product: .application, - swift: true) - - // Then - assertEqualSettings(results, [ - "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES", - "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", - "SDKROOT": "watchos", - "SKIP_INSTALL": "YES", - "SWIFT_COMPILATION_MODE": "wholemodule", - "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", - "TARGETED_DEVICE_FAMILY": "4" - ]) +@Suite("Build setting provider tests") +struct BuildSettingProviderTests { + @Test( + "Target settings", + arguments: [ + TestParameters( + title: "iOS App", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .iOS, product: .application, swift: true), + expectedOutput: [ + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", + "CODE_SIGN_IDENTITY": "iPhone Developer", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + ], + "SDKROOT": "iphoneos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2", + ] + ), + TestParameters( + title: "iOS Framework", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .iOS, product: .framework, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "DYLIB_COMPATIBILITY_VERSION": "1", + "DYLIB_CURRENT_VERSION": "1", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Frameworks", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "PRODUCT_NAME": "$(TARGET_NAME:c99extidentifier)", + "SDKROOT": "iphoneos", + "SKIP_INSTALL": "YES", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2", + "VERSIONING_SYSTEM": "apple-generic", + "VERSION_INFO_PREFIX": "", + ] + ), + TestParameters( + title: "iOS App extension", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .iOS, product: .appExtension, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ], + "SDKROOT": "iphoneos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2", + ] + ), + TestParameters( + title: "macOS App", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .macOS, product: .application, swift: true), + expectedOutput: [ + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", + "CODE_SIGN_IDENTITY": "-", + "COMBINE_HIDPI_IMAGES": "YES", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/../Frameworks", + ], + "SDKROOT": "macosx", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + ] + ), + TestParameters( + title: "tvOS App", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .tvOS, product: .application, swift: true), + expectedOutput: [ + "ASSETCATALOG_COMPILER_APPICON_NAME": "App Icon & Top Shelf Image", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", + "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME": "LaunchImage", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + ], + "SDKROOT": "appletvos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "3", + ] + ), + TestParameters( + title: "watchOS App", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .watchOS, product: .application, swift: true), + expectedOutput: [ + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + ], + "SDKROOT": "watchos", + "SKIP_INSTALL": "YES", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "4", + ] + ), + TestParameters( + title: "watchOS Framework", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .watchOS, product: .framework, swift: true), + expectedOutput: [ + "APPLICATION_EXTENSION_API_ONLY": "YES", + "CODE_SIGN_IDENTITY": "", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "DYLIB_COMPATIBILITY_VERSION": "1", + "DYLIB_CURRENT_VERSION": "1", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Frameworks", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "PRODUCT_NAME": "$(TARGET_NAME:c99extidentifier)", + "SDKROOT": "watchos", + "SKIP_INSTALL": "YES", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "4", + "VERSIONING_SYSTEM": "apple-generic", + "VERSION_INFO_PREFIX": "", + ] + ), + TestParameters( + title: "watchOS App extension", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .watchOS, product: .appExtension, swift: true), + expectedOutput: [ + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ], + "SDKROOT": "watchos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "4", + ] + ), + TestParameters( + title: "visionOS App", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .visionOS, product: .application, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "ENABLE_PREVIEWS": "YES", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + ], + "SDKROOT": "xros", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2,7", + ] + ), + TestParameters( + title: "visionOS Framework", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .visionOS, product: .framework, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "DYLIB_COMPATIBILITY_VERSION": "1", + "DYLIB_CURRENT_VERSION": "1", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Frameworks", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "PRODUCT_NAME": "$(TARGET_NAME:c99extidentifier)", + "SDKROOT": "xros", + "SKIP_INSTALL": "YES", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2,7", + "VERSIONING_SYSTEM": "apple-generic", + "VERSION_INFO_PREFIX": "", + ] + ), + TestParameters( + title: "visionOS App extension", + results: BuildSettingsProvider.targetDefault(variant: .release, platform: .visionOS, product: .appExtension, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ], + "SDKROOT": "xros", + "SWIFT_COMPILATION_MODE": "wholemodule", + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "TARGETED_DEVICE_FAMILY": "1,2,7", + ] + ), + TestParameters( + title: "iOS Unit tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .iOS, product: .unitTests, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "SDKROOT": "iphoneos", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2", + ] + ), + TestParameters( + title: "iOS UI tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .iOS, product: .uiTests, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "SDKROOT": "iphoneos", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2", + ] + ), + TestParameters( + title: "macOS Unit tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .macOS, product: .unitTests, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "-", + "SDKROOT": "macosx", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + ] + ), + TestParameters( + title: "tvOS Unit tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .tvOS, product: .unitTests, swift: true), + expectedOutput: [ + "SDKROOT": "appletvos", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "3", + ] + ), + TestParameters( + title: "visionOS Unit tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .visionOS, product: .unitTests, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "SDKROOT": "xros", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,7", + ] + ), + TestParameters( + title: "visionOS UI tests", + results: BuildSettingsProvider.targetDefault(variant: .debug, platform: .visionOS, product: .uiTests, swift: true), + expectedOutput: [ + "CODE_SIGN_IDENTITY": "iPhone Developer", + "SDKROOT": "xros", + "LD_RUNPATH_SEARCH_PATHS": [ + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ], + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": ["$(inherited)", "DEBUG"], + "SWIFT_COMPILATION_MODE": "singlefile", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,7", + ] + ), + ] + ) + func targetSettings(parameters: TestParameters) { + // Given / When / Then + #expect(parameters.results == parameters.expectedOutput) } - - func test_targetSettings_iOSUnitTests() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .debug, - platform: .iOS, - product: .unitTests, - swift: true) - - // Then - assertEqualSettings(results, [ - "CODE_SIGN_IDENTITY": "iPhone Developer", - "SDKROOT": "iphoneos", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks" - ], - "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG", - "SWIFT_COMPILATION_MODE": "singlefile", - "SWIFT_OPTIMIZATION_LEVEL": "-Onone", - "TARGETED_DEVICE_FAMILY": "1,2" - ]) - } - - func test_targetSettings_iOSUITests() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .debug, - platform: .iOS, - product: .uiTests, - swift: true) - - // Then - assertEqualSettings(results, [ - "CODE_SIGN_IDENTITY": "iPhone Developer", - "SDKROOT": "iphoneos", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks" - ], - "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG", - "SWIFT_COMPILATION_MODE": "singlefile", - "SWIFT_OPTIMIZATION_LEVEL": "-Onone", - "TARGETED_DEVICE_FAMILY": "1,2" - ]) - } - - func test_targetSettings_macOSUnitTests() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .debug, - platform: .macOS, - product: .unitTests, - swift: true) - - // Then - assertEqualSettings(results, [ - "CODE_SIGN_IDENTITY": "-", - "SDKROOT": "macosx", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks" - ], - "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG", - "SWIFT_COMPILATION_MODE": "singlefile", - "SWIFT_OPTIMIZATION_LEVEL": "-Onone", - ]) - } - - func test_targetSettings_tvOSUnitTests() { - // Given / When - let results = BuildSettingsProvider.targetDefault(variant: .debug, - platform: .tvOS, - product: .unitTests, - swift: true) - - // Then - assertEqualSettings(results, [ - "SDKROOT": "appletvos", - "LD_RUNPATH_SEARCH_PATHS": [ - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks" - ], - "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG", - "SWIFT_COMPILATION_MODE": "singlefile", - "SWIFT_OPTIMIZATION_LEVEL": "-Onone", - "TARGETED_DEVICE_FAMILY": "3" - ]) - } - - // MARK: - Helpers - - func assertEqualSettings(_ lhs: BuildSettings, _ rhs: BuildSettings, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(lhs as NSDictionary, - rhs as NSDictionary, - file: file, - line: line) + + struct TestParameters: CustomTestStringConvertible { + let title: String + let results: BuildSettings + let expectedOutput: BuildSettings + + var testDescription: String { + title + } } } diff --git a/Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift b/Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift index 505bae864..a09c79cb1 100644 --- a/Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift +++ b/Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift @@ -11,8 +11,13 @@ class ReferenceGeneratorTests: XCTestCase { let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference) let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy) let productsGroup = project.makeProductsGroup(children: [productReferenceProxy]) + let pluginDependency = project.makePluginDependency() + let (target, _) = project.makeTarget(productReferenceProxy: productReferenceProxy) + target.dependencies.append(pluginDependency) - pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference]) + pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference, + "ProjectRef": remoteProjectFileReference.reference]) + pbxProject.targets.append(target) let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) try referenceGenerator.generateReferences(proj: project) @@ -21,6 +26,134 @@ class ReferenceGeneratorTests: XCTestCase { XCTAssert(!containerItemProxy.reference.temporary) XCTAssert(!productReferenceProxy.reference.temporary) XCTAssert(!remoteProjectFileReference.reference.temporary) + XCTAssert(!pluginDependency.productReference!.temporary) + } + + func test_projectReferencingRemoteXcodeprojBundle_generatesDeterministicIdentifiers() throws { + func generateProject() throws -> [String] { + let project = PBXProj(rootObject: nil, objectVersion: 0, archiveVersion: 0, classes: [:], objects: []) + let pbxProject = project.makeProject() + let remoteProjectFileReference = project.makeFileReference() + let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference) + let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy) + let productsGroup = project.makeProductsGroup(children: [productReferenceProxy]) + let pluginDependency = project.makePluginDependency() + let (target, buildFile) = project.makeTarget(productReferenceProxy: productReferenceProxy) + target.dependencies.append(pluginDependency) + + pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference, + "ProjectRef": remoteProjectFileReference.reference]) + pbxProject.targets.append(target) + + let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) + try referenceGenerator.generateReferences(proj: project) + + return [remoteProjectFileReference, containerItemProxy, productReferenceProxy, productsGroup, buildFile, pluginDependency.productReference!.getObject()!] + .map(\.reference.value) + } + + let firstUUIDs = try generateProject() + let secondUUIDs = try generateProject() + + XCTAssertEqual(Set(firstUUIDs), Set(secondUUIDs)) + } + + func test_twoProjectsReferencingTwoDifferentRemoteXcodeprojBundles_generatesDifferentProductGroupIdentifiers() throws { + func generateProject(remoteProjectPath: String) throws -> String { + let project = PBXProj(rootObject: nil, objectVersion: 0, archiveVersion: 0, classes: [:], objects: []) + let pbxProject = project.makeProject() + let remoteProjectFileReference = project.makeFileReference(with: Path(remoteProjectPath)) + let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference) + let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy) + let productsGroup = project.makeProductsGroup(children: [productReferenceProxy]) + let (target, _) = project.makeTarget(productReferenceProxy: productReferenceProxy) + + pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference, + "ProjectRef": remoteProjectFileReference.reference]) + pbxProject.targets.append(target) + + let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) + try referenceGenerator.generateReferences(proj: project) + + return productsGroup.reference.value + } + + let firstProductsGroupUUID = try generateProject(remoteProjectPath: "../Remote1.xcodeproj") + let secondProductsGroupUUID = try generateProject(remoteProjectPath: "../Remote2.xcodeproj") + + XCTAssertNotEqual(firstProductsGroupUUID, secondProductsGroupUUID) + } + + func test_projectWithFilesystemSynchronizedRootGroup_convertsReferencesToPermanent() throws { + let project = PBXProj(rootObject: nil, objectVersion: 0, archiveVersion: 0, classes: [:], objects: []) + let pbxProject = project.makeProject() + + let syncedGroup = project.makeSynchronizedRootGroup() + let target = project.makeTarget() + target.fileSystemSynchronizedGroups = [syncedGroup] + pbxProject.targets.append(target) + + let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) + try referenceGenerator.generateReferences(proj: project) + + XCTAssert(!syncedGroup.reference.temporary) + } + + func test_projectWithFilesystemSynchronizedRootGroupWithExceptions_convertsReferencesToPermanent() throws { + let project = PBXProj(rootObject: nil, objectVersion: 0, archiveVersion: 0, classes: [:], objects: []) + let pbxProject = project.makeProject() + + let target = project.makeTarget() + pbxProject.targets.append(target) + + let buildFileException = PBXFileSystemSynchronizedBuildFileExceptionSet( + target: target, + membershipExceptions: ["Info.plist"], + publicHeaders: nil, + privateHeaders: nil, + additionalCompilerFlagsByRelativePath: nil, + attributesByRelativePath: nil + ) + project.add(object: buildFileException) + + let syncedGroup = project.makeSynchronizedRootGroup(exceptions: [buildFileException]) + target.fileSystemSynchronizedGroups = [syncedGroup] + + let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) + try referenceGenerator.generateReferences(proj: project) + + XCTAssert(!syncedGroup.reference.temporary, "Synced group reference should not be temporary") + XCTAssert(!buildFileException.reference.temporary, "Build file exception reference should not be temporary") + XCTAssertFalse(buildFileException.reference.value.hasPrefix("TEMP_"), "Build file exception reference should not have TEMP_ prefix") + } + + func test_projectWithFilesystemSynchronizedRootGroupWithBuildPhaseException_convertsReferencesToPermanent() throws { + let project = PBXProj(rootObject: nil, objectVersion: 0, archiveVersion: 0, classes: [:], objects: []) + let pbxProject = project.makeProject() + + let target = project.makeTarget() + pbxProject.targets.append(target) + + let buildPhase = PBXSourcesBuildPhase() + project.add(object: buildPhase) + target.buildPhases.append(buildPhase) + + let buildPhaseException = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet( + buildPhase: buildPhase, + membershipExceptions: ["ExcludedFile.swift"], + attributesByRelativePath: nil + ) + project.add(object: buildPhaseException) + + let syncedGroup = project.makeSynchronizedRootGroup(exceptions: [buildPhaseException]) + target.fileSystemSynchronizedGroups = [syncedGroup] + + let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings()) + try referenceGenerator.generateReferences(proj: project) + + XCTAssert(!syncedGroup.reference.temporary, "Synced group reference should not be temporary") + XCTAssert(!buildPhaseException.reference.temporary, "Build phase exception reference should not be temporary") + XCTAssertFalse(buildPhaseException.reference.value.hasPrefix("TEMP_"), "Build phase exception reference should not have TEMP_ prefix") } } @@ -33,6 +166,8 @@ private extension PBXProj { let project = PBXProject(name: "test", buildConfigurationList: XCConfigurationList.fixture(), compatibilityVersion: Xcode.Default.compatibilityVersion, + preferredProjectObjectVersion: nil, + minimizedProjectReferenceProxies: nil, mainGroup: mainGroup) add(object: mainGroup) @@ -43,7 +178,11 @@ private extension PBXProj { } func makeFileReference() -> PBXFileReference { - return try! rootObject!.mainGroup.addFile(at: Path("../Remote.xcodeproj"), sourceRoot: Path("/"), validatePresence: false) + makeFileReference(with: Path("../Remote.xcodeproj")) + } + + func makeFileReference(with path: Path) -> PBXFileReference { + try! rootObject!.mainGroup.addFile(at: path, sourceRoot: Path("/"), validatePresence: false) } func makeContainerItemProxy(fileReference: PBXFileReference) -> PBXContainerItemProxy { @@ -73,4 +212,52 @@ private extension PBXProj { add(object: productsGroup) return productsGroup } + + func makePluginDependency() -> PBXTargetDependency { + let packageReference = XCRemoteSwiftPackageReference(repositoryURL: "repository") + let packageDependency = XCSwiftPackageProductDependency(productName: "product", package: packageReference, isPlugin: true) + let targetDependency = PBXTargetDependency(product: packageDependency) + add(object: targetDependency.productReference!.getObject()!) + + return targetDependency + } + + func makeTarget(productReferenceProxy: PBXReferenceProxy) -> (target: PBXTarget, buildFile: PBXBuildFile) { + let buildFile = PBXBuildFile(file: productReferenceProxy) + add(object: buildFile) + + let buildPhase = PBXCopyFilesBuildPhase(dstPath: "", + dstSubfolderSpec: .frameworks, + name: "Embed Frameworks", + files: [buildFile]) + add(object: buildPhase) + + let target = PBXNativeTarget(name: "MyApp", + buildPhases: [buildPhase], + productName: "MyApp.app", + productType: .application) + add(object: target) + + return (target, buildFile) + } + + func makeTarget() -> PBXTarget { + let target = PBXNativeTarget(name: "MyApp", + productName: "MyApp.app", + productType: .application) + add(object: target) + return target + } + + func makeSynchronizedRootGroup(exceptions: [PBXFileSystemSynchronizedExceptionSet] = []) -> PBXFileSystemSynchronizedRootGroup { + let syncedGroup = PBXFileSystemSynchronizedRootGroup( + sourceTree: .group, + path: "SyncedPath", + name: "SyncedGroup", + exceptions: exceptions + ) + add(object: syncedGroup) + rootObject!.mainGroup.children.append(syncedGroup) + return syncedGroup + } } diff --git a/Tests/XcodeProjTests/Utils/XCConfigTests.swift b/Tests/XcodeProjTests/Utils/XCConfigTests.swift index 9940cf95a..a23cca1ee 100644 --- a/Tests/XcodeProjTests/Utils/XCConfigTests.swift +++ b/Tests/XcodeProjTests/Utils/XCConfigTests.swift @@ -13,8 +13,8 @@ final class XCConfigTests: XCTestCase { (Path("testA"), configA), (Path("testB"), configB), ], - buildSettings: ["a": "b"]) - XCTAssertEqual(config.buildSettings as! [String: String], ["a": "b"]) + buildSettings: ["a": "b"]) + XCTAssertEqual(config.buildSettings, ["a": "b"]) XCTAssertEqual(config.includes[0].config, configA) XCTAssertEqual(config.includes[1].config, configB) } @@ -26,10 +26,10 @@ final class XCConfigTests: XCTestCase { (Path("testA"), configA), (Path("testB"), configB), ], - buildSettings: ["b": "3"]) + buildSettings: ["b": "3"]) let buildSettings = config.flattenedBuildSettings() - XCTAssertEqual(buildSettings["a"] as? String, "2") - XCTAssertEqual(buildSettings["b"] as? String, "3") + XCTAssertEqual(buildSettings["a"], "2") + XCTAssertEqual(buildSettings["b"], "3") } func test_xcconfig_settingRegex() { @@ -81,34 +81,36 @@ final class XCConfigIntegrationTests: XCTestCase { func test_init_initializesXCConfigWithTheRightProperties() { let subject = try? XCConfig(path: childrenPath()) XCTAssertNotNil(subject) - if let subject = subject { + if let subject { assert(config: subject) } } - func test_write_writesTheContentProperly() { - testWrite(from: childrenPath(), - initModel: { try? XCConfig(path: $0) }, - modify: { $0 }) + func test_write_writesTheContentProperly() throws { + try testWrite(from: childrenPath(), + initModel: { try? XCConfig(path: $0) }, + modify: { $0 }) } private func childrenPath() -> Path { - return fixturesPath() + "XCConfigs/Children.xcconfig" + fixturesPath() + "XCConfigs/Children.xcconfig" } private func parentPath() -> Path { - return fixturesPath() + "XCConfigs/Parent.xcconfig" + fixturesPath() + "XCConfigs/Parent.xcconfig" } private func assert(config: XCConfig) { - XCTAssertEqual(config.buildSettings["CONFIGURATION_BUILD_DIR"] as? String, "Test/") - XCTAssertEqual(config.flattenedBuildSettings()["CONFIGURATION_BUILD_DIR"] as? String, "Test/") - XCTAssertEqual(config.buildSettings["GCC_PREPROCESSOR_DEFINITIONS"] as? String, "$(inherited)") - XCTAssertEqual(config.flattenedBuildSettings()["GCC_PREPROCESSOR_DEFINITIONS"] as? String, "$(inherited)") - XCTAssertEqual(config.buildSettings["WARNING_CFLAGS"] as? String, "-Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded") - XCTAssertEqual(config.flattenedBuildSettings()["WARNING_CFLAGS"] as? String, "-Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded") + XCTAssertEqual(config.buildSettings["CONFIGURATION_BUILD_DIR"], "Test/") + XCTAssertEqual(config.flattenedBuildSettings()["CONFIGURATION_BUILD_DIR"], "Test/") + XCTAssertEqual(config.buildSettings["GCC_PREPROCESSOR_DEFINITIONS"], "$(inherited)") + XCTAssertEqual(config.flattenedBuildSettings()["GCC_PREPROCESSOR_DEFINITIONS"], "$(inherited)") + XCTAssertEqual(config.buildSettings["WARNING_CFLAGS"], "-Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded") + XCTAssertEqual(config.flattenedBuildSettings()["WARNING_CFLAGS"], "-Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded") XCTAssertEqual(config.includes.count, 1) - XCTAssertEqual(config.flattenedBuildSettings()["OTHER_SWIFT_FLAGS_XCODE_0821"] as? String, "$(inherited)") - XCTAssertEqual(config.flattenedBuildSettings()["OTHER_SWIFT_FLAGS_XCODE_0830"] as? String, "$(inherited) -enable-bridging-pch") + XCTAssertEqual(config.flattenedBuildSettings()["OTHER_SWIFT_FLAGS_XCODE_0821"], "$(inherited)") + XCTAssertEqual(config.flattenedBuildSettings()["OTHER_SWIFT_FLAGS_XCODE_0830"], "$(inherited) -enable-bridging-pch") + XCTAssertEqual(config.flattenedBuildSettings()["PRODUCT_NAME"], "$(TARGET_NAME)") + XCTAssertEqual(config.flattenedBuildSettings()["SWIFT_OPTIMIZATION_LEVEL"], "-Onone") } } diff --git a/Tests/XcodeProjTests/Workspace/XCWorkspaceDataElementTests.swift b/Tests/XcodeProjTests/Workspace/XCWorkspaceDataElementTests.swift index f497aa3bc..8c2ecffa9 100644 --- a/Tests/XcodeProjTests/Workspace/XCWorkspaceDataElementTests.swift +++ b/Tests/XcodeProjTests/Workspace/XCWorkspaceDataElementTests.swift @@ -21,4 +21,38 @@ final class XCWorkspaceDataElementTests: XCTestCase { XCTAssertEqual(element.location, location) } + + func test_equatable_when_unequal_data_elements() { + // Given + let location: XCWorkspaceDataElementLocationType = .absolute("/path/to/file.swift") + let file = XCWorkspaceDataFileRef(location: location) + let element = XCWorkspaceDataElement.file(file) + + // When + let firstWorkspace = XCWorkspace(data: .init(children: [element])) + let secondWorkspace = XCWorkspace(data: .init(children: [])) + + // Then + XCTAssertNotEqual(firstWorkspace, secondWorkspace) + } + + func test_equatable_when_equal_data_elements() { + // Given + let groupLocation: XCWorkspaceDataElementLocationType = .absolute("/path/to/group") + let group = XCWorkspaceDataGroup(location: groupLocation, + name: "group", + children: []) + let elementOne = XCWorkspaceDataElement.group(group) + + let fileLocation: XCWorkspaceDataElementLocationType = .absolute("/path/to/file.swift") + let file = XCWorkspaceDataFileRef(location: fileLocation) + let elementTwo = XCWorkspaceDataElement.file(file) + + // When + let firstWorkspace = XCWorkspace(data: .init(children: [elementOne, elementTwo])) + let secondWorkspace = XCWorkspace(data: .init(children: [elementOne, elementTwo])) + + // Then + XCTAssertEqual(firstWorkspace, secondWorkspace) + } } diff --git a/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift b/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift index 68aa7c5e6..d3076ca0b 100644 --- a/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift +++ b/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift @@ -10,7 +10,7 @@ final class XCWorkspaceDataTests: XCTestCase { override func setUp() { super.setUp() fileRef = XCWorkspaceDataFileRef( - location: .self("path") + location: .current("path") ) subject = XCWorkspaceData(children: []) } @@ -26,7 +26,7 @@ final class XCWorkspaceDataIntegrationTests: XCTestCase { let path = fixturePath() let got = try XCWorkspaceData(path: path) if case let XCWorkspaceDataElement.file(location: fileRef) = got.children.first! { - XCTAssertEqual(fileRef.location, .self("")) + XCTAssertEqual(fileRef.location, .current("")) } else { XCTAssertTrue(false, "Expected file reference") } @@ -39,13 +39,13 @@ final class XCWorkspaceDataIntegrationTests: XCTestCase { } catch {} } - func test_write() { - testWrite( + func test_write() throws { + try testWrite( from: fixturePath(), initModel: { try? XCWorkspaceData(path: $0) }, modify: { $0.children.append( - .group(.init(location: .self("shakira"), + .group(.init(location: .current("shakira"), name: "shakira", children: [])) ) @@ -112,7 +112,7 @@ final class XCWorkspaceDataIntegrationTests: XCTestCase { // MARK: - Private private func fixturePath() -> Path { - return fixturesPath() + "iOS/Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata" + fixturesPath() + "iOS/Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata" } private func fixtureWorkspace() throws -> XCWorkspace { diff --git a/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift b/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift index 5827d5469..a4820b1c5 100644 --- a/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift +++ b/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift @@ -19,6 +19,41 @@ final class XCWorkspaceIntegrationTests: XCTestCase { func test_init_returnsAWorkspaceWithTheCorrectReference() { XCTAssertEqual(XCWorkspace().data.children.count, 1) - XCTAssertEqual(XCWorkspace().data.children.first, .file(.init(location: .self("")))) + XCTAssertEqual(XCWorkspace().data.children.first, .file(.init(location: .current("")))) + } + + func test_equatable_emptyWorkspacesAreEqual() { + // When + let firstWorkspace = XCWorkspace(data: .init(children: [])) + let secondWorkspace = XCWorkspace(data: .init(children: [])) + + // Then + XCTAssertEqual(firstWorkspace, secondWorkspace) + } + + func test_equatable_unEqualWorkspacesAreNotEqual() { + // Given + let pathOne = fixturesPath() + "iOS/Project.xcodeproj/project.xcworkspace" + let pathTwo = fixturesPath() + "iOS/Workspace.xcworkspace" + + // When + let firstWorkspace = try? XCWorkspace(path: pathOne) + let secondWorkspace = try? XCWorkspace(path: pathTwo) + + // Then + XCTAssertNotEqual(firstWorkspace, secondWorkspace) + } + + func test_equatable_equalWorkspacesAreEqual() { + // Given + let pathOne = fixturesPath() + "iOS/Workspace.xcworkspace" + let pathTwo = fixturesPath() + "iOS/Workspace.xcworkspace" + + // When + let firstWorkspace = try? XCWorkspace(path: pathOne) + let secondWorkspace = try? XCWorkspace(path: pathTwo) + + // Then + XCTAssertEqual(firstWorkspace, secondWorkspace) } } diff --git a/Tuist.swift b/Tuist.swift new file mode 100644 index 000000000..b6140696a --- /dev/null +++ b/Tuist.swift @@ -0,0 +1,3 @@ +import ProjectDescription + +let tuist = Tuist(generationOptions: .options()) diff --git a/TuistConfig.swift b/TuistConfig.swift deleted file mode 100644 index 78af64add..000000000 --- a/TuistConfig.swift +++ /dev/null @@ -1,6 +0,0 @@ -import ProjectDescription - -let config = TuistConfig(generationOptions: [ - // If we generate the manifest target Carthage will attempt to compile it too. - // .generateManifest -]) diff --git a/XcodeProj_Carthage.xcodeproj/project.pbxproj b/XcodeProj_Carthage.xcodeproj/project.pbxproj deleted file mode 100644 index 1f93c3b08..000000000 --- a/XcodeProj_Carthage.xcodeproj/project.pbxproj +++ /dev/null @@ -1,894 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 52; - objects = { - -/* Begin PBXBuildFile section */ - 0390AF375B465D1DC7AB6194 /* XCScheme+TestableReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE91C6982618C1EDDAAD596 /* XCScheme+TestableReference.swift */; }; - 03FCD0C75E642B6E50DE9095 /* PBXCopyFilesBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C42E5F8AAD8E3C04E8A9B5 /* PBXCopyFilesBuildPhase.swift */; }; - 062694C004A53F24E7A92B85 /* PBXProductType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A15BA73673DDD200166C9EC /* PBXProductType.swift */; }; - 0B364FCF8F7A557C252929EA /* XCConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B928006AD1C96D4D8072BD9 /* XCConfig.swift */; }; - 0C6F7F2EF3EE9D97F081D9FD /* PBXAggregateTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11172C9DBADEDF3203564B7 /* PBXAggregateTarget.swift */; }; - 1009BFBC8BD95556803FF2C9 /* Dictionary+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D03946AD764A02121872F7 /* Dictionary+Extras.swift */; }; - 13CBB83040E0B373CE80CB16 /* PBXBuildFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13177CF1296C6C32BF4C5B7C /* PBXBuildFile.swift */; }; - 147157016720CE9BC5943AF2 /* XCScheme+LaunchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE77276DEE6859DE961F94D /* XCScheme+LaunchAction.swift */; }; - 1ADDD97C53AEC0990F693CDA /* AEXML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C950F23FEF894B133070990 /* AEXML.framework */; }; - 1C99B6399AB8732EA97B1EEC /* XCSwiftPackageProductDependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9937B44A9FCDD847F79008 /* XCSwiftPackageProductDependency.swift */; }; - 209B0AC5FFCE800A35624C49 /* XCScheme+RemoteRunnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BFCA27253AECAC6A2EF5E66 /* XCScheme+RemoteRunnable.swift */; }; - 23A0E815358EB2D937F40513 /* PBXBatchUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B779835760A818E59969D1 /* PBXBatchUpdater.swift */; }; - 2B2D208723F7AE79632BAEAE /* PBXFileElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AA26E4818BC76E4CB28CEB /* PBXFileElement.swift */; }; - 2BC374D574269FB75F978D6F /* PBXProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93C80F128CBCDFA4EA63EFB /* PBXProject.swift */; }; - 3150041D02026FB07EA43E57 /* Shell.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398A0E3094DB5B51C1BEA1FE /* Shell.framework */; }; - 33C049021FCA541192F40AD1 /* XCWorkspaceDataFileRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92DC66EE80F3D7611319ACC /* XCWorkspaceDataFileRef.swift */; }; - 364C132E6ABF980BF9E84649 /* XCScheme+ProfileAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E17A977543393ED7B848A9 /* XCScheme+ProfileAction.swift */; }; - 3A59F800668B0D6F550B9C09 /* PBXGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249AAF8AE1978D1546C0ADB5 /* PBXGroup.swift */; }; - 3D5DBC9A4315D97D1B39CF19 /* PBXProjEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A031713D5F31DDCC2D61EF /* PBXProjEncoder.swift */; }; - 3E2EE77196F6B2BECE8DF3DB /* BuildSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1977C44246CCF9C13AB856C2 /* BuildSettingsProvider.swift */; }; - 46A5D9862729AC0C6D85D4FB /* XCScheme+BuildAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E525B9A065757F329F0E4002 /* XCScheme+BuildAction.swift */; }; - 4716E044E5786AC68205911A /* XCVersionGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39E419CA05304074ADCDACE8 /* XCVersionGroup.swift */; }; - 494EF8FD09FAEE56F6ACF3BE /* XCWorkspaceDataElementLocationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE25C88F5874509586B3AD4 /* XCWorkspaceDataElementLocationType.swift */; }; - 4C5079131989ADE80FF17EFF /* Bool+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = E23B1EFAE9DC6596E4EA4BCD /* Bool+Extras.swift */; }; - 4E9B74BA2A6381FEDEA26092 /* CommentedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683F0C05F5FD6006924AA43C /* CommentedString.swift */; }; - 4F427C99A47227F5592A1522 /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A113CBABFAE791D1B9C6BBE /* BuildSettings.swift */; }; - 53CE6B2F74AB5FAA69D58923 /* XCScheme+SerialAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694034B4B7C9DF231B007A84 /* XCScheme+SerialAction.swift */; }; - 586BB941B8D71312F85033A1 /* PBXContainerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA2A4EEFB9DE8F475B051FDE /* PBXContainerItem.swift */; }; - 5A9C450597EC43859570B863 /* PathKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F89DAD13D5505D59F10D0C9F /* PathKit.framework */; }; - 61FFC23B7055683822148DDE /* PBXContainerItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2370B4E19828CEFC032511A1 /* PBXContainerItemProxy.swift */; }; - 625CFEF76B6D5B02D3A1CEC6 /* Equality.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = E639EC9D100AFD2CEAEE82E0 /* Equality.generated.swift */; }; - 64DB0043086F77457C7B223D /* String+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DAB211D02099ECDC268AE2 /* String+Utils.swift */; }; - 65476234B5AA54D4EB33FEA7 /* PBXTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F121F70B66AA9787F99BC3 /* PBXTarget.swift */; }; - 6619158CCA6E2D05A39860B8 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325DDB80858F8DD8C5C27A07 /* Errors.swift */; }; - 69CBD38E1B96198049A02795 /* XCScheme+LocationScenarioReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B8CB253365692579B8C8E2 /* XCScheme+LocationScenarioReference.swift */; }; - 6AA90F1144C42F9275124B0F /* PBXFrameworksBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BEB2811DE4A8D3EE6DC46B7 /* PBXFrameworksBuildPhase.swift */; }; - 6BCC38DC97A295B5179C0565 /* PBXRezBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 577B27811EEAD46794E8DE90 /* PBXRezBuildPhase.swift */; }; - 730CEE3BF13DFC6608790F33 /* XCWorkspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15DE1D6B3000410D531F05AA /* XCWorkspace.swift */; }; - 735E1C6466FF56969767BAA0 /* XCScheme+CommandLineArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B837566EDF23D002AC881D07 /* XCScheme+CommandLineArguments.swift */; }; - 73E2CEC84B1EE4E25AF9030C /* PBXShellScriptBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC12E1A11D67971AEC27AA12 /* PBXShellScriptBuildPhase.swift */; }; - 74A56CC842A9E89D622A49A0 /* PBXProj.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B2C739F39DD3CC1B77E1075 /* PBXProj.swift */; }; - 7823F76B43696121C7401C35 /* PBXBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E927F019715C98BA849930 /* PBXBuildPhase.swift */; }; - 792583786825191D2E186E9D /* XCRemoteSwiftPackageReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CBC0FF736F69D538FFEC78 /* XCRemoteSwiftPackageReference.swift */; }; - 7C3555C09B170AEA2D56E8F2 /* XCScheme+EnvironmentVariable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B1225FED6E1D49EE385AA0 /* XCScheme+EnvironmentVariable.swift */; }; - 7CDAEBA81467E39CBAD2E6CF /* PBXSourcesBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC365B2E771B6E2690D751DB /* PBXSourcesBuildPhase.swift */; }; - 7DF42666456E27C51CC672C1 /* Array+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6023B10EB151C2FF48E53395 /* Array+Extras.swift */; }; - 7F9ED031DE6A06055F44C2E2 /* JSONDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9144C9BC5E10BE834E6824A1 /* JSONDecoding.swift */; }; - 809908EF4E6E71285D110707 /* PBXNativeTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1A6E5AC6502EDE59541C8 /* PBXNativeTarget.swift */; }; - 8242B39EB786ADA4A8B32EF3 /* XCBuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07BF1A8B705E35271076BA1 /* XCBuildConfiguration.swift */; }; - 84C672F79741BBA0937580EA /* Sourcery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A33AF969BDFF0250420A5E8 /* Sourcery.swift */; }; - 8EE72814658F0A23A01AD1E6 /* XCBreakpointList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0BF3D061052148207584A5 /* XCBreakpointList.swift */; }; - 903A9EC1B895A4920A322898 /* XCScheme+ArchiveAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F7E3085922F586144F562A /* XCScheme+ArchiveAction.swift */; }; - 9D897705DD334A50C4431887 /* PBXBuildRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50CD65FDAA9B3F4269F4AAF /* PBXBuildRule.swift */; }; - 9DBF777FEB60396E520D2595 /* KeyedDecodingContainer+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABCF05523B52C542BC48C31 /* KeyedDecodingContainer+Additions.swift */; }; - 9F27802B144AFC7C1A739492 /* PBXObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CC513CEDABAA9712FB4DC6 /* PBXObjects.swift */; }; - A1476A0B7C8DA244920775F1 /* Dictionary+Enumerate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 568D1BA84A39807930EA26CF /* Dictionary+Enumerate.swift */; }; - A2344E4A960C21715FAD3F86 /* PBXObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E93E589F09F66B673712EAE /* PBXObject.swift */; }; - AB74DA38A17EE261024A711C /* XCScheme+TestAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 161E967D0979EF34C7DF6348 /* XCScheme+TestAction.swift */; }; - ABE0F9CFD5549F302EB3A417 /* PBXSourceTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A0180EB1FDA8CC7D553950 /* PBXSourceTree.swift */; }; - AC49ED6F358FFBF504A3D0AE /* XCScheme+ExecutionAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3FE679A53603AE3FE43462F /* XCScheme+ExecutionAction.swift */; }; - ACC38711B1FD79AED6D63C9D /* PBXHeadersBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A909937001696EF804CE15C /* PBXHeadersBuildPhase.swift */; }; - AECDFCA5C2DD558AAC48E945 /* NSRecursiveLock+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C96888CEF5888DBA6D3136D /* NSRecursiveLock+Sync.swift */; }; - AEF9DAF6D599FAD8B64827C6 /* String+md5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E5583E488968962DD4B38D /* String+md5.swift */; }; - B07C3C11C0C5E9123C5D9D15 /* PBXObjectParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE4FB9142BF91F6E449998C /* PBXObjectParser.swift */; }; - B7703E87F0E986D0BECC6C92 /* PBXResourcesBuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8485AE4255BB935C7B7FE93C /* PBXResourcesBuildPhase.swift */; }; - B972348A969282D604A5C325 /* XCScheme+Runnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5C2732048660AC71431D9C /* XCScheme+Runnable.swift */; }; - BBA09A7C8D3D9697C10F300B /* PBXLegacyTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C46C4D934731EA4A844FD3 /* PBXLegacyTarget.swift */; }; - BD65503EF698EE4EAF11892C /* Path+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE041A9A60D18302045BA2AE /* Path+Extras.swift */; }; - BD7A3EBFD6C9D1AA78E6E822 /* Decoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F663AC6487521F684ED182 /* Decoders.swift */; }; - BEBC1583A85423F1F1BF3E30 /* XCSharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF73D1E4C0B20C16D61865F6 /* XCSharedData.swift */; }; - BEE9FCB78D7D7F7A0DBF6F73 /* PBXReferenceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095A027B0DAEF11404E9612F /* PBXReferenceProxy.swift */; }; - C2DB3BAC484E34097E80471E /* XCConfigurationList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412215420E6CF8CD85C2908C /* XCConfigurationList.swift */; }; - C333BEA47F06D2357DE8B1EA /* Writable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A4A785AD26E7657312118B9 /* Writable.swift */; }; - C907FC5616048E42925FB93E /* XCWorkspaceDataGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7419D9A4F0B11214441DFA3 /* XCWorkspaceDataGroup.swift */; }; - C95FAEE0675588942633875E /* XCScheme+BuildableReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF277186917C67A6FF4993F6 /* XCScheme+BuildableReference.swift */; }; - CE58E51C93B8A19FA4F79E22 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7DAFF5CC89FBB59D02F72B9 /* Xcode.swift */; }; - D07F87005EA408048A765039 /* XCScheme+BuildableProductRunnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39AC139F340CD22E72E37FF3 /* XCScheme+BuildableProductRunnable.swift */; }; - D32D0514F1A4256071EDB1A1 /* XCScheme+AditionalOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87054AE28F2715C09767FE77 /* XCScheme+AditionalOption.swift */; }; - D4CDCEFF10BE6E80B9057132 /* PBXOutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1325A961D9CCFB45B639868 /* PBXOutputSettings.swift */; }; - D5D82B59980521FBA427EED8 /* XCScheme+TestPlanReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87331972540CB510D7E0C333 /* XCScheme+TestPlanReference.swift */; }; - D5DB78ED46F818640BC41A9D /* PBXTargetDependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE15F98D3349F2FCED4A913A /* PBXTargetDependency.swift */; }; - D783F0B2AC4CA6F10A0FC5B9 /* XCScheme+SkippedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522A9ABE50677EEB0638B056 /* XCScheme+SkippedTests.swift */; }; - DA0ACE91B9EEA6D3BD7170F3 /* PBXVariantGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F7AC32AFFBEA9741906F2B /* PBXVariantGroup.swift */; }; - DE1F130B4C81A1ABE9DB6DC0 /* ReferenceGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9EA2BB676DB8C978248670 /* ReferenceGenerator.swift */; }; - DF37E12A489F0FBFDCF4C7A1 /* XCWorkspaceDataElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747281C8826015AE5CE869D8 /* XCWorkspaceDataElement.swift */; }; - DF91D1F6BECD6145A4BEEE7B /* PBXObjectReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06554AB0BF2937025BC45C6 /* PBXObjectReference.swift */; }; - E113D0F651A2213459C65387 /* XCScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D13E332186D46FFA83BBD50 /* XCScheme.swift */; }; - E31DA2A4BF344AC0BB4B1EC8 /* BuildPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72E6494F0E28769F9B11FD30 /* BuildPhase.swift */; }; - E82A4CE0E0E385A2DED27887 /* XcodeProj.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B667CA83B0BB0285F470F1 /* XcodeProj.swift */; }; - E863D2A025082CB472079D90 /* PBXFileReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A72367B782B23A3759F3A1D /* PBXFileReference.swift */; }; - EC0897B21DBC4594857BAD07 /* XCWorkspaceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59F2F50C66ADD5D0C12F3B70 /* XCWorkspaceData.swift */; }; - ED4BFA372B68053D67F0E379 /* WorkspaceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3353E37D0FBA49BA04C82476 /* WorkspaceSettings.swift */; }; - F48082472D45B18B36F00F09 /* AEXML+XcodeFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB3D1A6857702BDE0C036055 /* AEXML+XcodeFormat.swift */; }; - F6AF8B5725B94DC6136239DF /* PlistValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0549EED6A8FF85CA7FEBC5 /* PlistValue.swift */; }; - FEBD1114F05F3A9667F26CCC /* XCScheme+AnalyzeAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFE462CBB08512CB108E356 /* XCScheme+AnalyzeAction.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 7FAD067EE084B2EF5EFE4BFF /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08B779835760A818E59969D1 /* PBXBatchUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXBatchUpdater.swift; sourceTree = ""; }; - 095A027B0DAEF11404E9612F /* PBXReferenceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXReferenceProxy.swift; sourceTree = ""; }; - 0A909937001696EF804CE15C /* PBXHeadersBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXHeadersBuildPhase.swift; sourceTree = ""; }; - 0AE25C88F5874509586B3AD4 /* XCWorkspaceDataElementLocationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspaceDataElementLocationType.swift; sourceTree = ""; }; - 0AE91C6982618C1EDDAAD596 /* XCScheme+TestableReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+TestableReference.swift"; sourceTree = ""; }; - 0BFCA27253AECAC6A2EF5E66 /* XCScheme+RemoteRunnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+RemoteRunnable.swift"; sourceTree = ""; }; - 0C950F23FEF894B133070990 /* AEXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AEXML.framework; sourceTree = ""; }; - 13177CF1296C6C32BF4C5B7C /* PBXBuildFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXBuildFile.swift; sourceTree = ""; }; - 15DE1D6B3000410D531F05AA /* XCWorkspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspace.swift; sourceTree = ""; }; - 161E967D0979EF34C7DF6348 /* XCScheme+TestAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+TestAction.swift"; sourceTree = ""; }; - 1977C44246CCF9C13AB856C2 /* BuildSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildSettingsProvider.swift; sourceTree = ""; }; - 19C42E5F8AAD8E3C04E8A9B5 /* PBXCopyFilesBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXCopyFilesBuildPhase.swift; sourceTree = ""; }; - 1ABCF05523B52C542BC48C31 /* KeyedDecodingContainer+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Additions.swift"; sourceTree = ""; }; - 1D5C2732048660AC71431D9C /* XCScheme+Runnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+Runnable.swift"; sourceTree = ""; }; - 20B1225FED6E1D49EE385AA0 /* XCScheme+EnvironmentVariable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+EnvironmentVariable.swift"; sourceTree = ""; }; - 2370B4E19828CEFC032511A1 /* PBXContainerItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXContainerItemProxy.swift; sourceTree = ""; }; - 249AAF8AE1978D1546C0ADB5 /* PBXGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXGroup.swift; sourceTree = ""; }; - 2B928006AD1C96D4D8072BD9 /* XCConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCConfig.swift; sourceTree = ""; }; - 2BD1A6E5AC6502EDE59541C8 /* PBXNativeTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXNativeTarget.swift; sourceTree = ""; }; - 2D9937B44A9FCDD847F79008 /* XCSwiftPackageProductDependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCSwiftPackageProductDependency.swift; sourceTree = ""; }; - 325DDB80858F8DD8C5C27A07 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; - 32F121F70B66AA9787F99BC3 /* PBXTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXTarget.swift; sourceTree = ""; }; - 3353E37D0FBA49BA04C82476 /* WorkspaceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSettings.swift; sourceTree = ""; }; - 34A0180EB1FDA8CC7D553950 /* PBXSourceTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXSourceTree.swift; sourceTree = ""; }; - 398A0E3094DB5B51C1BEA1FE /* Shell.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Shell.framework; sourceTree = ""; }; - 39AC139F340CD22E72E37FF3 /* XCScheme+BuildableProductRunnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+BuildableProductRunnable.swift"; sourceTree = ""; }; - 39B8CB253365692579B8C8E2 /* XCScheme+LocationScenarioReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+LocationScenarioReference.swift"; sourceTree = ""; }; - 39E419CA05304074ADCDACE8 /* XCVersionGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCVersionGroup.swift; sourceTree = ""; }; - 3A15BA73673DDD200166C9EC /* PBXProductType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXProductType.swift; sourceTree = ""; }; - 3A33AF969BDFF0250420A5E8 /* Sourcery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sourcery.swift; sourceTree = ""; }; - 3BEB2811DE4A8D3EE6DC46B7 /* PBXFrameworksBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXFrameworksBuildPhase.swift; sourceTree = ""; }; - 3C96888CEF5888DBA6D3136D /* NSRecursiveLock+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRecursiveLock+Sync.swift"; sourceTree = ""; }; - 412215420E6CF8CD85C2908C /* XCConfigurationList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCConfigurationList.swift; sourceTree = ""; }; - 41A031713D5F31DDCC2D61EF /* PBXProjEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXProjEncoder.swift; sourceTree = ""; }; - 47E5583E488968962DD4B38D /* String+md5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+md5.swift"; sourceTree = ""; }; - 48D03946AD764A02121872F7 /* Dictionary+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extras.swift"; sourceTree = ""; }; - 4E93E589F09F66B673712EAE /* PBXObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXObject.swift; sourceTree = ""; }; - 4EE77276DEE6859DE961F94D /* XCScheme+LaunchAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+LaunchAction.swift"; sourceTree = ""; }; - 51CBC0FF736F69D538FFEC78 /* XCRemoteSwiftPackageReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCRemoteSwiftPackageReference.swift; sourceTree = ""; }; - 522A9ABE50677EEB0638B056 /* XCScheme+SkippedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+SkippedTests.swift"; sourceTree = ""; }; - 568D1BA84A39807930EA26CF /* Dictionary+Enumerate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Enumerate.swift"; sourceTree = ""; }; - 577B27811EEAD46794E8DE90 /* PBXRezBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXRezBuildPhase.swift; sourceTree = ""; }; - 59F2F50C66ADD5D0C12F3B70 /* XCWorkspaceData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspaceData.swift; sourceTree = ""; }; - 5A4A785AD26E7657312118B9 /* Writable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Writable.swift; sourceTree = ""; }; - 5D13E332186D46FFA83BBD50 /* XCScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCScheme.swift; sourceTree = ""; }; - 6023B10EB151C2FF48E53395 /* Array+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extras.swift"; sourceTree = ""; }; - 60E927F019715C98BA849930 /* PBXBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXBuildPhase.swift; sourceTree = ""; }; - 66F7E3085922F586144F562A /* XCScheme+ArchiveAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+ArchiveAction.swift"; sourceTree = ""; }; - 683F0C05F5FD6006924AA43C /* CommentedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentedString.swift; sourceTree = ""; }; - 694034B4B7C9DF231B007A84 /* XCScheme+SerialAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+SerialAction.swift"; sourceTree = ""; }; - 6A113CBABFAE791D1B9C6BBE /* BuildSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildSettings.swift; sourceTree = ""; }; - 6B2C739F39DD3CC1B77E1075 /* PBXProj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXProj.swift; sourceTree = ""; }; - 72E6494F0E28769F9B11FD30 /* BuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildPhase.swift; sourceTree = ""; }; - 747281C8826015AE5CE869D8 /* XCWorkspaceDataElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspaceDataElement.swift; sourceTree = ""; }; - 76F663AC6487521F684ED182 /* Decoders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Decoders.swift; sourceTree = ""; }; - 7A72367B782B23A3759F3A1D /* PBXFileReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXFileReference.swift; sourceTree = ""; }; - 8485AE4255BB935C7B7FE93C /* PBXResourcesBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXResourcesBuildPhase.swift; sourceTree = ""; }; - 87054AE28F2715C09767FE77 /* XCScheme+AditionalOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+AditionalOption.swift"; sourceTree = ""; }; - 87331972540CB510D7E0C333 /* XCScheme+TestPlanReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+TestPlanReference.swift"; sourceTree = ""; }; - 88DAB211D02099ECDC268AE2 /* String+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utils.swift"; sourceTree = ""; }; - 9144C9BC5E10BE834E6824A1 /* JSONDecoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDecoding.swift; sourceTree = ""; }; - A11172C9DBADEDF3203564B7 /* PBXAggregateTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXAggregateTarget.swift; sourceTree = ""; }; - A5CC513CEDABAA9712FB4DC6 /* PBXObjects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXObjects.swift; sourceTree = ""; }; - A7DAFF5CC89FBB59D02F72B9 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = ""; }; - A93C80F128CBCDFA4EA63EFB /* PBXProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXProject.swift; sourceTree = ""; }; - A9C46C4D934731EA4A844FD3 /* PBXLegacyTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXLegacyTarget.swift; sourceTree = ""; }; - AA2A4EEFB9DE8F475B051FDE /* PBXContainerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXContainerItem.swift; sourceTree = ""; }; - AAE4FB9142BF91F6E449998C /* PBXObjectParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXObjectParser.swift; sourceTree = ""; }; - AB3D1A6857702BDE0C036055 /* AEXML+XcodeFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AEXML+XcodeFormat.swift"; sourceTree = ""; }; - AF73D1E4C0B20C16D61865F6 /* XCSharedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCSharedData.swift; sourceTree = ""; }; - B2E17A977543393ED7B848A9 /* XCScheme+ProfileAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+ProfileAction.swift"; sourceTree = ""; }; - B50CD65FDAA9B3F4269F4AAF /* PBXBuildRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXBuildRule.swift; sourceTree = ""; }; - B837566EDF23D002AC881D07 /* XCScheme+CommandLineArguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+CommandLineArguments.swift"; sourceTree = ""; }; - B92DC66EE80F3D7611319ACC /* XCWorkspaceDataFileRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspaceDataFileRef.swift; sourceTree = ""; }; - BFFE462CBB08512CB108E356 /* XCScheme+AnalyzeAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+AnalyzeAction.swift"; sourceTree = ""; }; - C3FE679A53603AE3FE43462F /* XCScheme+ExecutionAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+ExecutionAction.swift"; sourceTree = ""; }; - C6F7AC32AFFBEA9741906F2B /* PBXVariantGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXVariantGroup.swift; sourceTree = ""; }; - CB0549EED6A8FF85CA7FEBC5 /* PlistValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlistValue.swift; sourceTree = ""; }; - CC0BF3D061052148207584A5 /* XCBreakpointList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBreakpointList.swift; sourceTree = ""; }; - CC365B2E771B6E2690D751DB /* PBXSourcesBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXSourcesBuildPhase.swift; sourceTree = ""; }; - D07BF1A8B705E35271076BA1 /* XCBuildConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBuildConfiguration.swift; sourceTree = ""; }; - D2D78F3ED94EB9E973644780 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D4AA26E4818BC76E4CB28CEB /* PBXFileElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXFileElement.swift; sourceTree = ""; }; - DE15F98D3349F2FCED4A913A /* PBXTargetDependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXTargetDependency.swift; sourceTree = ""; }; - DF277186917C67A6FF4993F6 /* XCScheme+BuildableReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+BuildableReference.swift"; sourceTree = ""; }; - E06554AB0BF2937025BC45C6 /* PBXObjectReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXObjectReference.swift; sourceTree = ""; }; - E1325A961D9CCFB45B639868 /* PBXOutputSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXOutputSettings.swift; sourceTree = ""; }; - E23B1EFAE9DC6596E4EA4BCD /* Bool+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bool+Extras.swift"; sourceTree = ""; }; - E525B9A065757F329F0E4002 /* XCScheme+BuildAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+BuildAction.swift"; sourceTree = ""; }; - E639EC9D100AFD2CEAEE82E0 /* Equality.generated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Equality.generated.swift; sourceTree = ""; }; - E63AB43AF9F4C4F938680EA9 /* XcodeProj.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = XcodeProj.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EE041A9A60D18302045BA2AE /* Path+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Path+Extras.swift"; sourceTree = ""; }; - F7419D9A4F0B11214441DFA3 /* XCWorkspaceDataGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCWorkspaceDataGroup.swift; sourceTree = ""; }; - F89DAD13D5505D59F10D0C9F /* PathKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = PathKit.framework; sourceTree = ""; }; - F8B667CA83B0BB0285F470F1 /* XcodeProj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeProj.swift; sourceTree = ""; }; - FC12E1A11D67971AEC27AA12 /* PBXShellScriptBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXShellScriptBuildPhase.swift; sourceTree = ""; }; - FF9EA2BB676DB8C978248670 /* ReferenceGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferenceGenerator.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - ECC1E0A3315EECC3AF534E1B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 1ADDD97C53AEC0990F693CDA /* AEXML.framework in Frameworks */, - 5A9C450597EC43859570B863 /* PathKit.framework in Frameworks */, - 3150041D02026FB07EA43E57 /* Shell.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0CD2DD5DADB19C09C88643D9 /* XcodeProj */ = { - isa = PBXGroup; - children = ( - 3F46E1251C6FA94B4422C839 /* Errors */, - 14D604A8346D8605E2CCE6AF /* Extensions */, - D282D20607C553BCACED0FF1 /* Objects */, - 9BE2FF13BAF4C1BEA7C47C73 /* Project */, - 0D370649A1842C31CDEAE9A6 /* Protocols */, - 3DB2726F254A120563894AF8 /* Scheme */, - 0EB3E92970501B73F90EACC6 /* Utils */, - 5481114562D03AFF284A4D4B /* Workspace */, - ); - path = XcodeProj; - sourceTree = ""; - }; - 0D370649A1842C31CDEAE9A6 /* Protocols */ = { - isa = PBXGroup; - children = ( - 5A4A785AD26E7657312118B9 /* Writable.swift */, - ); - path = Protocols; - sourceTree = ""; - }; - 0E4511E9D37FE834D40A107C /* SwiftPackage */ = { - isa = PBXGroup; - children = ( - 51CBC0FF736F69D538FFEC78 /* XCRemoteSwiftPackageReference.swift */, - 2D9937B44A9FCDD847F79008 /* XCSwiftPackageProductDependency.swift */, - ); - path = SwiftPackage; - sourceTree = ""; - }; - 0EB3E92970501B73F90EACC6 /* Utils */ = { - isa = PBXGroup; - children = ( - 1977C44246CCF9C13AB856C2 /* BuildSettingsProvider.swift */, - 683F0C05F5FD6006924AA43C /* CommentedString.swift */, - 76F663AC6487521F684ED182 /* Decoders.swift */, - 9144C9BC5E10BE834E6824A1 /* JSONDecoding.swift */, - 08B779835760A818E59969D1 /* PBXBatchUpdater.swift */, - CB0549EED6A8FF85CA7FEBC5 /* PlistValue.swift */, - FF9EA2BB676DB8C978248670 /* ReferenceGenerator.swift */, - 2B928006AD1C96D4D8072BD9 /* XCConfig.swift */, - ); - path = Utils; - sourceTree = ""; - }; - 14D604A8346D8605E2CCE6AF /* Extensions */ = { - isa = PBXGroup; - children = ( - AB3D1A6857702BDE0C036055 /* AEXML+XcodeFormat.swift */, - 6023B10EB151C2FF48E53395 /* Array+Extras.swift */, - E23B1EFAE9DC6596E4EA4BCD /* Bool+Extras.swift */, - 568D1BA84A39807930EA26CF /* Dictionary+Enumerate.swift */, - 48D03946AD764A02121872F7 /* Dictionary+Extras.swift */, - 1ABCF05523B52C542BC48C31 /* KeyedDecodingContainer+Additions.swift */, - 3C96888CEF5888DBA6D3136D /* NSRecursiveLock+Sync.swift */, - EE041A9A60D18302045BA2AE /* Path+Extras.swift */, - 88DAB211D02099ECDC268AE2 /* String+Utils.swift */, - 47E5583E488968962DD4B38D /* String+md5.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 1E404A60E69E892516E32929 /* Targets */ = { - isa = PBXGroup; - children = ( - A11172C9DBADEDF3203564B7 /* PBXAggregateTarget.swift */, - A9C46C4D934731EA4A844FD3 /* PBXLegacyTarget.swift */, - 2BD1A6E5AC6502EDE59541C8 /* PBXNativeTarget.swift */, - 3A15BA73673DDD200166C9EC /* PBXProductType.swift */, - 095A027B0DAEF11404E9612F /* PBXReferenceProxy.swift */, - 32F121F70B66AA9787F99BC3 /* PBXTarget.swift */, - DE15F98D3349F2FCED4A913A /* PBXTargetDependency.swift */, - ); - path = Targets; - sourceTree = ""; - }; - 25C374C70C6324F44964CA1A /* Products */ = { - isa = PBXGroup; - children = ( - E63AB43AF9F4C4F938680EA9 /* XcodeProj.framework */, - ); - name = Products; - sourceTree = ""; - }; - 3A8CD2E0DBF7926CA8099221 /* Mac */ = { - isa = PBXGroup; - children = ( - 0C950F23FEF894B133070990 /* AEXML.framework */, - F89DAD13D5505D59F10D0C9F /* PathKit.framework */, - 398A0E3094DB5B51C1BEA1FE /* Shell.framework */, - ); - path = Mac; - sourceTree = ""; - }; - 3DB2726F254A120563894AF8 /* Scheme */ = { - isa = PBXGroup; - children = ( - 87054AE28F2715C09767FE77 /* XCScheme+AditionalOption.swift */, - BFFE462CBB08512CB108E356 /* XCScheme+AnalyzeAction.swift */, - 66F7E3085922F586144F562A /* XCScheme+ArchiveAction.swift */, - E525B9A065757F329F0E4002 /* XCScheme+BuildAction.swift */, - 39AC139F340CD22E72E37FF3 /* XCScheme+BuildableProductRunnable.swift */, - DF277186917C67A6FF4993F6 /* XCScheme+BuildableReference.swift */, - B837566EDF23D002AC881D07 /* XCScheme+CommandLineArguments.swift */, - 20B1225FED6E1D49EE385AA0 /* XCScheme+EnvironmentVariable.swift */, - C3FE679A53603AE3FE43462F /* XCScheme+ExecutionAction.swift */, - 4EE77276DEE6859DE961F94D /* XCScheme+LaunchAction.swift */, - 39B8CB253365692579B8C8E2 /* XCScheme+LocationScenarioReference.swift */, - B2E17A977543393ED7B848A9 /* XCScheme+ProfileAction.swift */, - 0BFCA27253AECAC6A2EF5E66 /* XCScheme+RemoteRunnable.swift */, - 1D5C2732048660AC71431D9C /* XCScheme+Runnable.swift */, - 694034B4B7C9DF231B007A84 /* XCScheme+SerialAction.swift */, - 522A9ABE50677EEB0638B056 /* XCScheme+SkippedTests.swift */, - 161E967D0979EF34C7DF6348 /* XCScheme+TestAction.swift */, - 87331972540CB510D7E0C333 /* XCScheme+TestPlanReference.swift */, - 0AE91C6982618C1EDDAAD596 /* XCScheme+TestableReference.swift */, - 5D13E332186D46FFA83BBD50 /* XCScheme.swift */, - ); - path = Scheme; - sourceTree = ""; - }; - 3F46E1251C6FA94B4422C839 /* Errors */ = { - isa = PBXGroup; - children = ( - 325DDB80858F8DD8C5C27A07 /* Errors.swift */, - ); - path = Errors; - sourceTree = ""; - }; - 5481114562D03AFF284A4D4B /* Workspace */ = { - isa = PBXGroup; - children = ( - 15DE1D6B3000410D531F05AA /* XCWorkspace.swift */, - 59F2F50C66ADD5D0C12F3B70 /* XCWorkspaceData.swift */, - 747281C8826015AE5CE869D8 /* XCWorkspaceDataElement.swift */, - 0AE25C88F5874509586B3AD4 /* XCWorkspaceDataElementLocationType.swift */, - B92DC66EE80F3D7611319ACC /* XCWorkspaceDataFileRef.swift */, - F7419D9A4F0B11214441DFA3 /* XCWorkspaceDataGroup.swift */, - ); - path = Workspace; - sourceTree = ""; - }; - 75B1DAE9A58776B613C0505F /* Configuration */ = { - isa = PBXGroup; - children = ( - 6A113CBABFAE791D1B9C6BBE /* BuildSettings.swift */, - D07BF1A8B705E35271076BA1 /* XCBuildConfiguration.swift */, - 412215420E6CF8CD85C2908C /* XCConfigurationList.swift */, - ); - path = Configuration; - sourceTree = ""; - }; - 791C387E48AEA6D5F75AC47F = { - isa = PBXGroup; - children = ( - 80D59D67CEEE76EA108CFAAB /* Project */, - ECC157E4B43A303EC5B7C202 /* Frameworks */, - 25C374C70C6324F44964CA1A /* Products */, - ); - sourceTree = ""; - }; - 7EDBBC6ACE86FC676ABA8907 /* Build */ = { - isa = PBXGroup; - children = ( - 3A8CD2E0DBF7926CA8099221 /* Mac */, - ); - path = Build; - sourceTree = ""; - }; - 8036291DDCEF10624F3285CD /* Sourcery */ = { - isa = PBXGroup; - children = ( - E639EC9D100AFD2CEAEE82E0 /* Equality.generated.swift */, - 3A33AF969BDFF0250420A5E8 /* Sourcery.swift */, - ); - path = Sourcery; - sourceTree = ""; - }; - 80D59D67CEEE76EA108CFAAB /* Project */ = { - isa = PBXGroup; - children = ( - D2D78F3ED94EB9E973644780 /* Info.plist */, - BA1E76638FEE70CBAD8B9592 /* Sources */, - 844A2C8AF87F23D7DE2CD3AB /* Carthage */, - ); - name = Project; - sourceTree = ""; - }; - 844A2C8AF87F23D7DE2CD3AB /* Carthage */ = { - isa = PBXGroup; - children = ( - 7EDBBC6ACE86FC676ABA8907 /* Build */, - ); - path = Carthage; - sourceTree = ""; - }; - 8D7B131E2371306262CF7D29 /* Files */ = { - isa = PBXGroup; - children = ( - AA2A4EEFB9DE8F475B051FDE /* PBXContainerItem.swift */, - 2370B4E19828CEFC032511A1 /* PBXContainerItemProxy.swift */, - D4AA26E4818BC76E4CB28CEB /* PBXFileElement.swift */, - 7A72367B782B23A3759F3A1D /* PBXFileReference.swift */, - 249AAF8AE1978D1546C0ADB5 /* PBXGroup.swift */, - 34A0180EB1FDA8CC7D553950 /* PBXSourceTree.swift */, - C6F7AC32AFFBEA9741906F2B /* PBXVariantGroup.swift */, - 39E419CA05304074ADCDACE8 /* XCVersionGroup.swift */, - ); - path = Files; - sourceTree = ""; - }; - 9BE2FF13BAF4C1BEA7C47C73 /* Project */ = { - isa = PBXGroup; - children = ( - 3353E37D0FBA49BA04C82476 /* WorkspaceSettings.swift */, - CC0BF3D061052148207584A5 /* XCBreakpointList.swift */, - AF73D1E4C0B20C16D61865F6 /* XCSharedData.swift */, - A7DAFF5CC89FBB59D02F72B9 /* Xcode.swift */, - F8B667CA83B0BB0285F470F1 /* XcodeProj.swift */, - ); - path = Project; - sourceTree = ""; - }; - BA1E76638FEE70CBAD8B9592 /* Sources */ = { - isa = PBXGroup; - children = ( - 0CD2DD5DADB19C09C88643D9 /* XcodeProj */, - ); - path = Sources; - sourceTree = ""; - }; - D282D20607C553BCACED0FF1 /* Objects */ = { - isa = PBXGroup; - children = ( - F1CB61748771C9E402969232 /* BuildPhase */, - 75B1DAE9A58776B613C0505F /* Configuration */, - 8D7B131E2371306262CF7D29 /* Files */, - FBA38FBC1A539330B4F10351 /* Project */, - 8036291DDCEF10624F3285CD /* Sourcery */, - 0E4511E9D37FE834D40A107C /* SwiftPackage */, - 1E404A60E69E892516E32929 /* Targets */, - ); - path = Objects; - sourceTree = ""; - }; - ECC157E4B43A303EC5B7C202 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; - F1CB61748771C9E402969232 /* BuildPhase */ = { - isa = PBXGroup; - children = ( - 72E6494F0E28769F9B11FD30 /* BuildPhase.swift */, - 13177CF1296C6C32BF4C5B7C /* PBXBuildFile.swift */, - 60E927F019715C98BA849930 /* PBXBuildPhase.swift */, - B50CD65FDAA9B3F4269F4AAF /* PBXBuildRule.swift */, - 19C42E5F8AAD8E3C04E8A9B5 /* PBXCopyFilesBuildPhase.swift */, - 3BEB2811DE4A8D3EE6DC46B7 /* PBXFrameworksBuildPhase.swift */, - 0A909937001696EF804CE15C /* PBXHeadersBuildPhase.swift */, - 8485AE4255BB935C7B7FE93C /* PBXResourcesBuildPhase.swift */, - 577B27811EEAD46794E8DE90 /* PBXRezBuildPhase.swift */, - FC12E1A11D67971AEC27AA12 /* PBXShellScriptBuildPhase.swift */, - CC365B2E771B6E2690D751DB /* PBXSourcesBuildPhase.swift */, - ); - path = BuildPhase; - sourceTree = ""; - }; - FBA38FBC1A539330B4F10351 /* Project */ = { - isa = PBXGroup; - children = ( - 4E93E589F09F66B673712EAE /* PBXObject.swift */, - AAE4FB9142BF91F6E449998C /* PBXObjectParser.swift */, - E06554AB0BF2937025BC45C6 /* PBXObjectReference.swift */, - A5CC513CEDABAA9712FB4DC6 /* PBXObjects.swift */, - E1325A961D9CCFB45B639868 /* PBXOutputSettings.swift */, - 6B2C739F39DD3CC1B77E1075 /* PBXProj.swift */, - 41A031713D5F31DDCC2D61EF /* PBXProjEncoder.swift */, - A93C80F128CBCDFA4EA63EFB /* PBXProject.swift */, - ); - path = Project; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - A44FAD6035FC8C5CF9618B92 /* XcodeProj */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0F14FE388CA9F84B0EA8D7E6 /* Build configuration list for PBXNativeTarget "XcodeProj" */; - buildPhases = ( - 65398CA885D62783C577D0FA /* Sources */, - 987C550AD2D049BE77E633BD /* Resources */, - 2C9C6B5EADA641EDF5BC523E /* Embed Precompiled Frameworks */, - 7FAD067EE084B2EF5EFE4BFF /* Embed Frameworks */, - ECC1E0A3315EECC3AF534E1B /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = XcodeProj; - productName = XcodeProj; - productReference = E63AB43AF9F4C4F938680EA9 /* XcodeProj.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 223DB5C7FA35E8FD593C4040 /* Project object */ = { - isa = PBXProject; - attributes = { - }; - buildConfigurationList = 477F800A701B31C60A80EACC /* Build configuration list for PBXProject "XcodeProj_Carthage" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 791C387E48AEA6D5F75AC47F; - productRefGroup = 25C374C70C6324F44964CA1A /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - A44FAD6035FC8C5CF9618B92 /* XcodeProj */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 987C550AD2D049BE77E633BD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 2C9C6B5EADA641EDF5BC523E /* Embed Precompiled Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Embed Precompiled Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"Skipping, nothing to be embedded.\""; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 65398CA885D62783C577D0FA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6619158CCA6E2D05A39860B8 /* Errors.swift in Sources */, - F48082472D45B18B36F00F09 /* AEXML+XcodeFormat.swift in Sources */, - 7DF42666456E27C51CC672C1 /* Array+Extras.swift in Sources */, - 4C5079131989ADE80FF17EFF /* Bool+Extras.swift in Sources */, - A1476A0B7C8DA244920775F1 /* Dictionary+Enumerate.swift in Sources */, - 1009BFBC8BD95556803FF2C9 /* Dictionary+Extras.swift in Sources */, - 9DBF777FEB60396E520D2595 /* KeyedDecodingContainer+Additions.swift in Sources */, - AECDFCA5C2DD558AAC48E945 /* NSRecursiveLock+Sync.swift in Sources */, - BD65503EF698EE4EAF11892C /* Path+Extras.swift in Sources */, - 64DB0043086F77457C7B223D /* String+Utils.swift in Sources */, - AEF9DAF6D599FAD8B64827C6 /* String+md5.swift in Sources */, - E31DA2A4BF344AC0BB4B1EC8 /* BuildPhase.swift in Sources */, - 13CBB83040E0B373CE80CB16 /* PBXBuildFile.swift in Sources */, - 7823F76B43696121C7401C35 /* PBXBuildPhase.swift in Sources */, - 9D897705DD334A50C4431887 /* PBXBuildRule.swift in Sources */, - 03FCD0C75E642B6E50DE9095 /* PBXCopyFilesBuildPhase.swift in Sources */, - 6AA90F1144C42F9275124B0F /* PBXFrameworksBuildPhase.swift in Sources */, - ACC38711B1FD79AED6D63C9D /* PBXHeadersBuildPhase.swift in Sources */, - B7703E87F0E986D0BECC6C92 /* PBXResourcesBuildPhase.swift in Sources */, - 6BCC38DC97A295B5179C0565 /* PBXRezBuildPhase.swift in Sources */, - 73E2CEC84B1EE4E25AF9030C /* PBXShellScriptBuildPhase.swift in Sources */, - 7CDAEBA81467E39CBAD2E6CF /* PBXSourcesBuildPhase.swift in Sources */, - 4F427C99A47227F5592A1522 /* BuildSettings.swift in Sources */, - 8242B39EB786ADA4A8B32EF3 /* XCBuildConfiguration.swift in Sources */, - C2DB3BAC484E34097E80471E /* XCConfigurationList.swift in Sources */, - 586BB941B8D71312F85033A1 /* PBXContainerItem.swift in Sources */, - 61FFC23B7055683822148DDE /* PBXContainerItemProxy.swift in Sources */, - 2B2D208723F7AE79632BAEAE /* PBXFileElement.swift in Sources */, - E863D2A025082CB472079D90 /* PBXFileReference.swift in Sources */, - 3A59F800668B0D6F550B9C09 /* PBXGroup.swift in Sources */, - ABE0F9CFD5549F302EB3A417 /* PBXSourceTree.swift in Sources */, - DA0ACE91B9EEA6D3BD7170F3 /* PBXVariantGroup.swift in Sources */, - 4716E044E5786AC68205911A /* XCVersionGroup.swift in Sources */, - A2344E4A960C21715FAD3F86 /* PBXObject.swift in Sources */, - B07C3C11C0C5E9123C5D9D15 /* PBXObjectParser.swift in Sources */, - DF91D1F6BECD6145A4BEEE7B /* PBXObjectReference.swift in Sources */, - 9F27802B144AFC7C1A739492 /* PBXObjects.swift in Sources */, - D4CDCEFF10BE6E80B9057132 /* PBXOutputSettings.swift in Sources */, - 74A56CC842A9E89D622A49A0 /* PBXProj.swift in Sources */, - 3D5DBC9A4315D97D1B39CF19 /* PBXProjEncoder.swift in Sources */, - 2BC374D574269FB75F978D6F /* PBXProject.swift in Sources */, - 625CFEF76B6D5B02D3A1CEC6 /* Equality.generated.swift in Sources */, - 84C672F79741BBA0937580EA /* Sourcery.swift in Sources */, - 792583786825191D2E186E9D /* XCRemoteSwiftPackageReference.swift in Sources */, - 1C99B6399AB8732EA97B1EEC /* XCSwiftPackageProductDependency.swift in Sources */, - 0C6F7F2EF3EE9D97F081D9FD /* PBXAggregateTarget.swift in Sources */, - BBA09A7C8D3D9697C10F300B /* PBXLegacyTarget.swift in Sources */, - 809908EF4E6E71285D110707 /* PBXNativeTarget.swift in Sources */, - 062694C004A53F24E7A92B85 /* PBXProductType.swift in Sources */, - BEE9FCB78D7D7F7A0DBF6F73 /* PBXReferenceProxy.swift in Sources */, - 65476234B5AA54D4EB33FEA7 /* PBXTarget.swift in Sources */, - D5DB78ED46F818640BC41A9D /* PBXTargetDependency.swift in Sources */, - ED4BFA372B68053D67F0E379 /* WorkspaceSettings.swift in Sources */, - 8EE72814658F0A23A01AD1E6 /* XCBreakpointList.swift in Sources */, - BEBC1583A85423F1F1BF3E30 /* XCSharedData.swift in Sources */, - CE58E51C93B8A19FA4F79E22 /* Xcode.swift in Sources */, - E82A4CE0E0E385A2DED27887 /* XcodeProj.swift in Sources */, - C333BEA47F06D2357DE8B1EA /* Writable.swift in Sources */, - D32D0514F1A4256071EDB1A1 /* XCScheme+AditionalOption.swift in Sources */, - FEBD1114F05F3A9667F26CCC /* XCScheme+AnalyzeAction.swift in Sources */, - 903A9EC1B895A4920A322898 /* XCScheme+ArchiveAction.swift in Sources */, - 46A5D9862729AC0C6D85D4FB /* XCScheme+BuildAction.swift in Sources */, - D07F87005EA408048A765039 /* XCScheme+BuildableProductRunnable.swift in Sources */, - C95FAEE0675588942633875E /* XCScheme+BuildableReference.swift in Sources */, - 735E1C6466FF56969767BAA0 /* XCScheme+CommandLineArguments.swift in Sources */, - 7C3555C09B170AEA2D56E8F2 /* XCScheme+EnvironmentVariable.swift in Sources */, - AC49ED6F358FFBF504A3D0AE /* XCScheme+ExecutionAction.swift in Sources */, - 147157016720CE9BC5943AF2 /* XCScheme+LaunchAction.swift in Sources */, - 69CBD38E1B96198049A02795 /* XCScheme+LocationScenarioReference.swift in Sources */, - 364C132E6ABF980BF9E84649 /* XCScheme+ProfileAction.swift in Sources */, - 209B0AC5FFCE800A35624C49 /* XCScheme+RemoteRunnable.swift in Sources */, - B972348A969282D604A5C325 /* XCScheme+Runnable.swift in Sources */, - 53CE6B2F74AB5FAA69D58923 /* XCScheme+SerialAction.swift in Sources */, - D783F0B2AC4CA6F10A0FC5B9 /* XCScheme+SkippedTests.swift in Sources */, - AB74DA38A17EE261024A711C /* XCScheme+TestAction.swift in Sources */, - D5D82B59980521FBA427EED8 /* XCScheme+TestPlanReference.swift in Sources */, - 0390AF375B465D1DC7AB6194 /* XCScheme+TestableReference.swift in Sources */, - E113D0F651A2213459C65387 /* XCScheme.swift in Sources */, - 3E2EE77196F6B2BECE8DF3DB /* BuildSettingsProvider.swift in Sources */, - 4E9B74BA2A6381FEDEA26092 /* CommentedString.swift in Sources */, - BD7A3EBFD6C9D1AA78E6E822 /* Decoders.swift in Sources */, - 7F9ED031DE6A06055F44C2E2 /* JSONDecoding.swift in Sources */, - 23A0E815358EB2D937F40513 /* PBXBatchUpdater.swift in Sources */, - F6AF8B5725B94DC6136239DF /* PlistValue.swift in Sources */, - DE1F130B4C81A1ABE9DB6DC0 /* ReferenceGenerator.swift in Sources */, - 0B364FCF8F7A557C252929EA /* XCConfig.swift in Sources */, - 730CEE3BF13DFC6608790F33 /* XCWorkspace.swift in Sources */, - EC0897B21DBC4594857BAD07 /* XCWorkspaceData.swift in Sources */, - DF37E12A489F0FBFDCF4C7A1 /* XCWorkspaceDataElement.swift in Sources */, - 494EF8FD09FAEE56F6ACF3BE /* XCWorkspaceDataElementLocationType.swift in Sources */, - 33C049021FCA541192F40AD1 /* XCWorkspaceDataFileRef.swift in Sources */, - C907FC5616048E42925FB93E /* XCWorkspaceDataGroup.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 33295D1BD7296777883C7019 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/Carthage/Build/Mac", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj; - PRODUCT_NAME = XcodeProj; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_COMPILATION_MODE = singlefile; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 731F89D8DD1568204C64A902 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 87A4E737EC3E625DEC8CDEC0 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - D5796A15286EF9ADD0F090D4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/Carthage/Build/Mac", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = io.tuist.XcodeProj; - PRODUCT_NAME = XcodeProj; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0F14FE388CA9F84B0EA8D7E6 /* Build configuration list for PBXNativeTarget "XcodeProj" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33295D1BD7296777883C7019 /* Debug */, - D5796A15286EF9ADD0F090D4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; - 477F800A701B31C60A80EACC /* Build configuration list for PBXProject "XcodeProj_Carthage" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 87A4E737EC3E625DEC8CDEC0 /* Debug */, - 731F89D8DD1568204C64A902 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; -/* End XCConfigurationList section */ - }; - rootObject = 223DB5C7FA35E8FD593C4040 /* Project object */; -} diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 000000000..9f4edec6a --- /dev/null +++ b/cliff.toml @@ -0,0 +1,127 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[remote.github] +owner = "tuist" +repo = "XcodeProj" +# token = "" + +[changelog] +# template for the changelog header +header = """ +# Changelog\n +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).\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% if version -%} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else -%} + ## [Unreleased] +{% endif -%} + +### Details\ + +{% for group, commits in commits | group_by(attribute="group") %} + #### {{ group | upper_first }} + {%- for commit in commits %} + - {{ commit.message | upper_first | trim }}\ + {% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%} + {% if commit.github.pr_number %} in \ + [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}) \ + {%- endif -%} + {% endfor %} +{% endfor %} + +{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + ## New Contributors +{%- endif -%} + +{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} +{%- endfor %}\n +""" +# template for the changelog footer +footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% for release in releases -%} + {% if release.version -%} + {% if release.previous.version -%} + [{{ release.version | trim_start_matches(pat="v") }}]: \ + {{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }} + {% endif -%} + {% else -%} + [unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD + {% endif -%} +{% endfor %} + +Check out [GitHub releases](https://github.com/tuist/XcodeProj/releases) for older releases. + + +""" +# remove the leading and trailing whitespace from the templates +trim = true +# postprocessors +postprocessors = [] + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # remove issue numbers from commits + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, +] +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^style", group = "Styling" }, + { message = "^test", group = "Testing" }, + { message = "^chore\\(spm.*\\)", skip = false }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore|^ci", group = "Miscellaneous Tasks" }, + { body = ".*security", group = "Security" }, +] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false +# filter out the commits that are not matched by commit parsers +filter_commits = false +# regex for matching git tags +tag_pattern = "[0-9].*" +# regex for skipping tags +skip_tags = "beta|alpha" +# regex for ignoring tags +ignore_tags = "rc" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest" + +[bump] +breaking_always_bump_major = true +features_always_bump_minor=true +initial_tag="8.22.0" diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..a425f0734 --- /dev/null +++ b/renovate.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + ":semanticCommits", + ":disableDependencyDashboard", + "config:recommended" + ], + "packageRules": [ + { + "matchUpdateTypes": ["minor", "patch", "pin", "digest"], + "automerge": true, + "automergeType": "pr", + "automergeStrategy": "auto", + "semanticCommitScope": "deps" + }, + { + "matchManagers": ["swift"], + "semanticCommitScope": "spm" + } + ], + "lockFileMaintenance": { + "enabled": true, + "automerge": true + } +} diff --git a/sourcery.yml b/sourcery.yml deleted file mode 100644 index e575499c2..000000000 --- a/sourcery.yml +++ /dev/null @@ -1,6 +0,0 @@ -sources: - - Sources/ -templates: - - templates -output: - Sources/XcodeProj/Objects/Sourcery diff --git a/xcodeproj.podspec b/xcodeproj.podspec deleted file mode 100644 index b0fb096d5..000000000 --- a/xcodeproj.podspec +++ /dev/null @@ -1,18 +0,0 @@ - -Pod::Spec.new do |s| - s.name = "xcodeproj" - s.version = "7.5.0" - s.summary = "Read/Modify/Write your Xcode projects" - s.homepage = "https://github.com/tuist/xcodeproj" - s.license = 'MIT' - s.source = { :git => "https://github.com/tuist/xcodeproj.git", :tag => s.version.to_s } - s.requires_arc = true - s.authors = "Tuist" - s.swift_version = "5.1" - s.osx.deployment_target = '10.10' - - s.source_files = "Sources/**/*.{swift}" - - s.dependency "PathKit", "~> 1.0.0" - s.dependency "AEXML", "~> 4.4.0" -end