diff --git a/.all-contributorsrc b/.all-contributorsrc index 98c74b7b0..d8a1a4e14 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -67,6 +67,204 @@ "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" + ] } ], "contributorsPerLine": 7, @@ -74,5 +272,6 @@ "projectOwner": "tuist", "repoType": "github", "repoHost": "https://github.com", - "skipCi": true + "skipCi": true, + "commitConvention": "angular" } 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/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 4017f57c9..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 2 -updates: -- package-ecosystem: bundler - directory: "/" - schedule: - interval: daily - time: '11:00' - open-pull-requests-limit: 10 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 65d4a0e02..000000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,15 +0,0 @@ -# https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname -name: Checks - -on: [push, 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 }} diff --git a/.github/workflows/package.yml b/.github/workflows/xcodeproj.yml similarity index 65% rename from .github/workflows/package.yml rename to .github/workflows/xcodeproj.yml index b1af51c6d..5e61b10b9 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/xcodeproj.yml @@ -1,15 +1,19 @@ # https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname -name: Package +name: XcodeProj on: [push, pull_request] +concurrency: + group: xcodeproj-${{ github.head_ref }} + cancel-in-progress: true + jobs: build: name: Build (macOS) - runs-on: macos-latest + runs-on: macos-11 strategy: matrix: - xcode: ["11.3", "11.3.1", "11.4"] + xcode: ["12.4", "12.5", "13.0"] steps: - uses: actions/checkout@v1 - name: Select Xcode ${{ matrix.xcode }} @@ -25,10 +29,10 @@ jobs: run: swift build -c release test: name: Test (macOS) - runs-on: macos-latest + runs-on: macos-11 strategy: matrix: - xcode: ["11.3", "11.3.1", "11.4", "12.0.1"] + xcode: ["12.4", "12.5", "13.0"] steps: - uses: actions/checkout@v1 - name: Select Xcode ${{ matrix.xcode }} @@ -44,5 +48,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + - name: Set Git config + run: | + git config --global user.email "xcodeproj@tuist.io" + git config --global user.name "xcodeproj" + git config --global init.defaultBranch main - name: Build and run tests run: swift test --enable-test-discovery + swiftlint: + name: Swiftlint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: GitHub Action for SwiftLint + uses: norio-nomura/action-swiftlint@3.2.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.ruby-version b/.ruby-version index 57cf282eb..1f7da99d4 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.5 +2.7.7 diff --git a/.swift-version b/.swift-version deleted file mode 100644 index a75b92f1e..000000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -5.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ea80fa3..3c280c588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,174 @@ 🚀 Check out the guidelines [here](https://tuist.io/docs/contribution/changelog-guidelines/) -## Next +## 8.10.0 + +### Added + +- Add try-catch to getting started documentation [#745](https://github.com/tuist/XcodeProj/pull/745) by [@AlexKobachiJP](https://github.com/AlexKobachiJP) +- Add missing Build Settings Provider documentation [#747](https://github.com/tuist/XcodeProj/pull/747) by [@baekteun](https://github.com/baekteun) + +### Fixed + +- Fix unstable reads for `XCSchemeManagement` [#758](https://github.com/tuist/XcodeProj/pull/758) by [@kwridan](https://github.com/kwridan) +- Fix typos in `Writable.swift` [#755](https://github.com/tuist/XcodeProj/pull/755) by [@jpsim](https://github.com/jpsim) + +### Changed + +- Update `XCSharedData` Writable conformance to include `WorkspaceSettings` [#743](https://github.com/tuist/XcodeProj/pull/743) by [@dayton-bobbitt](https://github.com/dayton-bobbitt) + +## 8.9.0 + +### Added + +- Update watchOS application default settings for Xcode 14 [#711](https://github.com/tuist/XcodeProj/pull/711) by [@kwridan](https://github.com/kwridan) +- Implement `Hashable` for `XCScheme.BuildableReference` [#712](https://github.com/tuist/XcodeProj/pull/712) by [@cgrindel](https://github.com/cgrindel) +- Sets customWorkingDirectory for schemes [#720](https://github.com/tuist/XcodeProj/pull/720) by [@maxwellE](https://github.com/maxwellE) +- Add `XCScheme.ExecutionAction.shellToInvoke` [#721](https://github.com/tuist/XcodeProj/pull/721) by [@CrazyFanFan](https://github.com/CrazyFanFan) +- Add `platformFilters` attribute to `PBXBuildFile` and `PBXTargetDependency` [#737](https://github.com/tuist/XcodeProj/pull/737) by [@maxwellE](https://github.com/maxwellE) +- Add suppot for `XCUserData` [#739](https://github.com/tuist/XcodeProj/pull/739) by [@teameh](https://github.com/teameh) + +## 8.8.0 + +### Fixed + +- Fix equality checking of dictionaries [#667](https://github.com/tuist/XcodeProj/pull/667) by [@brentleyjones](https://github.com/brentleyjones) +- Quiet new warnings from Xcode 13.3 [#673](https://github.com/tuist/XcodeProj/pull/673) by [@hisaac](https://github.com/hisaac) +- Fix typo in PBXOutputSettings.swift [#678](https://github.com/tuist/XcodeProj/pull/678) by [@eltociear](https://github.com/eltociear) +- Fix syntax error in docs [#679](https://github.com/tuist/XcodeProj/pull/679) by [@maxwellE](https://github.com/maxwellE) +- Misspelling in comment [#680](https://github.com/tuist/XcodeProj/pull/680) by [@maxwellE](https://github.com/maxwellE) +- Misspelling in comment [#681](https://github.com/tuist/XcodeProj/pull/681) by [@maxwellE](https://github.com/maxwellE) +- Ensure correct `LaunchAction` scheme order [#686](https://github.com/tuist/XcodeProj/pull/686) by [@maxwellE](https://github.com/maxwellE) +- Fix TestAction scheme attribute ordering [#689](https://github.com/tuist/XcodeProj/pull/689) by [@maxwellE](https://github.com/maxwellE) +- Fix `Testables` element ordering [#702](https://github.com/tuist/XcodeProj/pull/702) by [@maxwellE](https://github.com/maxwellE) +- Fix `RemoteRunnable` scheme attr order [#701](https://github.com/tuist/XcodeProj/pull/701) by [@maxwellE](https://github.com/maxwellE) +- Fix ordering of Scheme toplevel attrs [#698](https://github.com/tuist/XcodeProj/pull/698) by [@maxwellE](https://github.com/maxwellE) +- Fix order for `askForAppToLaunch` attr [#705](https://github.com/tuist/XcodeProj/pull/705) by [@maxwellE](https://github.com/maxwellE) +- Fix ordering of diagnostics scheme options [#704](https://github.com/tuist/XcodeProj/pull/704) by [@kwridan](https://github.com/kwridan) + +### Added + +- Add `addDependency()` helper method to `PBXAggregateTarget` [#677](https://github.com/tuist/XcodeProj/pull/677) by [@brentleyjones](https://github.com/brentleyjones) +- Allow for initializing a PBXProj via a direct path [#682](https://github.com/tuist/XcodeProj/pull/682) by [@maxwellE](https://github.com/maxwellE) +- Add `.extensionKitExtension` as the new `PBXProductType` [#691](https://github.com/tuist/XcodeProj/pull/691) by [@mtj0928](https://github.com/mtj0928) +- Added `disablePerformanceAntipatternChecker` to `XCScheme` [#693](https://github.com/tuist/XcodeProj/pull/603) by [@Bogdan-Belogurov](https://github.com/Bogdan-Belogurov) +- Added missing `askForAppToLaunch` in `ProfileAction` [#700](https://github.com/tuist/XcodeProj/pull/700) by [@maxwellE](https://github.com/maxwellE) +- Add `launchAutomaticallySubstyle`->`ProfileAction` [#699](https://github.com/tuist/XcodeProj/pull/699) by [@maxwellE](https://github.com/maxwellE) +- Add `DEAD_CODE_STRIPPING` default project setting [#706](https://github.com/tuist/XcodeProj/pull/706) by [@kwridan](https://github.com/kwridan) + +### Changed + +- Use `Runnable` in `ProfileAction` [#703](https://github.com/tuist/XcodeProj/pull/703) by [@maxwellE](https://github.com/maxwellE) + +## 8.7.1 + +### Changed +- Make WorkspaceSettings initializer public [#658](https://github.com/tuist/XcodeProj/pull/658) by [@jakeatoms](https://github.com/jakeatoms) + +## 8.7.0 +### Added + +- Add DocC Xcode File Type (`.docc`) [#660](https://github.com/tuist/XcodeProj/pull/660) by [@Jake-Prickett](https://github.com/Jake-Prickett) + +## 8.6.0 +### Added + +- Support for location added to test targets (`TestableReference`) [#654](https://github.com/tuist/XcodeProj/pull/654) by [@KrisRJack](https://github.com/KrisRJack) + +## 8.5.0 + +### Added + +- Add XCSchemeManagement struct https://github.com/tuist/XcodeProj/pull/565 by @pepibumur. + +### Changed +- Update the last-known and default constants to align with Xcode 13. + +## 8.4.0 +### Added + +- Support customized DerrivedData path in `WorkspaceSettings` [#650](https://github.com/tuist/XcodeProj/pull/650) by [@freddi-kit](https://github.com/freddi-kit). + +### 8.3.1 +### Fixed + +- Fix Xcode 13 build [#648](https://github.com/tuist/XcodeProj/pull/648) by [@raptorxcz](https://github.com/raptorxcz) + +## 8.3.0 - Mojo +### Added + +- `CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED` to default build setting [#641](https://github.com/tuist/XcodeProj/pull/641) by [@flowbe](https://github.com/flowbe) + +### Fixed + +- Xcode 13 build issues [#646](https://github.com/tuist/XcodeProj/pull/646) by [@jsorge](https://github.com/jsorge) + +## 8.2.0 - Bubbles +### Added + +- Support obtaining the full path of a file element by passing the source root as a string [#624](https://github.com/tuist/XcodeProj/pull/624) by [@ileitch](https://github.com/ileitch). + +### Fixed + +- If RemoteRunnable doesn't contains BuildableReference XcodeProj removes xcscheme file [#627](https://github.com/tuist/XcodeProj/pull/627) by [@subdan](https://github.com/subdan). + +### Changed + +- Updated AEXML to 4.6.1 [#632](https://github.com/tuist/XcodeProj/pull/632) by [@nnsnodnb](https://github.com/nnsnodnb). +## 8.1.0 - Barcelona +### Changed + +- Improve performance of commented string [#635](https://github.com/tuist/XcodeProj/pull/635) by [@adellibovi](https://github.com/adellibovi) + +## 8.0.0 - Amor +### Fixed + +- Adding group set incorrect parent in case of complex path [#614](https://github.com/tuist/XcodeProj/pull/614) by [@avdyushin](https://github.com/avdyushin) +- **Breaking** Fixed issue where some schemes could not be deserialized because a buildable reference did not contain a blueprint identifier [#612](https://github.com/tuist/XcodeProj/pull/612) by [@daltonclaybrook](https://github.com/daltonclaybrook) +- Added the `com.apple.product-type.driver-extension` and `com.apple.product-type.system-extension` PBXProductType [#618](https://github.com/tuist/XcodeProj/pull/618) by [@vgorloff](https://github.com/vgorloff). + +### Changed + +- **Breaking** Make `runPostActionsOnFailure` optional [#619](https://github.com/tuist/XcodeProj/pull/619) by [@kwridan](https://github.com/kwridan) + +## 7.23.0 - Bonsai +### Added + +- Allows passing BuildableIdentifier String to BuildableReference initializer [#605](https://github.com/tuist/XcodeProj/pull/605) by [@freddi-kit](https://github.com/freddi-kit) + +### Fixed + +- Fixed building on Linux [#615](https://github.com/tuist/XcodeProj/pull/615) by [@yonaskolb](https://github.com/yonaskolb) + +## 7.22.0 - Ringui Dingui + +### Added + +- `CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER` to default build setting [#608](https://github.com/tuist/XcodeProj/pull/608) by [@fortmarek](https://github.com/fortmarek) + +### Fixed + +- Prevent overwriting identical workspace data [#607](https://github.com/tuist/XcodeProj/pull/607) by [@ferologics](https://github.com/ferologics) + +## 7.21.0 - Alfredo + +### Changed + +- Speed up md5 generation [#606](https://github.com/tuist/XcodeProj/pull/606) by [@adellibovi](https://github.com/adellibovi) + +## 7.20.0 - Sol + +### Added + +- Added `runPostActionsOnFailure` to `XCScheme` [#603](https://github.com/tuist/XcodeProj/pull/603) by [@FranzBusch](https://github.com/FranzBusch) + +## 7.19.0 - Kreuzberg ### Fixed - JSON decoder not properly decoding `defaultConfigurationIsVisible` in some projects [#593](https://github.com/tuist/XcodeProj/pull/593) by [@tjwio](https://github.com/tjwio) - JSON decoder not properly decoding `proxyType` in some projects [#596](https://github.com/tuist/XcodeProj/issues/596) by [@tjwio](https://github.com/tjwio) - BuildPhaseTests not handling failure cases properly [#597](https://github.com/tuist/XcodeProj/issues/597) by [@tjwio](https://github.com/tjwio) +- `xcconfig` parser does not support inline comments [#602](https://github.com/tuist/XcodeProj/issues/602) by [@dive](https://github.com/dive) ## 7.18.0 - Penguin diff --git a/Cartfile b/Cartfile index dfc7532ed..89344226f 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "tuist/PathKit" == 1.0.0 -github "tadija/AEXML" == 4.6.0 +github "tadija/AEXML" == 4.6.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index e53ee3616..25e945995 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "tadija/AEXML" "4.6.0" +github "tadija/AEXML" "4.6.1" github "tuist/PathKit" "1.0.0" diff --git a/Documentation/getting-started.md b/Documentation/getting-started.md index 26d6512e9..dfb3441c5 100644 --- a/Documentation/getting-started.md +++ b/Documentation/getting-started.md @@ -57,7 +57,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 +72,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 +108,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/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/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/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist index a03326804..931805c0a 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 119ecf609..fb8299e4a 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,9 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" codeCoverageEnabled = "YES" - onlyGenerateCoverageForSpecifiedTargets = "YES" - shouldUseLaunchSchemeArgsEnv = "YES"> + onlyGenerateCoverageForSpecifiedTargets = "YES"> @@ -79,6 +80,12 @@ + + + + + + + + + + - - - - - - - - - - + allowLocationSimulation = "YES" + customLaunchCommand = "custom command"> @@ -177,6 +179,10 @@ ReferencedContainer = "container:Project.xcodeproj"> + + - - + identifier = "../../Configuration.storekit"> + + + + + + + + + + + + + + + + + + + + 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/Gemfile b/Gemfile index 225c83192..3f25545c3 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" gem "rake" gem "jazzy" -gem "cocoapods", "1.10.1" +gem "cocoapods", "1.11.0" gem "colorize", "~> 0.8.1" gem "redcarpet", "~> 3.5.1" diff --git a/Gemfile.lock b/Gemfile.lock index c62584803..b8d278e6e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,23 +1,25 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (5.2.4.4) + CFPropertyList (3.0.5) + rexml + activesupport (6.1.7.3) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.7.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) - claide (1.0.3) - cocoapods (1.10.1) - addressable (~> 2.6) + claide (1.1.0) + cocoapods (1.11.0) + addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.10.1) + cocoapods-core (= 1.11.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -28,87 +30,90 @@ GEM escape (~> 0.0.4) fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) - molinillo (~> 0.6.6) + molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.19.0, < 2.0) - cocoapods-core (1.10.1) - activesupport (> 5.0, < 6) - addressable (~> 2.6) + ruby-macho (>= 1.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.11.0) + activesupport (>= 5.0, < 7) + addressable (~> 2.8) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) netrc (~> 0.11) - public_suffix + public_suffix (~> 4.0) typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.4.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap - cocoapods-search (1.0.0) - cocoapods-trunk (1.5.0) + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) colorize (0.8.1) - concurrent-ruby (1.1.7) + concurrent-ruby (1.2.2) escape (0.0.4) - ethon (0.12.0) - ffi (>= 1.3.0) - ffi (1.14.2) + ethon (0.15.0) + ffi (>= 1.15.0) + ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.8.7) + i18n (1.12.0) concurrent-ruby (~> 1.0) - jazzy (0.13.6) + jazzy (0.14.2) cocoapods (~> 1.5) mustache (~> 1.1) - open4 + open4 (~> 1.3) redcarpet (~> 3.4) + rexml (~> 3.2) rouge (>= 2.0.6, < 4.0) sassc (~> 2.1) sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) - json (2.5.1) + json (2.6.1) liferaft (0.0.6) - minitest (5.14.3) - molinillo (0.6.6) + minitest (5.18.0) + molinillo (0.8.0) mustache (1.1.1) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) open4 (1.3.4) public_suffix (4.0.6) - rake (13.0.3) + rake (13.0.6) redcarpet (3.5.1) - rouge (3.24.0) - ruby-macho (1.4.0) + rexml (3.2.5) + rouge (3.28.0) + ruby-macho (2.5.1) sassc (2.4.0) ffi (~> 1.9) sqlite3 (1.4.2) - thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.19.0) + xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + zeitwerk (2.6.7) PLATFORMS ruby DEPENDENCIES - cocoapods (= 1.10.1) + cocoapods (= 1.11.0) colorize (~> 0.8.1) jazzy rake diff --git a/Ghosta b/Ghosta new file mode 100644 index 000000000..851133855 --- /dev/null +++ b/Ghosta @@ -0,0 +1,838 @@ +/* + * Copyright (c) 1998-2014 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: device/device.defs + * Author: Douglas Orr + * Feb 10, 1988 + * Abstract: + * Mach device support. Mach devices are accessed through + * block and character device interfaces to the kernel. + */ + +#define IOKIT 1 + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + iokit 2800; + +#if IOKITSIMD || KERNEL_SERVER +#define IOKIT_ALL_IPC 1 +#endif + +#include +#include +#include +#include + +#if !__LP64__ +# define __ILP32__ 1 +#endif + +import ; + +serverprefix is_; + +type reply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE | polymorphic + ctype: mach_port_t; + +#if IOKIT + +type io_name_t = c_string[*:128]; +type io_string_t = c_string[*:512]; +type io_string_inband_t = c_string[*:4096]; +type io_struct_inband_t = array[*:4096] of char; +type io_buf_ptr_t = ^array[] of MACH_MSG_TYPE_INTEGER_8; +type NDR_record_t = struct[8] of char; + +#if KERNEL +type io_user_scalar_t = uint64_t; +type io_user_reference_t = uint64_t; +type io_scalar_inband_t = array[*:16] of int; +// must be the same type as OSAsyncReference +type io_async_ref_t = array[*:8] of natural_t; +type io_scalar_inband64_t = array[*:16] of io_user_scalar_t; +type io_async_ref64_t = array[*:8] of io_user_reference_t; +#elif __LP64__ +type io_user_scalar_t = uint64_t; +type io_user_reference_t = uint64_t; +type io_scalar_inband_t = array[*:16] of io_user_scalar_t; +type io_async_ref_t = array[*:8] of io_user_reference_t; +type io_scalar_inband64_t = array[*:16] of io_user_scalar_t; +type io_async_ref64_t = array[*:8] of io_user_reference_t; +#else +type io_user_scalar_t = int; +type io_user_reference_t = natural_t; +type io_scalar_inband_t = array[*:16] of io_user_scalar_t; +type io_async_ref_t = array[*:8] of io_user_reference_t; +type io_scalar_inband64_t = array[*:16] of uint64_t; +type io_async_ref64_t = array[*:8] of uint64_t; +#endif // __LP64__ + +type io_object_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: io_object_t iokit_lookup_object_port(mach_port_t) + outtran: mach_port_t iokit_make_object_port(io_object_t) + destructor: iokit_remove_reference(io_object_t) +#endif /* KERNEL_SERVER */ + ; + +type io_connect_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: io_connect_t iokit_lookup_connect_port(mach_port_t) + outtran: mach_port_t iokit_make_connect_port(io_connect_t) + destructor: iokit_remove_connect_reference(io_connect_t) +#endif /* KERNEL_SERVER */ + ; + +routine io_object_get_class( + object : io_object_t; + out className : io_name_t + ); + +routine io_object_conforms_to( + object : io_object_t; + in className : io_name_t; + out conforms : boolean_t + ); + +routine io_iterator_next( + iterator : io_object_t; + out object : io_object_t + ); + +routine io_iterator_reset( + iterator : io_object_t + ); + +routine io_service_get_matching_services( + master_port : mach_port_t; + in matching : io_string_t; + out existing : io_object_t + ); + +routine io_registry_entry_get_property( + registry_entry : io_object_t; + in property_name : io_name_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_registry_create_iterator( + master_port : mach_port_t; + in plane : io_name_t; + in options : uint32_t; + out iterator : io_object_t + ); + +routine io_registry_iterator_enter_entry( + iterator : io_object_t + ); + +routine io_registry_iterator_exit_entry( + iterator : io_object_t + ); + +routine io_registry_entry_from_path( + master_port : mach_port_t; + in path : io_string_t; + out registry_entry : io_object_t + ); + +routine io_registry_entry_get_name( + registry_entry : io_object_t; + out name : io_name_t + ); + +routine io_registry_entry_get_properties( + registry_entry : io_object_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_registry_entry_get_property_bytes( + registry_entry : io_object_t; + in property_name : io_name_t; + out data : io_struct_inband_t, CountInOut + ); + +routine io_registry_entry_get_child_iterator( + registry_entry : io_object_t; + in plane : io_name_t; + out iterator : io_object_t + ); + +routine io_registry_entry_get_parent_iterator( + registry_entry : io_object_t; + in plane : io_name_t; + out iterator : io_object_t + ); + +skip; +/* was routine io_service_open + service : io_object_t; + in owningTask : task_t; + in connect_type : uint32_t; + out connection : io_connect_t + ); +*/ + +routine io_service_close( + connection : io_connect_t + ); + +routine io_connect_get_service( + connection : io_connect_t; + out service : io_object_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_connect_set_notification_port( + connection : io_connect_t; + in notification_type : uint32_t; + in port : mach_port_make_send_t; + in reference : uint32_t + ); + +routine io_connect_map_memory( + connection : io_connect_t; + in memory_type : uint32_t; + in into_task : task_t; +#if IOKIT_ALL_IPC + inout address : uint32_t; + inout size : uint32_t; +#else + inout address : vm_address_t; + inout size : vm_size_t; +#endif + in flags : uint32_t + ); +#else +skip; +skip; +#endif + +routine io_connect_add_client( + connection : io_connect_t; + in connect_to : io_connect_t + ); + +routine io_connect_set_properties( + connection : io_connect_t; + in properties : io_buf_ptr_t, physicalcopy; + out result : kern_return_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_connect_method_scalarI_scalarO( + connection : io_connect_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + out output : io_scalar_inband_t, CountInOut + ); + +routine io_connect_method_scalarI_structureO( + connection : io_connect_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + out output : io_struct_inband_t, CountInOut + ); + +routine io_connect_method_scalarI_structureI( + connection : io_connect_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + in inputStruct : io_struct_inband_t + ); + +routine io_connect_method_structureI_structureO( + connection : io_connect_t; + in selector : uint32_t; + in input : io_struct_inband_t; + out output : io_struct_inband_t, CountInOut + ); +#else +skip; +skip; +skip; +skip; +#endif + +routine io_registry_entry_get_path( + registry_entry : io_object_t; + in plane : io_name_t; + out path : io_string_t + ); + +routine io_registry_get_root_entry( + master_port : mach_port_t; + out root : io_object_t + ); + +routine io_registry_entry_set_properties( + registry_entry : io_object_t; + in properties : io_buf_ptr_t, physicalcopy; + out result : kern_return_t + ); + +routine io_registry_entry_in_plane( + registry_entry : io_object_t; + in plane : io_name_t; + out inPlane : boolean_t + ); + +routine io_object_get_retain_count( + object : io_object_t; + out retainCount : uint32_t + ); + +routine io_service_get_busy_state( + service : io_object_t; + out busyState : uint32_t + ); + +routine io_service_wait_quiet( + service : io_object_t; + wait_time : mach_timespec_t + ); + +routine io_registry_entry_create_iterator( + registry_entry : io_object_t; + in plane : io_name_t; + in options : uint32_t; + out iterator : io_object_t + ); + +routine io_iterator_is_valid( + iterator : io_object_t; + out is_valid : boolean_t + ); + +skip; +/* was routine io_make_matching( + master_port : mach_port_t; + in of_type : uint32_t; + in options : uint32_t; + in input : io_struct_inband_t; + out matching : io_string_t + ); +*/ + +routine io_catalog_send_data( + master_port : mach_port_t; + in flag : uint32_t; + in inData : io_buf_ptr_t; + out result : kern_return_t + ); + +routine io_catalog_terminate( + master_port : mach_port_t; + in flag : uint32_t; + in name : io_name_t + ); + +routine io_catalog_get_data( + master_port : mach_port_t; + in flag : uint32_t; + out outData : io_buf_ptr_t + ); + +routine io_catalog_get_gen_count( + master_port : mach_port_t; + out genCount : uint32_t + ); + +routine io_catalog_module_loaded( + master_port : mach_port_t; + in name : io_name_t + ); + +routine io_catalog_reset( + master_port : mach_port_t; + in flag : uint32_t + ); + +routine io_service_request_probe( + service : io_object_t; + in options : uint32_t + ); + +routine io_registry_entry_get_name_in_plane( + registry_entry : io_object_t; + in plane : io_name_t; + out name : io_name_t + ); + +routine io_service_match_property_table( + service : io_object_t; + in matching : io_string_t; + out matches : boolean_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_async_method_scalarI_scalarO( + connection : io_connect_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + out output : io_scalar_inband_t, CountInOut + ); +routine io_async_method_scalarI_structureO( + connection : io_connect_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + out output : io_struct_inband_t, CountInOut + ); +routine io_async_method_scalarI_structureI( + connection : io_connect_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + in selector : uint32_t; + in input : io_scalar_inband_t; + in inputStruct : io_struct_inband_t + ); +routine io_async_method_structureI_structureO( + connection : io_connect_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + in selector : uint32_t; + in input : io_struct_inband_t; + out output : io_struct_inband_t, CountInOut + ); +#else +skip; +skip; +skip; +skip; +#endif + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_service_add_notification( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_string_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + out notification : io_object_t + ); +routine io_service_add_interest_notification( + service : io_object_t; + in type_of_interest : io_name_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + out notification : io_object_t + ); +routine io_service_acknowledge_notification( + service : io_object_t; + in notify_ref : natural_t; + in response : natural_t + ); +#else +skip; +skip; +skip; +#endif + +routine io_connect_get_notification_semaphore( + connection : io_connect_t; + in notification_type : natural_t; + out semaphore : semaphore_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_connect_unmap_memory( + connection : io_connect_t; + in memory_type : uint32_t; + in into_task : task_t; +#if IOKIT_ALL_IPC + in address : uint32_t +#else + in address : vm_address_t +#endif + ); +#else +skip; +#endif + +routine io_registry_entry_get_location_in_plane( + registry_entry : io_object_t; + in plane : io_name_t; + out location : io_name_t + ); + +routine io_registry_entry_get_property_recursively( + registry_entry : io_object_t; + in plane : io_name_t; + in property_name : io_name_t; + in options : uint32_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_service_get_state( + service : io_object_t; + out state : uint64_t; + out busy_state : uint32_t; + out accumulated_busy_time : uint64_t + ); + +routine io_service_get_matching_services_ool( + master_port : mach_port_t; + in matching : io_buf_ptr_t, physicalcopy; + out result : kern_return_t; + out existing : io_object_t + ); + +routine io_service_match_property_table_ool( + service : io_object_t; + in matching : io_buf_ptr_t, physicalcopy; + out result : kern_return_t; + out matches : boolean_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_service_add_notification_ool( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_buf_ptr_t, physicalcopy; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + out result : kern_return_t; + out notification : io_object_t + ); +#else +skip; +#endif + +routine io_object_get_superclass( + master_port : mach_port_t; + in obj_name : io_name_t; + out class_name : io_name_t + ); + +routine io_object_get_bundle_identifier( + master_port : mach_port_t; + in obj_name : io_name_t; + out class_name : io_name_t + ); + +routine io_service_open_extended( + service : io_object_t; + in owningTask : task_t; + in connect_type : uint32_t; + in ndr : NDR_record_t; + in properties : io_buf_ptr_t, physicalcopy; + out result : kern_return_t; + out connection : io_connect_t + ); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +routine io_connect_map_memory_into_task( + connection : io_connect_t; + in memory_type : uint32_t; + in into_task : task_t; + inout address : mach_vm_address_t; + inout size : mach_vm_size_t; + in flags : uint32_t + ); + +routine io_connect_unmap_memory_from_task( + connection : io_connect_t; + in memory_type : uint32_t; + in from_task : task_t; + in address : mach_vm_address_t + ); + +routine io_connect_method( + connection : io_connect_t; + in selector : uint32_t; + + in scalar_input : io_scalar_inband64_t; + in inband_input : io_struct_inband_t; + in ool_input : mach_vm_address_t; + in ool_input_size : mach_vm_size_t; + + out inband_output : io_struct_inband_t, CountInOut; + out scalar_output : io_scalar_inband64_t, CountInOut; + in ool_output : mach_vm_address_t; + inout ool_output_size : mach_vm_size_t + ); + +routine io_connect_async_method( + connection : io_connect_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref64_t; + in selector : uint32_t; + + in scalar_input : io_scalar_inband64_t; + in inband_input : io_struct_inband_t; + in ool_input : mach_vm_address_t; + in ool_input_size : mach_vm_size_t; + + out inband_output : io_struct_inband_t, CountInOut; + out scalar_output : io_scalar_inband64_t, CountInOut; + in ool_output : mach_vm_address_t; + inout ool_output_size : mach_vm_size_t + ); + + +#if IOKIT_ALL_IPC || __LP64__ + +#if IOKIT_ALL_IPC +#define FUNC_NAME(name) name ## _64 +#else +#define FUNC_NAME(name) name +#endif + +routine FUNC_NAME(io_connect_set_notification_port)( + connection : io_connect_t; + in notification_type : uint32_t; + in port : mach_port_make_send_t; + in reference : io_user_reference_t + ); + +routine FUNC_NAME(io_service_add_notification)( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_string_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref64_t; + out notification : io_object_t + ); + +routine FUNC_NAME(io_service_add_interest_notification)( + service : io_object_t; + in type_of_interest : io_name_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref64_t; + out notification : io_object_t + ); + +routine FUNC_NAME(io_service_add_notification_ool)( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_buf_ptr_t, physicalcopy; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref64_t; + out result : kern_return_t; + out notification : io_object_t + ); + +#else + + skip; + skip; + skip; + skip; + +#endif /* IOKIT_ALL_IPC || __LP64__ */ + +routine io_registry_entry_get_registry_entry_id( + registry_entry : io_object_t; + out entry_id : uint64_t + ); + +routine io_connect_method_var_output( + connection : io_connect_t; + in selector : uint32_t; + + in scalar_input : io_scalar_inband64_t; + in inband_input : io_struct_inband_t; + in ool_input : mach_vm_address_t; + in ool_input_size : mach_vm_size_t; + + out inband_output : io_struct_inband_t, CountInOut; + out scalar_output : io_scalar_inband64_t, CountInOut; + out var_output : io_buf_ptr_t, physicalcopy + ); + +routine io_service_get_matching_service( + master_port : mach_port_t; + in matching : io_string_t; + out service : io_object_t + ); + +routine io_service_get_matching_service_ool( + master_port : mach_port_t; + in matching : io_buf_ptr_t, physicalcopy; + out result : kern_return_t; + out service : io_object_t + ); + +routine io_service_get_authorization_id( + service : io_object_t; + out authorization_id : uint64_t + ); + +routine io_service_set_authorization_id( + service : io_object_t; + in authorization_id : uint64_t + ); + +/* */ + +routine io_server_version( + master_port : mach_port_t; + out version : uint64_t + ); + +routine io_registry_entry_get_properties_bin( + registry_entry : io_object_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_registry_entry_get_property_bin( + registry_entry : io_object_t; + in plane : io_name_t; + in property_name : io_name_t; + in options : uint32_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_service_get_matching_service_bin( + master_port : mach_port_t; + in matching : io_struct_inband_t; + out service : io_object_t + ); + +routine io_service_get_matching_services_bin( + master_port : mach_port_t; + in matching : io_struct_inband_t; + out existing : io_object_t + ); + +routine io_service_match_property_table_bin( + service : io_object_t; + in matching : io_struct_inband_t; + out matches : boolean_t + ); + +#if IOKIT_ALL_IPC || __ILP32__ +routine io_service_add_notification_bin( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_struct_inband_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref_t; + out notification : io_object_t + ); +#else +skip; +#endif + +#if IOKIT_ALL_IPC || __LP64__ +routine FUNC_NAME(io_service_add_notification_bin)( + master_port : mach_port_t; + in notification_type : io_name_t; + in matching : io_struct_inband_t; + in wake_port : mach_port_make_send_t; + in reference : io_async_ref64_t; + out notification : io_object_t + ); +#else +skip; +#endif + +#if !IOKITSIMD + +routine io_registry_entry_get_path_ool( + registry_entry : io_object_t; + in plane : io_name_t; + out path : io_string_inband_t; + out path_ool : io_buf_ptr_t, physicalcopy + ); + +routine io_registry_entry_from_path_ool( + master_port : mach_port_t; + in path : io_string_inband_t; + in path_ool : io_buf_ptr_t, physicalcopy; + out result : kern_return_t; + out registry_entry : io_object_t + ); + +#endif + +routine io_device_tree_entry_exists_with_name( + master_port : mach_port_t; + in name : io_name_t; + out exists : boolean_t + ); + +routine io_registry_entry_get_properties_bin_buf( + registry_entry : io_object_t; + in buf : mach_vm_address_t; + inout bufsize : mach_vm_size_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +routine io_registry_entry_get_property_bin_buf( + registry_entry : io_object_t; + in plane : io_name_t; + in property_name : io_name_t; + in options : uint32_t; + in buf : mach_vm_address_t; + inout bufsize : mach_vm_size_t; + out properties : io_buf_ptr_t, physicalcopy + ); + +#endif /* IOKIT */ + +/* vim: set ft=c : */ diff --git a/Package.resolved b/Package.resolved index 9b55b3616..234bed8df 100644 --- a/Package.resolved +++ b/Package.resolved @@ -3,20 +3,20 @@ "pins": [ { "package": "AEXML", - "repositoryURL": "https://github.com/tadija/AEXML", + "repositoryURL": "https://github.com/tadija/AEXML.git", "state": { "branch": null, - "revision": "502c4d43a6cc9c395d19111e09dc62ad834977b5", - "version": "4.6.0" + "revision": "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", + "version": "4.6.1" } }, { "package": "PathKit", - "repositoryURL": "https://github.com/kylef/PathKit", + "repositoryURL": "https://github.com/kylef/PathKit.git", "state": { "branch": null, - "revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511", - "version": "1.0.0" + "revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", + "version": "1.0.1" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/kylef/Spectre.git", "state": { "branch": null, - "revision": "f717bbce0e19f0129fc001b2b6bed43b70fd8b87", - "version": "0.9.1" + "revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7", + "version": "0.10.1" } } ] diff --git a/Package.swift b/Package.swift index 95e46cdf7..e31baf94b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.2.0 import PackageDescription @@ -8,8 +8,8 @@ let package = Package( .library(name: "XcodeProj", targets: ["XcodeProj"]), ], dependencies: [ - .package(url: "https://github.com/tadija/AEXML", .upToNextMinor(from: "4.5.0")), - .package(url: "https://github.com/kylef/PathKit", .upToNextMinor(from: "1.0.0")), + .package(url: "https://github.com/tadija/AEXML.git", .upToNextMinor(from: "4.6.1")), + .package(url: "https://github.com/kylef/PathKit.git", .upToNextMinor(from: "1.0.0")), ], targets: [ .target(name: "XcodeProj", diff --git a/Project.swift b/Project.swift index cf19b20f0..d96f3f5ac 100644 --- a/Project.swift +++ b/Project.swift @@ -11,6 +11,5 @@ let project = Project(name: "XcodeProj_Carthage", dependencies: [ .framework(path: "Carthage/Build/Mac/AEXML.framework"), .framework(path: "Carthage/Build/Mac/PathKit.framework"), - .framework(path: "Carthage/Build/Mac/XcodeProjCExt.framework"), ]), ]) diff --git a/README.md b/README.md index 71100a261..7b40e9b53 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,42 @@ # XcodeProj - -[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) - +[![All Contributors](https://img.shields.io/badge/all_contributors-28-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/main/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/main/LICENSE.md) 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) + - [Carthage](#carthage) + - [CocoaPods](#cocoapods) + - [Scripting](#scripting) + - [Documentation 📝](#documentation-) + - [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) | If you are also leveraging XcodeProj in your project, feel free to open a PR to include it in the list above. @@ -43,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.18.0")) - ], + .package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.9.0")), + ], targets: [ .target( name: "myproject", @@ -59,13 +66,13 @@ let package = Package( ```bash # Cartfile -github "tuist/xcodeproj" ~> 7.11. +github "tuist/xcodeproj" ~> 8.8.0 ``` ### CocoaPods ```ruby -pod 'xcodeproj', '~> 7.18.0 +pod 'xcodeproj', '~> 8.8.0 ``` ### Scripting @@ -77,7 +84,7 @@ git tag that represents the project’s version: ```swift #!/usr/bin/swift sh import Foundation -import XcodeProj // @tuist ~> 7.11. +import XcodeProj // @tuist ~> 8.8.0 import PathKit guard CommandLine.arguments.count == 3 else { @@ -133,8 +140,7 @@ Want to start using XcodeProj? Start by digging into our [documentation](/Docume ## 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 @@ -148,18 +154,48 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Joseph Colicchio

🤔

deatondg

🤔

Dan Fleming

💻

Sascha Schwabbauer

🤔

Marcin Iwanicki

🚧

Adam Khazi

🚧

Elliott Williams

💻
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

📖
- + diff --git a/RELEASE.md b/RELEASE.md index c58657b4b..9e9c4e925 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,6 +2,14 @@ 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. +1. Make sure you are in the `main` branch. +2. Determine the next version based on the unreleased changes. +3. Add the version section to the `CHANGELOG.md`, update the versions in the `README.md` and the `xcodeproj.podspec` file. +4. Commit the changes and tag them `git commit -m "Version x.y.z"` & `git tag x.y.z`. +5. Push the changes `git push origin main --tags` +6. Update Carthage dependencies by running `bundle exec rake carthage_update_dependencies`. +7. Generate the project by running `tuist generate`. +8. Run the release checks by running `bundle exec rake release_check`. +9. Publish a new version of the Pod by running `bundle exec pod trunk push --allow-warnings --verbose`. +10. Archive the Carthage binary by running `bundle exec rake archive_carthage`. +11. Create the release on GitHub including the release notes from the `CHANGELOG.md` and attach the archive `xcodeproj.framework.zip` generated by Carthage. diff --git a/Sources/XcodeProj/Errors/Errors.swift b/Sources/XcodeProj/Errors/Errors.swift index 1b781e8b4..6554f83fa 100644 --- a/Sources/XcodeProj/Errors/Errors.swift +++ b/Sources/XcodeProj/Errors/Errors.swift @@ -41,6 +41,22 @@ public enum XCSharedDataError: Error, CustomStringConvertible { } } +// 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): + return "xcuserdata not found at path \(path.string)" + } + } +} + // MARK: - XCWorkspace /// XCWorkspace Errors diff --git a/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift b/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift index 4e0617b46..33a5a9b65 100644 --- a/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift +++ b/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift @@ -13,6 +13,7 @@ let attributesOrder: [String: [String]] = [ "BuildAction": [ "parallelizeBuildables", "buildImplicitDependencies", + "runPostActionsOnFailure", ], "BuildActionEntry": [ "buildForTesting", @@ -32,20 +33,27 @@ let attributesOrder: [String: [String]] = [ "buildConfiguration", "selectedDebuggerIdentifier", "selectedLauncherIdentifier", + "customLLDBInitFile", "language", + "shouldUseLaunchSchemeArgsEnv", + "disableMainThreadChecker", "region", "codeCoverageEnabled", "onlyGenerateCoverageForSpecifiedTargets", - "shouldUseLaunchSchemeArgsEnv", ], "LaunchAction": [ "buildConfiguration", "selectedDebuggerIdentifier", "selectedLauncherIdentifier", + "customLLDBInitFile", + "disableMainThreadChecker", + "disablePerformanceAntipatternChecker", "language", "region", "launchStyle", + "askForAppToLaunch", "useCustomWorkingDirectory", + "customWorkingDirectory", "ignoresPersistentStateOnLaunch", "debugDocumentVersioning", "debugServiceExtension", @@ -59,6 +67,7 @@ let attributesOrder: [String: [String]] = [ "shouldUseLaunchSchemeArgsEnv", "savedToolIdentifier", "useCustomWorkingDirectory", + "customWorkingDirectory", "ignoresPersistentStateOnLaunch", "debugDocumentVersioning", "enableTestabilityWhenProfilingTests", @@ -89,6 +98,16 @@ let attributesOrder: [String: [String]] = [ "symbolName", "moduleName", ], + "RemoteRunnable": [ + "runnableDebuggingMode", + "BundleIdentifier", + "RemotePath" + ], + "Scheme": [ + "LastUpgradeVersion", + "wasCreatedForAppExtension", + "version" + ] ] extension AEXMLElement { diff --git a/Sources/XcodeProj/Extensions/Path+Extras.swift b/Sources/XcodeProj/Extensions/Path+Extras.swift index 9a20677cd..b26a48976 100644 --- a/Sources/XcodeProj/Extensions/Path+Extras.swift +++ b/Sources/XcodeProj/Extensions/Path+Extras.swift @@ -24,7 +24,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) diff --git a/Sources/XcodeProj/Extensions/String+md5.swift b/Sources/XcodeProj/Extensions/String+md5.swift index 77ad4a934..5abdf59a7 100644 --- a/Sources/XcodeProj/Extensions/String+md5.swift +++ b/Sources/XcodeProj/Extensions/String+md5.swift @@ -32,12 +32,8 @@ extension String { } #if canImport(CryptoKit) if #available(OSX 10.15, *) { - var hasher = Insecure.MD5() - hasher.update(data: data) - let digest = hasher.finalize() - return digest.reduce(into: "") { hexString, byte in - hexString.append(String(format: "%02hhx", byte)) - } + return Insecure.MD5.hash(data: data) + .withUnsafeBytes { Array($0) }.hexString } else { return data.slowMD5 } @@ -47,6 +43,31 @@ extension String { } } +private let charA = UInt8(UnicodeScalar("a").value) +private let char0 = UInt8(UnicodeScalar("0").value) + +private extension DataProtocol { + var hexString: String { + let hexLen = self.count * 2 + let ptr = UnsafeMutablePointer.allocate(capacity: hexLen) + var offset = 0 + + self.regions.forEach { (_) in + for i in self { + ptr[Int(offset * 2)] = itoh((i >> 4) & 0xF) + ptr[Int(offset * 2 + 1)] = itoh(i & 0xF) + offset += 1 + } + } + + return String(bytesNoCopy: ptr, length: hexLen, encoding: .utf8, freeWhenDone: true)! + } + + func itoh(_ value: UInt8) -> UInt8 { + return (value > 9) ? (charA + value - 10) : (char0 + value) + } +} + private extension Data { // Custom md5 for systems without CryptoKit. var slowMD5: String { @@ -59,12 +80,7 @@ private extension Data { let MD5Calculator = MD5(message) let MD5Data = MD5Calculator.calculate() - - var MD5String = String() - for c in MD5Data { - MD5String += String(format: "%02x", c) - } - return MD5String + return MD5Data.hexString } } diff --git a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift index 7982d6be5..99e8c8445 100644 --- a/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift +++ b/Sources/XcodeProj/Objects/BuildPhase/PBXBuildFile.swift @@ -37,6 +37,9 @@ public final class PBXBuildFile: PBXObject { /// 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 +53,14 @@ public final class PBXBuildFile: PBXObject { /// - settings: build file settings. public init(file: PBXFileElement? = nil, product: XCSwiftPackageProductDependency? = nil, - settings: [String: Any]? = nil) { + settings: [String: Any]? = 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 +71,7 @@ public final class PBXBuildFile: PBXObject { case settings case productRef case platformFilter + case platformFilters } public required init(from decoder: Decoder) throws { @@ -78,6 +86,7 @@ public final class PBXBuildFile: PBXObject { } settings = try container.decodeIfPresent([String: Any].self, forKey: .settings) platformFilter = try container.decodeIfPresent(.platformFilter) + platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) try super.init(from: decoder) } @@ -174,6 +183,9 @@ final class PBXBuildPhaseFile: PlistSerializable, Equatable { if let platformFilter = buildFile.platformFilter { dictionary["platformFilter"] = .string(.init(platformFilter)) } + 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)) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift index fb245cd55..573e8ee8e 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift @@ -144,6 +144,15 @@ public class PBXFileElement: PBXContainerItem, PlistSerializable { // 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. diff --git a/Sources/XcodeProj/Objects/Files/PBXGroup.swift b/Sources/XcodeProj/Objects/Files/PBXGroup.swift index 5387cdc3b..c2bded056 100644 --- a/Sources/XcodeProj/Objects/Files/PBXGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXGroup.swift @@ -138,7 +138,7 @@ public extension PBXGroup { 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) diff --git a/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift b/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift index c6813001d..374e725a1 100644 --- a/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift +++ b/Sources/XcodeProj/Objects/Project/PBXOutputSettings.swift @@ -81,7 +81,7 @@ 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. diff --git a/Sources/XcodeProj/Objects/Project/PBXProj.swift b/Sources/XcodeProj/Objects/Project/PBXProj.swift index 32b199453..1319ac847 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProj.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProj.swift @@ -52,6 +52,35 @@ 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 + ) + } + + private init( + rootObject: PBXProject? = nil, + objectVersion: UInt = Xcode.LastKnown.objectVersion, + archiveVersion: UInt = Xcode.LastKnown.archiveVersion, + classes: [String: Any] = [:], + objects: PBXObjects + ) { + self.archiveVersion = archiveVersion + self.objectVersion = objectVersion + self.classes = classes + rootObjectReference = rootObject?.reference + self.objects = objects + } + // MARK: - Decodable fileprivate enum CodingKeys: String, CodingKey { @@ -92,6 +121,35 @@ public final class PBXProj: Decodable { try rootGroup()?.assignParentToChildren() } + + // MARK: Static Methods + + private static func createPBXProj(path: Path) throws -> PBXProj { + let (pbxProjData, pbxProjDictionary) = try PBXProj.readPBXProj(path: path) + let context = ProjectDecodingContext( + pbxProjValueReader: { key in + pbxProjDictionary[key] + } + ) + + let plistDecoder = XcodeprojPropertyListDecoder(context: context) + let pbxproj: PBXProj = try plistDecoder.decode(PBXProj.self, from: pbxProjData) + try pbxproj.updateProjectName(path: path) + return pbxproj + } + + 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) + } } // MARK: - Public helpers @@ -198,7 +256,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. diff --git a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift index 9daa37a2d..7b9c985e4 100644 --- a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift +++ b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift @@ -15,7 +15,7 @@ extension PBXBuildFile { func isEqual(to rhs: PBXBuildFile) -> Bool { if fileReference != rhs.fileReference { return false } if productReference != rhs.productReference { return false } - if !NSDictionary(dictionary: settings ?? [:]).isEqual(to: rhs.settings ?? [:]) { return false } + if !NSDictionary(dictionary: settings ?? [:]).isEqual(NSDictionary(dictionary: rhs.settings ?? [:])) { return false } if platformFilter != rhs.platformFilter { return false } if buildPhase != rhs.buildPhase { return false } return super.isEqual(to: rhs) @@ -166,8 +166,8 @@ 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 !NSDictionary(dictionary: attributes).isEqual(NSDictionary(dictionary: rhs.attributes)) { return false } + if !NSDictionary(dictionary: targetAttributeReferences).isEqual(NSDictionary(dictionary: rhs.targetAttributeReferences)) { return false } if packageReferences != rhs.packageReferences { return false } return super.isEqual(to: rhs) } @@ -257,7 +257,7 @@ extension XCBuildConfiguration { /// :nodoc: func isEqual(to rhs: XCBuildConfiguration) -> Bool { if baseConfigurationReference != rhs.baseConfigurationReference { return false } - if !NSDictionary(dictionary: buildSettings).isEqual(to: rhs.buildSettings) { return false } + if !NSDictionary(dictionary: buildSettings).isEqual(NSDictionary(dictionary: rhs.buildSettings)) { return false } if name != rhs.name { return false } return super.isEqual(to: rhs) } diff --git a/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift index e963ecd2a..ec87371fe 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift @@ -15,3 +15,30 @@ extension PBXAggregateTarget: PlistSerializable { return 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/PBXNativeTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXNativeTarget.swift index 5b1296136..45c4bbcb3 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. diff --git a/Sources/XcodeProj/Objects/Targets/PBXProductType.swift b/Sources/XcodeProj/Objects/Targets/PBXProductType.swift index 389d0ee35..21dbf8b78 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXProductType.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXProductType.swift @@ -12,6 +12,7 @@ public enum PBXProductType: String, Decodable { 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" @@ -29,6 +30,8 @@ public enum PBXProductType: String, Decodable { 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? { @@ -45,7 +48,8 @@ public enum PBXProductType: String, Decodable { return "bundle" case .unitTestBundle, .uiTestBundle: return "xctest" - case .appExtension, .tvExtension, .watchExtension, .watch2Extension, .messagesExtension, .stickerPack, .xcodeExtension, .intentsServiceExtension: + case .appExtension, .extensionKitExtension, .tvExtension, .watchExtension, .watch2Extension, .messagesExtension, .stickerPack, .xcodeExtension, + .intentsServiceExtension: return "appex" case .commandLineTool: return nil @@ -59,6 +63,10 @@ public enum PBXProductType: String, Decodable { return "xcframework" case .metalLibrary: return "metallib" + case .systemExtension: + return "systemextension" + case .driverExtension: + return "dext" case .none: return nil } diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 9e2213586..b5440059f 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -282,4 +282,13 @@ public extension PBXTarget { .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 bd3379ac0..22603e5b3 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTargetDependency.swift @@ -50,6 +50,9 @@ public final class PBXTargetDependency: PBXObject { /// 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. @@ -57,15 +60,18 @@ public final class PBXTargetDependency: PBXObject { /// - 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 @@ -77,6 +83,7 @@ public final class PBXTargetDependency: PBXObject { fileprivate enum CodingKeys: String, CodingKey { case name case platformFilter + case platformFilters case target case targetProxy case productRef @@ -88,6 +95,7 @@ public final class PBXTargetDependency: PBXObject { 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) } @@ -118,6 +126,9 @@ extension PBXTargetDependency: PlistSerializable { if let platformFilter = platformFilter { dictionary["platformFilter"] = .string(CommentedString(platformFilter)) } + if let platformFilters = platformFilters { + dictionary["platformFilters"] = .array(platformFilters.map { .string(.init($0)) }) + } if let targetReference = targetReference { let targetObject: PBXTarget? = targetReference.getObject() dictionary["target"] = .string(CommentedString(targetReference.value, comment: targetObject?.name)) diff --git a/Sources/XcodeProj/Project/WorkspaceSettings.swift b/Sources/XcodeProj/Project/WorkspaceSettings.swift index 128f9f89d..c8f775735 100644 --- a/Sources/XcodeProj/Project/WorkspaceSettings.swift +++ b/Sources/XcodeProj/Project/WorkspaceSettings.swift @@ -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,41 @@ 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,6 +100,12 @@ public class WorkspaceSettings: Codable, Equatable, Writable { if buildSystem == .original { try container.encode(buildSystem.rawValue, forKey: .buildSystem) } + if let derivedDataLocationStyle = derivedDataLocationStyle { + try container.encode(derivedDataLocationStyle.rawValue, forKey: .derivedDataLocationStyle) + } + if let derivedDataCustomLocation = derivedDataCustomLocation { + try container.encode(derivedDataCustomLocation, forKey: .derivedDataCustomLocation) + } if let autoCreateSchemes = autoCreateSchemes { try container.encode(autoCreateSchemes, forKey: .autoCreateSchemes) } @@ -92,7 +133,9 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// - Returns: True if the two instances are the same. public static func == (lhs: WorkspaceSettings, rhs: WorkspaceSettings) -> Bool { lhs.buildSystem == rhs.buildSystem && - lhs.autoCreateSchemes == rhs.autoCreateSchemes + lhs.autoCreateSchemes == rhs.autoCreateSchemes && + lhs.derivedDataLocationStyle == rhs.derivedDataLocationStyle && + lhs.derivedDataCustomLocation == rhs.derivedDataCustomLocation } /// Writes the workspace settings. @@ -102,6 +145,7 @@ public class WorkspaceSettings: Codable, Equatable, Writable { /// - Throws: writing error if something goes wrong. public func write(path: Path, override: Bool) throws { let encoder = PropertyListEncoder() + encoder.outputFormat = .xml let data = try encoder.encode(self) if override, path.exists { try path.delete() @@ -109,3 +153,9 @@ public class WorkspaceSettings: Codable, Equatable, Writable { try path.write(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 fb76ff74e..60b5b8ff7 100644 --- a/Sources/XcodeProj/Project/XCBreakpointList.swift +++ b/Sources/XcodeProj/Project/XCBreakpointList.swift @@ -426,3 +426,13 @@ public final class XCBreakpointList: Equatable, Writable { lhs.version == rhs.version } } + +extension XCBreakpointList { + /// Returns breakpoints plist path relative to the given path. + /// + /// - Parameter path: debugger folder + /// - Returns: breakpoints plist path relative to the given path. + public 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 18d788efd..9469970c9 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 { @@ -55,4 +57,63 @@ public final class XCSharedData: Equatable { 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 = 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) + } +} + +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. + public 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..1d3bf328d --- /dev/null +++ b/Sources/XcodeProj/Project/XCUserData.swift @@ -0,0 +1,117 @@ +import Foundation +import PathKit +import AEXML + +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 = 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 = breakpoints else { return } + + let debuggerPath = XCDebugger.path(path) + try debuggerPath.mkpath() + try breakpoints.write(path: XCBreakpointList.path(debuggerPath), override: override) + } +} + +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. + public 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. + public 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 6384e9a59..4d8a1d154 100644 --- a/Sources/XcodeProj/Project/Xcode.swift +++ b/Sources/XcodeProj/Project/Xcode.swift @@ -7,41 +7,41 @@ public struct Xcode { /// Last known SDKs. public struct 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 = 54 + public static let objectVersion: UInt = 55 /// Last known upgrade check. - public static let upgradeCheck = "1000" + public static let upgradeCheck = "1240" /// Last known Swift upgrade check. - public static let swiftUpgradeCheck = "1000" + public static let swiftUpgradeCheck = "1240" } /// Default values. public struct 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 13.0" /// Default development region. public static let developmentRegion: String = "en" @@ -100,9 +100,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.cfbundle", + "bzl": "text.script.python", "c": "sourcecode.c.c", "c++": "sourcecode.cpp.cpp", "cc": "sourcecode.cpp.cpp", @@ -124,6 +126,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", diff --git a/Sources/XcodeProj/Project/XcodeProj.swift b/Sources/XcodeProj/Project/XcodeProj.swift index ceddb6bdf..83734f246 100644 --- a/Sources/XcodeProj/Project/XcodeProj.swift +++ b/Sources/XcodeProj/Project/XcodeProj.swift @@ -8,35 +8,28 @@ public final class XcodeProj: Equatable { /// Project workspace public var workspace: XCWorkspace - /// .pbxproj representatino + /// .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 +39,14 @@ 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.pbxproj = pbxproj self.workspace = workspace self.sharedData = sharedData + self.userData = userData } public convenience init(pathString: String) throws { @@ -60,10 +58,16 @@ 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] = []) { self.workspace = workspace self.pbxproj = pbxproj self.sharedData = sharedData + self.userData = userData } // MARK: - Equatable @@ -71,22 +75,8 @@ public final class XcodeProj: Equatable { public static func == (lhs: XcodeProj, rhs: XcodeProj) -> Bool { 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 } } @@ -112,14 +102,14 @@ 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 { path + "project.xcworkspace" } @@ -156,24 +146,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 { - path + "xcshareddata" + XCSharedData.path(path) } - /// Returns schemes folder path relative to the given 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) + } + + /// 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 { - 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 { - XcodeProj.schemesPath(path) + "\(schemeName).xcscheme" + XCScheme.path(schemesPath(path), schemeName: schemeName) } /// Writes all project schemes to the given path. @@ -183,34 +197,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 { - 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 { - XcodeProj.debuggerPath(path) + "Breakpoints_v2.xcbkptlist" + XCBreakpointList.path(debuggerPath(path)) } /// Writes all project breakpoints to the given path. @@ -220,13 +232,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..b41ecfcda 100644 --- a/Sources/XcodeProj/Protocols/Writable.swift +++ b/Sources/XcodeProj/Protocols/Writable.swift @@ -1,19 +1,19 @@ 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 } diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift index 2be673ade..147d7cf1a 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift @@ -75,6 +75,7 @@ extension XCScheme { public var buildActionEntries: [Entry] public var parallelizeBuild: Bool public var buildImplicitDependencies: Bool + public var runPostActionsOnFailure: Bool? // MARK: - Init @@ -82,16 +83,19 @@ extension XCScheme { preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], parallelizeBuild: Bool = false, - buildImplicitDependencies: Bool = false) { + buildImplicitDependencies: Bool = false, + runPostActionsOnFailure: Bool? = nil) { self.buildActionEntries = buildActionEntries self.parallelizeBuild = parallelizeBuild self.buildImplicitDependencies = buildImplicitDependencies + self.runPostActionsOnFailure = runPostActionsOnFailure 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) ?? [] @@ -110,12 +114,18 @@ extension XCScheme { // MARK: - XML func xmlElement() -> AEXMLElement { + var attributes = [ + "parallelizeBuildables": parallelizeBuild.xmlString, + "buildImplicitDependencies": buildImplicitDependencies.xmlString, + ] + + if let runPostActionsOnFailure = 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 @@ -131,7 +141,8 @@ extension XCScheme { return super.isEqual(to: to) && buildActionEntries == rhs.buildActionEntries && parallelizeBuild == rhs.parallelizeBuild && - buildImplicitDependencies == rhs.buildImplicitDependencies + buildImplicitDependencies == rhs.buildImplicitDependencies && + runPostActionsOnFailure == rhs.runPostActionsOnFailure } } } diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift index ba768c034..f35011ed7 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildableReference.swift @@ -2,7 +2,7 @@ import AEXML import Foundation extension XCScheme { - public final class BuildableReference: Equatable { + public final class BuildableReference: Equatable, Hashable { // MARK: - Attributes public var referencedContainer: String @@ -23,9 +23,9 @@ extension XCScheme { blueprint = .reference(object.reference) } - private var blueprint: Blueprint - public var blueprintIdentifier: String { - 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 + self.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,22 +74,26 @@ extension XCScheme { throw XCSchemeError.missing(property: "ReferencedContainer") } self.buildableIdentifier = buildableIdentifier - blueprint = .string(blueprintIdentifier) + let blueprintIdentifier = element.attributes["BlueprintIdentifier"] + self.blueprint = blueprintIdentifier.map(Blueprint.string) self.buildableName = buildableName self.blueprintName = blueprintName self.referencedContainer = referencedContainer } func xmlElement() -> AEXMLElement { - AEXMLElement(name: "BuildableReference", - value: nil, - attributes: [ - "BuildableIdentifier": buildableIdentifier, - "BlueprintIdentifier": blueprint.string, - "BuildableName": buildableName, - "BlueprintName": blueprintName, - "ReferencedContainer": referencedContainer, - ]) + 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: attributes) } // MARK: - Equatable @@ -92,5 +105,14 @@ extension XCScheme { 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+ExecutionAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift index 5dd3d7d21..484e99aaa 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ExecutionAction.swift @@ -9,19 +9,29 @@ extension XCScheme { 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,12 +41,16 @@ extension XCScheme { let element = AEXMLElement(name: "ExecutionAction", value: nil, attributes: ["ActionType": ExecutionAction.ActionType]) + var attributes = [ + "title": title, + "scriptText": scriptText, + ] + if let shellToInvoke = 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 { let environment = content.addChild(name: "EnvironmentBuildable") diff --git a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift index 3a4e8edca..6f3461ee1 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift @@ -42,6 +42,7 @@ extension XCScheme { public var launchStyle: Style public var askForAppToLaunch: Bool? public var pathRunnable: PathRunnable? + public var customWorkingDirectory: String? public var useCustomWorkingDirectory: Bool public var ignoresPersistentStateOnLaunch: Bool public var debugDocumentVersioning: Bool @@ -57,6 +58,7 @@ 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? @@ -81,6 +83,7 @@ extension XCScheme { launchStyle: Style = .auto, askForAppToLaunch: Bool? = nil, pathRunnable: PathRunnable? = nil, + customWorkingDirectory: String? = nil, useCustomWorkingDirectory: Bool = false, ignoresPersistentStateOnLaunch: Bool = false, debugDocumentVersioning: Bool = true, @@ -96,6 +99,7 @@ extension XCScheme { enableUBSanitizer: Bool = false, stopOnEveryUBSanitizerIssue: Bool = false, disableMainThreadChecker: Bool = false, + disablePerformanceAntipatternChecker: Bool = false, stopOnEveryMainThreadCheckerIssue: Bool = false, additionalOptions: [AdditionalOption] = [], commandlineArguments: CommandLineArguments? = nil, @@ -114,6 +118,7 @@ extension XCScheme { self.selectedLauncherIdentifier = selectedLauncherIdentifier self.askForAppToLaunch = askForAppToLaunch self.pathRunnable = pathRunnable + self.customWorkingDirectory = customWorkingDirectory self.useCustomWorkingDirectory = useCustomWorkingDirectory self.ignoresPersistentStateOnLaunch = ignoresPersistentStateOnLaunch self.debugDocumentVersioning = debugDocumentVersioning @@ -129,6 +134,7 @@ extension XCScheme { self.enableUBSanitizer = enableUBSanitizer self.stopOnEveryUBSanitizerIssue = stopOnEveryUBSanitizerIssue self.disableMainThreadChecker = disableMainThreadChecker + self.disablePerformanceAntipatternChecker = disablePerformanceAntipatternChecker self.stopOnEveryMainThreadCheckerIssue = stopOnEveryMainThreadCheckerIssue self.additionalOptions = additionalOptions self.commandlineArguments = commandlineArguments @@ -148,7 +154,7 @@ extension XCScheme { 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" } + 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 @@ -191,6 +197,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"] @@ -218,6 +225,9 @@ extension XCScheme { } customLaunchCommand = element.attributes["customLaunchCommand"] customLLDBInitFile = element.attributes["customLLDBInitFile"] + if let elementCustomWorkingDirectory: String = element.attributes["customWorkingDirectory"] { + customWorkingDirectory = elementCustomWorkingDirectory + } try super.init(element: element) } @@ -267,9 +277,15 @@ extension XCScheme { if disableMainThreadChecker { attributes["disableMainThreadChecker"] = disableMainThreadChecker.xmlString } + if disablePerformanceAntipatternChecker { + attributes["disablePerformanceAntipatternChecker"] = disablePerformanceAntipatternChecker.xmlString + } if stopOnEveryMainThreadCheckerIssue { attributes["stopOnEveryMainThreadCheckerIssue"] = stopOnEveryMainThreadCheckerIssue.xmlString } + if let customWorkingDirectory = customWorkingDirectory { + attributes["customWorkingDirectory"] = customWorkingDirectory + } return attributes } @@ -350,6 +366,7 @@ extension XCScheme { launchStyle == rhs.launchStyle && askForAppToLaunch == rhs.askForAppToLaunch && pathRunnable == rhs.pathRunnable && + customWorkingDirectory == rhs.customWorkingDirectory && useCustomWorkingDirectory == rhs.useCustomWorkingDirectory && ignoresPersistentStateOnLaunch == rhs.ignoresPersistentStateOnLaunch && debugDocumentVersioning == rhs.debugDocumentVersioning && @@ -365,6 +382,7 @@ extension XCScheme { enableUBSanitizer == rhs.enableUBSanitizer && stopOnEveryUBSanitizerIssue == rhs.stopOnEveryUBSanitizerIssue && disableMainThreadChecker == rhs.disableMainThreadChecker && + disablePerformanceAntipatternChecker == rhs.disablePerformanceAntipatternChecker && stopOnEveryMainThreadCheckerIssue == rhs.stopOnEveryMainThreadCheckerIssue && additionalOptions == rhs.additionalOptions && commandlineArguments == rhs.commandlineArguments && diff --git a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift index a91acf98a..023b79576 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift @@ -10,11 +10,16 @@ extension XCScheme { // 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? @@ -22,10 +27,11 @@ extension XCScheme { 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] = [], @@ -33,17 +39,20 @@ 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 @@ -51,22 +60,65 @@ extension XCScheme { 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" } + 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"] if buildableProductRunnableElement.error == nil { - buildableProductRunnable = try BuildableProductRunnable(element: buildableProductRunnableElement) + runnable = try BuildableProductRunnable(element: buildableProductRunnableElement) + } else if remoteRunnableElement.error == nil { + runnable = try RemoteRunnable(element: remoteRunnableElement) } + let buildableReferenceElement = element["MacroExpansion"]["BuildableReference"] if buildableReferenceElement.error == nil { macroExpansion = try BuildableReference(element: buildableReferenceElement) @@ -80,6 +132,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) } @@ -96,21 +152,30 @@ extension XCScheme { "debugDocumentVersioning": debugDocumentVersioning.xmlString, ]) super.writeXML(parent: element) + if let runnable = runnable { + element.addChild(runnable.xmlElement()) + } + if let askForAppToLaunch = 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 { element.addChild(commandlineArguments.xmlElement()) } if let environmentVariables = environmentVariables { element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) } + if let launchAutomaticallySubstyle = launchAutomaticallySubstyle { + element.attributes["launchAutomaticallySubstyle"] = launchAutomaticallySubstyle + } + if let customWorkingDirectory = customWorkingDirectory { + element.attributes["customWorkingDirectory"] = customWorkingDirectory + } if let macroExpansion = macroExpansion { let macro = element.addChild(name: "MacroExpansion") @@ -125,18 +190,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+Runnable.swift b/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift index 6fb8b489e..b7173dc2c 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift @@ -6,7 +6,7 @@ extension XCScheme { // MARK: - Attributes public var runnableDebuggingMode: String - public var buildableReference: BuildableReference + public var buildableReference: BuildableReference? // MARK: - Init @@ -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,7 +27,9 @@ extension XCScheme { let element = AEXMLElement(name: "Runnable", value: nil, attributes: ["runnableDebuggingMode": runnableDebuggingMode]) - element.addChild(buildableReference.xmlElement()) + if let buildableReference = buildableReference { + element.addChild(buildableReference.xmlElement()) + } return element } diff --git a/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift b/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift index 0f55ce8fc..4a02b06b3 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift @@ -39,7 +39,7 @@ 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 diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift index 7c2d0cc47..d95caeb15 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift @@ -194,15 +194,16 @@ extension XCScheme { } } - let testablesElement = element.addChild(name: "Testables") - testables.forEach { testable in - testablesElement.addChild(testable.xmlElement()) - } if let macroExpansion = macroExpansion { let macro = element.addChild(name: "MacroExpansion") macro.addChild(macroExpansion.xmlElement()) } + let testablesElement = element.addChild(name: "Testables") + testables.forEach { testable in + testablesElement.addChild(testable.xmlElement()) + } + if let commandlineArguments = commandlineArguments { element.addChild(commandlineArguments.xmlElement()) } diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift index fbe0c04f5..ac73ba3ac 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift @@ -10,6 +10,7 @@ extension XCScheme { public var randomExecutionOrdering: Bool public var useTestSelectionWhitelist: Bool? public var buildableReference: BuildableReference + public var locationScenarioReference: LocationScenarioReference? public var skippedTests: [TestItem] public var selectedTests: [TestItem] @@ -19,6 +20,7 @@ extension XCScheme { parallelizable: Bool = false, randomExecutionOrdering: Bool = false, buildableReference: BuildableReference, + locationScenarioReference: LocationScenarioReference? = nil, skippedTests: [TestItem] = [], selectedTests: [TestItem] = [], useTestSelectionWhitelist: Bool? = nil) { @@ -26,6 +28,7 @@ extension XCScheme { self.parallelizable = parallelizable self.randomExecutionOrdering = randomExecutionOrdering self.buildableReference = buildableReference + self.locationScenarioReference = locationScenarioReference self.useTestSelectionWhitelist = useTestSelectionWhitelist self.selectedTests = selectedTests self.skippedTests = skippedTests @@ -37,6 +40,12 @@ extension XCScheme { useTestSelectionWhitelist = element.attributes["useTestSelectionWhitelist"] == "YES" randomExecutionOrdering = element.attributes["testExecutionOrdering"] == "random" buildableReference = try BuildableReference(element: element["BuildableReference"]) + + 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) @@ -64,6 +73,10 @@ extension XCScheme { attributes: attributes) element.addChild(buildableReference.xmlElement()) + if let locationScenarioReference = locationScenarioReference { + element.addChild(locationScenarioReference.xmlElement()) + } + if useTestSelectionWhitelist == true { if !selectedTests.isEmpty { let selectedTestsElement = element.addChild(name: "SelectedTests") @@ -89,6 +102,7 @@ extension XCScheme { lhs.parallelizable == rhs.parallelizable && lhs.randomExecutionOrdering == rhs.randomExecutionOrdering && lhs.buildableReference == rhs.buildableReference && + 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 14f1989c5..c64a8e0fb 100644 --- a/Sources/XcodeProj/Scheme/XCScheme.swift +++ b/Sources/XcodeProj/Scheme/XCScheme.swift @@ -130,3 +130,22 @@ public final class XCScheme: Writable, Equatable { lhs.wasCreatedForAppExtension == rhs.wasCreatedForAppExtension } } + +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..09cc01d8a --- /dev/null +++ b/Sources/XcodeProj/Scheme/XCSchemeManagement.swift @@ -0,0 +1,195 @@ +import Foundation +import PathKit + +public enum XCSchemeManagementError: Error, Equatable, LocalizedError, CustomStringConvertible { + /// 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): + return "Couldn't initialize XCSchemeManagement because the file at path \(path.string) was not found." + } + } + + public var errorDescription: String? { + return 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) + self.orderHint = try container.decodeIfPresent(.orderHint) + self.isShown = try container.decodeIfPresent(.isShown) + self.shared = try container.decodeIfPresent(.shared) ?? false + self.name = try container.decode(.name) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if let orderHint = orderHint { + try container.encode(orderHint, forKey: .orderHint) + } + if let isShown = 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: try 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 = PropertyListEncoder() + encoder.outputFormat = .xml + try encoder.encode(self).write(to: path.url) + } + + // MARK: - Codable + + public init(from decoder: Decoder) throws { + let plistDecoder = XcodeprojPropertyListDecoder() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.suppressBuildableAutocreation = try container.decodeIfPresent(.suppressBuildableAutocreation) + if let schemeUserStateDictionary = try container.decodeIfPresent([String: Any].self, forKey: .schemeUserState) { + self.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 { + self.suppressBuildableAutocreation = nil + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + if let suppressBuildableAutocreation = suppressBuildableAutocreation { + try container.encode(suppressBuildableAutocreation, forKey: .suppressBuildableAutocreation) + } + + if let schemeUserState = 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 f9e2ffdd7..376466853 100644 --- a/Sources/XcodeProj/Utils/BuildSettingsProvider.swift +++ b/Sources/XcodeProj/Utils/BuildSettingsProvider.swift @@ -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 } @@ -79,6 +81,7 @@ public class BuildSettingsProvider { /// 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 { @@ -97,6 +100,7 @@ public class BuildSettingsProvider { private static func projectAll() -> 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", @@ -120,12 +124,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", @@ -238,6 +244,7 @@ public class BuildSettingsProvider { case (.watchOS, .application): return [ "SKIP_INSTALL": "YES", + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], ] case (.iOS, .framework): return [ diff --git a/Sources/XcodeProj/Utils/CommentedString.swift b/Sources/XcodeProj/Utils/CommentedString.swift index 69b7dc328..a3663eec8 100644 --- a/Sources/XcodeProj/Utils/CommentedString.swift +++ b/Sources/XcodeProj/Utils/CommentedString.swift @@ -28,49 +28,8 @@ struct CommentedString { return invalidSet }() - /// Substrings that cause Xcode to quote the string content. - /// - /// Matches the strings `___` and `//`. - private let invalidStrings: Trie = [ - "_": ["_": "_"], - "/": "/" - ] - - /// A tree of characters to efficiently match string prefixes. - private enum Trie: ExpressibleByDictionaryLiteral, ExpressibleByUnicodeScalarLiteral { - case match - case next([(UnicodeScalar, Trie)]) - - init(dictionaryLiteral elements: (UnicodeScalar, Trie)...) { - self = .next(elements) - } - - init(unicodeScalarLiteral value: UnicodeScalar) { - self = .next([(value, .match)]) - } - - /// Accepts a character and mutates to the subtree of strings which match that character. If the character does - /// not match, resets to `default`. - mutating func match(_ character: UnicodeScalar, orResetTo default: Trie) { - switch self { - case .match: - return - case .next(let options): - for (key, subtrie) in options where key == character { - self = subtrie - return - } - self = `default` - } - } - - var accepted: Bool { - switch self { - case .match: return true - case .next: return false - } - } - } + /// Set of characters that are invalid. + private static var specialCheckCharacters = CharacterSet(charactersIn: "_/") /// Returns a valid string for Xcode projects. var validString: String { @@ -81,19 +40,15 @@ struct CommentedString { default: break } - var needsQuoting = false - var matchingInvalidPrefix: Trie = self.invalidStrings + if string.rangeOfCharacter(from: CommentedString.invalidCharacters) == nil { + if string.rangeOfCharacter(from: CommentedString.specialCheckCharacters) == nil { + return string + } else if !string.contains("//") && !string.contains("___") { + return string + } + } let escaped = string.reduce(into: "") { escaped, character in - quote: if !needsQuoting { - for scalar in character.unicodeScalars { - matchingInvalidPrefix.match(scalar, orResetTo: self.invalidStrings) - if matchingInvalidPrefix.accepted || CommentedString.invalidCharacters.contains(scalar) { - needsQuoting = true - break quote - } - } - } // As an optimization, only look at the first scalar. This means we're doing a numeric comparison instead // of comparing arbitrary-length characters. This is safe because all our cases are a single scalar. switch character.unicodeScalars.first { @@ -109,10 +64,7 @@ struct CommentedString { escaped.append(character) } } - if needsQuoting { - return "\"\(escaped)\"" - } - return escaped + return "\"\(escaped)\"" } } diff --git a/Sources/XcodeProj/Utils/XCConfig.swift b/Sources/XcodeProj/Utils/XCConfig.swift index 77bdf7be9..640ad7668 100644 --- a/Sources/XcodeProj/Utils/XCConfig.swift +++ b/Sources/XcodeProj/Utils/XCConfig.swift @@ -99,7 +99,7 @@ final class XCConfigParser { // swiftlint:disable:next force_try private static var 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 var settingRegex = try! NSRegularExpression(pattern: "^([a-zA-Z0-9_\\[\\]=\\*~]+)\\s*=\\s*(\"?.*?\"?)\\s*(?:;\\s*)?(?=$|\\/\\/)", options: []) } // MARK: - XCConfig Extension (Equatable) diff --git a/Sources/XcodeProj/Workspace/XCWorkspace.swift b/Sources/XcodeProj/Workspace/XCWorkspace.swift index 55c575d3d..a2b549a6d 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspace.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspace.swift @@ -28,7 +28,7 @@ public final class XCWorkspace: Writable, Equatable { /// 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) } @@ -53,6 +53,15 @@ 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() diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceData.swift b/Sources/XcodeProj/Workspace/XCWorkspaceData.swift index 61b790ca4..2c2ff3a92 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceData.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceData.swift @@ -36,20 +36,24 @@ 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) } } diff --git a/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift b/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift index f7401ac96..aac0d8bdb 100644 --- a/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift +++ b/Sources/XcodeProj/Workspace/XCWorkspaceDataElementLocationType.swift @@ -9,7 +9,7 @@ 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 { @@ -22,7 +22,7 @@ public enum XCWorkspaceDataElementLocationType { return "developer" case .group: return "group" - case .self: + case .current: return "self" case let .other(schema, _): return schema @@ -39,7 +39,7 @@ public enum XCWorkspaceDataElementLocationType { return path case let .group(path): return path - case let .self(path): + case let .current(path): return path case let .other(_, path): return 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) } @@ -86,7 +86,7 @@ extension XCWorkspaceDataElementLocationType: Equatable { return lhs == rhs case let (.group(lhs), .group(rhs)): return lhs == rhs - case let (.self(lhs), .self(rhs)): + case let (.current(lhs), .current(rhs)): return lhs == rhs case let (.other(lhs0, lhs1), .other(rhs0, rhs1)): return lhs0 == rhs0 && lhs1 == rhs1 diff --git a/TapestryConfig.swift b/TapestryConfig.swift deleted file mode 100644 index 5054f6e3a..000000000 --- a/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 d8d155a8c..4d149477d 100644 --- a/Templates/Equality.stencil +++ b/Templates/Equality.stencil @@ -6,7 +6,7 @@ extension {{ type.name }} { 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(to: rhs.{{ variable.name }}{% if variable.typeName.isOptional %} ?? [:]{% endif %}) { return false } + 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 %} diff --git a/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift b/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift index f384be869..d56389809 100644 --- a/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift +++ b/Tests/XcodeProjTests/Extensions/AEXML+XcodeFormatTests.swift @@ -10,34 +10,195 @@ extension String { } class AEXML_XcodeFormatTests: XCTestCase { - private let expectedXml = + private let expectedBuildActionXml = """ + buildImplicitDependencies = "NO" + 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", + "runPostActionsOnFailure": "YES", + "buildImplicitDependencies": "NO", + ] + ) } 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", + ] + ) + } + + 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/XCTestCase+Shell.swift b/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift new file mode 100644 index 000000000..5ea654b4c --- /dev/null +++ b/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift @@ -0,0 +1,25 @@ +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? { + 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) +} diff --git a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift index 517e2dec9..517dad192 100644 --- a/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift +++ b/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift @@ -6,4 +6,11 @@ final class PBXBuildFileTests: XCTestCase { func test_isa_returnsTheCorrectValue() { XCTAssertEqual(PBXBuildFile.isa, "PBXBuildFile") } + + func test_platformFilterIsSet() { + let pbxBuildFile: PBXBuildFile = PBXBuildFile( + platformFilter: "platformFilter" + ) + XCTAssertEqual(pbxBuildFile.platformFilter, "platformFilter") + } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift index d0e5a72b8..6fd5360d5 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXGroupTests.swift @@ -39,6 +39,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: [], diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift index 9a672d9c0..93838e4d0 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjIntegrationTests.swift @@ -84,27 +84,3 @@ final class PBXProjIntegrationTests: XCTestCase { 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) - } - - 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) -} diff --git a/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift index 3e879e75a..d16bfd075 100644 --- a/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift +++ b/Tests/XcodeProjTests/Objects/Targets/PBXAggregateTargetTests.swift @@ -27,4 +27,34 @@ 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", + 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/PBXProductTypeTests.swift b/Tests/XcodeProjTests/Objects/Targets/PBXProductTypeTests.swift index f783d7649..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") } @@ -94,4 +98,12 @@ final class PBXProductTypeTests: XCTestCase { 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/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/XCUserDataTests.swift b/Tests/XcodeProjTests/Project/XCUserDataTests.swift new file mode 100644 index 000000000..0f3b76d01 --- /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() { + 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/XcodeProjTests.swift b/Tests/XcodeProjTests/Project/XcodeProjTests.swift new file mode 100644 index 000000000..dabb8348d --- /dev/null +++ b/Tests/XcodeProjTests/Project/XcodeProjTests.swift @@ -0,0 +1,79 @@ +import Foundation +import PathKit +import XCTest +@testable import XcodeProj + +final class XcodeProjIntegrationTests: XCTestCase { + func test_read_iosXcodeProj() throws { + let subject = try XcodeProj(path: iosProjectPath) + assert(project: subject) + } + + func test_write_iosXcodeProj() { + 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_write_includes_workspace_settings() throws { + // Define workspace settings that should be written + let workspaceSettings = WorkspaceSettings(buildSystem: .new, derivedDataLocationStyle: .default, autoCreateSchemes: false) + + 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" + } +} diff --git a/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift b/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift new file mode 100644 index 000000000..d1ee337dc --- /dev/null +++ b/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift @@ -0,0 +1,24 @@ +@testable import XcodeProj +import XCTest + +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..f94252f48 --- /dev/null +++ b/Tests/XcodeProjTests/Scheme/XCSchemeManagementTests.swift @@ -0,0 +1,85 @@ +import Foundation +import XCTest +import AEXML +import PathKit +@testable import XcodeProj + +final class XCSchemeManagementTests: XCTestCase { + func test_init_from_path() throws { + // Given + let path = xcschememanagementPath + + // When + let got = try XCSchemeManagement.init(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 { + 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) + } + } + + private var xcschememanagementPath: Path { + fixturesPath() + "Schemes/xcschememanagement.plist" + } +} diff --git a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift index 7447f4cd4..3d1f16112 100644 --- a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift +++ b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift @@ -16,6 +16,39 @@ final class XCSchemeIntegrationTests: XCTestCase { assertion: { assert(scheme: $1) }) } + func test_read_write_produces_no_diff() throws { + try testReadWriteProducesNoDiff(from: iosSchemePath, + initModel: XCScheme.init(path:)) + } + + func test_read_runnableWithoutBuildableReferenceScheme() { + let subject = try? XCScheme(path: runnableWithoutBuildableReferenceSchemePath) + + XCTAssertNotNil(subject) + if let subject = 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() { + testWrite(from: runnableWithoutBuildableReferenceSchemePath, + initModel: { try? XCScheme(path: $0) }, + modify: { $0 }, + assertion: { assert(runnableWithoutBuildableReferenceScheme: $1) }) + } + func test_read_minimalScheme() { let subject = try? XCScheme(path: minimalSchemePath) @@ -245,6 +278,41 @@ final class XCSchemeIntegrationTests: XCTestCase { 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) + } + // MARK: - Private private func assert(scheme: XCScheme) { @@ -253,6 +321,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) @@ -265,8 +334,10 @@ 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") @@ -332,15 +403,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) XCTAssertNil(scheme.profileAction?.askForAppToLaunch) - 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") + 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") @@ -362,17 +434,18 @@ final class XCSchemeIntegrationTests: XCTestCase { 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, "") @@ -396,6 +469,7 @@ 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) @@ -412,6 +486,113 @@ final class XCSchemeIntegrationTests: XCTestCase { 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) + + // 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) { XCTAssertEqual(scheme.version, "1.3") XCTAssertNil(scheme.lastUpgradeVersion) @@ -419,6 +600,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) @@ -454,6 +636,7 @@ final class XCSchemeIntegrationTests: XCTestCase { 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) @@ -468,6 +651,7 @@ 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) @@ -479,10 +663,11 @@ 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) @@ -521,7 +706,21 @@ final class XCSchemeIntegrationTests: XCTestCase { 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" + } } diff --git a/Tests/XcodeProjTests/Tests/testWrite.swift b/Tests/XcodeProjTests/Tests/testWrite.swift index 47bb10142..01e8ebfc3 100644 --- a/Tests/XcodeProjTests/Tests/testWrite.swift +++ b/Tests/XcodeProjTests/Tests/testWrite.swift @@ -37,3 +37,33 @@ func testWrite(file: StaticString = #file, } try? copyPath.delete() } + +func testReadWriteProducesNoDiff(file: StaticString = #file, + line: UInt = #line, + from path: Path, + initModel: (Path) throws -> T) throws { + 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(try checkedOutput("git", ["diff"])) + XCTAssertEqual(diff, "") + } +} diff --git a/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift b/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift index 9e8e86de5..b7cad6cde 100644 --- a/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift +++ b/Tests/XcodeProjTests/Utils/BuildSettingsProviderTests.swift @@ -138,6 +138,7 @@ class BuildSettingProviderTests: XCTestCase { "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES", "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", "ENABLE_PREVIEWS": "YES", + "LD_RUNPATH_SEARCH_PATHS": ["$(inherited)", "@executable_path/Frameworks"], "SDKROOT": "watchos", "SKIP_INSTALL": "YES", "SWIFT_COMPILATION_MODE": "wholemodule", diff --git a/Tests/XcodeProjTests/Utils/XCConfigTests.swift b/Tests/XcodeProjTests/Utils/XCConfigTests.swift index 692c7d6c0..ade947b42 100644 --- a/Tests/XcodeProjTests/Utils/XCConfigTests.swift +++ b/Tests/XcodeProjTests/Utils/XCConfigTests.swift @@ -110,5 +110,7 @@ final class XCConfigIntegrationTests: XCTestCase { 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()["PRODUCT_NAME"] as? String, "$(TARGET_NAME)") + XCTAssertEqual(config.flattenedBuildSettings()["SWIFT_OPTIMIZATION_LEVEL"] as? String, "-Onone") } } diff --git a/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift b/Tests/XcodeProjTests/Workspace/XCWorkspaceDataTests.swift index e3effd94a..420ea7ed2 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") } @@ -45,7 +45,7 @@ final class XCWorkspaceDataIntegrationTests: XCTestCase { initModel: { try? XCWorkspaceData(path: $0) }, modify: { $0.children.append( - .group(.init(location: .self("shakira"), + .group(.init(location: .current("shakira"), name: "shakira", children: [])) ) diff --git a/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift b/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift index 0142bca86..a4820b1c5 100644 --- a/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift +++ b/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift @@ -19,7 +19,7 @@ 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() { diff --git a/Tuist/Config.swift b/Tuist/Config.swift index 78af64add..4b9d60274 100644 --- a/Tuist/Config.swift +++ b/Tuist/Config.swift @@ -1,6 +1,3 @@ import ProjectDescription -let config = TuistConfig(generationOptions: [ - // If we generate the manifest target Carthage will attempt to compile it too. - // .generateManifest -]) +let config = Config(generationOptions: .options()) diff --git a/XcodeProj_Carthage.xcodeproj/project.pbxproj b/XcodeProj_Carthage.xcodeproj/project.pbxproj index 684580c81..d63d3eac4 100644 --- a/XcodeProj_Carthage.xcodeproj/project.pbxproj +++ b/XcodeProj_Carthage.xcodeproj/project.pbxproj @@ -25,8 +25,10 @@ 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 */; }; + 3AF2A0A0965EB5EC74923244 /* XCDebugger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A96BC0C027BB173BC23C8D /* XCDebugger.swift */; }; 3D5DBC9A4315D97D1B39CF19 /* PBXProjEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A031713D5F31DDCC2D61EF /* PBXProjEncoder.swift */; }; 3E2EE77196F6B2BECE8DF3DB /* BuildSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1977C44246CCF9C13AB856C2 /* BuildSettingsProvider.swift */; }; + 468F48B3D19F3D1611181A2E /* XCSchemeManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69BA327A9CAF3CCFF60AD469 /* XCSchemeManagement.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 */; }; @@ -57,10 +59,10 @@ 80988B37A199C50A3C7A352A /* XCScheme+StoreKitConfigurationFileReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8B63E53C03F46C90D951D /* XCScheme+StoreKitConfigurationFileReference.swift */; }; 809908EF4E6E71285D110707 /* PBXNativeTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1A6E5AC6502EDE59541C8 /* PBXNativeTarget.swift */; }; 8242B39EB786ADA4A8B32EF3 /* XCBuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07BF1A8B705E35271076BA1 /* XCBuildConfiguration.swift */; }; - 82FA5AD52A7F345C0E7CB14D /* XcodeProjCExt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC65006CBA60CAA1F78F6CED /* XcodeProjCExt.framework */; }; 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 */; }; + 9D3FF6643048C116F1881471 /* XCUserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D25A0190B14740F9DDDB98B /* XCUserData.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 */; }; @@ -110,6 +112,7 @@ 7FAD067EE084B2EF5EFE4BFF /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; + dstPath = ""; dstSubfolderSpec = 10; files = ( ); @@ -136,6 +139,7 @@ 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 = ""; }; + 26A96BC0C027BB173BC23C8D /* XCDebugger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCDebugger.swift; sourceTree = ""; }; 26EF076F4983A1ABBD3CC81B /* XCScheme+PathRunnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCScheme+PathRunnable.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 = ""; }; @@ -164,11 +168,13 @@ 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 = ""; }; + 5D25A0190B14740F9DDDB98B /* XCUserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUserData.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 = ""; }; + 69BA327A9CAF3CCFF60AD469 /* XCSchemeManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCSchemeManagement.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 = ""; }; @@ -203,7 +209,6 @@ D07BF1A8B705E35271076BA1 /* XCBuildConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBuildConfiguration.swift; sourceTree = ""; }; D2D78F3ED94EB9E973644780 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; D4AA26E4818BC76E4CB28CEB /* PBXFileElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXFileElement.swift; sourceTree = ""; }; - DC65006CBA60CAA1F78F6CED /* XcodeProjCExt.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = XcodeProjCExt.framework; 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 = ""; }; @@ -227,7 +232,6 @@ files = ( 1ADDD97C53AEC0990F693CDA /* AEXML.framework in Frameworks */, 5A9C450597EC43859570B863 /* PathKit.framework in Frameworks */, - 82FA5AD52A7F345C0E7CB14D /* XcodeProjCExt.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -325,7 +329,6 @@ children = ( 0C950F23FEF894B133070990 /* AEXML.framework */, F89DAD13D5505D59F10D0C9F /* PathKit.framework */, - DC65006CBA60CAA1F78F6CED /* XcodeProjCExt.framework */, ); path = Mac; sourceTree = ""; @@ -355,6 +358,7 @@ 161E967D0979EF34C7DF6348 /* XCScheme+TestAction.swift */, 7DFDD2E86D3F0ADE3502A29A /* XCScheme+TestItem.swift */, 87331972540CB510D7E0C333 /* XCScheme+TestPlanReference.swift */, + 69BA327A9CAF3CCFF60AD469 /* XCSchemeManagement.swift */, ); path = Scheme; sourceTree = ""; @@ -454,9 +458,11 @@ children = ( 3353E37D0FBA49BA04C82476 /* WorkspaceSettings.swift */, CC0BF3D061052148207584A5 /* XCBreakpointList.swift */, + 26A96BC0C027BB173BC23C8D /* XCDebugger.swift */, A7DAFF5CC89FBB59D02F72B9 /* Xcode.swift */, F8B667CA83B0BB0285F470F1 /* XcodeProj.swift */, AF73D1E4C0B20C16D61865F6 /* XCSharedData.swift */, + 5D25A0190B14740F9DDDB98B /* XCUserData.swift */, ); path = Project; sourceTree = ""; @@ -532,7 +538,6 @@ buildPhases = ( 65398CA885D62783C577D0FA /* Sources */, 987C550AD2D049BE77E633BD /* Resources */, - 2C9C6B5EADA641EDF5BC523E /* Embed Precompiled Frameworks */, 7FAD067EE084B2EF5EFE4BFF /* Embed Frameworks */, ECC1E0A3315EECC3AF534E1B /* Frameworks */, ); @@ -555,7 +560,7 @@ }; }; buildConfigurationList = 477F800A701B31C60A80EACC /* Build configuration list for PBXProject "XcodeProj_Carthage" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -582,23 +587,6 @@ }; /* 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; @@ -658,7 +646,9 @@ D5DB78ED46F818640BC41A9D /* PBXTargetDependency.swift in Sources */, ED4BFA372B68053D67F0E379 /* WorkspaceSettings.swift in Sources */, 8EE72814658F0A23A01AD1E6 /* XCBreakpointList.swift in Sources */, + 3AF2A0A0965EB5EC74923244 /* XCDebugger.swift in Sources */, BEBC1583A85423F1F1BF3E30 /* XCSharedData.swift in Sources */, + 9D3FF6643048C116F1881471 /* XCUserData.swift in Sources */, CE58E51C93B8A19FA4F79E22 /* Xcode.swift in Sources */, E82A4CE0E0E385A2DED27887 /* XcodeProj.swift in Sources */, C333BEA47F06D2357DE8B1EA /* Writable.swift in Sources */, @@ -684,6 +674,7 @@ D5D82B59980521FBA427EED8 /* XCScheme+TestPlanReference.swift in Sources */, 0390AF375B465D1DC7AB6194 /* XCScheme+TestableReference.swift in Sources */, E113D0F651A2213459C65387 /* XCScheme.swift in Sources */, + 468F48B3D19F3D1611181A2E /* XCSchemeManagement.swift in Sources */, 3E2EE77196F6B2BECE8DF3DB /* BuildSettingsProvider.swift in Sources */, 4E9B74BA2A6381FEDEA26092 /* CommentedString.swift in Sources */, BD7A3EBFD6C9D1AA78E6E822 /* Decoders.swift in Sources */, @@ -730,11 +721,10 @@ 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.3; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -744,6 +734,7 @@ 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"; @@ -766,6 +757,7 @@ 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; @@ -773,6 +765,7 @@ 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; @@ -794,6 +787,7 @@ 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"; @@ -816,6 +810,7 @@ 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; @@ -823,6 +818,7 @@ 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; @@ -872,10 +868,9 @@ PRODUCT_NAME = XcodeProj; SDKROOT = macosx; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.3; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; diff --git a/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 94b2795e2..919434a62 100644 --- a/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/XcodeProj_Carthage.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,4 +1,7 @@ + + diff --git a/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme b/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme index 906385865..876464d1f 100644 --- a/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme +++ b/XcodeProj_Carthage.xcodeproj/xcshareddata/xcschemes/XcodeProj.xcscheme @@ -40,8 +40,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES" - disableMainThreadChecker = "YES"> + allowLocationSimulation = "YES"> - - - - - - - - + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Project.xcscheme b/XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Workspace.xcscheme similarity index 97% rename from XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Project.xcscheme rename to XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Workspace.xcscheme index 876464d1f..066eafdeb 100644 --- a/XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Project.xcscheme +++ b/XcodeProj_Carthage.xcworkspace/xcshareddata/xcschemes/XcodeProj_Carthage-Workspace.xcscheme @@ -26,8 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - disableMainThreadChecker = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/xcodeproj.podspec b/xcodeproj.podspec index 320874d42..05a1954cc 100644 --- a/xcodeproj.podspec +++ b/xcodeproj.podspec @@ -1,20 +1,19 @@ - Pod::Spec.new do |s| - s.name = "xcodeproj" - s.version = "7.18.0" - s.summary = "Read/Modify/Write your Xcode projects" - s.homepage = "https://github.com/tuist/xcodeproj" + s.name = 'xcodeproj' + s.version = '8.10.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.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.authors = 'Tuist' + s.swift_version = '5.1' s.osx.deployment_target = '10.10' - s.source_files = "Sources/**/*.{swift}" + s.source_files = 'Sources/**/*.{swift}' s.module_name = 'XcodeProj' - s.dependency "PathKit", "~> 1.0.0" - s.dependency "AEXML", "~> 4.6.0" + s.dependency 'PathKit', '~> 1.0.0' + s.dependency 'AEXML', '~> 4.6.1' end