diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c396e3489..39508bc5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,11 @@ on: pull_request: {} jobs: run: - runs-on: macOS-latest + runs-on: macos-11 name: Xcode ${{ matrix.xcode }} strategy: matrix: - xcode: ["12"] + xcode: ["12.5.1", "13.0"] steps: - uses: actions/checkout@master - name: Set Xcode diff --git a/.gitignore b/.gitignore index caf10c284..1f55464ba 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ xcuserdata *.xcuserstate XcodeGen.xcodeproj xcodegen.zip +.vscode/launch.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4096688d2..168e7458e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,992 +2,1121 @@ ## Next Version +## 2.32.0 + +### Added + +- Add support for `mlmodelc` files #1236 @antonsergeev88 +- Add `enable` option for `include` #1242 @freddi-kit + +### Fixed + +- Fix checking environment variable in `include` #1242 @freddi-kit +- Fix profile action for frameworks in Xcode 14 #1245 @SSheldon + +## 2.31.0 + +### Added + +- Added a new CopyFilesBuildPhase, "Embed ExtensionKit Extensions" #1230 @mtj0928 +- Added duplicate dependencies validation #1234 @aleksproger + +## 2.30.0 + +### Added + +- Added support for new target type `extensionkit-extension` in Xcode 14 #1228 @aleksproger + +### Changed + +- Speed up generating build settings for large projects #1221 @jpsim + +### Fixed + +- Fix XcodeGen building as library after breaking XcodeProj update 8.8.0 #1228 @aleksproger + +## 2.29.0 + +Some support for Xcode Test Plans has been added. For now test plans are not generated by XcodeGen and must be created in Xcode and checked in, and then referenced by path. If the test targets are added, removed or renamed, the test plans may need to be updated in Xcode + +#### Added + +- Schemes and Target Schemes can now reference existing Test Plans under `{scheme}.test.testPlans` and `{target}.scheme.testPlans`, respectively. #716 @yonaskolb @omares + +#### Fixed + +- Fixed an issue where DocC was not added to source file list #1202 @hiragram + +#### Changed + +- Updated XcodeProj to 8.7.1 #1213 @yonaskolb + +## 2.28.0 + +#### Added + +- Support for specifying custom group locations for SPM packages. #1173 @John-Connolly + +### Fixed + +- Fix Monterey macOS shell version, shell login flag for environments #1167 @bimawa +- Fixed crash caused by a simultaneous write during a glob processing #1177 @tr1ckyf0x + +### Changed + +- Run target source pattern matching in parallel #1197 @alvarhansen + +## 2.27.0 + +#### Added + +- Support test target for local Swift Package #1074 @freddi-kit +- Added `coverageTargets` for target test schemes. This enables to gather code coverage for specific targets. #1189 @gabriellanata +- Fixed issue where .gyb files could not be added to source file list #1191 @hakkurishian + +### Fixed + +- Fixed crash caused by a simultaneous write during a glob processing #1177 @tr1ckyf0x +- Skip generating empty compile sources build phases for watch apps #1185 @evandcoleman + +## 2.26.0 + +### Added + +- Added the option to specify a `location` in a test target #1150 @KrisRJack + +### Changed + +- Speed up source inclusion checking for big projects #1122 @PaulTaykalo + +## 2.25.0 + +### Added + +- Allow specifying a `copy` setting for each dependency. #1038 @JakubBednar + +### Fixed + +- Fix broken codesign option for bundle dependency #1104 @kateinoigakukun +- Ensure fileTypes are mapped to JSON value #1112 @namolnad +- Fix platform filter for package dependecies #1123 @raptorxcz +- Fix Xcode 13 build #1130 @raptorxcz @mthole + +### Changed + +- Update XcodeProj to 8.2.0 #1125 @nnsnodnb + ## 2.24.0 ### Added -- Added support for DocC Catalogs [#1091](https://github.com/yonaskolb/XcodeGen/pull/1091) @brevansio -- Added support for "driver-extension" and "system-extension" product types [#1092](https://github.com/yonaskolb/XcodeGen/issues/1092) @vgorloff -- Add support for conditionally linking dependencies for specific platforms [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook -- Add ability to specify UI testing screenshot behavior in test schemes [#942](https://github.com/yonaskolb/XcodeGen/pull/942) @daltonclaybrook +- Added support for DocC Catalogs #1091 @brevansio +- Added support for "driver-extension" and "system-extension" product types #1092 @vgorloff +- Add support for conditionally linking dependencies for specific platforms #1087 @daltonclaybrook +- Add ability to specify UI testing screenshot behavior in test schemes #942 @daltonclaybrook ### Changed -- **Breaking**: Rename the `platform` field on `Dependency` to `platformFilter` [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.23.1...2.24.0) +- **Breaking**: Rename the `platform` field on `Dependency` to `platformFilter` #1087 @daltonclaybrook ## 2.23.1 ### Changed -- Reverted "Change FRAMEWORK_SEARCH_PATH for xcframeworks (#1015)", introduced in 2.20.0. XCFrameworks need to be - referenced directly in the project for Xcode's build system to extract the appropriate frameworks [#1081](https://github.com/yonaskolb/XcodeGen/pull/1081) @elliottwilliams -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.23.0...2.23.1) +- Reverted "Change FRAMEWORK_SEARCH_PATH for xcframeworks (#1015)", introduced in 2.20.0. XCFrameworks need to be + referenced directly in the project for Xcode's build system to extract the appropriate frameworks #1081 @elliottwilliams ## 2.23.0 #### Added -- Added ability to set custom platform for dependency [#934](https://github.com/yonaskolb/XcodeGen/pull/934) @raptorxcz + +- Added ability to set custom platform for dependency #934 @raptorxcz #### Fixed -- Added `()` to config variant trimming charater set to fix scheme config variant lookups for some configs like `Debug (Development)` that broke in 2.22.0 [#1078](https://github.com/yonaskolb/XcodeGen/pull/1078) @DavidWoohyunLee -- Fixed Linux builds on Swift 5.4 [#1083](https://github.com/yonaskolb/XcodeGen/pull/1083) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.22.0...2.23.0) +- Added `()` to config variant trimming charater set to fix scheme config variant lookups for some configs like `Debug (Development)` that broke in 2.22.0 #1078 @DavidWoohyunLee +- Fixed Linux builds on Swift 5.4 #1083 @yonaskolb ## 2.22.0 #### Added -- Support `runPostActionsOnFailure` for running build post scripts on failing build [#1075](https://github.com/yonaskolb/XcodeGen/pull/1075) @freddi-kit + +- Support `runPostActionsOnFailure` for running build post scripts on failing build #1075 @freddi-kit #### Changed -- Xcode no longer alerts to project changes after regeneration, due to internal workspace not regenerating if identical [#1072](https://github.com/yonaskolb/XcodeGen/pull/1072) @yonaskolb + +- Xcode no longer alerts to project changes after regeneration, due to internal workspace not regenerating if identical #1072 @yonaskolb #### Fixed -- Fixed no such module `DOT` error when package is used as a dependency [#1067](https://github.com/yonaskolb/XcodeGen/pull/1067) @yanamura -- Fixed scheme config variant lookups for some configs like `ProdDebug` and `Prod-Debug` that broke in 2.21.0 [#1070](https://github.com/yonaskolb/XcodeGen/pull/1070) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.21.0...2.22.0) +- Fixed no such module `DOT` error when package is used as a dependency #1067 @yanamura +- Fixed scheme config variant lookups for some configs like `ProdDebug` and `Prod-Debug` that broke in 2.21.0 #1070 @yonaskolb ## 2.21.0 #### Added -- Support weak link for Swift Package Dependency [#1064](https://github.com/yonaskolb/XcodeGen/pull/1064) @freddi-kit + +- Support weak link for Swift Package Dependency #1064 @freddi-kit #### Changed -- Carthage frameworks are no longer embedded for "order-only" target dependencies. This avoid redundant embeds in situations where a target's sources _import_ a Carthage framework but do not have a binary dependency on it (like a test target which runs in a host app). [#1041](https://github.com/yonaskolb/XcodeGen/pull/1041) @elliottwilliams + +- Carthage frameworks are no longer embedded for "order-only" target dependencies. This avoid redundant embeds in situations where a target's sources _import_ a Carthage framework but do not have a binary dependency on it (like a test target which runs in a host app). #1041 @elliottwilliams #### Fixed -- The `Core` target is renamed to avoid collisions with other packages. [#1057](https://github.com/yonaskolb/XcodeGen/pull/1057) @elliottwilliams -- Lookup scheme config variants by whole words, fixing incorrect assignment in names that contain subtrings of each other (eg PreProd and Prod) [#976](https://github.com/yonaskolb/XcodeGen/pull/976) @stefanomondino -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.20.0...2.21.0) +- The `Core` target is renamed to avoid collisions with other packages. #1057 @elliottwilliams +- Lookup scheme config variants by whole words, fixing incorrect assignment in names that contain subtrings of each other (eg PreProd and Prod) #976 @stefanomondino ## 2.20.0 #### Added -- Allow specifying a `github` name like `JohnSundell/Ink` instead of a full `url` for Swift Packages [#1029](https://github.com/yonaskolb/XcodeGen/pull/1029) @yonaskolb + +- Allow specifying a `github` name like `JohnSundell/Ink` instead of a full `url` for Swift Packages #1029 @yonaskolb - Added explicity `LastUpgradeCheck` and `LastUpgradeVersion` override support so it's possible to override these properties without using the `project.xcodeVersion`. [1013](https://github.com/yonaskolb/XcodeGen/pull/1013) @Andre113 -- Added `macroExpansion` for `run` in `schemes` [#1036](https://github.com/yonaskolb/XcodeGen/pull/1036) @freddi-kit -- Added `askForAppToLaunch` for `profile` in `schemes` [#1035](https://github.com/yonaskolb/XcodeGen/pull/1035) @freddi-kit -- Added support for selectedTests in schemes `Test` configuration. [#913](https://github.com/yonaskolb/XcodeGen/pull/913) @ooodin +- Added `macroExpansion` for `run` in `schemes` #1036 @freddi-kit +- Added `askForAppToLaunch` for `profile` in `schemes` #1035 @freddi-kit +- Added support for selectedTests in schemes `Test` configuration. #913 @ooodin #### Fixed -- Fixed regression on `.storekit` configuration files' default build phase. [#1026](https://github.com/yonaskolb/XcodeGen/pull/1026) @jcolicchio -- Fixed framework search paths when using `.xcframework`s. [#1015](https://github.com/yonaskolb/XcodeGen/pull/1015) @FranzBusch -- Fixed bug where schemes without a build target would crash instead of displaying an error [#1040](https://github.com/yonaskolb/XcodeGen/pull/1040) @dalemyers -- Fixed files with names ending in **Info.plist** (such as **GoogleServices-Info.plist**) from being omitted from the Copy Resources build phase. Now, only the resolved info plist file for each specific target is omitted. [#1027](https://github.com/yonaskolb/XcodeGen/pull/1027) @liamnichols + +- Fixed regression on `.storekit` configuration files' default build phase. #1026 @jcolicchio +- Fixed framework search paths when using `.xcframework`s. #1015 @FranzBusch +- Fixed bug where schemes without a build target would crash instead of displaying an error #1040 @dalemyers +- Fixed files with names ending in **Info.plist** (such as **GoogleServices-Info.plist**) from being omitted from the Copy Resources build phase. Now, only the resolved info plist file for each specific target is omitted. #1027 @liamnichols #### Internal -- Build universal binaries for release. XcodeGen now runs natively on Apple Silicon. [#1024](https://github.com/yonaskolb/XcodeGen/pull/1024) @thii -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.19.0...2.20.0) +- Build universal binaries for release. XcodeGen now runs natively on Apple Silicon. #1024 @thii ## 2.19.0 #### Added -- Added support for building and running on Linux platforms. Tested for compatibility with Swift 5.3+ and Ubuntu 18.04. [#988](https://github.com/yonaskolb/XcodeGen/pull/988) @elliottwilliams -- Added `useBaseInternationalization` to Project Spec Options to opt out of Base Internationalization. [#961](https://github.com/yonaskolb/XcodeGen/pull/961) @liamnichols -- Added `storeKitConfiguration` to allow specifying StoreKit Configuration in Scheme and TargetScheme, supporting either xcodeproj or xcworkspace via `schemePathPrefix` option. [#964](https://github.com/yonaskolb/XcodeGen/pull/964) @jcolicchio -- Added more detailed error message with method arguments. [#990](https://github.com/yonaskolb/XcodeGen/pull/990) @bannzai -- Added `basedOnDependencyAnalysis` to Project Spec Build Script to be able to choose not to skip the script. [#992](https://github.com/yonaskolb/XcodeGen/pull/992) @myihsan -- Added `BuildRule.runOncePerArchitecture` to allow running build rules once per architecture. [#950](https://github.com/yonaskolb/XcodeGen/pull/950) @sascha -- Added discovered dependency file for a build script [#1012](https://github.com/yonaskolb/XcodeGen/pull/1012) @polac24 @fggeraissate + +- Added support for building and running on Linux platforms. Tested for compatibility with Swift 5.3+ and Ubuntu 18.04. #988 @elliottwilliams +- Added `useBaseInternationalization` to Project Spec Options to opt out of Base Internationalization. #961 @liamnichols +- Added `storeKitConfiguration` to allow specifying StoreKit Configuration in Scheme and TargetScheme, supporting either xcodeproj or xcworkspace via `schemePathPrefix` option. #964 @jcolicchio +- Added more detailed error message with method arguments. #990 @bannzai +- Added `basedOnDependencyAnalysis` to Project Spec Build Script to be able to choose not to skip the script. #992 @myihsan +- Added `BuildRule.runOncePerArchitecture` to allow running build rules once per architecture. #950 @sascha +- Added discovered dependency file for a build script #1012 @polac24 @fggeraissate #### Changed -- **Breaking**: Info.plists with custom prefixes are no longer added to the Copy Bundle Resources build phase [#945](https://github.com/yonaskolb/XcodeGen/pull/945) @anivaros -- **Breaking**: `workingDirectory` of included legacy targets is now made relative to including project [#981](https://github.com/yonaskolb/XcodeGen/pull/981) @jcolicchio -- **Breaking**: Make `simulateLocation` respect `schemePathPrefix` option. [#973](https://github.com/yonaskolb/XcodeGen/pull/973) @jcolicchio + +- **Breaking**: Info.plists with custom prefixes are no longer added to the Copy Bundle Resources build phase #945 @anivaros +- **Breaking**: `workingDirectory` of included legacy targets is now made relative to including project #981 @jcolicchio +- **Breaking**: Make `simulateLocation` respect `schemePathPrefix` option. #973 @jcolicchio #### Fixed -- Fixed error message output for `minimumXcodeGenVersion`. [#967](https://github.com/yonaskolb/XcodeGen/pull/967) @joshwalker -- Remove force-unwrapping causing crash for `LegacyTarget`s [#982](https://github.com/yonaskolb/XcodeGen/pull/982) @jcolicchio -- Fixed a race condition in an internal JSON decoder, which would occasionally fail with an error like `Parsing project spec failed: Error Domain=Unspecified error Code=0`. [#995](https://github.com/yonaskolb/XcodeGen/pull/995) @elliottwilliams -- Fixed issue where frameworks with `MACH_O_TYPE: staticlib` were being incorrectly embedded. [#1003](https://github.com/yonaskolb/XcodeGen/pull/1003) @mrabiciu + +- Fixed error message output for `minimumXcodeGenVersion`. #967 @joshwalker +- Remove force-unwrapping causing crash for `LegacyTarget`s #982 @jcolicchio +- Fixed a race condition in an internal JSON decoder, which would occasionally fail with an error like `Parsing project spec failed: Error Domain=Unspecified error Code=0`. #995 @elliottwilliams +- Fixed issue where frameworks with `MACH_O_TYPE: staticlib` were being incorrectly embedded. #1003 @mrabiciu #### Internal -- Updated to Yams 4.0.0 [#984](https://github.com/yonaskolb/XcodeGen/pull/984) @swiftty -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.18.0...2.19.0) +- Updated to Yams 4.0.0 #984 @swiftty ## 2.18.0 #### Added -- Add `Scheme.Test.TestTarget.skipped` to allow skipping of an entire test target. [#916](https://github.com/yonaskolb/XcodeGen/pull/916) @codeman9 -- Added ability to set custom LLDBInit scripts for launch and test schemes [#929](https://github.com/yonaskolb/XcodeGen/pull/929) @polac24 -- Adds App Clip support. [#909](https://github.com/yonaskolb/XcodeGen/pull/909) @brentleyjones @dflems -- Application extension schemes now default to `launchAutomaticallySubstyle = 2` and the correct debugger and launcher identifiers [#932](https://github.com/yonaskolb/XcodeGen/pull/932) @brentleyjones -- Updated SettingsPresets to use new defaults from Xcode 12. [#953](https://github.com/yonaskolb/XcodeGen/pull/953) @liamnichols -- Enable Base Internationalization by default as per Xcode 12 behavior. [#954](https://github.com/yonaskolb/XcodeGen/issues/954) @liamnichols + +- Add `Scheme.Test.TestTarget.skipped` to allow skipping of an entire test target. #916 @codeman9 +- Added ability to set custom LLDBInit scripts for launch and test schemes #929 @polac24 +- Adds App Clip support. #909 @brentleyjones @dflems +- Application extension schemes now default to `launchAutomaticallySubstyle = 2` and the correct debugger and launcher identifiers #932 @brentleyjones +- Updated SettingsPresets to use new defaults from Xcode 12. #953 @liamnichols +- Enable Base Internationalization by default as per Xcode 12 behavior. #954 @liamnichols #### Changed -- Change default project version to Xcode 12 [#960](https://github.com/yonaskolb/XcodeGen/pull/960) @yonaskolb + +- Change default project version to Xcode 12 #960 @yonaskolb #### Internal -- Updates CI to run on Xcode 12. [#936](https://github.com/yonaskolb/XcodeGen/pull/936) [#960](https://github.com/yonaskolb/XcodeGen/pull/960) @dflems @yonaskolb + +- Updates CI to run on Xcode 12. #936 @dflems @yonaskolb #### Fixed -- Select the first runnable build target, if present. [#957](https://github.com/yonaskolb/XcodeGen/pull/957) @codeman9 -- Allow SDK dependencies to be embedded. [#922](https://github.com/yonaskolb/XcodeGen/pull/922) @k-thorat -- Allow creating intermediary groups outside of the project directory. [#892](https://github.com/yonaskolb/XcodeGen/pull/892) @segiddins -- Fix appex's Runpath Search Paths under macOS target. [#952](https://github.com/yonaskolb/XcodeGen/pull/952) @rinsuki -- `onlyCopyFilesOnInstall` is extended for the Embed App Extensions build phase. [#948](https://github.com/yonaskolb/XcodeGen/pull/948) @RomanPodymov -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.17.0...2.18.0) +- Select the first runnable build target, if present. #957 @codeman9 +- Allow SDK dependencies to be embedded. #922 @k-thorat +- Allow creating intermediary groups outside of the project directory. #892 @segiddins +- Fix appex's Runpath Search Paths under macOS target. #952 @rinsuki +- `onlyCopyFilesOnInstall` is extended for the Embed App Extensions build phase. #948 @RomanPodymov ## 2.17.0 #### Added -- Added `options.fileTypes` which lets you set cross project defaults for certain file extensions [#914](https://github.com/yonaskolb/XcodeGen/pull/914) @yonaskolb -- Added `onlyCopyFilesOnInstall` option to targets for the Embed Files build phase. [#912](https://github.com/yonaskolb/XcodeGen/pull/912) @jsorge + +- Added `options.fileTypes` which lets you set cross project defaults for certain file extensions #914 @yonaskolb +- Added `onlyCopyFilesOnInstall` option to targets for the Embed Files build phase. #912 @jsorge #### Fixed -- Treat all directories with known UTI as file wrapper. [#896](https://github.com/yonaskolb/XcodeGen/pull/896) @KhaosT -- Generated schemes for application extensions now contain `wasCreatedForAppExtension = YES`. [#898](https://github.com/yonaskolb/XcodeGen/issues/898) @muizidn -- Allow package dependencies to use `link: false` [#920](https://github.com/yonaskolb/XcodeGen/pull/920) @k-thorat -- Fixed issue computing relative paths. [#915](https://github.com/yonaskolb/XcodeGen/pull/915) @andrewreach + +- Treat all directories with known UTI as file wrapper. #896 @KhaosT +- Generated schemes for application extensions now contain `wasCreatedForAppExtension = YES`. #898 @muizidn +- Allow package dependencies to use `link: false` #920 @k-thorat +- Fixed issue computing relative paths. #915 @andrewreach #### Internal -- Updated to XcodeProj 7.13.0 [#908](https://github.com/yonaskolb/XcodeGen/pull/908) @brentleyjones -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.16.0...2.17.0) +- Updated to XcodeProj 7.13.0 #908 @brentleyjones ## 2.16.0 #### Added -- Improve speed of metadata parsing and dependency resolution. [#803](https://github.com/yonaskolb/XcodeGen/pull/803) @michaeleisel -- Improve support for iOS sticker packs and add support for `launchAutomaticallySubstyle` to run schemes. [#824](https://github.com/yonaskolb/XcodeGen/pull/824) @scelis -- Add --project-root option to generate command. [#828](https://github.com/yonaskolb/XcodeGen/pull/828) @ileitch -- Add an ability to set an order of groups with `options.groupOrdering` [#613](https://github.com/yonaskolb/XcodeGen/pull/613) @Beniamiiin -- Add the ability to output a dependency graph in graphviz format [#852](https://github.com/yonaskolb/XcodeGen/pull/852) @jeffctown -- Adds uncluttering the project manifest dumped to YAML from empty values [#858](https://github.com/yonaskolb/XcodeGen/pull/858) @paciej00 -- Added ability to name the executable target when declaring schemes. [#869](https://github.com/yonaskolb/XcodeGen/pull/869) @elland -- Added ability to set executable to Ask to Launch. [#871](https://github.com/yonaskolb/XcodeGen/pull/871) @pinda - -#### Fixed -- Fixed issue when linking and embeding static frameworks: they should be linked and NOT embed. [#820](https://github.com/yonaskolb/XcodeGen/pull/820) @acecilia -- Fixed issue when generating projects for paths with a dot in the folder for swift sources. [#826](https://github.com/yonaskolb/XcodeGen/pull/826) @asifmohd -- Prefix static library target filenames with 'lib' to match Xcode. [#831](https://github.com/yonaskolb/XcodeGen/pull/831), [#842](https://github.com/yonaskolb/XcodeGen/pull/842) @ileitch -- Fixed duplicate addition of carthage static frameworks. [#829](https://github.com/yonaskolb/XcodeGen/pull/829) @funzin -- Fix handling of SWIFT_INSTALL_OBJC_HEADER when its value is YES/NO. [#827](https://github.com/yonaskolb/XcodeGen/pull/827) @ileitch -- Set `preActions` and `postActions` on the `build` action of a TargetScheme instead of the other actions. [#823](https://github.com/yonaskolb/XcodeGen/pull/823) @brentleyjones -- Prevent test targets from being set as a scheme's launch action [#835](https://github.com/yonaskolb/XcodeGen/pull/835) @brentleyjones -- Implicitly include bundles in the Copy Bundle Resources build phase. [#838](https://github.com/yonaskolb/XcodeGen/pull/838) @skirchmeier -- Fixed dumping a project manifest which contains an array of project references [#840](https://github.com/yonaskolb/XcodeGen/pull/840) @paciej00 -- Generate correct PBXTargetDependency for external targets. [#843](https://github.com/yonaskolb/XcodeGen/pull/843) @ileitch -- Fix linking of multiple products from the same Swift Package [#830](https://github.com/yonaskolb/XcodeGen/pull/830) @toshi0383 -- Don't deduplicate files in `include` with different path but same name. [#849](https://github.com/yonaskolb/XcodeGen/pull/849) @akkyie -- Don't link transitive static carthage libraries. [#853](https://github.com/yonaskolb/XcodeGen/pull/853) @akkyie -- Optimize simplifying paths for faster project generation. [#857](https://github.com/yonaskolb/XcodeGen/pull/857) @akkyie -- Fixed issue where wrapper folders may not include correctly in the generated project. [#862](https://github.com/yonaskolb/XcodeGen/pull/862) @KhaosT -- Compile `xcmappingmodel` files instead of copying bundle resources. [#834](https://github.com/yonaskolb/XcodeGen/pull/834) @jcolicchio -- Fixed issue where `Complie Sources` build phase is generated for resource bundles even when they have no files to compile [#878](https://github.com/yonaskolb/XcodeGen/pull/878) @nkukushkin - -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.15.1...2.16.0) + +- Improve speed of metadata parsing and dependency resolution. #803 @michaeleisel +- Improve support for iOS sticker packs and add support for `launchAutomaticallySubstyle` to run schemes. #824 @scelis +- Add --project-root option to generate command. #828 @ileitch +- Add an ability to set an order of groups with `options.groupOrdering` #613 @Beniamiiin +- Add the ability to output a dependency graph in graphviz format #852 @jeffctown +- Adds uncluttering the project manifest dumped to YAML from empty values #858 @paciej00 +- Added ability to name the executable target when declaring schemes. #869 @elland +- Added ability to set executable to Ask to Launch. #871 @pinda + +#### Fixed + +- Fixed issue when linking and embeding static frameworks: they should be linked and NOT embed. #820 @acecilia +- Fixed issue when generating projects for paths with a dot in the folder for swift sources. #826 @asifmohd +- Prefix static library target filenames with 'lib' to match Xcode. #831 @ileitch +- Fixed duplicate addition of carthage static frameworks. #829 @funzin +- Fix handling of SWIFT_INSTALL_OBJC_HEADER when its value is YES/NO. #827 @ileitch +- Set `preActions` and `postActions` on the `build` action of a TargetScheme instead of the other actions. #823 @brentleyjones +- Prevent test targets from being set as a scheme's launch action #835 @brentleyjones +- Implicitly include bundles in the Copy Bundle Resources build phase. #838 @skirchmeier +- Fixed dumping a project manifest which contains an array of project references #840 @paciej00 +- Generate correct PBXTargetDependency for external targets. #843 @ileitch +- Fix linking of multiple products from the same Swift Package #830 @toshi0383 +- Don't deduplicate files in `include` with different path but same name. #849 @akkyie +- Don't link transitive static carthage libraries. #853 @akkyie +- Optimize simplifying paths for faster project generation. #857 @akkyie +- Fixed issue where wrapper folders may not include correctly in the generated project. #862 @KhaosT +- Compile `xcmappingmodel` files instead of copying bundle resources. #834 @jcolicchio +- Fixed issue where `Complie Sources` build phase is generated for resource bundles even when they have no files to compile #878 @nkukushkin ## 2.15.1 #### Fixed -- Fixed issue which caused watch app schemes to be generated incorrectly, preventing these apps from launching. [#798](https://github.com/yonaskolb/XcodeGen/pull/798) @daltonclaybrook -- Added build presets for the target type `framework.static`. [#819](https://github.com/yonaskolb/XcodeGen/pull/819) @acecilia -- Fixed XcodeProj resolution and updated to 7.10.0 [#822](https://github.com/yonaskolb/XcodeGen/pull/822) @soffes -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.15.0...2.15.1) +- Fixed issue which caused watch app schemes to be generated incorrectly, preventing these apps from launching. #798 @daltonclaybrook +- Added build presets for the target type `framework.static`. #819 @acecilia +- Fixed XcodeProj resolution and updated to 7.10.0 #822 @soffes ## 2.15.0 #### Added -- Add support for local Swift Packages in `packages` using `path`. [#808](https://github.com/yonaskolb/XcodeGen/pull/808) @freddi-kit -- Add `buildImplicitDependencies` as an option on `TargetScheme`. [#810](https://github.com/yonaskolb/XcodeGen/pull/810) @evandcoleman + +- Add support for local Swift Packages in `packages` using `path`. #808 @freddi-kit +- Add `buildImplicitDependencies` as an option on `TargetScheme`. #810 @evandcoleman #### Fixed -- Fixed resolving path to local Swift Packages [#796](https://github.com/yonaskolb/XcodeGen/pull/796) @freddi-kit -- Added ability to stop on every main thread checker issue on Run schemes and TargetSchemes [#799](https://github.com/yonaskolb/XcodeGen/pull/799) @ionutivan -- Avoid copying ObjC interface header when SWIFT_INSTALL_OBJC_HEADER=false. [#805](https://github.com/yonaskolb/XcodeGen/pull/805) @kateinoigakukun -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.14.0...2.15.0) +- Fixed resolving path to local Swift Packages #796 @freddi-kit +- Added ability to stop on every main thread checker issue on Run schemes and TargetSchemes #799 @ionutivan +- Avoid copying ObjC interface header when SWIFT_INSTALL_OBJC_HEADER=false. #805 @kateinoigakukun ## 2.14.0 #### Added -- Add ability to embed and code sign Swift package dependencies with dynamic products. [#788](https://github.com/yonaskolb/XcodeGen/pull/788) @alexruperez + +- Add ability to embed and code sign Swift package dependencies with dynamic products. #788 @alexruperez #### Fixed -- Revert "Add Base to known regions even if one doesn't exist" [#791](https://github.com/yonaskolb/XcodeGen/pull/792) @bryansum -- Set `defaultConfigurationName` for every target which is defined in a project. [#787](https://github.com/yonaskolb/XcodeGen/pull/787) @ken0nek -- Set `TEST_TARGET_NAME` only when a project has UITest bundle. [#792](https://github.com/yonaskolb/XcodeGen/pull/792) @ken0nek -- Set xcodeproj path in project.xcworkspace/contents.xcworkspacedata [#793](https://github.com/yonaskolb/XcodeGen/pull/793) @ken0nek -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.13.1...2.14.0) +- Revert "Add Base to known regions even if one doesn't exist" #791 @bryansum +- Set `defaultConfigurationName` for every target which is defined in a project. #787 @ken0nek +- Set `TEST_TARGET_NAME` only when a project has UITest bundle. #792 @ken0nek +- Set xcodeproj path in project.xcworkspace/contents.xcworkspacedata #793 @ken0nek ## 2.13.1 #### Fixed -- Validate scheme test action and test coverage target references before generating. [#775](https://github.com/yonaskolb/XcodeGen/pull/775) @liamnichols -- Fixed parsing prerelease identifiers in Swift package versions [#779](https://github.com/yonaskolb/XcodeGen/pull/779) @yonaskolb -- Fixed using legacy targets as dependencies [#778](https://github.com/yonaskolb/XcodeGen/pull/778) @yonaskolb + +- Validate scheme test action and test coverage target references before generating. #775 @liamnichols +- Fixed parsing prerelease identifiers in Swift package versions #779 @yonaskolb +- Fixed using legacy targets as dependencies #778 @yonaskolb #### Internal -- Updated to XcodeProj 7.8.0 [#777](https://github.com/yonaskolb/XcodeGen/pull/777) @yonaskolb -- Use https://github.com/mxcl/Version [#779](https://github.com/yonaskolb/XcodeGen/pull/779) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.13.0...2.13.1) +- Updated to XcodeProj 7.8.0 #777 @yonaskolb +- Use #779 @yonaskolb ## 2.13.0 #### Added -- Support External Target References via subprojects. [#701](https://github.com/yonaskolb/XcodeGen/pull/701) @evandcoleman + +- Support External Target References via subprojects. #701 @evandcoleman #### Fixed -- Fixed compilation as library by locking down XcodeProj version [#767](https://github.com/yonaskolb/XcodeGen/pull/767) @yonaskolb -- Stabilized sorting of groups with duplicate names/paths. [#671](https://github.com/yonaskolb/XcodeGen/pull/671) @ChristopherRogers -- Moved `Copy Bundle Resources` to after `Link with Libraries` build phase [#768](https://github.com/yonaskolb/XcodeGen/pull/768) @yonaskolb + +- Fixed compilation as library by locking down XcodeProj version #767 @yonaskolb +- Stabilized sorting of groups with duplicate names/paths. #671 @ChristopherRogers +- Moved `Copy Bundle Resources` to after `Link with Libraries` build phase #768 @yonaskolb #### Internal -- Updated to XcodeProj 7.7.0 [#767](https://github.com/yonaskolb/XcodeGen/pull/767) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.12.0...2.13.0) +- Updated to XcodeProj 7.7.0 #767 @yonaskolb ## 2.12.0 #### Added -- Added pre and post command options. Useful for running `pod install` in combination with `--use-cache` [#759](https://github.com/yonaskolb/XcodeGen/pull/759) @yonaskolb -- Support for language and region settings on a target basis [#728](https://github.com/yonaskolb/XcodeGen/pull/728) @FranzBusch -- Added option to generate only Info.plist files with `--only-plists` [#739](https://github.com/yonaskolb/XcodeGen/pull/739) @namolnad -- Added the option to specify a `simulateLocation` in a scheme [#722](https://github.com/yonaskolb/XcodeGen/issues/722) @basvankuijck -- Support for On Demand Resources tags [#753](https://github.com/yonaskolb/XcodeGen/pull/753) @sipao + +- Added pre and post command options. Useful for running `pod install` in combination with `--use-cache` #759 @yonaskolb +- Support for language and region settings on a target basis #728 @FranzBusch +- Added option to generate only Info.plist files with `--only-plists` #739 @namolnad +- Added the option to specify a `simulateLocation` in a scheme #722 @basvankuijck +- Support for On Demand Resources tags #753 @sipao #### Fixed -- Fixed resolving a relative path for `projectReference.path` [#740](https://github.com/yonaskolb/XcodeGen/pull/740) @kateinoigakukun -- Don't add framework dependency's directory to `FRAMEWORK_SEARCH_PATHS` if it is implicit [#744](https://github.com/yonaskolb/XcodeGen/pull/744) @ikesyo @yutailang0119 -- Fixed resolving relative path passed to `XcodeProj` [#751](https://github.com/yonaskolb/XcodeGen/pull/751) @PycKamil -- Prefer configurations named "Debug" or "Release" for default scheme build configurations [#752](https://github.com/yonaskolb/XcodeGen/pull/752) @john-flanagan -- Added an extra check for package versions. [#755](https://github.com/yonaskolb/XcodeGen/pull/755) @basvankuijck + +- Fixed resolving a relative path for `projectReference.path` #740 @kateinoigakukun +- Don't add framework dependency's directory to `FRAMEWORK_SEARCH_PATHS` if it is implicit #744 @ikesyo @yutailang0119 +- Fixed resolving relative path passed to `XcodeProj` #751 @PycKamil +- Prefer configurations named "Debug" or "Release" for default scheme build configurations #752 @john-flanagan +- Added an extra check for package versions. #755 @basvankuijck #### Internal -- Update to SwiftCLI 6.0 and use the new property wrappers [#749](https://github.com/yonaskolb/XcodeGen/pull/749) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.11.0...2.12.0) +- Update to SwiftCLI 6.0 and use the new property wrappers #749 @yonaskolb ## 2.11.0 #### Added -- Add Carthage static framework dependencies support. [#688](https://github.com/yonaskolb/XcodeGen/pull/688) @giginet -- Added `xcodegen dump` command [#710](https://github.com/yonaskolb/XcodeGen/pull/710) @yonaskolb -- Added `--no-env` option to disable environment variables expansion [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari -- Added custom group support for target sources [#621](https://github.com/yonaskolb/XcodeGen/pull/621) @sroebert @rcari -- Added new dependency type, `bundle`. This allows targets to copy bundles from other projects [#616](https://github.com/yonaskolb/XcodeGen/pull/616) @bsmith11 + +- Add Carthage static framework dependencies support. #688 @giginet +- Added `xcodegen dump` command #710 @yonaskolb +- Added `--no-env` option to disable environment variables expansion #704 @rcari +- Added custom group support for target sources #621 @sroebert @rcari +- Added new dependency type, `bundle`. This allows targets to copy bundles from other projects #616 @bsmith11 #### Fixed -- Improved variable expansion runtime [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari -- Fixed missing headers for static framework targets [#705](https://github.com/yonaskolb/XcodeGen/pull/705) @wag-miles -- Using more file types from XcodeProj for PBXFileReferences resulting in less project diffs [#715](https://github.com/yonaskolb/XcodeGen/pull/715) @yonaskolb -- Fixed localized `*.intentdefinition` not being added to build source phases [#720](https://github.com/yonaskolb/XcodeGen/pull/720) @giginet -- Fixed `selectedLauncherIdentifier` not being set `Xcode.IDEFoundation.Launcher.PosixSpawn` when `debugEnabled: false` is defined in test action [#725](https://github.com/yonaskolb/XcodeGen/pull/725) @ken0nek -- Fixed unnecessary dependencies related to SwiftPM [#726](https://github.com/yonaskolb/XcodeGen/pull/726) @tid-kijyun + +- Improved variable expansion runtime #704 @rcari +- Fixed missing headers for static framework targets #705 @wag-miles +- Using more file types from XcodeProj for PBXFileReferences resulting in less project diffs #715 @yonaskolb +- Fixed localized `*.intentdefinition` not being added to build source phases #720 @giginet +- Fixed `selectedLauncherIdentifier` not being set `Xcode.IDEFoundation.Launcher.PosixSpawn` when `debugEnabled: false` is defined in test action #725 @ken0nek +- Fixed unnecessary dependencies related to SwiftPM #726 @tid-kijyun #### Changed -- Deprecated `$old_form` variables in favor of `${new_form}` variables [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari -- Updated XcodeProj to 7.4.0 [#709](https://github.com/yonaskolb/XcodeGen/pull/709) [#715](https://github.com/yonaskolb/XcodeGen/pull/715) @yonaskolb -- Updated to Swift 5.1 [#714](https://github.com/yonaskolb/XcodeGen/pull/714) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.10.1...2.11.0) +- Deprecated `$old_form` variables in favor of `${new_form}` variables #704 @rcari +- Updated XcodeProj to 7.4.0 #709 @yonaskolb +- Updated to Swift 5.1 #714 @yonaskolb ## 2.10.1 #### Fixed -- Add Base to knownRegions even if one doesn't exist [#694](https://github.com/yonaskolb/XcodeGen/pull/694) @bryansum -- Fixed missing `onlyGenerateCoverageForSpecifiedTargets` issue [#700](https://github.com/yonaskolb/XcodeGen/pull/700) @kateinoigakukun -- Fixed regression on dependencies `link` flag [#703](https://github.com/yonaskolb/XcodeGen/pull/703) @rcari -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.10.0...2.10.1) +- Add Base to knownRegions even if one doesn't exist #694 @bryansum +- Fixed missing `onlyGenerateCoverageForSpecifiedTargets` issue #700 @kateinoigakukun +- Fixed regression on dependencies `link` flag #703 @rcari ## 2.10.0 #### Added -- Support Target Reference to another project. [#655](https://github.com/yonaskolb/XcodeGen/pull/655) @kateinoigakukun -- Added `coverageTargets` for test target. This enables to gather code coverage for specific targets. [#656](https://github.com/yonaskolb/XcodeGen/pull/656) @kateinoigakukun + +- Support Target Reference to another project. #655 @kateinoigakukun +- Added `coverageTargets` for test target. This enables to gather code coverage for specific targets. #656 @kateinoigakukun #### Fixed -- Add base localisation by default even if no base localised files were found. Fixes warning in Xcode 11 [#685](https://github.com/yonaskolb/XcodeGen/pull/685) @yonaskolb -- Don't generate CFBundleExecutable in default generated Info.plist for `bundle` target types [#689](https://github.com/yonaskolb/XcodeGen/pull/689) @FranzBusch -- Fixed resolving relative paths with custom project destination [#681](https://github.com/yonaskolb/XcodeGen/pull/681) @giginet -- Fixed resolving relative paths for Info.plist [#683](https://github.com/yonaskolb/XcodeGen/pull/683) @giginet -- Fixed macOS unit test target TEST_HOST [#696](https://github.com/yonaskolb/XcodeGen/pull/696) @mjarvis + +- Add base localisation by default even if no base localised files were found. Fixes warning in Xcode 11 #685 @yonaskolb +- Don't generate CFBundleExecutable in default generated Info.plist for `bundle` target types #689 @FranzBusch +- Fixed resolving relative paths with custom project destination #681 @giginet +- Fixed resolving relative paths for Info.plist #683 @giginet +- Fixed macOS unit test target TEST_HOST #696 @mjarvis #### Internal -- Restructure targets [#698](https://github.com/yonaskolb/XcodeGen/pull/698) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.9.0...2.10.0) +- Restructure targets #698 @yonaskolb ## 2.9.0 #### Added -- Added Scheme Templates [#672](https://github.com/yonaskolb/XcodeGen/pull/672) @bclymer + +- Added Scheme Templates #672 @bclymer #### Fixed -- Fixed macOS unit test setting preset [#665](https://github.com/yonaskolb/XcodeGen/pull/665) @yonaskolb -- Add `rcproject` files to sources build phase instead of resources [#669](https://github.com/yonaskolb/XcodeGen/pull/669) @Qusic -- Prefer default configuration names for generated schemes [#673](https://github.com/yonaskolb/XcodeGen/pull/673) @giginet -- Fixed some resource files being placed to "Recovered References" group [#679](https://github.com/yonaskolb/XcodeGen/pull/679) @nivanchikov + +- Fixed macOS unit test setting preset #665 @yonaskolb +- Add `rcproject` files to sources build phase instead of resources #669 @Qusic +- Prefer default configuration names for generated schemes #673 @giginet +- Fixed some resource files being placed to "Recovered References" group #679 @nivanchikov #### Internal -- Updated to SwiftCLI 5.3.2 [#667](https://github.com/yonaskolb/XcodeGen/pull/667) @giginet -- Fixed tests in case-sensitive file system [#670](https://github.com/yonaskolb/XcodeGen/pull/670) @Qusic -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.8.0...2.9.0) +- Updated to SwiftCLI 5.3.2 #667 @giginet +- Fixed tests in case-sensitive file system #670 @Qusic ## 2.8.0 #### Added -- Added support for Swift Package dependencies [#624](https://github.com/yonaskolb/XcodeGen/pull/624) @yonaskolb -- Added `includes` to `sources` for a Target. This follows the same glob-style as `excludes` but functions as a way to only include files that match a specified pattern. Useful if you only want a certain file type, for example specifying `**/*.swift`. [#637](https://github.com/yonaskolb/XcodeGen/pull/637) @bclymer -- Support `dylib` SDK. [#650](https://github.com/yonaskolb/XcodeGen/pull/650) @kateinoigakukun -- Added `language` and `region` options for `run` and `test` scheme [#654](https://github.com/yonaskolb/XcodeGen/pull/654) @kateinoigakukun -- Added `debugEnabled` option for `run` and `test` scheme [#657](https://github.com/yonaskolb/XcodeGen/pull/657) @kateinoigakukun + +- Added support for Swift Package dependencies #624 @yonaskolb +- Added `includes` to `sources` for a Target. This follows the same glob-style as `excludes` but functions as a way to only include files that match a specified pattern. Useful if you only want a certain file type, for example specifying `**/*.swift`. #637 @bclymer +- Support `dylib` SDK. #650 @kateinoigakukun +- Added `language` and `region` options for `run` and `test` scheme #654 @kateinoigakukun +- Added `debugEnabled` option for `run` and `test` scheme #657 @kateinoigakukun #### Fixed -- Expand template variable in Array of Any [#651](https://github.com/yonaskolb/XcodeGen/pull/651) @kateinoigakukun -- Significantly improve performance when running with a large number files. [#658](https://github.com/yonaskolb/XcodeGen/pull/658) @kateinoigakukun -- Removed some more diffs between the generated .pbxproj and when Xcode resaves it [#663](https://github.com/yonaskolb/XcodeGen/pull/663) @yonaskolb + +- Expand template variable in Array of Any #651 @kateinoigakukun +- Significantly improve performance when running with a large number files. #658 @kateinoigakukun +- Removed some more diffs between the generated .pbxproj and when Xcode resaves it #663 @yonaskolb #### Internal -- Removed needless `Array` initialization. [#661](https://github.com/yonaskolb/XcodeGen/pull/661) @RomanPodymov -- Updated to XcodeProj 7.1.0 [#624](https://github.com/yonaskolb/XcodeGen/pull/624) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.7.0...2.8.0) +- Removed needless `Array` initialization. #661 @RomanPodymov +- Updated to XcodeProj 7.1.0 #624 @yonaskolb ## 2.7.0 #### Added -- Added Bash 4 style recursive globbing (`**/*`) in target sources `excludes` [#636](https://github.com/yonaskolb/XcodeGen/pull/636) @bclymer -- Added ability to disable main thread checker in Schemes [#601](https://github.com/yonaskolb/XcodeGen/pull/601) @wag-miles + +- Added Bash 4 style recursive globbing (`**/*`) in target sources `excludes` #636 @bclymer +- Added ability to disable main thread checker in Schemes #601 @wag-miles #### Fixed -- Fixed included specs that were referenced multiple times from duplicating content [#599](https://github.com/yonaskolb/XcodeGen/pull/599) @haritowa -- Fixed `.orig` files being added to the project [#627](https://github.com/yonaskolb/XcodeGen/pull/627) @keith + +- Fixed included specs that were referenced multiple times from duplicating content #599 @haritowa +- Fixed `.orig` files being added to the project #627 @keith #### Changed -- Allow linking of dependencies into static libraries when `link` is set to true [#635](https://github.com/yonaskolb/XcodeGen/pull/635) @kateinoigakukun -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.6.0...2.7.0) +- Allow linking of dependencies into static libraries when `link` is set to true #635 @kateinoigakukun ## 2.6.0 #### Added -- Added ability to skip tests [#582](https://github.com/yonaskolb/XcodeGen/pull/582) @kadarandras -- Added ability to set `attributes` on build files [#583](https://github.com/yonaskolb/XcodeGen/pull/583) @min -- Allow using environment variables in the form of `${SOME_VARIABLE}`. This might be a **breaking** change when a target template attribute is also defined as an environment variable [#594](https://github.com/yonaskolb/XcodeGen/pull/594) @tomquist -- Added support for `watchapp2-container` and `framework.static` product types [#604](https://github.com/yonaskolb/XcodeGen/pull/604) @yonaskolb + +- Added ability to skip tests #582 @kadarandras +- Added ability to set `attributes` on build files #583 @min +- Allow using environment variables in the form of `${SOME_VARIABLE}`. This might be a **breaking** change when a target template attribute is also defined as an environment variable #594 @tomquist +- Added support for `watchapp2-container` and `framework.static` product types #604 @yonaskolb #### Fixed -- Fixed `.pch` files being bundled as resources [#597](https://github.com/yonaskolb/XcodeGen/pull/597) @thii -- Fixed an issue that prevents watchOS Intents Extension from running correctly. [#571](https://github.com/yonaskolb/XcodeGen/pull/571) @KhaosT + +- Fixed `.pch` files being bundled as resources #597 @thii +- Fixed an issue that prevents watchOS Intents Extension from running correctly. #571 @KhaosT #### Changed -- Updated the default `compatibilityVersion` project setting from `Xcode 9.3` to `Xcode 10.0` [#581](https://github.com/yonaskolb/XcodeGen/pull/581) @acecilia -- Updated to XcodeProj 7.0.0. Note that the length of generated UUIDs has changed [#604](https://github.com/yonaskolb/XcodeGen/pull/604) @yonaskolb + +- Updated the default `compatibilityVersion` project setting from `Xcode 9.3` to `Xcode 10.0` #581 @acecilia +- Updated to XcodeProj 7.0.0. Note that the length of generated UUIDs has changed #604 @yonaskolb #### Internal -- Added ability to encode ProjectSpec [#545](https://github.com/yonaskolb/XcodeGen/pull/545) @ryohey -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.5.0...2.6.0) +- Added ability to encode ProjectSpec #545 @ryohey ## 2.5.0 #### Added -- Added support for `app-extension.intents-service` target type [#536](https://github.com/yonaskolb/XcodeGen/pull/536) @yonaskolb -- Added support for custom `root` in `sdk` dependency [#562](https://github.com/yonaskolb/XcodeGen/pull/562) @raptorxcz + +- Added support for `app-extension.intents-service` target type #536 @yonaskolb +- Added support for custom `root` in `sdk` dependency #562 @raptorxcz #### Changed -- Updated to xcodeproj 6.7.0 including its performance improvements [#536](https://github.com/yonaskolb/XcodeGen/pull/536) @yonaskolb -- Updated default generated settings for Xcode 10.2 [#555](https://github.com/yonaskolb/XcodeGen/pull/555) @yonaskolb -- Changed order of file generation so that plists are now generated before the project, so they will be included in the projects files [#544](https://github.com/yonaskolb/XcodeGen/issues/544) @tomquist + +- Updated to xcodeproj 6.7.0 including its performance improvements #536 @yonaskolb +- Updated default generated settings for Xcode 10.2 #555 @yonaskolb +- Changed order of file generation so that plists are now generated before the project, so they will be included in the projects files #544 @tomquist - Updated Yams to 2.0.0 @yonaskolb #### Fixed -- Fixed groups from sources outside a project spec's directory from being flattened. [#550](https://github.com/yonaskolb/XcodeGen/pull/550) @sroebert -- Fixed `optional` file sources not being added to the project [#557](https://github.com/yonaskolb/XcodeGen/pull/557) @yonaskolb -- Fixed Carthage dependencies being incorrectly embedded in WatchKit app bundles instead of a WatchKit app extension [#558](https://github.com/yonaskolb/XcodeGen/pull/558) @KhaosT -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.4.0...2.5.0) +- Fixed groups from sources outside a project spec's directory from being flattened. #550 @sroebert +- Fixed `optional` file sources not being added to the project #557 @yonaskolb +- Fixed Carthage dependencies being incorrectly embedded in WatchKit app bundles instead of a WatchKit app extension #558 @KhaosT ## 2.4.0 -#### Fixed: -- Fixed installation when building in Swift 5 [#549](https://github.com/yonaskolb/XcodeGen/pull/549) @yonaskolb +#### Fixed + +- Fixed installation when building in Swift 5 #549 @yonaskolb #### Changed -- Updated to Swift 5 and dropped Swift 4.2 [#549](https://github.com/yonaskolb/XcodeGen/pull/549) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.3.0...2.4.0) +- Updated to Swift 5 and dropped Swift 4.2 #549 @yonaskolb ## 2.3.0 #### Added -- Added ability to automatically find all the frameworks for Carthage dependencies via the global `options.findCarthageFrameworks` or dependency specfic `dependency.findFrameworks`. See the [Carthage](Docs/Usage.md#carthage) usage docs for more info [#506](https://github.com/yonaskolb/XcodeGen/pull/506) [#543](https://github.com/yonaskolb/XcodeGen/pull/543) @rpassis @yonaskolb -- Added support for nested target templates [#534](https://github.com/yonaskolb/XcodeGen/pull/534) @tomquist -- Added ability to define `templateAttributes` within a target to be able to parameterize templates. [#533](https://github.com/yonaskolb/XcodeGen/pull/533) @tomquist -- Added ability to set `link` to false in framework dependencies [#532](https://github.com/yonaskolb/XcodeGen/pull/532) @dimatosaurus + +- Added ability to automatically find all the frameworks for Carthage dependencies via the global `options.findCarthageFrameworks` or dependency specfic `dependency.findFrameworks`. See the [Carthage](Docs/Usage.md#carthage) usage docs for more info #506 @rpassis @yonaskolb +- Added support for nested target templates #534 @tomquist +- Added ability to define `templateAttributes` within a target to be able to parameterize templates. #533 @tomquist +- Added ability to set `link` to false in framework dependencies #532 @dimatosaurus - Added `missingConfigFiles` to `options.disabledValidations` to optionally skip checking for the existence of config files. -- Added ability to define a per-platform `deploymentTarget` for Multi-Platform targets. [#510](https://github.com/yonaskolb/XcodeGen/pull/510) @ainopara +- Added ability to define a per-platform `deploymentTarget` for Multi-Platform targets. #510 @ainopara #### Changed -- **DEPRECATION**: Placeholders `$target_name` and `$platform` have been deprecated in favour of `${target_name}` and `${platform}`. Support for the old placeholders will be removed in a future version [#533](https://github.com/yonaskolb/XcodeGen/pull/533) @tomquist + +- **DEPRECATION**: Placeholders `$target_name` and `$platform` have been deprecated in favour of `${target_name}` and `${platform}`. Support for the old placeholders will be removed in a future version #533 @tomquist #### Fixed -- Sources outside a project spec's directory will be correctly referenced as relative paths in the project file. [#524](https://github.com/yonaskolb/XcodeGen/pull/524) -- Fixed error when `optional` directory source is missing [#527](https://github.com/yonaskolb/XcodeGen/pull/527) @yonaskolb -- Fixed excludes within included spec [#535](https://github.com/yonaskolb/XcodeGen/pull/535) @yonaskolb -- Fixed paths in target templates within included files not being relative [#537](https://github.com/yonaskolb/XcodeGen/pull/537) @yonaskolb -- Fix multi-platform target templates [#541](https://github.com/yonaskolb/XcodeGen/pull/541) @yonaskolb -- Fixed sources in an included target not being relative when the sources are mix of string and dictionaries [#542](https://github.com/yonaskolb/XcodeGen/pull/542) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.2.0...2.3.0) +- Sources outside a project spec's directory will be correctly referenced as relative paths in the project file. #524 +- Fixed error when `optional` directory source is missing #527 @yonaskolb +- Fixed excludes within included spec #535 @yonaskolb +- Fixed paths in target templates within included files not being relative #537 @yonaskolb +- Fix multi-platform target templates #541 @yonaskolb +- Fixed sources in an included target not being relative when the sources are mix of string and dictionaries #542 @yonaskolb ## 2.2.0 #### Added -- Added ability to generate empty directories via `options.generateEmptyDirectories` [#480](https://github.com/yonaskolb/XcodeGen/pull/480) @Beniamiiin -- Added support for the `instrumentsPackage` product type [#482](https://github.com/yonaskolb/XcodeGen/pull/482) @ksulliva -- Added support for `inputFileLists` and `outputFileLists` within project build scripts [#500](https://github.com/yonaskolb/XcodeGen/pull/500) @lukewakeford -- Added support for a `$target_name` replacement string within target templates [#504](https://github.com/yonaskolb/XcodeGen/pull/504) @yonaskolb -- Added `createIntermediateGroups` to individual Target Sources which overrides the top level option [#505](https://github.com/yonaskolb/XcodeGen/pull/505) @yonaskolb + +- Added ability to generate empty directories via `options.generateEmptyDirectories` #480 @Beniamiiin +- Added support for the `instrumentsPackage` product type #482 @ksulliva +- Added support for `inputFileLists` and `outputFileLists` within project build scripts #500 @lukewakeford +- Added support for a `$target_name` replacement string within target templates #504 @yonaskolb +- Added `createIntermediateGroups` to individual Target Sources which overrides the top level option #505 @yonaskolb #### Changed -- **BREAKING**: All the paths within `include` files are now relative to that file and not the root spec. This can be disabled with a `relativePaths: false` on the include. See the [documentation](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#include) for more details [#489](https://github.com/yonaskolb/XcodeGen/pull/489) @ellneal -- Updated the Xcode compatibility version from 3.2 to 9.3 [#497](https://github.com/yonaskolb/XcodeGen/pull/497) @yonaskolb -- Exact matches to config names in build settings won't partial apply to other configs [#503](https://github.com/yonaskolb/XcodeGen/pull/503) @yonaskolb + +- **BREAKING**: All the paths within `include` files are now relative to that file and not the root spec. This can be disabled with a `relativePaths: false` on the include. See the [documentation](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#include) for more details #489 @ellneal +- Updated the Xcode compatibility version from 3.2 to 9.3 #497 @yonaskolb +- Exact matches to config names in build settings won't partial apply to other configs #503 @yonaskolb - UUIDs in the project are standard and don't contain any type prefixes anymore #### Fixed -- Fixed `--project` argument not taking effect [#487](https://github.com/yonaskolb/XcodeGen/pull/487) @monowerker -- Fixed Sticker Packs from generating an empty Source file phase which caused in error in the new build system [#492](https://github.com/yonaskolb/XcodeGen/pull/492) @rpassis -- Fixed generated schemes for tool targets not setting the executable [#496](https://github.com/yonaskolb/XcodeGen/pull/496) @yonaskolb -- Fixed resolving Carthage dependencies for iOS app with watchOS target. [465](https://github.com/yonaskolb/XcodeGen/pull/465) @raptorxcz -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.1.0...2.2.0) +- Fixed `--project` argument not taking effect #487 @monowerker +- Fixed Sticker Packs from generating an empty Source file phase which caused in error in the new build system #492 @rpassis +- Fixed generated schemes for tool targets not setting the executable #496 @yonaskolb +- Fixed resolving Carthage dependencies for iOS app with watchOS target. [465](https://github.com/yonaskolb/XcodeGen/pull/465) @raptorxcz ## 2.1.0 #### Added -- Added an experiment new caching feature. Pass `--use-cache` to opt in. This will read and write from a cache file to prevent unnecessarily generating the project. Give it a try as it may become the default in a future release [#412](https://github.com/yonaskolb/XcodeGen/pull/412) @yonaskolb + +- Added an experiment new caching feature. Pass `--use-cache` to opt in. This will read and write from a cache file to prevent unnecessarily generating the project. Give it a try as it may become the default in a future release #412 @yonaskolb #### Changed + - Changed spelling of build phases to **preBuildPhase** and **postBuildPhase**. The older names are deprecated but still work [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones -- Moved generation to a specific subcommand `xcodegen generate`. Simple `xcodegen` will continue to work for now [#437](https://github.com/yonaskolb/XcodeGen/pull/437) @yonaskolb -- If `INFOPLIST_FILE` has been set on a target, then an `info` path won't ovewrite it [#443](https://github.com/yonaskolb/XcodeGen/pull/443) @feischl97 +- Moved generation to a specific subcommand `xcodegen generate`. Simple `xcodegen` will continue to work for now #437 @yonaskolb +- If `INFOPLIST_FILE` has been set on a target, then an `info` path won't ovewrite it #443 @feischl97 #### Fixed -- Fixed XPC Service package type in generated `Info.plist` [#435](https://github.com/yonaskolb/XcodeGen/pull/435) @alvarhansen -- Fixed phase ordering for modulemap and static libary header Copy File phases. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones -- Fixed intermittent errors when running multiple `xcodegen`s concurrently [#450](https://github.com/yonaskolb/XcodeGen/pull/450) @bryansum -- Fixed `--project` argument not working [#437](https://github.com/yonaskolb/XcodeGen/pull/437) @yonaskolb -- Fixed unit tests not hooking up to host applications properly by default. They now generate a `TEST_HOST` and a `TestTargetID` [#452](https://github.com/yonaskolb/XcodeGen/pull/452) @yonaskolb -- Fixed static libraries not including external frameworks in their search paths [#454](https://github.com/yonaskolb/XcodeGen/pull/454) @brentleyjones -- Add `.intentdefinition` files to sources build phase instead of resources [#442](https://github.com/yonaskolb/XcodeGen/pull/442) @yonaskolb -- Add `mlmodel` files to sources build phase instead of resources [#457](https://github.com/yonaskolb/XcodeGen/pull/457) @dwb357 -[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.0.0...2.1.0) +- Fixed XPC Service package type in generated `Info.plist` #435 @alvarhansen +- Fixed phase ordering for modulemap and static libary header Copy File phases. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones +- Fixed intermittent errors when running multiple `xcodegen`s concurrently #450 @bryansum +- Fixed `--project` argument not working #437 @yonaskolb +- Fixed unit tests not hooking up to host applications properly by default. They now generate a `TEST_HOST` and a `TestTargetID` #452 @yonaskolb +- Fixed static libraries not including external frameworks in their search paths #454 @brentleyjones +- Add `.intentdefinition` files to sources build phase instead of resources #442 @yonaskolb +- Add `mlmodel` files to sources build phase instead of resources #457 @dwb357 ## 2.0.0 #### Added -- Added `weak` linking setting for dependencies [#411](https://github.com/yonaskolb/XcodeGen/pull/411) @alvarhansen -- Added `info` to targets for generating an `Info.plist` [#415](https://github.com/yonaskolb/XcodeGen/pull/415) @yonaskolb -- Added `entitlements` to targets for generating an `.entitlement` file [#415](https://github.com/yonaskolb/XcodeGen/pull/415) @yonaskolb -- Added `sdk` dependency type for linking system frameworks and libs [#430](https://github.com/yonaskolb/XcodeGen/pull/430) @yonaskolb -- Added `parallelizable` and `randomExecutionOrder` to `Scheme` test targets in an expanded form [#434](https://github.com/yonaskolb/XcodeGen/pull/434) @yonaskolb -- Validate incorrect config setting definitions [#431](https://github.com/yonaskolb/XcodeGen/pull/431) @yonaskolb -- Automatically set project `SDKROOT` if there is only a single platform within the project [#433](https://github.com/yonaskolb/XcodeGen/pull/433) @yonaskolb + +- Added `weak` linking setting for dependencies #411 @alvarhansen +- Added `info` to targets for generating an `Info.plist` #415 @yonaskolb +- Added `entitlements` to targets for generating an `.entitlement` file #415 @yonaskolb +- Added `sdk` dependency type for linking system frameworks and libs #430 @yonaskolb +- Added `parallelizable` and `randomExecutionOrder` to `Scheme` test targets in an expanded form #434 @yonaskolb +- Validate incorrect config setting definitions #431 @yonaskolb +- Automatically set project `SDKROOT` if there is only a single platform within the project #433 @yonaskolb #### Changed -- Performance improvements for large projects [#388](https://github.com/yonaskolb/XcodeGen/pull/388) [#417](https://github.com/yonaskolb/XcodeGen/pull/417) [#416](https://github.com/yonaskolb/XcodeGen/pull/416) @yonaskolb @kastiglione -- Upgraded to xcodeproj 6 [#388](https://github.com/yonaskolb/XcodeGen/pull/388) @yonaskolb -- Upgraded to Swift 4.2 [#388](https://github.com/yonaskolb/XcodeGen/pull/388) @yonaskolb -- Remove iOS codesigning sdk restriction in setting preset [#414](https://github.com/yonaskolb/XcodeGen/pull/414) @yonaskolb -- Changed default project version to Xcode 10.0 and default Swift version to 4.2 [#423](https://github.com/yonaskolb/XcodeGen/pull/423) @yonaskolb -- Added ability to not link Carthage frameworks [#432](https://github.com/yonaskolb/XcodeGen/pull/432) @yonaskolb + +- Performance improvements for large projects #388 @yonaskolb @kastiglione +- Upgraded to xcodeproj 6 #388 @yonaskolb +- Upgraded to Swift 4.2 #388 @yonaskolb +- Remove iOS codesigning sdk restriction in setting preset #414 @yonaskolb +- Changed default project version to Xcode 10.0 and default Swift version to 4.2 #423 @yonaskolb +- Added ability to not link Carthage frameworks #432 @yonaskolb #### Fixed -- Fixed code signing issues [#414](https://github.com/yonaskolb/XcodeGen/pull/414) @yonaskolb -- Fixed `TargetSource.headerVisibility` not being set in initializer [#419](https://github.com/yonaskolb/XcodeGen/pull/419) @jerrymarino -- Fixed crash when using Xcode Legacy targets as dependencies [#427](https://github.com/yonaskolb/XcodeGen/pull/427) @dflems -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.11.2...2.0.0) +- Fixed code signing issues #414 @yonaskolb +- Fixed `TargetSource.headerVisibility` not being set in initializer #419 @jerrymarino +- Fixed crash when using Xcode Legacy targets as dependencies #427 @dflems ## 1.11.2 If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project will not be deterministic. This will be fixed in an upcoming release with an update to xcodeproj 6.0 #### Fixed -- Fixed release builds in Swift 4.2 [#404](https://github.com/yonaskolb/XcodeGen/pull/404) @pepibumur -- Fixed default settings for macOS unit-tests [#387](https://github.com/yonaskolb/XcodeGen/pull/387) @frankdilo -- Fixed Copy Headers phase ordering for Xcode 10 [#401](https://github.com/yonaskolb/XcodeGen/pull/401) @brentleyjones -- Fixed generated schemes on aggregate targets [#394](https://github.com/yonaskolb/XcodeGen/pull/394) @vgorloff + +- Fixed release builds in Swift 4.2 #404 @pepibumur +- Fixed default settings for macOS unit-tests #387 @frankdilo +- Fixed Copy Headers phase ordering for Xcode 10 #401 @brentleyjones +- Fixed generated schemes on aggregate targets #394 @vgorloff #### Changed -- Added `en` as default value for knownRegions [#390](https://github.com/yonaskolb/XcodeGen/pull/390) @Saik0s -- Update `PathKit`, `Spectre`, `Yams` and `xcodeproj` dependencies -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.11.1...1.11.2) +- Added `en` as default value for knownRegions #390 @Saik0s +- Update `PathKit`, `Spectre`, `Yams` and `xcodeproj` dependencies ## 1.11.1 #### Fixed -- Fixed `FRAMEWORK_SEARCH_PATHS` for `framework` dependency paths with spaces [#382](https://github.com/yonaskolb/XcodeGen/pull/382) @brentleyjones -- Fixed aggregate targets not being found with `transitivelyLinkDependencies` [#383](https://github.com/yonaskolb/XcodeGen/pull/383) @brentleyjones -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.11.0...1.11.1) +- Fixed `FRAMEWORK_SEARCH_PATHS` for `framework` dependency paths with spaces #382 @brentleyjones +- Fixed aggregate targets not being found with `transitivelyLinkDependencies` #383 @brentleyjones ## 1.11.0 #### Added -- Added `showEnvVars` to build scripts to disable printing the environment [#351](https://github.com/yonaskolb/XcodeGen/pull/351) @keith -- Added `requiresObjCLinking` to `target` [#354](https://github.com/yonaskolb/XcodeGen/pull/354) @brentleyjones -- Added `targetTemplates` [#355](https://github.com/yonaskolb/XcodeGen/pull/355) @yonaskolb -- Added `aggregateTargets` [#353](https://github.com/yonaskolb/XcodeGen/pull/353) [#381](https://github.com/yonaskolb/XcodeGen/pull/381) @yonaskolb -- Added `options.groupSortPosition` [#356](https://github.com/yonaskolb/XcodeGen/pull/356) @yonaskolb -- Added ability to specify `copyFiles` build phase for sources [#345](https://github.com/yonaskolb/XcodeGen/pull/345) @brentleyjones -- Added ability to specify a `minimumXcodeGenVersion` [#349](https://github.com/yonaskolb/XcodeGen/pull/349) @brentleyjones -- Added `customArchiveName` and `revealArchiveInOrganizer` to `archive` [#367](https://github.com/yonaskolb/XcodeGen/pull/367) @sxua - -#### Fixed -- Sort files using localizedStandardCompare [#341](https://github.com/yonaskolb/XcodeGen/pull/341) @rohitpal440 -- Use the latest `xcdatamodel` when sorted by version [#341](https://github.com/yonaskolb/XcodeGen/pull/341) @rohitpal440 -- Fixed compiler flags being set on non source files in mixed build phase target sources [#347](https://github.com/yonaskolb/XcodeGen/pull/347) @brentleyjones -- Fixed `options.xcodeVersion` not being parsed [#348](https://github.com/yonaskolb/XcodeGen/pull/348) @brentleyjones -- Fixed non-application targets using `carthage copy-frameworks` [#361](https://github.com/yonaskolb/XcodeGen/pull/361) @brentleyjones -- Set `xcdatamodel` based on `xccurrentversion` if available [#364](https://github.com/yonaskolb/XcodeGen/pull/364) @rpassis -- XPC Services are now correctly copied [#368](https://github.com/yonaskolb/XcodeGen/pull/368) @brentley -- Fixed `.metal` files being added to resources [#380](https://github.com/yonaskolb/XcodeGen/pull/380) @vgorloff - -#### Changed -- Improved linking for `static.library` targets [#352](https://github.com/yonaskolb/XcodeGen/pull/352) @brentleyjones -- Changed default group sorting to be after files [#356](https://github.com/yonaskolb/XcodeGen/pull/356) @yonaskolb -- Moved `Frameworks` and `Products` top level groups to bottom [#356](https://github.com/yonaskolb/XcodeGen/pull/356) @yonaskolb -- `modulemap` files are automatically copied to the products directory for static library targets [#346](https://github.com/yonaskolb/XcodeGen/pull/346) @brentleyjones -- Public header files are automatically copied to the products directory for static library targets [#365](https://github.com/yonaskolb/XcodeGen/pull/365) @brentleyjones -- Swift Objective-C Interface Header files are automatically copied to the products directory for static library targets [#366](https://github.com/yonaskolb/XcodeGen/pull/366) @brentleyjones -- `FRAMEWORK_SEARCH_PATHS` are adjusted for `framework` dependencies [#373](https://github.com/yonaskolb/XcodeGen/pull/373) @brentley -- `library.static` targets have `SKIP_INSTALL` set to `YES` [#358](https://github.com/yonaskolb/XcodeGen/pull/358) @brentley -- Copy files phases have descriptive names [#360](https://github.com/yonaskolb/XcodeGen/pull/360) @brentley + +- Added `showEnvVars` to build scripts to disable printing the environment #351 @keith +- Added `requiresObjCLinking` to `target` #354 @brentleyjones +- Added `targetTemplates` #355 @yonaskolb +- Added `aggregateTargets` #353 @yonaskolb +- Added `options.groupSortPosition` #356 @yonaskolb +- Added ability to specify `copyFiles` build phase for sources #345 @brentleyjones +- Added ability to specify a `minimumXcodeGenVersion` #349 @brentleyjones +- Added `customArchiveName` and `revealArchiveInOrganizer` to `archive` #367 @sxua + +#### Fixed + +- Sort files using localizedStandardCompare #341 @rohitpal440 +- Use the latest `xcdatamodel` when sorted by version #341 @rohitpal440 +- Fixed compiler flags being set on non source files in mixed build phase target sources #347 @brentleyjones +- Fixed `options.xcodeVersion` not being parsed #348 @brentleyjones +- Fixed non-application targets using `carthage copy-frameworks` #361 @brentleyjones +- Set `xcdatamodel` based on `xccurrentversion` if available #364 @rpassis +- XPC Services are now correctly copied #368 @brentley +- Fixed `.metal` files being added to resources #380 @vgorloff + +#### Changed + +- Improved linking for `static.library` targets #352 @brentleyjones +- Changed default group sorting to be after files #356 @yonaskolb +- Moved `Frameworks` and `Products` top level groups to bottom #356 @yonaskolb +- `modulemap` files are automatically copied to the products directory for static library targets #346 @brentleyjones +- Public header files are automatically copied to the products directory for static library targets #365 @brentleyjones +- Swift Objective-C Interface Header files are automatically copied to the products directory for static library targets #366 @brentleyjones +- `FRAMEWORK_SEARCH_PATHS` are adjusted for `framework` dependencies #373 @brentley +- `library.static` targets have `SKIP_INSTALL` set to `YES` #358 @brentley +- Copy files phases have descriptive names #360 @brentley #### Internal + - Moved brew formula to homebrew core - Added `CONTRIBUTING.md` -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.10.3...1.11.0) - ## 1.10.3 #### Fixed -- Fixed Mint installations finding `SettingPresets` [#338](https://github.com/yonaskolb/XcodeGen/pull/338) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.10.2...1.10.3) +- Fixed Mint installations finding `SettingPresets` #338 @yonaskolb ## 1.10.2 #### Changed -- Set `transitivelyLinkDependencies` to false by default -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.10.1...1.10.2) +- Set `transitivelyLinkDependencies` to false by default ## 1.10.1 #### Fixed -- Fixed `transitivelyLinkDependencies` typo [#332](https://github.com/yonaskolb/XcodeGen/pull/332) @brentleyjones -- Fixed framework target dependencies not being code signed by default [#332](https://github.com/yonaskolb/XcodeGen/pull/332) @yonaskolb + +- Fixed `transitivelyLinkDependencies` typo #332 @brentleyjones +- Fixed framework target dependencies not being code signed by default #332 @yonaskolb #### Changed -- Code sign all dependencies by default except target executables [#332](https://github.com/yonaskolb/XcodeGen/pull/332) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.10.0...1.10.1) +- Code sign all dependencies by default except target executables #332 @yonaskolb ## 1.10.0 #### Added -- Added build rule support [#306](https://github.com/yonaskolb/XcodeGen/pull/306) @yonaskolb -- Added support for frameworks in sources [#308](https://github.com/yonaskolb/XcodeGen/pull/308) @keith -- Added ability to automatically embed transient dependencies. Controlled with `transitivelyLinkDependencies` [#327](https://github.com/yonaskolb/XcodeGen/pull/327) @brentleyjones + +- Added build rule support #306 @yonaskolb +- Added support for frameworks in sources #308 @keith +- Added ability to automatically embed transient dependencies. Controlled with `transitivelyLinkDependencies` #327 @brentleyjones #### Changed + - Upgraded to Swift 4.1 -- Improved Carthage dependency lookup performance with many targets [#298](https://github.com/yonaskolb/XcodeGen/pull/298) @keith -- By default don't CodeSignOnCopy `target` dependencies. This can still be controlled with `Dependency.codeSign` [#324](https://github.com/yonaskolb/XcodeGen/pull/324) @yonaskolb - -#### Fixed -- Fixed PBXBuildFile and PBXFileReference being incorrectly generated for Legacy targets [#296](https://github.com/yonaskolb/XcodeGen/pull/296) @sascha -- Fixed required sources build phase not being generated if there are no sources [#307](https://github.com/yonaskolb/XcodeGen/pull/307) @yonaskolb -- Fixed install script in binary release [#303](https://github.com/yonaskolb/XcodeGen/pull/303) @alvarhansen -- Removed `ENABLE_TESTABILITY` from framework setting presets [#299](https://github.com/yonaskolb/XcodeGen/pull/299) @allu22 -- Fixed homebrew installation [#297](https://github.com/yonaskolb/XcodeGen/pull/297) @vhbit -- `cc` files are now automatically recognized as source files [#317](https://github.com/yonaskolb/XcodeGen/pull/317) @maicki -- Fixed `commandLineArguments` not parsing when they had dots in them [#323](https://github.com/yonaskolb/XcodeGen/pull/323) @yonaskolb -- Fixed excluding directories that only have sub directories [#326](https://github.com/yonaskolb/XcodeGen/pull/326) @brentleyjones -- Made `PBXContainerItemProxy` ID more deterministic -- Fixed generated framework schemes from being executable [#328](https://github.com/yonaskolb/XcodeGen/pull/328) @brentleyjones +- Improved Carthage dependency lookup performance with many targets #298 @keith +- By default don't CodeSignOnCopy `target` dependencies. This can still be controlled with `Dependency.codeSign` #324 @yonaskolb + +#### Fixed -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.9.0...1.10.0) +- Fixed PBXBuildFile and PBXFileReference being incorrectly generated for Legacy targets #296 @sascha +- Fixed required sources build phase not being generated if there are no sources #307 @yonaskolb +- Fixed install script in binary release #303 @alvarhansen +- Removed `ENABLE_TESTABILITY` from framework setting presets #299 @allu22 +- Fixed homebrew installation #297 @vhbit +- `cc` files are now automatically recognized as source files #317 @maicki +- Fixed `commandLineArguments` not parsing when they had dots in them #323 @yonaskolb +- Fixed excluding directories that only have sub directories #326 @brentleyjones +- Made `PBXContainerItemProxy` ID more deterministic +- Fixed generated framework schemes from being executable #328 @brentleyjones ## 1.9.0 #### Added -- Scheme pre and post actions can now be added to `target.scheme` [#280](https://github.com/yonaskolb/XcodeGen/pull/280) @yonaskolb -- Individual files can now be added to `fileGroups` [#293](https://github.com/yonaskolb/XcodeGen/pull/293) @yonaskolb + +- Scheme pre and post actions can now be added to `target.scheme` #280 @yonaskolb +- Individual files can now be added to `fileGroups` #293 @yonaskolb #### Changed + - Updated to `xcproj` 4.3.0 for Xcode 9.3 updates -- Update default Xcode version to 9.3 including new settings [#284](https://github.com/yonaskolb/XcodeGen/pull/284) @LinusU -- **Breaking for ProjectSpec library users** Changed `ProjectSpec` to `Project` and `ProjectSpec.Options` to `SpecOptions` [#281](https://github.com/yonaskolb/XcodeGen/pull/281) @jerrymarino +- Update default Xcode version to 9.3 including new settings #284 @LinusU +- **Breaking for ProjectSpec library users** Changed `ProjectSpec` to `Project` and `ProjectSpec.Options` to `SpecOptions` #281 @jerrymarino #### Fixed -- Fixed manual build phase of `none` not being applied to folders [#288](https://github.com/yonaskolb/XcodeGen/pull/288) @yonaskolb -- Quoted values now correctly get parsed as strings [#282](https://github.com/yonaskolb/XcodeGen/pull/282) @yonaskolb -- Fixed adding a root source folder when `createIntermediateGroups` is on [#291](https://github.com/yonaskolb/XcodeGen/pull/291) @yonaskolb -- Fixed Homebrew installations issues on some machines [#289](https://github.com/yonaskolb/XcodeGen/pull/289) @vhbit -- Fixed files that are added as root sources from having invalid parent groups outside the project directory [#293](https://github.com/yonaskolb/XcodeGen/pull/293) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.8.0...1.9.0) +- Fixed manual build phase of `none` not being applied to folders #288 @yonaskolb +- Quoted values now correctly get parsed as strings #282 @yonaskolb +- Fixed adding a root source folder when `createIntermediateGroups` is on #291 @yonaskolb +- Fixed Homebrew installations issues on some machines #289 @vhbit +- Fixed files that are added as root sources from having invalid parent groups outside the project directory #293 @yonaskolb ## 1.8.0 #### Added -- Added Project `defaultConfig` [#269](https://github.com/yonaskolb/XcodeGen/pull/269) @keith -- Added Target `attributes` [#276](https://github.com/yonaskolb/XcodeGen/pull/276) @yonaskolb -- Automatically set `DevelopmentTeam` and `ProvisioningStyle` within `TargetAttributes` if relevant build settings are defined [#277](https://github.com/yonaskolb/XcodeGen/pull/277) @yonaskolb + +- Added Project `defaultConfig` #269 @keith +- Added Target `attributes` #276 @yonaskolb +- Automatically set `DevelopmentTeam` and `ProvisioningStyle` within `TargetAttributes` if relevant build settings are defined #277 @yonaskolb #### Fixed -- Fixed default `LD_RUNPATH_SEARCH_PATHS` for app extensions [#272](https://github.com/yonaskolb/XcodeGen/pull/272) @LinusU + +- Fixed default `LD_RUNPATH_SEARCH_PATHS` for app extensions #272 @LinusU #### Internal -- Make `LegacyTarget` init public [#264](https://github.com/yonaskolb/XcodeGen/pull/264) @jerrymarino -- Upgrade to *xcproj* to 4.2.0, *Yams* to 0.6.0 and *PathKit* to 0.9.1 @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.7.0...1.8.0) +- Make `LegacyTarget` init public #264 @jerrymarino +- Upgrade to _xcproj_ to 4.2.0, _Yams_ to 0.6.0 and _PathKit_ to 0.9.1 @yonaskolb ## 1.7.0 #### Added -- Added support for scheme environment variables [#239](https://github.com/yonaskolb/XcodeGen/pull/239) [#254](https://github.com/yonaskolb/XcodeGen/pull/254) [#259](https://github.com/yonaskolb/XcodeGen/pull/259) @turekj @toshi0383 -- Added `carthageExecutablePath` option [#244](https://github.com/yonaskolb/XcodeGen/pull/244) @akkyie -- Added `parallelizeBuild` and `buildImplicitDependencies` to Schemes [#241](https://github.com/yonaskolb/XcodeGen/pull/241) @rahul-malik +- Added support for scheme environment variables #239 @turekj @toshi0383 +- Added `carthageExecutablePath` option #244 @akkyie +- Added `parallelizeBuild` and `buildImplicitDependencies` to Schemes #241 @rahul-malik @yonaskolb -- Added support for Core Data `xcdatamodeld` files [#249](https://github.com/yonaskolb/XcodeGen/pull/249) @yonaskolb -- Projects are now generated atomically by writing to a temporary directory first [#250](https://github.com/yonaskolb/XcodeGen/pull/250) @yonaskolb -- Added script for adding precompiled binary to releases [#246](https://github.com/yonaskolb/XcodeGen/pull/246) @toshi0383 -- Added optional `headerVisibilty` to target source. This still defaults to public [#252](https://github.com/yonaskolb/XcodeGen/pull/252) @yonaskolb +- Added support for Core Data `xcdatamodeld` files #249 @yonaskolb +- Projects are now generated atomically by writing to a temporary directory first #250 @yonaskolb +- Added script for adding precompiled binary to releases #246 @toshi0383 +- Added optional `headerVisibilty` to target source. This still defaults to public #252 @yonaskolb - Releases now include a pre-compiled binary and setting presets, including an install script #### Fixed -- Fixed Mint installation from reading setting presets [#248](https://github.com/yonaskolb/XcodeGen/pull/248) @yonaskolb -- Fixed setting `buildPhase` on a `folder` source. This allows for a folder of header files [#254](https://github.com/yonaskolb/XcodeGen/pull/254) @toshi0383 -- Carthage dependencies are not automatically embedded into test targets [#256](https://github.com/yonaskolb/XcodeGen/pull/256) @yonaskolb -- Carthage dependencies now respect the `embed` property [#256](https://github.com/yonaskolb/XcodeGen/pull/256) @yonaskolb -- iMessage extensions now have proper setting presets in regards to app icon and runtime search paths [#255](https://github.com/yonaskolb/XcodeGen/pull/255) @yonaskolb -- Excluded files are not added within .lproj directories [#238](https://github.com/yonaskolb/XcodeGen/pull/238) @toshi0383 -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.6.0...1.7.0) +- Fixed Mint installation from reading setting presets #248 @yonaskolb +- Fixed setting `buildPhase` on a `folder` source. This allows for a folder of header files #254 @toshi0383 +- Carthage dependencies are not automatically embedded into test targets #256 @yonaskolb +- Carthage dependencies now respect the `embed` property #256 @yonaskolb +- iMessage extensions now have proper setting presets in regards to app icon and runtime search paths #255 @yonaskolb +- Excluded files are not added within .lproj directories #238 @toshi0383 ## 1.6.0 #### Added -- Added scheme pre-actions and post-actions [#231](https://github.com/yonaskolb/XcodeGen/pull/231) @kastiglione -- Added `options.disabledValidations` including `missingConfigs` to disable project validation errors [#220](https://github.com/yonaskolb/XcodeGen/pull/220) @keith -- Generate UI Test Target Attributes [#221](https://github.com/yonaskolb/XcodeGen/pull/221) @anreitersimon + +- Added scheme pre-actions and post-actions #231 @kastiglione +- Added `options.disabledValidations` including `missingConfigs` to disable project validation errors #220 @keith +- Generate UI Test Target Attributes #221 @anreitersimon #### Fixed -- Filter out duplicate source files [#217](https://github.com/yonaskolb/XcodeGen/pull/217) @allu22 -- Fixed how `lastKnownFileType` and `explicitFileType` were generated across platforms [#115](https://github.com/yonaskolb/XcodeGen/pull/115) @toshi0383 + +- Filter out duplicate source files #217 @allu22 +- Fixed how `lastKnownFileType` and `explicitFileType` were generated across platforms #115 @toshi0383 - Removed a few cases of project diffs when opening the project in Xcode @yonaskolb - Fixed Swift not being embedded by default in watch apps @yonaskolb #### Changed -- Change arrays to strings in setting presets [#218](https://github.com/yonaskolb/XcodeGen/pull/218) @allu22 -- Updated to xcproj 4.0 [#227](https://github.com/yonaskolb/XcodeGen/pull/227) -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.5.0...1.6.0) +- Change arrays to strings in setting presets #218 @allu22 +- Updated to xcproj 4.0 #227 ## 1.5.0 #### Added -- added support for `gatherCoverageData` flag in target schemes [#170](https://github.com/yonaskolb/XcodeGen/pull/170) @alexruperez -- added support for `commandLineOptions` in target schemes [#172](https://github.com/yonaskolb/XcodeGen/pull/172) @rahul-malik -- added Project spec as a SwiftPM library for reuse in other projects [#164](https://github.com/yonaskolb/XcodeGen/pull/164) @soffes -- added `implicit` option for framework dependencies [#166](https://github.com/yonaskolb/XcodeGen/pull/166) @sbarow -- added `--quite` option to CLI [#167](https://github.com/yonaskolb/XcodeGen/pull/167) @soffes -- can now print version with `-v` in addition to `--version` [#174](https://github.com/yonaskolb/XcodeGen/pull/174) @kastiglione -- added support for legacy targets [#175](https://github.com/yonaskolb/XcodeGen/pull/175) @bkase -- added support for indentation options [#190](https://github.com/yonaskolb/XcodeGen/pull/190) @bkase -- added source excludes [#135](https://github.com/yonaskolb/XcodeGen/pull/135) [#161](https://github.com/yonaskolb/XcodeGen/pull/161) [#190](https://github.com/yonaskolb/XcodeGen/pull/190) @peymankh @ -- added `options.xcodeVersion` [#197](https://github.com/yonaskolb/XcodeGen/pull/197) @yonaskolb @peymankh -- add test targets to Scheme [#195](https://github.com/yonaskolb/XcodeGen/pull/195) @vhbit -- add option to make a source file optional incase it will be generated later [#200](https://github.com/yonaskolb/XcodeGen/pull/200) @vhbit -- finalize Scheme spec [#201](https://github.com/yonaskolb/XcodeGen/pull/201) @yonaskolb -- added `buildPhase` setting to target source for overriding the guessed build phase of files [#206](https://github.com/yonaskolb/XcodeGen/pull/206) @yonaskolb -- added `deploymentTarget` setting to project and target [#205](https://github.com/yonaskolb/XcodeGen/pull/205) @yonaskolb + +- added support for `gatherCoverageData` flag in target schemes #170 @alexruperez +- added support for `commandLineOptions` in target schemes #172 @rahul-malik +- added Project spec as a SwiftPM library for reuse in other projects #164 @soffes +- added `implicit` option for framework dependencies #166 @sbarow +- added `--quite` option to CLI #167 @soffes +- can now print version with `-v` in addition to `--version` #174 @kastiglione +- added support for legacy targets #175 @bkase +- added support for indentation options #190 @bkase +- added source excludes #135 @peymankh @ +- added `options.xcodeVersion` #197 @yonaskolb @peymankh +- add test targets to Scheme #195 @vhbit +- add option to make a source file optional incase it will be generated later #200 @vhbit +- finalize Scheme spec #201 @yonaskolb +- added `buildPhase` setting to target source for overriding the guessed build phase of files #206 @yonaskolb +- added `deploymentTarget` setting to project and target #205 @yonaskolb #### Changed + - huge performance improvements when writing the project file due to changes in xcproj - updated dependencies - minor logging changes - updated Project Spec documentation -- scan for `Info.plist` lazely [#194](https://github.com/yonaskolb/XcodeGen/pull/194) @kastiglione -- change setting presets so that icon settings only get applied to application targets [#204](https://github.com/yonaskolb/XcodeGen/pull/204) @yonaskolb -- changed scheme build targets format [#203](https://github.com/yonaskolb/XcodeGen/pull/203) @yonaskolb -- when specifying a `--spec` argument, the default for the `--project` path is now the directory containing the spec [#211](https://github.com/yonaskolb/XcodeGen/pull/211) @yonaskolb +- scan for `Info.plist` lazely #194 @kastiglione +- change setting presets so that icon settings only get applied to application targets #204 @yonaskolb +- changed scheme build targets format #203 @yonaskolb +- when specifying a `--spec` argument, the default for the `--project` path is now the directory containing the spec #211 @yonaskolb #### Fixed -- fixed shell scripts escaping quotes twice [#186](https://github.com/yonaskolb/XcodeGen/pull/186) @allu22 -- fixed `createIntermediateGroups` when using a relative spec path [#184](https://github.com/yonaskolb/XcodeGen/pull/184) @kastiglione -- fixed command line arguments for test and profile from being overridden [#199](https://github.com/yonaskolb/XcodeGen/pull/199) @vhbit -- fixed files deep within a hierarchy having the path for a name -- fixed source files from being duplicated if referenced with different casing [#212](https://github.com/yonaskolb/XcodeGen/pull/212) @yonaskolb -- fixed target product name not being written. Fixes integration with R.swift [#213](https://github.com/yonaskolb/XcodeGen/pull/213) @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.4.0...1.5.0) +- fixed shell scripts escaping quotes twice #186 @allu22 +- fixed `createIntermediateGroups` when using a relative spec path #184 @kastiglione +- fixed command line arguments for test and profile from being overridden #199 @vhbit +- fixed files deep within a hierarchy having the path for a name +- fixed source files from being duplicated if referenced with different casing #212 @yonaskolb +- fixed target product name not being written. Fixes integration with R.swift #213 @yonaskolb ## 1.4.0 #### Added -- added `--version` flag [#112](https://github.com/yonaskolb/XcodeGen/pull/112) @mironal -- added support for adding individual file sources [#106](https://github.com/yonaskolb/XcodeGen/pull/106) [#133](https://github.com/yonaskolb/XcodeGen/pull/133) [#142](https://github.com/yonaskolb/XcodeGen/pull/142) [#139](https://github.com/yonaskolb/XcodeGen/pull/139) @bkase -- added source compiler flag support [#121](https://github.com/yonaskolb/XcodeGen/pull/121) @bkase -- added `ProjectSpec.options.createIntermediateGroups` [#108](https://github.com/yonaskolb/XcodeGen/pull/108) @bkase -- added better json loading support [#127](https://github.com/yonaskolb/XcodeGen/pull/127) @rahul-malik -- added source `name` for customizing names of source directories and file [#146](https://github.com/yonaskolb/XcodeGen/pull/146) @yonaskolb -- added folder reference source support via a new `type` property [#151](https://github.com/yonaskolb/XcodeGen/pull/151) @yonaskolb -- added `ProjectSpec.options.developmentLanguage` [#155](https://github.com/yonaskolb/XcodeGen/pull/155) @yonaskolb + +- added `--version` flag #112 @mironal +- added support for adding individual file sources #106 @bkase +- added source compiler flag support #121 @bkase +- added `ProjectSpec.options.createIntermediateGroups` #108 @bkase +- added better json loading support #127 @rahul-malik +- added source `name` for customizing names of source directories and file #146 @yonaskolb +- added folder reference source support via a new `type` property #151 @yonaskolb +- added `ProjectSpec.options.developmentLanguage` #155 @yonaskolb #### Changed -- updated to xcproj 1.2.0 [#113](https://github.com/yonaskolb/XcodeGen/pull/113) @yonaskolb -- build settings from presets will be removed if they are provided in `xcconfig` files [#77](https://github.com/yonaskolb/XcodeGen/pull/77) @toshi0383 -- all files and groups are sorted by type and then alphabetically [#144](https://github.com/yonaskolb/XcodeGen/pull/144) @yonaskolb -- target sources can now have an expanded form [#119](https://github.com/yonaskolb/XcodeGen/pull/119) @yonaskolb -- empty build phases are now not generated [#149](https://github.com/yonaskolb/XcodeGen/pull/149) @yonaskolb -- make UUIDs more deterministic [#154](https://github.com/yonaskolb/XcodeGen/pull/154) @yonaskolb + +- updated to xcproj 1.2.0 #113 @yonaskolb +- build settings from presets will be removed if they are provided in `xcconfig` files #77 @toshi0383 +- all files and groups are sorted by type and then alphabetically #144 @yonaskolb +- target sources can now have an expanded form #119 @yonaskolb +- empty build phases are now not generated #149 @yonaskolb +- make UUIDs more deterministic #154 @yonaskolb #### Fixed -- only add headers to frameworks and libraries [#118](https://github.com/yonaskolb/XcodeGen/pull/118) @ryohey -- fixed localized files with the same name [#126](https://github.com/yonaskolb/XcodeGen/pull/126) @ryohey -- fix intermediate sources [#144](https://github.com/yonaskolb/XcodeGen/pull/144) @yonaskolb -- fix cyclical target dependencies not working [#147](https://github.com/yonaskolb/XcodeGen/pull/147) @yonaskolb -- fix directory bundles not being added properly when referenced directly [#148](https://github.com/yonaskolb/XcodeGen/pull/1478) @yonaskolb -- made `mm`, `c` and `S` file be parsed as source files [#120](https://github.com/yonaskolb/XcodeGen/pull/120) [#125](https://github.com/yonaskolb/XcodeGen/pull/125) [#138](https://github.com/yonaskolb/XcodeGen/pull/138) @bkase @enmiller -- fix the generation of localized variant groups if there is no `Base.lproj` [#157](https://github.com/yonaskolb/XcodeGen/pull/157) @ryohey -- all localizations found are added to a projects known regions [#157](https://github.com/yonaskolb/XcodeGen/pull/157) @ryohey + +- only add headers to frameworks and libraries #118 @ryohey +- fixed localized files with the same name #126 @ryohey +- fix intermediate sources #144 @yonaskolb +- fix cyclical target dependencies not working #147 @yonaskolb +- fix directory bundles not being added properly when referenced directly #148 @yonaskolb +- made `mm`, `c` and `S` file be parsed as source files #120 @bkase @enmiller +- fix the generation of localized variant groups if there is no `Base.lproj` #157 @ryohey +- all localizations found are added to a projects known regions #157 @ryohey #### Internal + - refactoring - more tests - added release scripts -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.3.0...1.4.0) - ## 1.3.0 #### Added -- generate output files for Carthage copy-frameworks script [#84](https://github.com/yonaskolb/XcodeGen/pull/84) @mironal -- added options.settingPreset to choose which setting presets get applied [#100](https://github.com/yonaskolb/XcodeGen/pull/101) @yonaskolb -- added `link` option for target dependencies [#109](https://github.com/yonaskolb/XcodeGen/pull/109) @keith + +- generate output files for Carthage copy-frameworks script #84 @mironal +- added options.settingPreset to choose which setting presets get applied #100 @yonaskolb +- added `link` option for target dependencies #109 @keith #### Changed -- updated to xcproj 0.4.1 [#85](https://github.com/yonaskolb/XcodeGen/pull/85) @enmiller -- don't copy base settings if config type has been left out [#100](https://github.com/yonaskolb/XcodeGen/pull/100) @yonaskolb -- generate localised files under a single variant group [#70](https://github.com/yonaskolb/XcodeGen/pull/70) @ryohey -- don't apply common project settings to configs with no type [#100](https://github.com/yonaskolb/XcodeGen/pull/100) @yonaskolb -- config references in settings can now be partially matched and are case insensitive [#111](https://github.com/yonaskolb/XcodeGen/pull/111) @yonaskolb + +- updated to xcproj 0.4.1 #85 @enmiller +- don't copy base settings if config type has been left out #100 @yonaskolb +- generate localised files under a single variant group #70 @ryohey +- don't apply common project settings to configs with no type #100 @yonaskolb +- config references in settings can now be partially matched and are case insensitive #111 @yonaskolb - other small internal changes @yonaskolb #### Fixed -- embed Carthage frameworks for macOS [#82](https://github.com/yonaskolb/XcodeGen/pull/82) @toshi0383 -- fixed copying of watchOS app resources [#96](https://github.com/yonaskolb/XcodeGen/pull/96) @keith -- automatically ignore more file types for a target's sources (entitlements, gpx, apns) [#94](https://github.com/yonaskolb/XcodeGen/pull/94) @keith -- change make build to a PHONY task [#98](https://github.com/yonaskolb/XcodeGen/pull/98) @keith -- allow copying of resource files from dependant targets [#95](https://github.com/yonaskolb/XcodeGen/pull/95) @keith -- fixed library linking [#93](https://github.com/yonaskolb/XcodeGen/pull/93) @keith -- fixed duplicate carthage file references [#107](https://github.com/yonaskolb/XcodeGen/pull/107) @yonaskolb -- an error is now shown if you try and generate a target scheme and don't have debug and release builds @yonaskolb -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.2.4...1.3.0) +- embed Carthage frameworks for macOS #82 @toshi0383 +- fixed copying of watchOS app resources #96 @keith +- automatically ignore more file types for a target's sources (entitlements, gpx, apns) #94 @keith +- change make build to a PHONY task #98 @keith +- allow copying of resource files from dependant targets #95 @keith +- fixed library linking #93 @keith +- fixed duplicate carthage file references #107 @yonaskolb +- an error is now shown if you try and generate a target scheme and don't have debug and release builds @yonaskolb ## 1.2.4 #### Fixed + - setting presets only apply `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: YES` to applications - don't add carthage dependency to `copy-frameworks` script if `embed: false` - sort group children on APFS #### Changed -- update to xcproj 0.3.0 -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.2.3...1.2.4) +- update to xcproj 0.3.0 ## 1.2.3 #### Fixed -- Fixed wrong carthage directory name reference for macOS [#74](https://github.com/yonaskolb/XcodeGen/pull/74) @toshi0383 -- Removed unnecessary `carthage copy-frameworks` for macOS app target [#76](https://github.com/yonaskolb/XcodeGen/pull/76) @toshi0383 + +- Fixed wrong carthage directory name reference for macOS #74 @toshi0383 +- Removed unnecessary `carthage copy-frameworks` for macOS app target #76 @toshi0383 - Added some missing default settings for framework targets. `SKIP_INSTALL: YES` fixes archiving - Filter out nulls from setting presets if specifying an empty string -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.2.2...1.2.3) - ## 1.2.2 #### Added + - automatically set `TEST_TARGET_NAME` on UI test targets if one of the dependencies is an application target #### Fixed + - set `DYLIB_INSTALL_NAME_BASE` to `@rpath` in framework target presets - fixed tvOS launch screen setting. `ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME` is now `LaunchImage` not `tvOS LaunchImage` - -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.2.0...1.2.2) - ## 1.2.0 #### Added + - `include` now supports a single string as well as a list -- add support setting xcconfig files on a project with `configFiles` [#64](https://github.com/yonaskolb/XcodeGen/pull/64) -- add `fileGroups` to project spec for adding groups of files that aren't target source files [#64](https://github.com/yonaskolb/XcodeGen/pull/64) +- add support setting xcconfig files on a project with `configFiles` #64 +- add `fileGroups` to project spec for adding groups of files that aren't target source files #64 - better output (more info, emoji, colors) -- add `options.bundleIdPrefix` for autogenerating `PRODUCT_BUNDLE_IDENTIFIER` [#67](https://github.com/yonaskolb/XcodeGen/pull/67) -- add `:REPLACE` syntax when merging `include` [#68](https://github.com/yonaskolb/XcodeGen/pull/68) +- add `options.bundleIdPrefix` for autogenerating `PRODUCT_BUNDLE_IDENTIFIER` #67 +- add `:REPLACE` syntax when merging `include` #68 - add `mint` installation support #### Fixed + - fixed homebrew installation -- fixed target xcconfig files not working via `configFiles` [#64](https://github.com/yonaskolb/XcodeGen/pull/64) -- look for `INFOPLIST_FILE` setting in project and xcconfig files before adding it automatically. It was just looking in target settings before [#64](https://github.com/yonaskolb/XcodeGen/pull/64) +- fixed target xcconfig files not working via `configFiles` #64 +- look for `INFOPLIST_FILE` setting in project and xcconfig files before adding it automatically. It was just looking in target settings before #64 - exit with error on failure -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.1.0...1.2.0) - ## 1.1.0 #### Changed + - set project version to Xcode 9 - `LastUpgradeVersion` attribute to `0900` - set default Swift version to 4.0 - `SWIFT_VERSION` build setting to `4.0` -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.0.1...1.1.0) - ### 1.0.1 ### Fixed + - fixed incorrect default build script shell path - fixed install scripts -[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.0.0...1.0.1) - ## 1.0.0 #### Added -- Swift 4 support [#52](https://github.com/yonaskolb/XcodeGen/pull/52) -- Support for C and C++ files [#48](https://github.com/yonaskolb/XcodeGen/pull/48) by @antoniocasero + +- Swift 4 support #52 +- Support for C and C++ files #48 by @antoniocasero - Xcode 9 default settings #### Fixed -- fixed empty string in YAML not being parsed properly [#50](https://github.com/yonaskolb/XcodeGen/pull/50) by @antoniocasero -#### Changed -- updated to xcodeproj 0.1.2 [#56](https://github.com/yonaskolb/XcodeGen/pull/56) -- **BREAKING**: changed target definitions from list to map [#54](https://github.com/yonaskolb/XcodeGen/pull/54) See [Project Spec](docs/ProjectSpec.md) +- fixed empty string in YAML not being parsed properly #50 by @antoniocasero +#### Changed -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.6.1...1.0.0) +- updated to xcodeproj 0.1.2 #56 +- **BREAKING**: changed target definitions from list to map #54 ## 0.6.1 #### Added -- Ability to set PBXProject attributes [#45](https://github.com/yonaskolb/XcodeGen/pull/45) + +- Ability to set PBXProject attributes #45 #### Changed -- Don't bother linking target frameworks for target dependencies. -- Move code signing default settings from all iOS targets to iOS application targets, via Product + Platform setting preset files [#46](https://github.com/yonaskolb/XcodeGen/pull/46) -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.6.0...0.6.1) +- Don't bother linking target frameworks for target dependencies. +- Move code signing default settings from all iOS targets to iOS application targets, via Product + Platform setting preset files #46 ## 0.6.0 #### Added -- Allow a project spec to include other project specs [#44](https://github.com/yonaskolb/XcodeGen/pull/44) + +- Allow a project spec to include other project specs #44 #### Changed + - Changed default spec path to `project.yml` - Changed default project directory to the current directory instead of the spec file's directory -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.5.1...0.6.0) - ## 0.5.1 #### Fixed + - Fix embedded framework dependencies - Add `CODE_SIGN_IDENTITY[sdk=iphoneos*]` back to iOS targets -- Fix build scripts with "" generating invalid projects [#43](https://github.com/yonaskolb/XcodeGen/pull/43) - -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.5.0...0.5.1) +- Fix build scripts with "" generating invalid projects #43 ## 0.5.0 + #### Added -- Added multi platform targets [#35](https://github.com/yonaskolb/XcodeGen/pull/35) -- Automatically generate platform specific `FRAMEWORK_SEARCH_PATHS` for Carthage dependencies [#38](https://github.com/yonaskolb/XcodeGen/pull/38) -- Automatically find Info.plist and set `INFOPLIST_FILE` build setting if it doesn't exist on a target [#40](https://github.com/yonaskolb/XcodeGen/pull/40) -- Add options for controlling embedding of dependencies [#37](https://github.com/yonaskolb/XcodeGen/pull/37) + +- Added multi platform targets #35 +- Automatically generate platform specific `FRAMEWORK_SEARCH_PATHS` for Carthage dependencies #38 +- Automatically find Info.plist and set `INFOPLIST_FILE` build setting if it doesn't exist on a target #40 +- Add options for controlling embedding of dependencies #37 #### Fixed + - Fixed localized files not being added to a target's resources #### Changed + - Renamed Setting Presets to Setting Groups - Carthage group is now created under top level Frameworks group -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.4.0...0.5.0) - ## 0.4.0 ##### Added -- Homebrew support [#16](https://github.com/yonaskolb/XcodeGen/pull/16) by @pepibumur -- Added `runOnlyWhenInstalling` to build scripts [#32](https://github.com/yonaskolb/XcodeGen/pull/32) -- Added `carthageBuildPath` option [#34](https://github.com/yonaskolb/XcodeGen/pull/34) + +- Homebrew support #16 by @pepibumur +- Added `runOnlyWhenInstalling` to build scripts #32 +- Added `carthageBuildPath` option #34 #### Fixed + - Fixed installations of XcodeGen not applying build setting presets for configs, products, and platforms, due to missing resources #### Changed -- Upgraded to https://github.com/swift-xcode/xcodeproj 0.1.1 [#33](https://github.com/yonaskolb/XcodeGen/pull/33) -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.3.0...0.4.0) +- Upgraded to 0.1.1 #33 ## 0.3.0 - Extensions and Scheme Tests #### Added -- Support for app extension dependencies, using the same `target: MyExtension` syntax [#19](https://github.com/yonaskolb/XcodeGen/pull/19) -- Added test targets to generated target schemes via `Target.scheme.testTargets` [#21](https://github.com/yonaskolb/XcodeGen/pull/21) + +- Support for app extension dependencies, using the same `target: MyExtension` syntax #19 +- Added test targets to generated target schemes via `Target.scheme.testTargets` #21 #### Changed + - Updated xcodeproj to 0.0.9 #### Fixed + - Fixed watch and messages apps not copying carthage dependencies #### Breaking changes -- Changed `Target.generatedSchemes` to `Target.scheme.configVariants` -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.2...0.3.0) +- Changed `Target.generatedSchemes` to `Target.scheme.configVariants` ## 0.2.0 - Build scripts #### Added -- Added Target build scripts with `Target.prebuildScripts` and `Target.postbuildScripts` [#17](https://github.com/yonaskolb/XcodeGen/pull/17) + +- Added Target build scripts with `Target.prebuildScripts` and `Target.postbuildScripts` #17 - Support for absolute paths in target sources, run script files, and config files - Add validation for incorrect `Target.configFiles` #### Fixed -- Fixed some project objects sometimes having duplicate ids -[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.1...0.2) +- Fixed some project objects sometimes having duplicate ids ## 0.1.0 + First official release diff --git a/Docs/Examples.md b/Docs/Examples.md index 19dc40975..b2120985c 100644 --- a/Docs/Examples.md +++ b/Docs/Examples.md @@ -10,3 +10,5 @@ These are a bunch of real world examples of XcodeGen project specs. Feel free to - [scelis/XcodeGen-TestStickers](https://github.com/scelis/XcodeGen-TestStickers/blob/master/project.yml) - [minvws/nl-covid19-notification-app-ios](https://github.com/minvws/nl-covid19-notification-app-ios/blob/master/project.yml) - [pvinis/react-native-xcodegen](https://github.com/pvinis/react-native-xcodegen/blob/master/templates) +- [covid19cz/erouska-ios](https://github.com/covid19cz/erouska-ios/blob/develop/project.yml) +- [markst/hotreloading-vscode-ios](https://github.com/markst/hotreloading-vscode-ios) diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 91d80e3f6..209655886 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -64,12 +64,13 @@ An include can be provided via a string (the path) or an object of the form: - [x] **path**: **String** - The path to the included file. - [ ] **relativePaths**: **Bool** - Dictates whether the included spec specifies paths relative to itself (the default) or the root spec file. - +- [ ] **enable**: **Bool** - Dictates whether the specified spec should be included or not. You can also specify it by environment variable. ```yaml include: - includedFile.yml - path: path/to/includedFile.yml relativePaths: false + enable: ${INCLUDE_ADDITIONAL_YAML} ``` By default specs are merged additively. That is for every value: @@ -114,6 +115,7 @@ Note that target names can also be changed by adding a `name` property to a targ - [ ] **disabledValidations**: **[String]** - A list of validations that can be disabled if they're too strict for your use case. By default this is set to an empty array. Currently these are the available options: - `missingConfigs`: Disable errors for configurations in yaml files that don't exist in the project itself. This can be useful if you include the same yaml file in different projects - `missingConfigFiles`: Disable checking for the existence of configuration files. This can be useful for generating a project in a context where config files are not available. + - `missingTestPlans`: Disable checking if test plan paths exist. This can be useful if your test plans haven't been created yet. - [ ] **defaultConfig**: **String** - The default configuration for command line builds from Xcode. If the configuration provided here doesn't match one in your [configs](#configs) key, XcodeGen will fail. If you don't set this, the first configuration alphabetically will be chosen. - [ ] **groupSortPosition**: **String** - Where groups are sorted in relation to other files. Either: - `none` - sorted alphabetically with all the other files @@ -273,6 +275,7 @@ This will provide default build settings for a certain product type. It can be a - `application.messages` - `application.watchapp` - `application.watchapp2` +- `application.watchapp2-container` - `app-extension` - `app-extension.intents-service` - `app-extension.messages` @@ -281,6 +284,7 @@ This will provide default build settings for a certain product type. It can be a - `bundle.ocunit-test` - `bundle.ui-testing` - `bundle.unit-test` +- `extensionkit-extension` - `framework` - `instruments-package` - `library.dynamic` @@ -288,7 +292,6 @@ This will provide default build settings for a certain product type. It can be a - `framework.static` - `tool` - `tv-app-extension` -- `watchapp2-container` - `watchkit-extension` - `watchkit2-extension` - `xcode-extension` @@ -432,6 +435,19 @@ A dependency can be one of a 6 types: - [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false - [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all** - [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms. +- **copy** - Copy Files Phase for this dependency. This only applies when `embed` is true. Must be specified as an object with the following fields: + - [x] **destination**: **String** - Destination of the Copy Files phase. This can be one of the following values: + - `absolutePath` + - `productsDirectory` + - `wrapper` + - `executables` + - `resources` + - `javaResources` + - `frameworks` + - `sharedFrameworks` + - `sharedSupport` + - `plugins` + - [ ] **subpath**: **String** - The path inside of the destination to copy the files. **Implicit Framework options**: @@ -640,6 +656,7 @@ This is a convenience used to automatically generate schemes for a target based - [x] **configVariants**: **[String]** - This generates a scheme for each entry, using configs that contain the name with debug and release variants. This is useful for having different environment schemes. - [ ] **testTargets**: **[[Test Target](#test-target)]** - a list of test targets that should be included in the scheme. These will be added to the build targets and the test entries. Each entry can either be a simple string, or a [Test Target](#test-target) - [ ] **gatherCoverageData**: **Bool** - a boolean that indicates if this scheme should gather coverage data. This defaults to false +- [ ] **coverageTargets**: **[[Testable Target Reference](#testable-target-reference) - a list of targets to gather code coverage. Each entry can either be a simple string, a string using [Project Reference](#project-reference) or [Testable Target Reference](#testable-target-reference) - [ ] **disableMainThreadChecker**: **Bool** - a boolean that indicates if this scheme should disable the Main Thread Checker. This defaults to false - [ ] **stopOnEveryMainThreadCheckerIssue**: **Bool** - a boolean that indicates if this scheme should stop at every Main Thread Checker issue. This defaults to false - [ ] **buildImplicitDependencies**: **Bool** - Flag to determine if Xcode should build implicit dependencies of this scheme. By default this is `true` if not set. @@ -647,6 +664,7 @@ This is a convenience used to automatically generate schemes for a target based - [ ] **region**: **String** - a String that indicates the region used for running and testing. This defaults to nil - [ ] **commandLineArguments**: **[String:Bool]** - a dictionary from the argument name (`String`) to if it is enabled (`Bool`). These arguments will be added to the Test, Profile and Run scheme actions - [ ] **environmentVariables**: **[[Environment Variable](#environment-variable)]** or **[String:String]** - environment variables for Run, Test and Profile scheme actions. When passing a dictionary, every key-value entry maps to a corresponding variable that is enabled. +- [ ] **testPlans**: **[[Test Plan](#test-plan)]** - List of test plan locations that will be referenced in the scheme. - [ ] **preActions**: **[[Execution Action](#execution-action)]** - Scripts that are run *before* the build action - [ ] **postActions**: **[[Execution Action](#execution-action)]** - Scripts that are run *after* the build action - [ ] **storeKitConfiguration**: **String** - specify storekit configuration to use during run. See [Options](#options). @@ -678,6 +696,9 @@ targets: - Staging - Production gatherCoverageData: true + coverageTargets: + - MyTarget1 + - ExternalTarget/OtherTarget1 commandLineArguments: "-MyEnabledArg": true "-MyDisabledArg": false @@ -709,7 +730,7 @@ This is used to override settings or run build scripts in specific targets ## Target Template -This is a template that can be referenced from a normal target using the `templates` property. The properties of this template are the same as a [Target](#target)]. +This is a template that can be referenced from a normal target using the `templates` property. The properties of this template are the same as a [Target](#target). Any instances of `${target_name}` within each template will be replaced by the final target name which references the template. Any attributes defined within a targets `templateAttributes` will be used to replace any attribute references in the template using the syntax `${attribute_name}`. @@ -811,20 +832,36 @@ A multiline script can be written using the various YAML multiline methods, for ### Test Action - [ ] **gatherCoverageData**: **Bool** - a boolean that indicates if this scheme should gather coverage data. This defaults to false -- [ ] **coverageTargets**: **[String]** - a list of targets to gather code coverage. Each entry can either be a simple string, or a string using [Project Reference](#project-reference) +- [ ] **coverageTargets**: **[[Testable Target Reference](#testable-target-reference)]** - a list of targets to gather code coverage. Each entry can either be a simple string, a string using [Project Reference](#project-reference) or [Testable Target Reference](#testable-target-reference) - [ ] **targets**: **[[Test Target](#test-target)]** - a list of targets to test. Each entry can either be a simple string, or a [Test Target](#test-target) - [ ] **customLLDBInit**: **String** - the absolute path to the custom `.lldbinit` file - [ ] **captureScreenshotsAutomatically**: **Bool** - indicates whether screenshots should be captured automatically while UI Testing. This defaults to true. - [ ] **deleteScreenshotsWhenEachTestSucceeds**: **Bool** - whether successful UI tests should cause automatically-captured screenshots to be deleted. If `captureScreenshotsAutomatically` is false, this value is ignored. This defaults to true. +- [ ] **testPlans**: **[[Test Plan](#test-plan)]** - List of test plan locations that will be referenced in the scheme. #### Test Target -- [x] **name**: **String** - The name of the target +A target can be one of a 2 types: + +- **name**: **String** - The name of the target. +- **target**: **[Testable Target Reference](#testable-target-reference)** - The information of the target. You can specify more detailed information than `name:`. + +As syntax suger, you can also specify **[Testable Target Reference](#testable-target-reference)** without `target`. + +#### Other Parameters + - [ ] **parallelizable**: **Bool** - Whether to run tests in parallel. Defaults to false - [ ] **randomExecutionOrder**: **Bool** - Whether to run tests in a random order. Defaults to false +- [ ] **location**: **String** - GPX file or predefined value for simulating location. See [Simulate Location](#simulate-location) for location examples. - [ ] **skipped**: **Bool** - Whether to skip all of the test target tests. Defaults to false - [ ] **skippedTests**: **[String]** - List of tests in the test target to skip. Defaults to empty - [ ] **selectedTests**: **[String]** - List of tests in the test target to whitelist and select. Defaults to empty. This will override `skippedTests` if provided +#### Testable Target Reference +A Testable Target Reference can be one of 3 types: +- `package: {local-swift-package-name}/{target-name}`: Name of local swift package and its target. +- `local: {target-name}`: Name of local target. +- `project: {project-reference-name}/{target-name}`: Name of local swift package and its target. + ### Archive Action - [ ] **customArchiveName**: **String** - the custom name to give to the archive @@ -884,12 +921,16 @@ schemes: coverageTargets: - MyTarget1 - ExternalTarget/OtherTarget1 + - package: LocalPackage/TestTarget targets: - Tester1 - name: Tester2 parallelizable: true randomExecutionOrder: true skippedTests: [Test/testExample()] + - package: APIClient/APIClientTests + parallelizable: true + randomExecutionOrder: true environmentVariables: - variable: TEST_ENV_VAR value: VALUE @@ -904,6 +945,21 @@ schemes: revealArchiveInOrganizer: false ``` +### Test Plan +For now test plans are not generated by XcodeGen and must be created in Xcode and checked in, and then referenced by path. If the test targets are added, removed or renamed, the test plans may need to be updated in Xcode. + +- [x] **path**: **String** - path that provides the `xctestplan` location. +- [ ] **defaultPlan**: **Bool** - a bool that defines if given plan is the default one. Defaults to false. If no default is set on any test plan, the first plan is set as the default. + +```yaml +schemes: + TestTarget: + test: + testPlans: + - path: app.xctestplan + defaultPlan: true +``` + ## Scheme Template This is a template that can be referenced from a normal scheme using the `templates` property. The properties of this template are the same as a [Scheme](#scheme). This functions identically in practice to [Target Template](#target-template). @@ -957,17 +1013,21 @@ Swift packages are defined at a project level, and then linked to individual tar ### Local Package - [x] **path**: **String** - the path to the package in local. The path must be directory with a `Package.swift`. +- [ ] **group** : **String**- Optional path that specifies the location where the package will live in your xcode project. ```yml packages: Yams: url: https://github.com/jpsim/Yams from: 2.0.0 - Yams: + Ink: github: JohnSundell/Ink from: 0.5.0 RxClient: path: ../RxClient + AppFeature: + path: ../Packages + group: Domains/AppFeature ``` ## Project Reference diff --git a/Docs/Usage.md b/Docs/Usage.md index a746650f4..c69ab3368 100644 --- a/Docs/Usage.md +++ b/Docs/Usage.md @@ -36,7 +36,7 @@ The values from [xcconfig files](#xcconfig-files) will then sit a level above th XcodeGen applies default settings to your project and targets similar to how Xcode creates them when you create a new project or target. Debug and Release settings will be applied to your project. Targets will also get specific settings depending on the platform and product type. ->You can change or disable how these setting presets are applied via the `options.settingPresets` which you can find more about in [Options](#options) +>You can change or disable how these setting presets are applied via the `options.settingPresets` which you can find more about in [Options](ProjectSpec.md#options) ### Settings The `project` and each `target` have a `settings` object that you can define. This can be a simple map of build settings or can provide build settings per `config` via `configs` or `base`. See [Settings](ProjectSpec.md#settings) for more details. diff --git a/Makefile b/Makefile index 9f5dad1d1..82db425cb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TOOL_NAME = XcodeGen export EXECUTABLE_NAME = xcodegen -VERSION = 2.24.0 +VERSION = 2.32.0 PREFIX = /usr/local INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME) @@ -46,4 +46,4 @@ brew: brew bump-formula-pr --url=$(RELEASE_TAR) XcodeGen archive: build - ./scripts/archive.sh $(EXECUTABLE_PATH) + ./scripts/archive.sh "$(EXECUTABLE_PATH)" diff --git a/Package.resolved b/Package.resolved index 4d75c7312..509ec1c4d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -78,8 +78,8 @@ "repositoryURL": "https://github.com/tuist/XcodeProj.git", "state": { "branch": null, - "revision": "446f3a0db73e141c7f57e26fcdb043096b1db52c", - "version": "8.3.1" + "revision": "b6de1bfe021b861c94e7c83821b595083f74b997", + "version": "8.8.0" } }, { diff --git a/Package.swift b/Package.swift index 899547eda..9d709baf7 100644 --- a/Package.swift +++ b/Package.swift @@ -11,13 +11,13 @@ let package = Package( .library(name: "ProjectSpec", targets: ["ProjectSpec"]), ], dependencies: [ - .package(url: "https://github.com/kylef/PathKit.git", from: "1.0.0"), + .package(url: "https://github.com/kylef/PathKit.git", from: "1.0.1"), .package(url: "https://github.com/jpsim/Yams.git", from: "4.0.0"), .package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.2.0"), .package(url: "https://github.com/kylef/Spectre.git", from: "0.9.2"), .package(url: "https://github.com/onevcat/Rainbow.git", from: "3.0.0"), - .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.0.0"), - .package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.0"), + .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.8.0"), + .package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.3"), .package(url: "https://github.com/mxcl/Version", from: "2.0.0"), .package(url: "https://github.com/SwiftDocOrg/GraphViz.git", .exact("0.2.0")), ], diff --git a/README.md b/README.md index 5b4d507c6..befe058cf 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ swift run xcodegen Add the following to your Package.swift file's dependencies: ```swift -.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.24.0"), +.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.32.0"), ``` And then import wherever needed: `import XcodeGenKit` @@ -156,7 +156,7 @@ To a file: xcodegen dump --type graphviz --file Graph.viz ``` -During implementation, `graphviz` formatting was validated using [GraphvizOnline](https://dreampuf.github.io/GraphvizOnline/), [WebGraphviz](http://www.webgraphviz.com), and [Graphviz on MacOS](graphviz.org). +During implementation, `graphviz` formatting was validated using [GraphvizOnline](https://dreampuf.github.io/GraphvizOnline/), [WebGraphviz](http://www.webgraphviz.com), and [Graphviz on MacOS](https://graphviz.org). ## Editing diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index 795d0ed60..1c01a8d8f 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -17,6 +17,7 @@ public struct Dependency: Equatable { public var weakLink: Bool = weakLinkDefault public var platformFilter: PlatformFilter = platformFilterDefault public var platforms: Set? + public var copyPhase: BuildPhaseSpec.CopyFilesSettings? public init( type: DependencyType, @@ -27,7 +28,8 @@ public struct Dependency: Equatable { implicit: Bool = implicitDefault, weakLink: Bool = weakLinkDefault, platformFilter: PlatformFilter = platformFilterDefault, - platforms: Set? = nil + platforms: Set? = nil, + copyPhase: BuildPhaseSpec.CopyFilesSettings? = nil ) { self.type = type self.reference = reference @@ -38,6 +40,7 @@ public struct Dependency: Equatable { self.weakLink = weakLink self.platformFilter = platformFilter self.platforms = platforms + self.copyPhase = copyPhase } public enum PlatformFilter: String, Equatable { @@ -53,7 +56,7 @@ public struct Dependency: Equatable { public static let `default` = dynamic } - public enum DependencyType: Equatable { + public enum DependencyType: Hashable { case target case framework case carthage(findFrameworks: Bool?, linkType: CarthageLinkType) @@ -135,6 +138,10 @@ extension Dependency: JSONObjectConvertible { if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") { self.platforms = Set(platforms) } + + if let object: JSONDictionary = jsonDictionary.json(atKeyPath: "copy") { + copyPhase = try BuildPhaseSpec.CopyFilesSettings(jsonDictionary: object) + } } } @@ -144,7 +151,8 @@ extension Dependency: JSONEncodable { "embed": embed, "codeSign": codeSign, "link": link, - "platforms": platforms?.map(\.rawValue).sorted() + "platforms": platforms?.map(\.rawValue).sorted(), + "copy": copyPhase?.toJSONValue(), ] if removeHeaders != Dependency.removeHeadersDefault { diff --git a/Sources/ProjectSpec/FileType.swift b/Sources/ProjectSpec/FileType.swift index 372bc79d8..c794b64e2 100644 --- a/Sources/ProjectSpec/FileType.swift +++ b/Sources/ProjectSpec/FileType.swift @@ -75,6 +75,7 @@ extension FileType { // sources "swift": FileType(buildPhase: .sources), + "gyb": FileType(buildPhase: .sources), "m": FileType(buildPhase: .sources), "mm": FileType(buildPhase: .sources), "cpp": FileType(buildPhase: .sources), @@ -86,8 +87,10 @@ extension FileType { "intentdefinition": FileType(buildPhase: .sources), "metal": FileType(buildPhase: .sources), "mlmodel": FileType(buildPhase: .sources), + "mlmodelc": FileType(buildPhase: .resources), "rcproject": FileType(buildPhase: .sources), "iig": FileType(buildPhase: .sources), + "docc": FileType(buildPhase: .sources), // headers "h": FileType(buildPhase: .headers), @@ -112,6 +115,6 @@ extension FileType { "xcfilelist": FileType(buildPhase: BuildPhaseSpec.none), "apns": FileType(buildPhase: BuildPhaseSpec.none), "pch": FileType(buildPhase: BuildPhaseSpec.none), - "docc": FileType(buildPhase: BuildPhaseSpec.none), + "xctestplan": FileType(buildPhase: BuildPhaseSpec.none), ] } diff --git a/Sources/ProjectSpec/Linkage.swift b/Sources/ProjectSpec/Linkage.swift index 0e134fa08..250f8207f 100644 --- a/Sources/ProjectSpec/Linkage.swift +++ b/Sources/ProjectSpec/Linkage.swift @@ -35,7 +35,8 @@ extension Target { .xcodeExtension, .xpcService, .systemExtension, - .driverExtension: + .driverExtension, + .extensionKitExtension: return .none case .framework, .xcFramework: // Check the MACH_O_TYPE for "Static Framework" diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 2d6842dcf..bc82e9aa5 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -83,6 +83,10 @@ public struct Project: BuildSettingsContainer { targetsMap[targetName] } + public func getPackage(_ packageName: String) -> SwiftPackage? { + packages[packageName] + } + public func getAggregateTarget(_ targetName: String) -> AggregateTarget? { aggregateTargetsMap[targetName] } @@ -189,7 +193,7 @@ extension Project { packages.merge(localPackages.reduce(into: [String: SwiftPackage]()) { // Project name will be obtained by resolved abstractpath's lastComponent for dealing with some path case, like "../" let packageName = (basePath + Path($1).normalize()).lastComponent - $0[packageName] = .local(path: $1) + $0[packageName] = .local(path: $1, group: nil) } ) } @@ -226,6 +230,7 @@ extension Project: PathContainer { .object("targets", Target.pathProperties), .object("targetTemplates", Target.pathProperties), .object("aggregateTargets", AggregateTarget.pathProperties), + .object("schemes", Scheme.pathProperties), .object("projectReferences", ProjectReference.pathProperties), ] } diff --git a/Sources/ProjectSpec/Scheme.swift b/Sources/ProjectSpec/Scheme.swift index ed921c89d..00ffafd19 100644 --- a/Sources/ProjectSpec/Scheme.swift +++ b/Sources/ProjectSpec/Scheme.swift @@ -123,7 +123,7 @@ public struct Scheme: Equatable { public var macroExpansion: String? public init( - config: String, + config: String? = nil, executable: String? = nil, commandLineArguments: [String: Bool] = [:], preActions: [ExecutionAction] = [], @@ -169,7 +169,7 @@ public struct Scheme: Equatable { public var config: String? public var gatherCoverageData: Bool - public var coverageTargets: [TargetReference] + public var coverageTargets: [TestableTargetReference] public var disableMainThreadChecker: Bool public var commandLineArguments: [String: Bool] public var targets: [TestTarget] @@ -182,23 +182,27 @@ public struct Scheme: Equatable { public var customLLDBInit: String? public var captureScreenshotsAutomatically: Bool public var deleteScreenshotsWhenEachTestSucceeds: Bool + public var testPlans: [TestPlan] public struct TestTarget: Equatable, ExpressibleByStringLiteral { + public static let randomExecutionOrderDefault = false public static let parallelizableDefault = false public var name: String { targetReference.name } - public let targetReference: TargetReference + public let targetReference: TestableTargetReference public var randomExecutionOrder: Bool public var parallelizable: Bool + public var location: String? public var skipped: Bool public var skippedTests: [String] public var selectedTests: [String] public init( - targetReference: TargetReference, + targetReference: TestableTargetReference, randomExecutionOrder: Bool = randomExecutionOrderDefault, parallelizable: Bool = parallelizableDefault, + location: String? = nil, skipped: Bool = false, skippedTests: [String] = [], selectedTests: [String] = [] @@ -206,6 +210,7 @@ public struct Scheme: Equatable { self.targetReference = targetReference self.randomExecutionOrder = randomExecutionOrder self.parallelizable = parallelizable + self.location = location self.skipped = skipped self.skippedTests = skippedTests self.selectedTests = selectedTests @@ -213,9 +218,10 @@ public struct Scheme: Equatable { public init(stringLiteral value: String) { do { - targetReference = try TargetReference(value) + targetReference = try TestableTargetReference(value) randomExecutionOrder = false parallelizable = false + location = nil skipped = false skippedTests = [] selectedTests = [] @@ -226,9 +232,9 @@ public struct Scheme: Equatable { } public init( - config: String, + config: String? = nil, gatherCoverageData: Bool = gatherCoverageDataDefault, - coverageTargets: [TargetReference] = [], + coverageTargets: [TestableTargetReference] = [], disableMainThreadChecker: Bool = disableMainThreadCheckerDefault, randomExecutionOrder: Bool = false, parallelizable: Bool = false, @@ -237,6 +243,7 @@ public struct Scheme: Equatable { preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], environmentVariables: [XCScheme.EnvironmentVariable] = [], + testPlans: [TestPlan] = [], language: String? = nil, region: String? = nil, debugEnabled: Bool = debugEnabledDefault, @@ -253,6 +260,7 @@ public struct Scheme: Equatable { self.preActions = preActions self.postActions = postActions self.environmentVariables = environmentVariables + self.testPlans = testPlans self.language = language self.region = region self.debugEnabled = debugEnabled @@ -282,7 +290,7 @@ public struct Scheme: Equatable { public var askForAppToLaunch: Bool? public init( - config: String, + config: String? = nil, commandLineArguments: [String: Bool] = [:], preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], @@ -311,7 +319,7 @@ public struct Scheme: Equatable { public var preActions: [ExecutionAction] public var postActions: [ExecutionAction] public init( - config: String, + config: String? = nil, customArchiveName: String? = nil, revealArchiveInOrganizer: Bool = revealArchiveInOrganizerDefault, preActions: [ExecutionAction] = [], @@ -326,16 +334,27 @@ public struct Scheme: Equatable { } public struct BuildTarget: Equatable, Hashable { - public var target: TargetReference + public var target: TestableTargetReference public var buildTypes: [BuildType] - public init(target: TargetReference, buildTypes: [BuildType] = BuildType.all) { + public init(target: TestableTargetReference, buildTypes: [BuildType] = BuildType.all) { self.target = target self.buildTypes = buildTypes } } } +extension Scheme: PathContainer { + + static var pathProperties: [PathProperty] { + [ + .dictionary([ + .object("test", Test.pathProperties), + ]), + ] + } +} + protocol BuildAction: Equatable { var config: String? { get } } @@ -455,18 +474,42 @@ extension Scheme.Run: JSONEncodable { } } +extension Scheme.Test: PathContainer { + + static var pathProperties: [PathProperty] { + [ + .object("testPlans", TestPlan.pathProperties), + ] + } +} + extension Scheme.Test: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { config = jsonDictionary.json(atKeyPath: "config") gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? Scheme.Test.gatherCoverageDataDefault - coverageTargets = try (jsonDictionary.json(atKeyPath: "coverageTargets") ?? []).map { try TargetReference($0) } + + if let coverages = jsonDictionary["coverageTargets"] as? [Any] { + coverageTargets = try coverages.compactMap { target in + if let string = target as? String { + return try TestableTargetReference(string) + } else if let dictionary = target as? JSONDictionary, + let target: TestableTargetReference = try? .init(jsonDictionary: dictionary) { + return target + } else { + return nil + } + } + } else { + coverageTargets = [] + } + disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Test.disableMainThreadCheckerDefault commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:] if let targets = jsonDictionary["targets"] as? [Any] { self.targets = try targets.compactMap { target in if let string = target as? String { - return try TestTarget(targetReference: TargetReference(string)) + return try TestTarget(targetReference: TestableTargetReference(string)) } else if let dictionary = target as? JSONDictionary { return try TestTarget(jsonDictionary: dictionary) } else { @@ -479,6 +522,7 @@ extension Scheme.Test: JSONObjectConvertible { preActions = jsonDictionary.json(atKeyPath: "preActions") ?? [] postActions = jsonDictionary.json(atKeyPath: "postActions") ?? [] environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary) + testPlans = try (jsonDictionary.json(atKeyPath: "testPlans") ?? []).map { try TestPlan(jsonDictionary: $0) } language = jsonDictionary.json(atKeyPath: "language") region = jsonDictionary.json(atKeyPath: "region") debugEnabled = jsonDictionary.json(atKeyPath: "debugEnabled") ?? Scheme.Test.debugEnabledDefault @@ -496,6 +540,7 @@ extension Scheme.Test: JSONEncodable { "preActions": preActions.map { $0.toJSONValue() }, "postActions": postActions.map { $0.toJSONValue() }, "environmentVariables": environmentVariables.map { $0.toJSONValue() }, + "testPlans": testPlans.map { $0.toJSONValue() }, "config": config, "language": language, "region": region, @@ -533,9 +578,20 @@ extension Scheme.Test: JSONEncodable { extension Scheme.Test.TestTarget: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { - targetReference = try TargetReference(jsonDictionary.json(atKeyPath: "name")) + if let name: String = jsonDictionary.json(atKeyPath: "name") { + targetReference = try TestableTargetReference(name) + } else if let local: String = jsonDictionary.json(atKeyPath: "local") { + self.targetReference = TestableTargetReference.local(local) + } else if let project: String = jsonDictionary.json(atKeyPath: "project") { + self.targetReference = TestableTargetReference.project(project) + } else if let package: String = jsonDictionary.json(atKeyPath: "package") { + self.targetReference = TestableTargetReference.package(package) + } else { + self.targetReference = try jsonDictionary.json(atKeyPath: "target") + } randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? Scheme.Test.TestTarget.randomExecutionOrderDefault parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? Scheme.Test.TestTarget.parallelizableDefault + location = jsonDictionary.json(atKeyPath: "location") ?? nil skipped = jsonDictionary.json(atKeyPath: "skipped") ?? false skippedTests = jsonDictionary.json(atKeyPath: "skippedTests") ?? [] selectedTests = jsonDictionary.json(atKeyPath: "selectedTests") ?? [] @@ -559,6 +615,9 @@ extension Scheme.Test.TestTarget: JSONEncodable { if parallelizable != Scheme.Test.TestTarget.parallelizableDefault { dict["parallelizable"] = parallelizable } + if let location = location { + dict["location"] = location + } if skipped { dict["skipped"] = skipped } @@ -685,7 +744,7 @@ extension Scheme.Build: JSONObjectConvertible { } else { buildTypes = BuildType.all } - let target = try TargetReference(targetRepr) + let target = try TestableTargetReference(targetRepr) targets.append(Scheme.BuildTarget(target: target, buildTypes: buildTypes)) } self.targets = targets.sorted { $0.target.name < $1.target.name } diff --git a/Sources/ProjectSpec/SpecFile.swift b/Sources/ProjectSpec/SpecFile.swift index adf974711..6a1c59b43 100644 --- a/Sources/ProjectSpec/SpecFile.swift +++ b/Sources/ProjectSpec/SpecFile.swift @@ -1,6 +1,7 @@ import Foundation import JSONUtilities import PathKit +import Yams public struct SpecFile { public let basePath: Path @@ -13,17 +14,20 @@ public struct SpecFile { fileprivate struct Include { let path: Path let relativePaths: Bool + let enable: Bool static let defaultRelativePaths = true + static let defaultEnable = true init?(any: Any) { if let string = any as? String { path = Path(string) relativePaths = Include.defaultRelativePaths - } else if let dictionary = any as? JSONDictionary, - let path = dictionary["path"] as? String { + enable = Include.defaultEnable + } else if let dictionary = any as? JSONDictionary, let path = dictionary["path"] as? String { self.path = Path(path) - relativePaths = dictionary["relativePaths"] as? Bool ?? Include.defaultRelativePaths + relativePaths = Self.resolveBoolean(dictionary, key: "relativePaths") ?? Include.defaultRelativePaths + enable = Self.resolveBoolean(dictionary, key: "enable") ?? Include.defaultEnable } else { return nil } @@ -38,10 +42,14 @@ public struct SpecFile { return [] } } + + private static func resolveBoolean(_ dictionary: [String: Any], key: String) -> Bool? { + dictionary[key] as? Bool ?? (dictionary[key] as? NSString)?.boolValue + } } - public init(path: Path) throws { - try self.init(filePath: path, basePath: path.parent()) + public init(path: Path, variables: [String: String] = [:]) throws { + try self.init(filePath: path, basePath: path.parent(), variables: variables) } public init(filePath: Path, jsonDictionary: JSONDictionary, basePath: Path = "", relativePath: Path = "", subSpecs: [SpecFile] = []) { @@ -52,21 +60,23 @@ public struct SpecFile { self.filePath = filePath } - private init(include: Include, basePath: Path, relativePath: Path) throws { + private init(include: Include, basePath: Path, relativePath: Path, variables: [String: String]) throws { let basePath = include.relativePaths ? (basePath + relativePath) : (basePath + relativePath + include.path.parent()) let relativePath = include.relativePaths ? include.path.parent() : Path() - try self.init(filePath: include.path, basePath: basePath, relativePath: relativePath) + try self.init(filePath: include.path, basePath: basePath, variables: variables, relativePath: relativePath) } - private init(filePath: Path, basePath: Path, relativePath: Path = "") throws { + private init(filePath: Path, basePath: Path, variables: [String: String], relativePath: Path = "") throws { let path = basePath + relativePath + filePath.lastComponent - let jsonDictionary = try SpecFile.loadDictionary(path: path) + let jsonDictionary = try SpecFile.loadDictionary(path: path).expand(variables: variables) let includes = Include.parse(json: jsonDictionary["include"]) - let subSpecs: [SpecFile] = try includes.map { include in - try SpecFile(include: include, basePath: basePath, relativePath: relativePath) - } + let subSpecs: [SpecFile] = try includes + .filter(\.enable) + .map { include in + try SpecFile(include: include, basePath: basePath, relativePath: relativePath, variables: variables) + } self.init(filePath: filePath, jsonDictionary: jsonDictionary, basePath: basePath, relativePath: relativePath, subSpecs: subSpecs) } @@ -85,8 +95,8 @@ public struct SpecFile { } } - public func resolvedDictionary(variables: [String: String] = [:]) -> JSONDictionary { - resolvedDictionaryWithUniqueTargets().expand(variables: variables) + public func resolvedDictionary() -> JSONDictionary { + resolvedDictionaryWithUniqueTargets() } private func resolvedDictionaryWithUniqueTargets() -> JSONDictionary { diff --git a/Sources/ProjectSpec/SpecLoader.swift b/Sources/ProjectSpec/SpecLoader.swift index 142d3bf1d..d4493891a 100644 --- a/Sources/ProjectSpec/SpecLoader.swift +++ b/Sources/ProjectSpec/SpecLoader.swift @@ -16,8 +16,8 @@ public class SpecLoader { } public func loadProject(path: Path, projectRoot: Path? = nil, variables: [String: String] = [:]) throws -> Project { - let spec = try SpecFile(path: path) - let resolvedDictionary = spec.resolvedDictionary(variables: variables) + let spec = try SpecFile(path: path, variables: variables) + let resolvedDictionary = spec.resolvedDictionary() let project = try Project(basePath: projectRoot ?? spec.basePath, jsonDictionary: resolvedDictionary) self.project = project diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index c1f0645d4..a5df4e28d 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -41,6 +41,7 @@ public struct SpecOptions: Equatable { public enum ValidationType: String { case missingConfigs case missingConfigFiles + case missingTestPlans } public enum SettingPresets: String { @@ -187,7 +188,7 @@ extension SpecOptions: JSONEncodable { "localPackagesGroup": localPackagesGroup, "preGenCommand": preGenCommand, "postGenCommand": postGenCommand, - "fileTypes": fileTypes + "fileTypes": fileTypes.mapValues { $0.toJSONValue() } ] if settingPresets != SpecOptions.settingPresetsDefault { diff --git a/Sources/ProjectSpec/SpecValidation.swift b/Sources/ProjectSpec/SpecValidation.swift index fd1049c6e..376c0153f 100644 --- a/Sources/ProjectSpec/SpecValidation.swift +++ b/Sources/ProjectSpec/SpecValidation.swift @@ -54,7 +54,7 @@ extension Project { } for (name, package) in packages { - if case let .local(path) = package, !(basePath + Path(path).normalize()).exists { + if case let .local(path, _) = package, !(basePath + Path(path).normalize()).exists { errors.append(.invalidLocalPackage(name)) } } @@ -119,9 +119,18 @@ extension Project { for testTarget in scheme.testTargets { if getTarget(testTarget.name) == nil { + // For test case of local Swift Package + if case .package(let name) = testTarget.targetReference.location, getPackage(name) != nil { + continue + } errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget.name)) } } + + if !options.disabledValidations.contains(.missingTestPlans) { + let invalidTestPlans: [TestPlan] = scheme.testPlans.filter { !(basePath + $0.path).exists } + errors.append(contentsOf: invalidTestPlans.map{ .invalidTestPlan($0) }) + } } for script in target.buildScripts { @@ -145,38 +154,16 @@ extension Project { } for target in targets { + var uniqueDependencies = Set() + for dependency in target.dependencies { - switch dependency.type { - case .target: - let dependencyTargetReference = try TargetReference(dependency.reference) - - switch dependencyTargetReference.location { - case .local: - if getProjectTarget(dependency.reference) == nil { - errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) - } - case .project(let dependencyProjectName): - if getProjectReference(dependencyProjectName) == nil { - errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) - } - } - case .sdk: - let path = Path(dependency.reference) - if !dependency.reference.contains("/") { - switch path.extension { - case "framework"?, - "tbd"?, - "dylib"?: - break - default: - errors.append(.invalidSDKDependency(target: target.name, dependency: dependency.reference)) - } - } - case .package: - if packages[dependency.reference] == nil { - errors.append(.invalidSwiftPackage(name: dependency.reference, target: target.name)) - } - default: break + let dependencyValidationErrors = try validate(dependency, in: target) + errors.append(contentsOf: dependencyValidationErrors) + + if uniqueDependencies.contains(dependency) { + errors.append(.duplicateDependencies(target: target.name, dependencyReference: dependency.reference)) + } else { + uniqueDependencies.insert(dependency) } } @@ -201,6 +188,17 @@ extension Project { if let action = scheme.run, let config = action.config, getConfig(config) == nil { errors.append(.invalidSchemeConfig(scheme: scheme.name, config: config)) } + + if !options.disabledValidations.contains(.missingTestPlans) { + let invalidTestPlans: [TestPlan] = scheme.test?.testPlans.filter { !(basePath + $0.path).exists } ?? [] + errors.append(contentsOf: invalidTestPlans.map{ .invalidTestPlan($0) }) + } + + let defaultPlanCount = scheme.test?.testPlans.filter { $0.defaultPlan }.count ?? 0 + if (defaultPlanCount > 1) { + errors.append(.multipleDefaultTestPlans) + } + if let action = scheme.test, let config = action.config, getConfig(config) == nil { errors.append(.invalidSchemeConfig(scheme: scheme.name, config: config)) } @@ -232,6 +230,46 @@ extension Project { } } + // Returns error if the given dependency from target is invalid. + private func validate(_ dependency: Dependency, in target: Target) throws -> [SpecValidationError.ValidationError] { + var errors: [SpecValidationError.ValidationError] = [] + + switch dependency.type { + case .target: + let dependencyTargetReference = try TargetReference(dependency.reference) + + switch dependencyTargetReference.location { + case .local: + if getProjectTarget(dependency.reference) == nil { + errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) + } + case .project(let dependencyProjectName): + if getProjectReference(dependencyProjectName) == nil { + errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference)) + } + } + case .sdk: + let path = Path(dependency.reference) + if !dependency.reference.contains("/") { + switch path.extension { + case "framework"?, + "tbd"?, + "dylib"?: + break + default: + errors.append(.invalidSDKDependency(target: target.name, dependency: dependency.reference)) + } + } + case .package: + if packages[dependency.reference] == nil { + errors.append(.invalidSwiftPackage(name: dependency.reference, target: target.name)) + } + default: break + } + + return errors + } + /// Returns a descriptive error if the given target reference was invalid otherwise `nil`. private func validationError(for targetReference: TargetReference, in scheme: Scheme, action: String) -> SpecValidationError.ValidationError? { switch targetReference.location { @@ -243,4 +281,18 @@ extension Project { return nil } } + + /// Returns a descriptive error if the given target reference was invalid otherwise `nil`. + private func validationError(for testableTargetReference: TestableTargetReference, in scheme: Scheme, action: String) -> SpecValidationError.ValidationError? { + switch testableTargetReference.location { + case .local where getProjectTarget(testableTargetReference.name) == nil: + return .invalidSchemeTarget(scheme: scheme.name, target: testableTargetReference.name, action: action) + case .project(let project) where getProjectReference(project) == nil: + return .invalidProjectReference(scheme: scheme.name, reference: project) + case .package(let package) where getPackage(package) == nil: + return .invalidLocalPackage(package) + case .local, .project, .package: + return nil + } + } } diff --git a/Sources/ProjectSpec/SpecValidationError.swift b/Sources/ProjectSpec/SpecValidationError.swift index c19486526..eadfca3b9 100644 --- a/Sources/ProjectSpec/SpecValidationError.swift +++ b/Sources/ProjectSpec/SpecValidationError.swift @@ -20,6 +20,7 @@ public struct SpecValidationError: Error, CustomStringConvertible { case invalidSchemeTarget(scheme: String, target: String, action: String) case invalidSchemeConfig(scheme: String, config: String) case invalidSwiftPackage(name: String, target: String) + case invalidPackageDependencyReference(name: String) case invalidLocalPackage(String) case invalidConfigFile(configFile: String, config: String) case invalidBuildSettingConfig(String) @@ -32,6 +33,9 @@ public struct SpecValidationError: Error, CustomStringConvertible { case invalidPerConfigSettings case invalidProjectReference(scheme: String, reference: String) case invalidProjectReferencePath(ProjectReference) + case invalidTestPlan(TestPlan) + case multipleDefaultTestPlans + case duplicateDependencies(target: String, dependencyReference: String) public var description: String { switch self { @@ -69,6 +73,8 @@ public struct SpecValidationError: Error, CustomStringConvertible { return "Target \(target.quoted) has an invalid package dependency \(name.quoted)" case let .invalidLocalPackage(path): return "Invalid local package \(path.quoted)" + case let .invalidPackageDependencyReference(name): + return "Package reference \(name) must be specified as package dependency, not target" case let .missingConfigForTargetScheme(target, configType): return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme" case let .missingDefaultConfig(name): @@ -79,6 +85,12 @@ public struct SpecValidationError: Error, CustomStringConvertible { return "Scheme \(scheme.quoted) has invalid project reference \(project.quoted)" case let .invalidProjectReferencePath(reference): return "Project reference \(reference.name) has a project file path that doesn't exist \"\(reference.path)\"" + case let .invalidTestPlan(testPlan): + return "Test plan path \"\(testPlan.path)\" doesn't exist" + case .multipleDefaultTestPlans: + return "Your test plans contain more than one default test plan" + case let .duplicateDependencies(target, dependencyReference): + return "Target \(target.quoted) has the dependency \(dependencyReference.quoted) multiple times" } } } diff --git a/Sources/ProjectSpec/SwiftPackage.swift b/Sources/ProjectSpec/SwiftPackage.swift index 254882323..216a60617 100644 --- a/Sources/ProjectSpec/SwiftPackage.swift +++ b/Sources/ProjectSpec/SwiftPackage.swift @@ -10,7 +10,7 @@ public enum SwiftPackage: Equatable { static let githubPrefix = "https://github.com/" case remote(url: String, versionRequirement: VersionRequirement) - case local(path: String) + case local(path: String, group: String?) public var isLocal: Bool { if case .local = self { @@ -23,8 +23,10 @@ public enum SwiftPackage: Equatable { extension SwiftPackage: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { - if let path: String = jsonDictionary.json(atKeyPath: "path") { - self = .local(path: path) + if let path: String = jsonDictionary.json(atKeyPath: "path"), let customLocation: String = jsonDictionary.json(atKeyPath: "group") { + self = .local(path: path, group: customLocation) + } else if let path: String = jsonDictionary.json(atKeyPath: "path") { + self = .local(path: path, group: nil) } else { let versionRequirement: VersionRequirement = try VersionRequirement(jsonDictionary: jsonDictionary) try Self.validateVersion(versionRequirement: versionRequirement) @@ -90,8 +92,9 @@ extension SwiftPackage: JSONEncodable { dictionary["revision"] = revision } return dictionary - case .local(let path): + case let .local(path, group): dictionary["path"] = path + dictionary["group"] = group } return dictionary diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index 60752f445..9736694ce 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -144,6 +144,7 @@ extension Target: PathContainer { .object("postCompileScripts", BuildScript.pathProperties), .object("postBuildScripts", BuildScript.pathProperties), .object("legacy", LegacyTarget.pathProperties), + .object("scheme", TargetScheme.pathProperties), ]), ] } diff --git a/Sources/ProjectSpec/TargetReference.swift b/Sources/ProjectSpec/TargetReference.swift index 1a64e258a..22da0a2e1 100644 --- a/Sources/ProjectSpec/TargetReference.swift +++ b/Sources/ProjectSpec/TargetReference.swift @@ -46,8 +46,8 @@ extension TargetReference: CustomStringConvertible { public var reference: String { switch location { case .local: return name - case .project(let projectPath): - return "\(projectPath)/\(name)" + case .project(let root): + return "\(root)/\(name)" } } diff --git a/Sources/ProjectSpec/TargetScheme.swift b/Sources/ProjectSpec/TargetScheme.swift index 1beedb6a9..59818a070 100644 --- a/Sources/ProjectSpec/TargetScheme.swift +++ b/Sources/ProjectSpec/TargetScheme.swift @@ -11,6 +11,7 @@ public struct TargetScheme: Equatable { public var testTargets: [Scheme.Test.TestTarget] public var configVariants: [String] public var gatherCoverageData: Bool + public var coverageTargets: [TestableTargetReference] public var storeKitConfiguration: String? public var language: String? public var region: String? @@ -21,11 +22,14 @@ public struct TargetScheme: Equatable { public var environmentVariables: [XCScheme.EnvironmentVariable] public var preActions: [Scheme.ExecutionAction] public var postActions: [Scheme.ExecutionAction] + public var testPlans: [TestPlan] public init( testTargets: [Scheme.Test.TestTarget] = [], + testPlans: [TestPlan] = [], configVariants: [String] = [], gatherCoverageData: Bool = gatherCoverageDataDefault, + coverageTargets: [TestableTargetReference] = [], storeKitConfiguration: String? = nil, language: String? = nil, region: String? = nil, @@ -38,8 +42,10 @@ public struct TargetScheme: Equatable { postActions: [Scheme.ExecutionAction] = [] ) { self.testTargets = testTargets + self.testPlans = testPlans self.configVariants = configVariants self.gatherCoverageData = gatherCoverageData + self.coverageTargets = coverageTargets self.storeKitConfiguration = storeKitConfiguration self.language = language self.region = region @@ -59,9 +65,10 @@ extension TargetScheme: JSONObjectConvertible { if let targets = jsonDictionary["testTargets"] as? [Any] { testTargets = try targets.compactMap { target in if let string = target as? String { - return .init(targetReference: try TargetReference(string)) - } else if let dictionary = target as? JSONDictionary { - return try .init(jsonDictionary: dictionary) + return .init(targetReference: try TestableTargetReference(string)) + } else if let dictionary = target as? JSONDictionary, + let target: Scheme.Test.TestTarget = try? .init(jsonDictionary: dictionary) { + return target } else { return nil } @@ -69,6 +76,23 @@ extension TargetScheme: JSONObjectConvertible { } else { testTargets = [] } + + if let targets = jsonDictionary["coverageTargets"] as? [Any] { + coverageTargets = try targets.compactMap { target in + if let string = target as? String { + return try TestableTargetReference(string) + } else if let dictionary = target as? JSONDictionary, + let target: TestableTargetReference = try? .init(jsonDictionary: dictionary) { + return target + } else { + return nil + } + } + } else { + coverageTargets = [] + } + + testPlans = try (jsonDictionary.json(atKeyPath: "testPlans") ?? []).map { try TestPlan(jsonDictionary: $0) } configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? [] gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? TargetScheme.gatherCoverageDataDefault storeKitConfiguration = jsonDictionary.json(atKeyPath: "storeKitConfiguration") @@ -88,8 +112,10 @@ extension TargetScheme: JSONEncodable { public func toJSONValue() -> Any { var dict: [String: Any] = [ "configVariants": configVariants, + "coverageTargets": coverageTargets.map { $0.reference }, "commandLineArguments": commandLineArguments, "testTargets": testTargets.map { $0.toJSONValue() }, + "testPlans": testPlans.map { $0.toJSONValue() }, "environmentVariables": environmentVariables.map { $0.toJSONValue() }, "preActions": preActions.map { $0.toJSONValue() }, "postActions": postActions.map { $0.toJSONValue() }, @@ -126,3 +152,12 @@ extension TargetScheme: JSONEncodable { return dict } } + +extension TargetScheme: PathContainer { + + static var pathProperties: [PathProperty] { + [ + .object("testPlans", TestPlan.pathProperties), + ] + } +} diff --git a/Sources/ProjectSpec/TestPlan.swift b/Sources/ProjectSpec/TestPlan.swift new file mode 100644 index 000000000..c3ebf0d49 --- /dev/null +++ b/Sources/ProjectSpec/TestPlan.swift @@ -0,0 +1,39 @@ +import Foundation +import JSONUtilities + +public struct TestPlan: Equatable { + public var path: String + public var defaultPlan: Bool + + public init(path: String, defaultPlan: Bool = false) { + self.defaultPlan = defaultPlan + self.path = path + } +} + + +extension TestPlan: JSONObjectConvertible { + + public init(jsonDictionary: JSONDictionary) throws { + path = try jsonDictionary.json(atKeyPath: "path") + defaultPlan = jsonDictionary.json(atKeyPath: "defaultPlan") ?? false + } +} + +extension TestPlan: JSONEncodable { + public func toJSONValue() -> Any { + [ + "path": path, + "defaultPlan": defaultPlan, + ] + } +} + +extension TestPlan: PathContainer { + + static var pathProperties: [PathProperty] { + [ + .string("path"), + ] + } +} diff --git a/Sources/ProjectSpec/TestTargeReference.swift b/Sources/ProjectSpec/TestTargeReference.swift new file mode 100644 index 000000000..17ea557bb --- /dev/null +++ b/Sources/ProjectSpec/TestTargeReference.swift @@ -0,0 +1,112 @@ +import Foundation +import JSONUtilities + +public struct TestableTargetReference: Hashable { + public var name: String + public var location: Location + + public var targetReference: TargetReference { + switch location { + case .local: + return TargetReference(name: name, location: .local) + case .project(let projectName): + return TargetReference(name: name, location: .project(projectName)) + case .package: + fatalError("Package target is only available for testable") + } + } + + public enum Location: Hashable { + case local + case project(String) + case package(String) + } + + public init(name: String, location: Location) { + self.name = name + self.location = location + } +} + +extension TestableTargetReference { + public init(_ string: String) throws { + let paths = string.split(separator: "/") + switch paths.count { + case 2: + location = .project(String(paths[0])) + name = String(paths[1]) + case 1: + location = .local + name = String(paths[0]) + default: + throw SpecParsingError.invalidTargetReference(string) + } + } + + public static func local(_ name: String) -> TestableTargetReference { + TestableTargetReference(name: name, location: .local) + } + + public static func project(_ name: String) -> TestableTargetReference { + let paths = name.split(separator: "/") + return TestableTargetReference(name: String(paths[1]), location: .project(String(paths[0]))) + } + + public static func package(_ name: String) -> TestableTargetReference { + let paths = name.split(separator: "/") + return TestableTargetReference(name: String(paths[1]), location: .package(String(paths[0]))) + } +} + +extension TestableTargetReference: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + try! self.init(value) + } +} + +extension TestableTargetReference: CustomStringConvertible { + public var reference: String { + switch location { + case .local: return name + case .project(let root), .package(let root): + return "\(root)/\(name)" + } + } + + public var description: String { + reference + } +} + +extension TestableTargetReference: JSONObjectConvertible { + + public init(jsonDictionary: JSONDictionary) throws { + if let project: String = jsonDictionary.json(atKeyPath: "project") { + let paths = project.split(separator: "/") + name = String(paths[1]) + location = .project(String(paths[0])) + } else if let project: String = jsonDictionary.json(atKeyPath: "package") { + let paths = project.split(separator: "/") + name = String(paths[1]) + location = .package(String(paths[0])) + } else { + name = try jsonDictionary.json(atKeyPath: "local") + location = .local + } + } +} + +extension TestableTargetReference: JSONEncodable { + public func toJSONValue() -> Any { + var dictionary: JSONDictionary = [:] + switch self.location { + case .package(let packageName): + dictionary["package"] = "\(packageName)/\(name)" + case .project(let projectName): + dictionary["project"] = "\(projectName)/\(name)" + case .local: + dictionary["local"] = name + } + return dictionary + } +} diff --git a/Sources/ProjectSpec/XCProjExtensions.swift b/Sources/ProjectSpec/XCProjExtensions.swift index bf3d36d21..9bee91be9 100644 --- a/Sources/ProjectSpec/XCProjExtensions.swift +++ b/Sources/ProjectSpec/XCProjExtensions.swift @@ -48,8 +48,8 @@ extension PBXProductType { public var canSkipCompileSourcesBuildPhase: Bool { switch self { - case .bundle, .stickerPack, .messagesApplication: - // Bundles, sticker packs and simple messages applications without sources should not include a + case .bundle, .watch2App, .stickerPack, .messagesApplication: + // Bundles, watch apps, sticker packs and simple messages applications without sources should not include a // compile sources build phase. Doing so can cause Xcode to produce an error on build. return true default: diff --git a/Sources/XcodeGen/main.swift b/Sources/XcodeGen/main.swift index b952f7af6..31f102bae 100644 --- a/Sources/XcodeGen/main.swift +++ b/Sources/XcodeGen/main.swift @@ -3,6 +3,6 @@ import ProjectSpec import XcodeGenCLI import Version -let version = Version("2.24.0") +let version = Version("2.32.0") let cli = XcodeGenCLI(version: version) cli.execute() diff --git a/Sources/XcodeGenCLI/Commands/ProjectCommand.swift b/Sources/XcodeGenCLI/Commands/ProjectCommand.swift index c47967784..e20b82ea0 100644 --- a/Sources/XcodeGenCLI/Commands/ProjectCommand.swift +++ b/Sources/XcodeGenCLI/Commands/ProjectCommand.swift @@ -28,9 +28,9 @@ class ProjectCommand: Command { } func execute() throws { - + let projectSpecPath = (spec ?? "project.yml").absolute() - + if !projectSpecPath.exists { throw GenerationError.missingProjectSpec(projectSpecPath) } diff --git a/Sources/XcodeGenCore/ArrayExtensions.swift b/Sources/XcodeGenCore/ArrayExtensions.swift new file mode 100644 index 000000000..52c87ff77 --- /dev/null +++ b/Sources/XcodeGenCore/ArrayExtensions.swift @@ -0,0 +1,79 @@ +import Foundation + +public extension Array { + + func parallelMap(transform: (Element) -> T) -> [T] { + var result = ContiguousArray(repeating: nil, count: count) + return result.withUnsafeMutableBufferPointer { buffer in + DispatchQueue.concurrentPerform(iterations: buffer.count) { idx in + buffer[idx] = transform(self[idx]) + } + return buffer.map { $0! } + } + } +} + +/// Holds a sorted array, created from specified sequence +/// This structure is needed for the cases, when some part of application requires array to be sorted, but don't trust any inputs :) +public struct SortedArray { + public let value: Array + public init(_ value: S) where S.Element == T { + self.value = value.sorted() + } +} + +public extension SortedArray { + /// Returns the first index in which an element of the collection satisfies the given predicate. + /// The collection assumed to be sorted. If collection is not have sorted values the result is undefined. + /// + /// The idea is to get first index of a function for which the given predicate evaluates to true. + /// + /// let values = [1,2,3,4,5] + /// let idx = values.firstIndexAssumingSorted(where: { $0 > 3 }) + /// + /// // false, false, false, true, true + /// // ^ + /// // therefore idx == 3 + /// + /// - Parameter predicate: A closure that takes an element as its argument + /// and returns a Boolean value that indicates whether the passed element + /// represents a match. + /// + /// - Returns: The index of the first element for which `predicate` returns + /// `true`. If no elements in the collection satisfy the given predicate, + /// returns `nil`. + /// + /// - Complexity: O(log(*n*)), where *n* is the length of the collection. + @inlinable + func firstIndex(where predicate: (T) throws -> Bool) rethrows -> Int? { + // Predicate should divide a collection to two pairs of values + // "bad" values for which predicate returns `false`` + // "good" values for which predicate return `true` + // false false false false false true true true + // ^ + // The idea is to get _first_ index which for which the predicate returns `true` + let lastIndex = value.count + + // The index that represents where bad values start + var badIndex = -1 + + // The index that represents where good values start + var goodIndex = lastIndex + var midIndex = (badIndex + goodIndex) / 2 + + while badIndex + 1 < goodIndex { + if try predicate(value[midIndex]) { + goodIndex = midIndex + } else { + badIndex = midIndex + } + midIndex = (badIndex + goodIndex) / 2 + } + + // We're out of bounds, no good items in array + if midIndex == lastIndex || goodIndex == lastIndex { + return nil + } + return goodIndex + } +} diff --git a/Sources/XcodeGenCore/Atomic.swift b/Sources/XcodeGenCore/Atomic.swift new file mode 100644 index 000000000..b3b1b2940 --- /dev/null +++ b/Sources/XcodeGenCore/Atomic.swift @@ -0,0 +1,52 @@ +// +// Atomic.swift +// +// +// Created by Vladislav Lisianskii on 23.02.2022. +// + +import Foundation + +@propertyWrapper +public final class Atomic { + + private var value: Value + + private let queue = DispatchQueue( + label: "com.xcodegencore.atomic.\(UUID().uuidString)", + qos: .utility, + attributes: .concurrent, + autoreleaseFrequency: .inherit, + target: .global() + ) + + public init(wrappedValue: Value) { + self.value = wrappedValue + } + + public var wrappedValue: Value { + get { + queue.sync { value } + } + set { + queue.async(flags: .barrier) { [weak self] in + self?.value = newValue + } + } + } + + /// Allows us to get the actual `Atomic` instance with the $ + /// prefix. + public var projectedValue: Atomic { + return self + } + + /// Modifies the protected value using `closure`. + public func with( + _ closure: (inout Value) throws -> R + ) rethrows -> R { + try queue.sync(flags: .barrier) { + try closure(&value) + } + } +} diff --git a/Sources/XcodeGenCore/Glob.swift b/Sources/XcodeGenCore/Glob.swift index b263cf5d3..4cee0a801 100644 --- a/Sources/XcodeGenCore/Glob.swift +++ b/Sources/XcodeGenCore/Glob.swift @@ -57,7 +57,7 @@ public class Glob: Collection { public static let defaultBlacklistedDirectories = ["node_modules", "Pods"] - private var isDirectoryCache = [String: Bool]() + @Atomic private var isDirectoryCache = [String: Bool]() public let behavior: Behavior public let blacklistedDirectories: [String] @@ -89,15 +89,13 @@ public class Glob: Collection { } let patterns = behavior.supportsGlobstar ? expandGlobstar(pattern: adjustedPattern) : [adjustedPattern] - - for pattern in patterns { - var gt = glob_t() - if executeGlob(pattern: pattern, gt: >) { - populateFiles(gt: gt, includeFiles: includeFiles) - } - - globfree(>) - } + + #if os(macOS) + paths = patterns.parallelMap { paths(usingPattern: $0, includeFiles: includeFiles) }.flatMap { $0 } + #else + // Parallel invocations of Glob on Linux seems to be causing unexpected crashes + paths = patterns.map { paths(usingPattern: $0, includeFiles: includeFiles) }.flatMap { $0 } + #endif paths = Array(Set(paths)).sorted { lhs, rhs in lhs.compare(rhs) != ComparisonResult.orderedDescending @@ -200,16 +198,30 @@ public class Glob: Collection { var isDirectoryBool = ObjCBool(false) let isDirectory = FileManager.default.fileExists(atPath: path, isDirectory: &isDirectoryBool) && isDirectoryBool.boolValue - isDirectoryCache[path] = isDirectory + $isDirectoryCache.with { isDirectoryCache in + isDirectoryCache[path] = isDirectory + } return isDirectory } private func clearCaches() { - isDirectoryCache.removeAll() + $isDirectoryCache.with { isDirectoryCache in + isDirectoryCache.removeAll() + } } - private func populateFiles(gt: glob_t, includeFiles: Bool) { + private func paths(usingPattern pattern: String, includeFiles: Bool) -> [String] { + var gt = glob_t() + defer { globfree(>) } + if executeGlob(pattern: pattern, gt: >) { + return populateFiles(gt: gt, includeFiles: includeFiles) + } + return [] + } + + private func populateFiles(gt: glob_t, includeFiles: Bool) -> [String] { + var paths = [String]() let includeDirectories = behavior.includesDirectoriesInResults #if os(macOS) @@ -229,6 +241,7 @@ public class Glob: Collection { paths.append(path) } } + return paths } } diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index 3b4d4b188..60ebdfca3 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -128,8 +128,8 @@ public class PBXProjGenerator { var explicitFileType: String? var lastKnownFileType: String? - let fileType = Xcode.fileType(path: Path(target.filename)) - if target.platform == .macOS || target.platform == .watchOS || target.type == .framework { + let fileType = Xcode.fileType(path: Path(target.filename), productType: target.type) + if target.platform == .macOS || target.platform == .watchOS || target.type == .framework || target.type == .extensionKitExtension { explicitFileType = fileType } else { lastKnownFileType = fileType @@ -168,8 +168,8 @@ public class PBXProjGenerator { let packageReference = XCRemoteSwiftPackageReference(repositoryURL: url, versionRequirement: versionRequirement) packageReferences[name] = packageReference addObject(packageReference) - case let .local(path): - try sourceGenerator.createLocalPackage(path: Path(path)) + case let .local(path, group): + try sourceGenerator.createLocalPackage(path: Path(path), group: group.map { Path($0) }) } } @@ -662,6 +662,7 @@ public class PBXProjGenerator { var dependencies: [PBXTargetDependency] = [] var targetFrameworkBuildFiles: [PBXBuildFile] = [] var frameworkBuildPaths = Set() + var customCopyDependenciesReferences: [PBXBuildFile] = [] var copyFilesBuildPhasesFiles: [BuildPhaseSpec.CopyFilesSettings: [PBXBuildFile]] = [:] var copyFrameworksReferences: [PBXBuildFile] = [] var copyResourcesReferences: [PBXBuildFile] = [] @@ -669,6 +670,7 @@ public class PBXProjGenerator { var copyWatchReferences: [PBXBuildFile] = [] var packageDependencies: [XCSwiftPackageProductDependency] = [] var extensions: [PBXBuildFile] = [] + var extensionKitExtensions: [PBXBuildFile] = [] var systemExtensions: [PBXBuildFile] = [] var appClips: [PBXBuildFile] = [] var carthageFrameworksToEmbed: [String] = [] @@ -689,7 +691,11 @@ public class PBXProjGenerator { if dependency.removeHeaders { embedAttributes.append("RemoveHeadersOnCopy") } - return ["ATTRIBUTES": embedAttributes] + var retval: [String:Any] = ["ATTRIBUTES": embedAttributes] + if let copyPhase = dependency.copyPhase { + retval["COPY_PHASE"] = copyPhase + } + return retval } func getDependencyFrameworkSettings(dependency: Dependency) -> [String: Any]? { @@ -727,9 +733,17 @@ public class PBXProjGenerator { pbxBuildFile.platformFilter = platform let embedFile = addObject(pbxBuildFile) - if dependencyTarget.type.isExtension { - // embed app extension - extensions.append(embedFile) + if dependency.copyPhase != nil { + // custom copy takes precedence + customCopyDependenciesReferences.append(embedFile) + } else if dependencyTarget.type.isExtension { + if dependencyTarget.type == .extensionKitExtension { + // embed extension kit extension + extensionKitExtensions.append(embedFile) + } else { + // embed app extension + extensions.append(embedFile) + } } else if dependencyTarget.type.isSystemExtension { // embed system extension systemExtensions.append(embedFile) @@ -807,7 +821,12 @@ public class PBXProjGenerator { let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true)) pbxBuildFile.platformFilter = platform let embedFile = addObject(pbxBuildFile) - copyFrameworksReferences.append(embedFile) + + if dependency.copyPhase != nil { + customCopyDependenciesReferences.append(embedFile) + } else { + copyFrameworksReferences.append(embedFile) + } } case .sdk(let root): @@ -858,7 +877,12 @@ public class PBXProjGenerator { let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true)) pbxBuildFile.platformFilter = platform let embedFile = addObject(pbxBuildFile) - copyFrameworksReferences.append(embedFile) + + if dependency.copyPhase != nil { + customCopyDependenciesReferences.append(embedFile) + } else { + copyFrameworksReferences.append(embedFile) + } } case .carthage(let findFrameworks, let linkType): @@ -907,9 +931,9 @@ public class PBXProjGenerator { let link = dependency.link ?? (target.type != .staticLibrary) if link { - let buildFile = addObject( - PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency)) - ) + let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency)) + file.platformFilter = platform + let buildFile = addObject(file) targetFrameworkBuildFiles.append(buildFile) } else { let targetDependency = addObject( @@ -923,7 +947,12 @@ public class PBXProjGenerator { settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true)) pbxBuildFile.platformFilter = platform let embedFile = addObject(pbxBuildFile) - copyFrameworksReferences.append(embedFile) + + if dependency.copyPhase != nil { + customCopyDependenciesReferences.append(embedFile) + } else { + copyFrameworksReferences.append(embedFile) + } } case .bundle: // Static and dynamic libraries can't copy resources @@ -935,7 +964,10 @@ public class PBXProjGenerator { sourceTree: .buildProductsDir ) - let pbxBuildFile = PBXBuildFile(file: fileReference, settings: nil) + let pbxBuildFile = PBXBuildFile( + file: fileReference, + settings: embed ? getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true) : nil + ) pbxBuildFile.platformFilter = platform let buildFile = addObject(pbxBuildFile) copyBundlesReferences.append(buildFile) @@ -969,7 +1001,11 @@ public class PBXProjGenerator { let embedFile = addObject( PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true)) ) - copyFrameworksReferences.append(embedFile) + if dependency.copyPhase != nil { + customCopyDependenciesReferences.append(embedFile) + } else { + copyFrameworksReferences.append(embedFile) + } } else { carthageFrameworksToEmbed.append(dependency.reference) } @@ -1016,6 +1052,19 @@ public class PBXProjGenerator { ) } + func splitCopyDepsByDestination(_ references: [PBXBuildFile]) -> [BuildPhaseSpec.CopyFilesSettings : [PBXBuildFile]] { + + var retval = [BuildPhaseSpec.CopyFilesSettings : [PBXBuildFile]]() + for reference in references { + + guard let key = reference.settings?["COPY_PHASE"] as? BuildPhaseSpec.CopyFilesSettings else { continue } + var filesWithSameDestination = retval[key] ?? [PBXBuildFile]() + filesWithSameDestination.append(reference) + retval[key] = filesWithSameDestination + } + return retval + } + copyFilesBuildPhasesFiles.merge(getBuildFilesForCopyFilesPhases()) { $0 + $1 } buildPhases += try target.preBuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) } @@ -1087,7 +1136,7 @@ public class PBXProjGenerator { name: "Carthage", inputPaths: inputPaths, outputPaths: outputPaths, - shellPath: "/bin/sh", + shellPath: "/bin/sh -l", shellScript: "\(carthageExecutable) copy-frameworks\n" ) ) @@ -1113,13 +1162,21 @@ public class PBXProjGenerator { if !extensions.isEmpty { - let copyFilesPhase = addObject( + let copyFilesPhase = addObject( getPBXCopyFilesBuildPhase(dstSubfolderSpec: .plugins, name: "Embed App Extensions", files: extensions) ) buildPhases.append(copyFilesPhase) } + if !extensionKitExtensions.isEmpty { + + let copyFilesPhase = addObject( + getPBXCopyFilesBuildPhase(dstSubfolderSpec: .productsDirectory, dstPath: "$(EXTENSIONS_FOLDER_PATH)", name: "Embed ExtensionKit Extensions", files: extensionKitExtensions) + ) + buildPhases.append(copyFilesPhase) + } + if !systemExtensions.isEmpty { let copyFilesPhase = addObject( @@ -1154,6 +1211,21 @@ public class PBXProjGenerator { buildPhases.append(copyFilesPhase) } + if !customCopyDependenciesReferences.isEmpty { + + let splitted = splitCopyDepsByDestination(customCopyDependenciesReferences) + for (phase, references) in splitted { + + guard let destination = phase.destination.destination else { continue } + + let copyFilesPhase = addObject( + getPBXCopyFilesBuildPhase(dstSubfolderSpec: destination, dstPath:phase.subpath, name: "Embed Dependencies", files: references) + ) + + buildPhases.append(copyFilesPhase) + } + } + if !copyWatchReferences.isEmpty { let copyFilesPhase = addObject( diff --git a/Sources/XcodeGenKit/ProjectGenerator.swift b/Sources/XcodeGenKit/ProjectGenerator.swift index 65f3216ec..798b112f1 100644 --- a/Sources/XcodeGenKit/ProjectGenerator.swift +++ b/Sources/XcodeGenKit/ProjectGenerator.swift @@ -32,7 +32,7 @@ public class ProjectGenerator { } func generateWorkspace() throws -> XCWorkspace { - let selfReference = XCWorkspaceDataFileRef(location: .`self`("")) + let selfReference = XCWorkspaceDataFileRef(location: .current("")) let dataElement = XCWorkspaceDataElement.file(selfReference) let workspaceData = XCWorkspaceData(children: [dataElement]) return XCWorkspace(data: workspaceData) diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index 67f681964..d188c11aa 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -136,9 +136,27 @@ public class SchemeGenerator { blueprintName: target.name ) } + + func getBuildableTestableReference(_ target: TestableTargetReference) throws -> XCScheme.BuildableReference { + switch target.location { + case .package(let packageName): + guard let package = self.project.getPackage(packageName), + case let .local(path, _) = package else { + throw SchemeGenerationError.missingPackage(packageName) + } + return XCScheme.BuildableReference( + referencedContainer: "container:\(path)", + blueprintIdentifier: target.name, + buildableName: target.name, + blueprintName: target.name + ) + default: + return try getBuildableReference(target.targetReference) + } + } func getBuildEntry(_ buildTarget: Scheme.BuildTarget) throws -> XCScheme.BuildAction.Entry { - let buildableReference = try getBuildableReference(buildTarget.target) + let buildableReference = try getBuildableTestableReference(buildTarget.target) return XCScheme.BuildAction.Entry(buildableReference: buildableReference, buildFor: buildTarget.buildTypes) } @@ -187,12 +205,28 @@ public class SchemeGenerator { runPostActionsOnFailure: scheme.build.runPostActionsOnFailure ) - let testables = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuilEntries in - XCScheme.TestableReference( + let testables: [XCScheme.TestableReference] = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuildEntries in + + var locationScenarioReference: XCScheme.LocationScenarioReference? + if var location = testTarget.location { + + if location.contains(".gpx") { + var path = Path(components: [project.options.schemePathPrefix, location]) + path = path.simplifyingParentDirectoryReferences() + location = path.string + } + + let referenceType = location.contains(".gpx") ? "0" : "1" + locationScenarioReference = XCScheme.LocationScenarioReference(identifier: location, referenceType: referenceType) + + } + + return XCScheme.TestableReference( skipped: testTarget.skipped, parallelizable: testTarget.parallelizable, randomExecutionOrdering: testTarget.randomExecutionOrder, - buildableReference: testBuilEntries.buildableReference, + buildableReference: testBuildEntries.buildableReference, + locationScenarioReference: locationScenarioReference, skippedTests: testTarget.skippedTests.map(XCScheme.TestItem.init), selectedTests: testTarget.selectedTests.map(XCScheme.TestItem.init), useTestSelectionWhitelist: !testTarget.selectedTests.isEmpty ? true : nil @@ -200,7 +234,7 @@ public class SchemeGenerator { } let coverageBuildableTargets = try scheme.test?.coverageTargets.map { - try getBuildableReference($0) + try getBuildableTestableReference($0) } ?? [] let testCommandLineArgs = scheme.test.map { XCScheme.CommandLineArguments($0.commandLineArguments) } @@ -211,10 +245,16 @@ public class SchemeGenerator { let launchVariables = scheme.run.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables } let profileVariables = scheme.profile.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables } + let defaultTestPlanIndex = scheme.test?.testPlans.firstIndex { $0.defaultPlan } ?? 0 + let testPlans = scheme.test?.testPlans.enumerated().map { index, testPlan in + XCScheme.TestPlanReference(reference: "container:\(testPlan.path)", default: defaultTestPlanIndex == index) + } ?? [] + let testAction = XCScheme.TestAction( buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name, macroExpansion: buildableReference, testables: testables, + testPlans: testPlans.isEmpty ? nil : testPlans, preActions: scheme.test?.preActions.map(getExecutionAction) ?? [], postActions: scheme.test?.postActions.map(getExecutionAction) ?? [], selectedDebuggerIdentifier: (scheme.test?.debugEnabled ?? Scheme.Test.debugEnabledDefault) ? XCScheme.defaultDebugger : "", @@ -280,10 +320,11 @@ public class SchemeGenerator { ) let profileAction = XCScheme.ProfileAction( - buildableProductRunnable: runnables.profile, + buildableProductRunnable: shouldExecuteOnLaunch ? runnables.profile : nil, buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name, preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [], postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [], + macroExpansion: shouldExecuteOnLaunch ? nil : buildableReference, shouldUseLaunchSchemeArgsEnv: scheme.profile?.shouldUseLaunchSchemeArgsEnv ?? true, askForAppToLaunch: scheme.profile?.askForAppToLaunch, commandlineArguments: profileCommandLineArgs, @@ -359,6 +400,7 @@ public class SchemeGenerator { enum SchemeGenerationError: Error, CustomStringConvertible { case missingTarget(TargetReference, projectPath: String) + case missingPackage(String) case missingProject(String) case missingBuildTargets(String) @@ -370,6 +412,8 @@ enum SchemeGenerationError: Error, CustomStringConvertible { return "Unable to find project reference named \"\(project)\" in project.yml" case .missingBuildTargets(let name): return "Unable to find at least one build target in scheme \"\(name)\"" + case .missingPackage(let package): + return "Unable to find swift package named \"\(package)\" in project.yml" } } } @@ -397,10 +441,12 @@ extension Scheme { test: .init( config: debugConfig, gatherCoverageData: targetScheme.gatherCoverageData, + coverageTargets: targetScheme.coverageTargets, disableMainThreadChecker: targetScheme.disableMainThreadChecker, commandLineArguments: targetScheme.commandLineArguments, targets: targetScheme.testTargets, environmentVariables: targetScheme.environmentVariables, + testPlans: targetScheme.testPlans, language: targetScheme.language, region: targetScheme.region ), @@ -419,14 +465,14 @@ extension Scheme { } private static func buildTargets(for target: Target, project: Project) -> [BuildTarget] { - let buildTarget = Scheme.BuildTarget(target: TargetReference.local(target.name)) + let buildTarget = Scheme.BuildTarget(target: TestableTargetReference.local(target.name)) switch target.type { case .watchApp, .watch2App: let hostTarget = project.targets .first { projectTarget in projectTarget.dependencies.contains { $0.reference == target.name } } - .map { BuildTarget(target: TargetReference.local($0.name)) } + .map { BuildTarget(target: TestableTargetReference.local($0.name)) } return hostTarget.map { [buildTarget, $0] } ?? [buildTarget] default: return [buildTarget] diff --git a/Sources/XcodeGenKit/SettingsBuilder.swift b/Sources/XcodeGenKit/SettingsBuilder.swift index 4ac1853c3..36a252db9 100644 --- a/Sources/XcodeGenKit/SettingsBuilder.swift +++ b/Sources/XcodeGenKit/SettingsBuilder.swift @@ -11,12 +11,10 @@ extension Project { var buildSettings: BuildSettings = [:] // set project SDKROOT is a single platform - if targets.count > 0 { - let platforms = Dictionary(grouping: targets) { $0.platform } - if platforms.count == 1 { - let platform = platforms.first!.key - buildSettings["SDKROOT"] = platform.sdkRoot - } + if let firstPlatform = targets.first?.platform, + targets.allSatisfy({ $0.platform == firstPlatform }) + { + buildSettings["SDKROOT"] = firstPlatform.sdkRoot } if let type = config.type, options.settingPresets.applyProject { @@ -31,7 +29,7 @@ extension Project { } } - // Prevent setting presets from overrwriting settings in project xcconfig files + // Prevent setting presets from overwriting settings in project xcconfig files if let configPath = configFiles[config.name] { buildSettings = removeConfigFileSettings(from: buildSettings, configPath: configPath) } diff --git a/Sources/XcodeGenKit/SourceGenerator.swift b/Sources/XcodeGenKit/SourceGenerator.swift index c3987b743..e788ef09d 100644 --- a/Sources/XcodeGenKit/SourceGenerator.swift +++ b/Sources/XcodeGenKit/SourceGenerator.swift @@ -53,16 +53,22 @@ class SourceGenerator { return object } - func createLocalPackage(path: Path) throws { - - if localPackageGroup == nil { + func createLocalPackage(path: Path, group: Path?) throws { + var pbxGroup: PBXGroup? + + if let location = group { + let fullLocationPath = project.basePath + location + pbxGroup = getGroup(path: fullLocationPath, mergingChildren: [], createIntermediateGroups: true, hasCustomParent: false, isBaseGroup: true) + } + + if localPackageGroup == nil && group == nil { let groupName = project.options.localPackagesGroup ?? "Packages" localPackageGroup = addObject(PBXGroup(sourceTree: .sourceRoot, name: groupName)) rootGroups.insert(localPackageGroup!) } - + let absolutePath = project.basePath + path.normalize() - + // Get the local package's relative path from the project root let fileReferencePath = try? absolutePath.relativePath(from: projectDirectory ?? project.basePath).string @@ -74,7 +80,11 @@ class SourceGenerator { path: fileReferencePath ) ) - localPackageGroup!.children.append(fileReference) + if let pbxGroup = pbxGroup { + pbxGroup.children.append(fileReference) + } else { + localPackageGroup!.children.append(fileReference) + } } /// Collects an array complete of all `SourceFile` objects that make up the target based on the provided `TargetSource` definitions. @@ -349,7 +359,7 @@ class SourceGenerator { let rootSourcePath = project.basePath + targetSource.path return Set( - patterns.map { pattern in + patterns.parallelMap { pattern in guard !pattern.isEmpty else { return [] } return Glob(pattern: "\(rootSourcePath)/\(pattern)") .map { Path($0) } @@ -367,19 +377,23 @@ class SourceGenerator { } /// Checks whether the path is not in any default or TargetSource excludes - func isIncludedPath(_ path: Path, excludePaths: Set, includePaths: Set) -> Bool { - !defaultExcludedFiles.contains(where: { path.lastComponent.contains($0) }) + func isIncludedPath(_ path: Path, excludePaths: Set, includePaths: SortedArray) -> Bool { + return !defaultExcludedFiles.contains(where: { path.lastComponent == $0 }) && !(path.extension.map(defaultExcludedExtensions.contains) ?? false) && !excludePaths.contains(path) // If includes is empty, it's included. If it's not empty, the path either needs to match exactly, or it needs to be a direct parent of an included path. - && (includePaths.isEmpty || includePaths.contains(where: { includedFile in - if path == includedFile { return true } - return includedFile.description.contains(path.description) - })) + && (includePaths.value.isEmpty || _isIncludedPathSorted(path, sortedPaths: includePaths)) } + + private func _isIncludedPathSorted(_ path: Path, sortedPaths: SortedArray) -> Bool { + guard let idx = sortedPaths.firstIndex(where: { $0 >= path }) else { return false } + let foundPath = sortedPaths.value[idx] + return foundPath.description.hasPrefix(path.description) + } + /// Gets all the children paths that aren't excluded - private func getSourceChildren(targetSource: TargetSource, dirPath: Path, excludePaths: Set, includePaths: Set) throws -> [Path] { + private func getSourceChildren(targetSource: TargetSource, dirPath: Path, excludePaths: Set, includePaths: SortedArray) throws -> [Path] { try dirPath.children() .filter { if $0.isDirectory { @@ -408,7 +422,7 @@ class SourceGenerator { isBaseGroup: Bool, hasCustomParent: Bool, excludePaths: Set, - includePaths: Set, + includePaths: SortedArray, buildPhases: [Path: BuildPhaseSpec] ) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) { @@ -634,7 +648,7 @@ class SourceGenerator { isBaseGroup: true, hasCustomParent: hasCustomParent, excludePaths: excludePaths, - includePaths: includePaths, + includePaths: SortedArray(includePaths), buildPhases: buildPhases ) diff --git a/Sources/XcodeGenKit/SpecGenerator.swift b/Sources/XcodeGenKit/SpecGenerator.swift index 1aa955c79..cba964201 100644 --- a/Sources/XcodeGenKit/SpecGenerator.swift +++ b/Sources/XcodeGenKit/SpecGenerator.swift @@ -587,7 +587,7 @@ private extension Scheme { self.init( name: scheme.name, build: Scheme.Build( - targets: [BuildTarget(target: TargetReference(name: buildableReference.blueprintName, location: .local))], + targets: [BuildTarget(target: TestableTargetReference(name: buildableReference.blueprintName, location: .local))], parallelizeBuild: buildAction.parallelizeBuild, buildImplicitDependencies: buildAction.buildImplicitDependencies, preActions: buildAction.preActions.map(Scheme.ExecutionAction.init), @@ -617,7 +617,7 @@ private extension Scheme { test: scheme.testAction.flatMap { testAction in let targets = testAction.testables.map { Scheme.Test.TestTarget( - targetReference: TargetReference( + targetReference: TestableTargetReference( name: $0.buildableReference.blueprintName, location: .local), randomExecutionOrder: $0.parallelizable, @@ -630,7 +630,7 @@ private extension Scheme { config: testAction.buildConfiguration, gatherCoverageData: testAction.codeCoverageEnabled, coverageTargets: testAction.codeCoverageTargets.map { - TargetReference(name: $0.blueprintName, location: .local) + TestableTargetReference(name: $0.blueprintName, location: .local) }, disableMainThreadChecker: testAction.disableMainThreadChecker, randomExecutionOrder: targets.allSatisfy { $0.randomExecutionOrder }, diff --git a/Sources/XcodeGenKit/Version.swift b/Sources/XcodeGenKit/Version.swift index f94d42e26..fe1b24af8 100644 --- a/Sources/XcodeGenKit/Version.swift +++ b/Sources/XcodeGenKit/Version.swift @@ -8,11 +8,11 @@ extension Project { } var schemeVersion: String { - "1.3" + "1.7" } var compatibilityVersion: String { - "Xcode 10.0" + "Xcode 11.0" } var objectVersion: UInt { diff --git a/Sources/XcodeGenKit/XCProjExtensions.swift b/Sources/XcodeGenKit/XCProjExtensions.swift index dc03fe9d5..4247ce910 100644 --- a/Sources/XcodeGenKit/XCProjExtensions.swift +++ b/Sources/XcodeGenKit/XCProjExtensions.swift @@ -53,10 +53,12 @@ extension Dictionary { extension Xcode { - public static func fileType(path: Path) -> String? { + public static func fileType(path: Path, productType: PBXProductType? = nil) -> String? { guard let fileExtension = path.extension else { return nil } - switch fileExtension { + switch (fileExtension, productType) { // cases that aren't handled (yet) in XcodeProj. + case ("appex", .extensionKitExtension): + return "wrapper.extensionkit-extension" default: // fallback to XcodeProj defaults return Xcode.filetype(extension: fileExtension) diff --git a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj index 1c1440059..b99951bd2 100644 --- a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj @@ -317,7 +317,7 @@ }; }; buildConfigurationList = D91E14E36EC0B415578456F2 /* Build configuration list for PBXProject "Project" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 11.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( diff --git a/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj b/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj index 0f9810390..9efdbfe4f 100644 --- a/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj @@ -7,10 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 23C6626698DE560017A89F2F /* XcodeGen in Frameworks */ = {isa = PBXBuildFile; productRef = 6F7DEA2D82649EDF903FBDBD /* XcodeGen */; }; 2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */; }; 3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; }; 4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */; }; - 578E78BC3627CF48FB2CE129 /* App.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = A9601593D0AD02931266A4E5 /* App.xctestplan */; }; 9AD886A88D3E4A1B5E900687 /* SPMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7970A2253B14A9B27C307FAC /* SPMTests.swift */; }; 9C4AD0711D706FD3ED0E436D /* StaticLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */; }; B89EA0F3859878A1DCF7BAFD /* SwiftRoaringDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -58,8 +58,8 @@ 61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLibrary.swift; sourceTree = ""; }; 7970A2253B14A9B27C307FAC /* SPMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPMTests.swift; sourceTree = ""; }; A9601593D0AD02931266A4E5 /* App.xctestplan */ = {isa = PBXFileReference; path = App.xctestplan; sourceTree = ""; }; - C1DE9A872F470EAA65B9B0B0 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; }; CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = libStaticLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; }; + ED284AB7C13DCC0A95DAA680 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -70,6 +70,7 @@ CE46CBA5671B951B546C8673 /* Codability in Frameworks */, 3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */, 4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */, + 23C6626698DE560017A89F2F /* XcodeGen in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -83,6 +84,7 @@ 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */, 4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */, 464ACF8D8F2D9F219BCFD3E7 /* Info.plist */, + ED284AB7C13DCC0A95DAA680 /* XcodeGen */, ); path = SPM; sourceTree = ""; @@ -98,7 +100,6 @@ 218F6C96DF9E182F526258CF = { isa = PBXGroup; children = ( - AD0F3623091EEA8D1EA3DFF8 /* Packages */, 17DD374CC81D710476AFF41C /* SPM */, CF3BD77AEAA56553289456BA /* SPMTests */, 1FA59BFD192FB5A68D5F587C /* StaticLibrary */, @@ -116,14 +117,6 @@ name = Products; sourceTree = ""; }; - AD0F3623091EEA8D1EA3DFF8 /* Packages */ = { - isa = PBXGroup; - children = ( - C1DE9A872F470EAA65B9B0B0 /* XcodeGen */, - ); - name = Packages; - sourceTree = SOURCE_ROOT; - }; CF3BD77AEAA56553289456BA /* SPMTests */ = { isa = PBXGroup; children = ( @@ -193,6 +186,7 @@ packageProductDependencies = ( 16E6FE01D5BD99F78D4A17E2 /* Codability */, DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */, + 6F7DEA2D82649EDF903FBDBD /* XcodeGen */, ); productName = App; productReference = 097F2DB5622B591E21BC3C73 /* App.app */; @@ -209,7 +203,7 @@ }; }; buildConfigurationList = 425866ADA259DB93FC4AF1E3 /* Build configuration list for PBXProject "SPM" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 11.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -236,7 +230,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 578E78BC3627CF48FB2CE129 /* App.xctestplan in Resources */, E368431019ABC696E4FFC0CF /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -596,6 +589,10 @@ isa = XCSwiftPackageProductDependency; productName = XcodeGen; }; + 6F7DEA2D82649EDF903FBDBD /* XcodeGen */ = { + isa = XCSwiftPackageProductDependency; + productName = XcodeGen; + }; AF233B61592982A7F6431FC6 /* Codability */ = { isa = XCSwiftPackageProductDependency; package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */; diff --git a/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme index ed6efd9af..8e271576f 100644 --- a/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme +++ b/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + + + + + + + + + diff --git a/Tests/Fixtures/SPM/project.yml b/Tests/Fixtures/SPM/project.yml index f8cac8b5e..818f66ff9 100644 --- a/Tests/Fixtures/SPM/project.yml +++ b/Tests/Fixtures/SPM/project.yml @@ -6,14 +6,18 @@ packages: SwiftRoaring: url: https://github.com/piotte13/SwiftRoaring majorVersion: 1.0.4 - XcodeGen: + XcodeGen: path: ../../.. #XcodeGen itself + group: SPM targets: App: type: application platform: iOS sources: [SPM] - scheme: {} + scheme: + testTargets: + - package: XcodeGen/XcodeGenKitTests + - Tests dependencies: - package: Codability weak: true @@ -21,6 +25,7 @@ targets: product: SwiftRoaringDynamic embed: true - target: StaticLibrary + - package: XcodeGen Tests: type: bundle.unit-test platform: iOS diff --git a/Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.pbxproj b/Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.pbxproj index c0b561029..6813fe75d 100644 --- a/Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/TestProject/AnotherProject/AnotherProject.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 6023D61BF2C57E6AE09CE7A3 /* BundleX.bundle */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = BundleX.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 60D6679FB526839EAFEA2EEE /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = config.xcconfig; sourceTree = ""; }; D6340FC7DEBC81E0127BAFD6 /* ExternalTarget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExternalTarget.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F1EFFCA88BFC3A1DD2D89DA7 /* BundleY.bundle */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = BundleY.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -34,6 +35,7 @@ isa = PBXGroup; children = ( 6023D61BF2C57E6AE09CE7A3 /* BundleX.bundle */, + F1EFFCA88BFC3A1DD2D89DA7 /* BundleY.bundle */, D6340FC7DEBC81E0127BAFD6 /* ExternalTarget.framework */, ); name = Products; @@ -59,6 +61,20 @@ /* End PBXLegacyTarget section */ /* Begin PBXNativeTarget section */ + 201AC870383B8CD218AD0FAB /* BundleY */ = { + isa = PBXNativeTarget; + buildConfigurationList = EF9E2AA4073D3B2EC8195688 /* Build configuration list for PBXNativeTarget "BundleY" */; + buildPhases = ( + ); + buildRules = ( + ); + dependencies = ( + ); + name = BundleY; + productName = BundleY; + productReference = F1EFFCA88BFC3A1DD2D89DA7 /* BundleY.bundle */; + productType = "com.apple.product-type.bundle"; + }; 63A2D4898D974A06E85B07F8 /* BundleX */ = { isa = PBXNativeTarget; buildConfigurationList = 32C09717E388BCD9DB9E513C /* Build configuration list for PBXNativeTarget "BundleX" */; @@ -99,7 +115,7 @@ }; }; buildConfigurationList = 3DFC1105373EDB6483D4BC5D /* Build configuration list for PBXProject "AnotherProject" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 11.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -110,6 +126,7 @@ projectRoot = ""; targets = ( 63A2D4898D974A06E85B07F8 /* BundleX */, + 201AC870383B8CD218AD0FAB /* BundleY */, E76A5F5E363E470416D3B487 /* ExternalTarget */, A6D9FB94860C005F0B723B5F /* IncludedLegacy */, ); @@ -322,6 +339,18 @@ }; name = "Test Debug"; }; + 1CE986A8B593E707AB71BDBA /* Production Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Production Release"; + }; 270E1D32776D2D196D435FDA /* Staging Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -570,6 +599,18 @@ }; name = "Production Debug"; }; + 58F418B6745A09C6479FDD6E /* Staging Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Staging Release"; + }; 5F14CE04E33ACD729A0EE6B6 /* Test Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -648,6 +689,18 @@ }; name = "Test Release"; }; + 816E80EA88CB645CE988138C /* Staging Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Staging Debug"; + }; 9BD6CAD5463121A1C3FED138 /* Production Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -760,6 +813,42 @@ }; name = "Production Release"; }; + E7907C46C6282D78E009083B /* Test Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Test Debug"; + }; + FB66976FF75B2B0B255D5AA4 /* Test Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Test Release"; + }; + FD9323224BE5A91248B7BEF2 /* Production Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Production Debug"; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -815,6 +904,19 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = "Production Debug"; }; + EF9E2AA4073D3B2EC8195688 /* Build configuration list for PBXNativeTarget "BundleY" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FD9323224BE5A91248B7BEF2 /* Production Debug */, + 1CE986A8B593E707AB71BDBA /* Production Release */, + 816E80EA88CB645CE988138C /* Staging Debug */, + 58F418B6745A09C6479FDD6E /* Staging Release */, + E7907C46C6282D78E009083B /* Test Debug */, + FB66976FF75B2B0B255D5AA4 /* Test Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "Production Debug"; + }; /* End XCConfigurationList section */ }; rootObject = 1B166EB49192B73A9DD8E108 /* Project object */; diff --git a/Tests/Fixtures/TestProject/AnotherProject/project.yml b/Tests/Fixtures/TestProject/AnotherProject/project.yml index 65e729ede..3c0cc4867 100644 --- a/Tests/Fixtures/TestProject/AnotherProject/project.yml +++ b/Tests/Fixtures/TestProject/AnotherProject/project.yml @@ -8,6 +8,9 @@ targets: BundleX: type: bundle platform: iOS + BundleY: + type: bundle + platform: iOS ExternalTarget: type: framework platform: iOS diff --git a/Tests/Fixtures/TestProject/App_iOS/App_iOS.xctestplan b/Tests/Fixtures/TestProject/App_iOS/App_iOS.xctestplan new file mode 100644 index 000000000..53056d698 --- /dev/null +++ b/Tests/Fixtures/TestProject/App_iOS/App_iOS.xctestplan @@ -0,0 +1,45 @@ +{ + "configurations" : [ + { + "id" : "AC4AE1EF-F65C-4037-8994-D607D2E5841E", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "commandLineArgumentEntries" : [ + { + "argument" : "MyDisabledArgument", + "enabled" : false + }, + { + "argument" : "MyEnabledArgument" + } + ], + "mainThreadCheckerEnabled" : false, + "targetForVariableExpansion" : { + "containerPath" : "container:Project.xcodeproj", + "identifier" : "0867B0DACEF28C11442DE8F7", + "name" : "App_iOS" + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:Project.xcodeproj", + "identifier" : "DC2F16BAA6E13B44AB62F888", + "name" : "App_iOS_Tests" + } + }, + { + "target" : { + "containerPath" : "container:Project.xcodeproj", + "identifier" : "F674B2CFC4738EEC49BAD0DA", + "name" : "App_iOS_UITests" + } + } + ], + "version" : 1 +} diff --git a/Tests/Fixtures/TestProject/ExtensionKit Extension/EntryPoint.swift b/Tests/Fixtures/TestProject/ExtensionKit Extension/EntryPoint.swift new file mode 100644 index 000000000..0e665c55f --- /dev/null +++ b/Tests/Fixtures/TestProject/ExtensionKit Extension/EntryPoint.swift @@ -0,0 +1,5 @@ +import AppIntents + +@main +struct EntryPoint: AppIntentsExtension { +} diff --git a/Tests/Fixtures/TestProject/ExtensionKit Extension/Info.plist b/Tests/Fixtures/TestProject/ExtensionKit Extension/Info.plist new file mode 100644 index 000000000..8d15acbed --- /dev/null +++ b/Tests/Fixtures/TestProject/ExtensionKit Extension/Info.plist @@ -0,0 +1,11 @@ + + + + + EXAppExtensionAttributes + + EXExtensionPointIdentifier + com.apple.appintents-extension + + + diff --git a/Tests/Fixtures/TestProject/ExtensionKit Extension/Intent.swift b/Tests/Fixtures/TestProject/ExtensionKit Extension/Intent.swift new file mode 100644 index 000000000..0449c505c --- /dev/null +++ b/Tests/Fixtures/TestProject/ExtensionKit Extension/Intent.swift @@ -0,0 +1,9 @@ +import AppIntents + +struct Intent: AppIntent { + static var title: LocalizedStringResource = "Intent" + + func perform() async throws -> some IntentResult { + return .result() + } +} diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj index 0e634a03d..cc3fa31dc 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 09617AB755651FFEB2564CBC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1A2F579A6F79C62DDA0571 /* AppDelegate.swift */; settings = {COMPILER_FLAGS = "-Werror"; }; }; 0AB541AE3163B063E7012877 /* StaticLibrary_ObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A2B916A11DCC2565241359F /* StaticLibrary_ObjC.h */; }; 0BDA156BEBFCB9E65910F838 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0D0E2466833FC2636B92C43D /* Swinject in Frameworks */ = {isa = PBXBuildFile; platformFilter = ios; productRef = D7917D10F77DA9D69937D493 /* Swinject */; }; 0F99AECCB4691803C791CDCE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2FC2A8A829CE71B1CF415FF7 /* Main.storyboard */; }; 15129B8D9ED000BDA1FEEC27 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2F16890ECF2EE3FED72AE /* AppDelegate.swift */; }; 1551370B0ACAC632E15C853B /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF47010E7368583405AA50CB /* SwiftyJSON.framework */; }; @@ -41,6 +42,7 @@ 1E03FC7312293997599C6435 /* Empty.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 068EDF47F0B087F6A4052AC0 /* Empty.h */; }; 1E2A4D61E96521FF7123D7B0 /* XPC Service.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 22237B8EBD9E6BE8EBC8735F /* XPC Service.xpc */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 1E457F55331FD2C3E8E00BE2 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 1F9168A43FD8E2FCC2699E14 /* Documentation.docc in Sources */ = {isa = PBXBuildFile; fileRef = B5C943D39DD7812CAB94B614 /* Documentation.docc */; settings = {COMPILER_FLAGS = "-Werror"; }; }; 210B49C23B9717C668B40C8C /* FrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5F527F2590C14956518174 /* FrameworkFile.swift */; }; 2116F89CF5A04EA0EFA30A89 /* TestProjectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D88C6BF7355702B74396791 /* TestProjectUITests.swift */; }; 212BCB51DAF3212993DDD49E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D51CC8BCCBD68A90E90A3207 /* Assets.xcassets */; }; @@ -63,11 +65,12 @@ 339578307B9266AB3D7722D9 /* File2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56891DA7446EAC8C2F27EB /* File2.swift */; }; 3535891EC86283BB5064E7E1 /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; }; 3788E1382B38DF4ACE3D2BB1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3BBCA6F76E5F212E9C55FB78 /* BundleX.bundle in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 45C12576F5AA694DD0CE2132 /* BundleX.bundle */; }; + 3BBCA6F76E5F212E9C55FB78 /* BundleX.bundle in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 45C12576F5AA694DD0CE2132 /* BundleX.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3C5134EE524310ACF7B7CD6E /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAF6C55B555E3E1352645B6 /* ExtensionDelegate.swift */; }; 42BC5788871D1D838B253952 /* App_watchOS Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0D09D243DBCF9D32E239F1E8 /* App_watchOS Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 447D59BE2E0993D7245EA247 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3797E591F302ECC0AA2FC607 /* Assets.xcassets */; }; 45E6702CD9C088FF1FC25F34 /* App_watchOS.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = A680BE9F68A255B0FB291AE6 /* App_watchOS.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 470D3493CDBFE56E2083A5FD /* BundleY.bundle in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = E3958AB20082EA12D4D5E60C /* BundleY.bundle */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 47D1F439B8E6D287B3F3E8D1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; 47FC57B04A3AD83359F433EA /* StaticLibrary_ObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A2B916A11DCC2565241359F /* StaticLibrary_ObjC.h */; }; 49A4B8937BB5520B36EA33F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 814D72C2B921F60B759C2D4B /* Main.storyboard */; }; @@ -111,6 +114,7 @@ 7F658343A505B824321E086B /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; }; 803B7CE086CFBA409F9D1ED7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 108BB29172D27BE3BD1E7F35 /* Assets.xcassets */; }; 818D448D4DDD6649B5B26098 /* example.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 28360ECA4D727FAA58557A81 /* example.mp4 */; settings = {ASSET_TAGS = (tag1, tag2, ); }; }; + 81DFAB3A7633CE97929B9B2A /* Framework.framework in Embed Dependencies */ = {isa = PBXBuildFile; fileRef = 41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8267B75289E9D6C7B38FC426 /* DriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0A428E67153BB40184F37BE /* DriverKit.framework */; }; 87927928A8A3460166ACB819 /* SwiftFileInDotPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F430AABE04B7499B458D9DB /* SwiftFileInDotPath.swift */; settings = {COMPILER_FLAGS = "-Werror"; }; }; 8C941A6EF08069CB3CB88FC1 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -142,7 +146,6 @@ BAA1C1E3828F5D43546AF997 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB1B49A91B892152D68ED76 /* libc++.tbd */; }; BB06A57E259D0D2A001EA21F /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BD1419893577E6CEDF8CBA83 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - BD95416F2005199F6B3572CF /* Framework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BFCCC56337A5D9D513C1C791 /* module.modulemap in CopyFiles */ = {isa = PBXBuildFile; fileRef = F2950763C4C568CC85021D18 /* module.modulemap */; }; C093BF20B99FE892D0F06B2D /* libEndpointSecurity.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BC75409252FF15F540FBB7B /* libEndpointSecurity.tbd */; }; C3672B561F456794151C047C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C3FE6B986506724DAB5D0F /* ViewController.swift */; }; @@ -578,6 +581,17 @@ name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + CF6B94E7B2D2312582A526F5 /* Embed Dependencies */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = test; + dstSubfolderSpec = 13; + files = ( + 81DFAB3A7633CE97929B9B2A /* Framework.framework in Embed Dependencies */, + ); + name = "Embed Dependencies"; + runOnlyForDeploymentPostprocessing = 0; + }; DE875E9A37F7CB9C347AEFA0 /* Embed System Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -597,6 +611,7 @@ dstSubfolderSpec = 7; files = ( 3BBCA6F76E5F212E9C55FB78 /* BundleX.bundle in Copy Bundle Resources */, + 470D3493CDBFE56E2083A5FD /* BundleY.bundle in Copy Bundle Resources */, ); name = "Copy Bundle Resources"; runOnlyForDeploymentPostprocessing = 0; @@ -607,7 +622,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - BD95416F2005199F6B3572CF /* Framework.framework in Embed Frameworks */, A7D1A9942302569A9515696A /* Result.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -747,14 +761,16 @@ A680BE9F68A255B0FB291AE6 /* App_watchOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App_watchOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; AAA49985DFFE797EE8416887 /* inputList.xcfilelist */ = {isa = PBXFileReference; lastKnownFileType = text.xcfilelist; path = inputList.xcfilelist; sourceTree = ""; }; AB055761199DF36DB0C629A6 /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AEBCA8CFF769189C0D52031E /* App_iOS.xctestplan */ = {isa = PBXFileReference; path = App_iOS.xctestplan; sourceTree = ""; }; B17B8D9C9B391332CD176A35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LocalizedStoryboard.storyboard; sourceTree = ""; }; B198242976C3395E31FE000A /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; B1C33BB070583BE3B0EC0E68 /* App_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; B221F5A689AD7D3AD52F56B8 /* libStaticLibrary_ObjC.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = libStaticLibrary_ObjC.a; sourceTree = BUILT_PRODUCTS_DIR; }; - B5C943D39DD7812CAB94B614 /* Documentation.docc */ = {isa = PBXFileReference; path = Documentation.docc; sourceTree = ""; }; + B5C943D39DD7812CAB94B614 /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = ""; }; B76E17CE3574081D5BF45B44 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; BA040F1F7D6CA08878323A55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; BB178D03E75929F3F5B10C56 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; + BB677D970923F663D846D7E0 /* BundleY.bundle */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = BundleY.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; BC56891DA7446EAC8C2F27EB /* File2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = File2.swift; path = Group2/File2.swift; sourceTree = ""; }; BDCA996D141DD8A16B18D68F /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = ""; }; BECEA4A483ADEB8158F640B3 /* Tool */ = {isa = PBXFileReference; includeInIndex = 0; path = Tool; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -777,6 +793,7 @@ D8A016580A3B8F72B820BFBF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; DAA7880242A9DE61E68026CC /* Folder */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Folder; sourceTree = SOURCE_ROOT; }; DFE6A6FAAFF701FE729293DE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + E3958AB20082EA12D4D5E60C /* BundleY.bundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper.cfbundle; path = BundleY.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; E42335D1200CB7B8B91E962F /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = Base; path = Base.lproj/Localizable.stringsdict; sourceTree = ""; }; E43116070AFEF5D8C3A5A957 /* TestFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TestFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E55F45EACB0F382722D61C8D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -808,6 +825,7 @@ 2FAE950E8FF2E7C0F7FF1FE9 /* Framework.framework in Frameworks */, B142965C5AE9C6200BF65802 /* Result.framework in Frameworks */, B20617116B230DED1F7AF5E5 /* libStaticLibrary_ObjC.a in Frameworks */, + 0D0E2466833FC2636B92C43D /* Swinject in Frameworks */, 1551370B0ACAC632E15C853B /* SwiftyJSON.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -969,6 +987,7 @@ isa = PBXGroup; children = ( 2F80635127D17ECB7748067B /* FolderWithDot2.0 */, + AEBCA8CFF769189C0D52031E /* App_iOS.xctestplan */, F0D48A913C087D049C8EDDD7 /* App.entitlements */, 7F1A2F579A6F79C62DDA0571 /* AppDelegate.swift */, 3797E591F302ECC0AA2FC607 /* Assets.xcassets */, @@ -1158,6 +1177,7 @@ isa = PBXGroup; children = ( 45C12576F5AA694DD0CE2132 /* BundleX.bundle */, + E3958AB20082EA12D4D5E60C /* BundleY.bundle */, ); name = Bundles; sourceTree = ""; @@ -1254,6 +1274,7 @@ 0D09D243DBCF9D32E239F1E8 /* App_watchOS Extension.appex */, A680BE9F68A255B0FB291AE6 /* App_watchOS.app */, 84317819C92F78425870E483 /* BundleX.bundle */, + BB677D970923F663D846D7E0 /* BundleY.bundle */, 83B5EC7EF81F7E4B6F426D4E /* DriverKitDriver.dext */, E5E0A80CCE8F8DB662DCD2D0 /* EndpointSecuritySystemExtension.systemextension */, 7D700FA699849D2F95216883 /* EntitledApp.app */, @@ -1529,6 +1550,7 @@ A6E1C88C073F8CC6B5B072B6 /* Frameworks */, DE875E9A37F7CB9C347AEFA0 /* Embed System Extensions */, F8CDEFED6ED131A09041F995 /* Embed Frameworks */, + CF6B94E7B2D2312582A526F5 /* Embed Dependencies */, ); buildRules = ( ); @@ -1589,6 +1611,9 @@ 981D116D40DBA0407D0E0E94 /* PBXTargetDependency */, ); name = App_iOS; + packageProductDependencies = ( + D7917D10F77DA9D69937D493 /* Swinject */, + ); productName = App_iOS; productReference = B1C33BB070583BE3B0EC0E68 /* App_iOS.app */; productType = "com.apple.product-type.application"; @@ -1675,7 +1700,6 @@ isa = PBXNativeTarget; buildConfigurationList = 6B5C5F08C0EF06457756E379 /* Build configuration list for PBXNativeTarget "App_watchOS" */; buildPhases = ( - 91C895DE8170C96A75D29426 /* Sources */, B7B71FA7D279029BF7A7FC7C /* Resources */, C765431E5FF4B02F59DE79B0 /* Embed App Extensions */, ); @@ -1689,6 +1713,20 @@ productReference = A680BE9F68A255B0FB291AE6 /* App_watchOS.app */; productType = "com.apple.product-type.application.watchapp2"; }; + 271CAC331D24F4F7E12C819C /* BundleY */ = { + isa = PBXNativeTarget; + buildConfigurationList = DA49CF5A1AC4FC1A7EE979E8 /* Build configuration list for PBXNativeTarget "BundleY" */; + buildPhases = ( + ); + buildRules = ( + ); + dependencies = ( + ); + name = BundleY; + productName = BundleY; + productReference = BB677D970923F663D846D7E0 /* BundleY.bundle */; + productType = "com.apple.product-type.bundle"; + }; 307AE3FA155FFD09B74AE351 /* App_watchOS Extension */ = { isa = PBXNativeTarget; buildConfigurationList = 3F3C272D2EA61F6B88B80D44 /* Build configuration list for PBXNativeTarget "App_watchOS Extension" */; @@ -2160,7 +2198,7 @@ ); }; buildConfigurationList = D91E14E36EC0B415578456F2 /* Build configuration list for PBXProject "Project" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 11.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -2168,6 +2206,9 @@ en, ); mainGroup = 293D0FF827366B513839236A; + packageReferences = ( + 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */, + ); projectDirPath = ""; projectReferences = ( { @@ -2188,6 +2229,7 @@ 208179651927D1138D19B5AD /* App_watchOS */, 307AE3FA155FFD09B74AE351 /* App_watchOS Extension */, DA40AB367B606CCE2FDD398D /* BundleX */, + 271CAC331D24F4F7E12C819C /* BundleY */, 428715FBC1D86458DA70CBDE /* DriverKitDriver */, 9F551F66949B55E8328EB995 /* EndpointSecuritySystemExtension */, B61ED4688789B071275E2B7A /* EntitledApp */, @@ -2344,7 +2386,7 @@ "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; + shellPath = "/bin/sh -l"; shellScript = "carthage copy-frameworks\n"; }; 3D0637F4554EAD6FA48105BF /* MyScript */ = { @@ -2433,7 +2475,7 @@ "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; + shellPath = "/bin/sh -l"; shellScript = "carthage copy-frameworks\n"; }; BA454AAC926EDFCDA9226CBC /* MyScript */ = { @@ -2506,7 +2548,7 @@ "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; + shellPath = "/bin/sh -l"; shellScript = "carthage copy-frameworks\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -2616,6 +2658,7 @@ buildActionMask = 2147483647; files = ( 09617AB755651FFEB2564CBC /* AppDelegate.swift in Sources */, + 1F9168A43FD8E2FCC2699E14 /* Documentation.docc in Sources */, 666DEC173BC78C7641AB22EC /* File1.swift in Sources */, 339578307B9266AB3D7722D9 /* File2.swift in Sources */, F788A3FA1CE6489BC257C1C3 /* Model.xcdatamodeld in Sources */, @@ -2666,13 +2709,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 91C895DE8170C96A75D29426 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 96BB43F4706B031DA45166E8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4144,6 +4180,19 @@ }; name = "Production Debug"; }; + 414544E2FA4DE102442A71CD /* Production Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Production Release"; + }; 436FB4981A66822DAF6F22AC /* Production Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4994,6 +5043,19 @@ }; name = "Test Debug"; }; + 71529460FB00BCDF2064C57F /* Staging Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Staging Debug"; + }; 72EDF2E14A4CE916F4E2B01B /* Staging Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5179,6 +5241,19 @@ }; name = "Test Debug"; }; + 7D73A7FB339A39293BD2DB9E /* Staging Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Staging Release"; + }; 7E101F97604A0990174A46CD /* Staging Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5431,6 +5506,19 @@ }; name = "Production Release"; }; + 924BB9ED8B14A02ABF88CC23 /* Production Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Production Debug"; + }; 92602C025633FBA848F91812 /* Production Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6079,6 +6167,19 @@ }; name = "Production Release"; }; + B227B91964080DEF6C426483 /* Test Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Test Debug"; + }; B24243F387A725EAFE802321 /* Production Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6285,6 +6386,19 @@ }; name = "Production Release"; }; + BA21E149424C2D03E5E50EC1 /* Test Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.project.BundleY; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Test Release"; + }; BA5AD3137CD90C50E5E1BDA0 /* Production Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -8111,6 +8225,19 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = "Production Debug"; }; + DA49CF5A1AC4FC1A7EE979E8 /* Build configuration list for PBXNativeTarget "BundleY" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 924BB9ED8B14A02ABF88CC23 /* Production Debug */, + 414544E2FA4DE102442A71CD /* Production Release */, + 71529460FB00BCDF2064C57F /* Staging Debug */, + 7D73A7FB339A39293BD2DB9E /* Staging Release */, + B227B91964080DEF6C426483 /* Test Debug */, + BA21E149424C2D03E5E50EC1 /* Test Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "Production Debug"; + }; ED1A174BA92C6E5172B519B7 /* Build configuration list for PBXNativeTarget "iMessageExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -8152,6 +8279,25 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Swinject/Swinject"; + requirement = { + kind = exactVersion; + version = 2.8.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + D7917D10F77DA9D69937D493 /* Swinject */ = { + isa = XCSwiftPackageProductDependency; + package = 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */; + productName = Swinject; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ 306796628DD52FA55E833B65 /* Model.xcdatamodeld */ = { isa = XCVersionGroup; diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Clip.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Clip.xcscheme index 45020c38c..a82515872 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Clip.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Clip.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + + + @@ -51,15 +60,6 @@ - - - - diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Scheme.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Scheme.xcscheme index 47aae2213..b4e470e1b 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Scheme.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_Scheme.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + + + + + + + @@ -53,17 +68,12 @@ BlueprintName = "App_iOS_Tests" ReferencedContainer = "container:Project.xcodeproj"> + + - - - - @@ -71,13 +81,13 @@ buildConfiguration = "Production Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "${SRCROOT}/.lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES" - customLLDBInitFile = "${SRCROOT}/.lldbinit"> + allowLocationSimulation = "YES"> + version = "1.7"> + disableMainThreadChecker = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + @@ -53,15 +62,6 @@ - - - - + + + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Staging.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Staging.xcscheme index a43fc9b3c..d68715c4c 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Staging.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Staging.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> + disableMainThreadChecker = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + @@ -53,15 +62,6 @@ - - - - + + + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Test.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Test.xcscheme index 5f05e0642..38d23c89b 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Test.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Test.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> + disableMainThreadChecker = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + @@ -53,15 +62,6 @@ - - - - + + + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_macOS.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_macOS.xcscheme index c34ffd274..b4c58b615 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_macOS.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_macOS.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme index cc8b37198..63ec7f6a8 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + @@ -68,8 +68,8 @@ debugServiceExtension = "internal" allowLocationSimulation = "YES"> + runnableDebuggingMode = "2" + BundleIdentifier = "com.apple.Carousel"> + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/EndpointSecuritySystemExtension.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/EndpointSecuritySystemExtension.xcscheme index 65c7766dc..be2f3dab4 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/EndpointSecuritySystemExtension.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/EndpointSecuritySystemExtension.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Framework.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Framework.xcscheme index e759440c0..3f0af839d 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Framework.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Framework.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + @@ -100,8 +100,7 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> - + - + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/NetworkSystemExtension.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/NetworkSystemExtension.xcscheme index f4f211925..81af9fc04 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/NetworkSystemExtension.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/NetworkSystemExtension.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Tool.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Tool.xcscheme index 2e8e81306..4eea0b5b4 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Tool.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/Tool.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageApp.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageApp.xcscheme index a599878c0..30a5fccf5 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageApp.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageApp.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageExtension.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageExtension.xcscheme index e1c32a606..5a9e872ba 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageExtension.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/iMessageExtension.xcscheme @@ -1,8 +1,8 @@ + wasCreatedForAppExtension = "YES" + version = "1.7"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + onlyGenerateCoverageForSpecifiedTargets = "NO"> + + diff --git a/Tests/Fixtures/TestProject/carthage_dynamic.xcconfig b/Tests/Fixtures/TestProject/carthage_dynamic.xcconfig index 1207c460d..fc72c48c0 100644 --- a/Tests/Fixtures/TestProject/carthage_dynamic.xcconfig +++ b/Tests/Fixtures/TestProject/carthage_dynamic.xcconfig @@ -1 +1 @@ -#include "xcode12_workaround.xcconfig" +#include "xcode12_and_13_workaround.xcconfig" diff --git a/Tests/Fixtures/TestProject/carthage_static.xcconfig b/Tests/Fixtures/TestProject/carthage_static.xcconfig index 5248a0506..f1b875b96 100644 --- a/Tests/Fixtures/TestProject/carthage_static.xcconfig +++ b/Tests/Fixtures/TestProject/carthage_static.xcconfig @@ -1,2 +1,2 @@ -#include "xcode12_workaround.xcconfig" +#include "xcode12_and_13_workaround.xcconfig" MACH_O_TYPE = staticlib diff --git a/Tests/Fixtures/TestProject/fixtures.xcconfig b/Tests/Fixtures/TestProject/fixtures.xcconfig index 1372a09ff..8f9d84d4c 100644 --- a/Tests/Fixtures/TestProject/fixtures.xcconfig +++ b/Tests/Fixtures/TestProject/fixtures.xcconfig @@ -1,4 +1,4 @@ -#include "xcode12_workaround.xcconfig" +#include "xcode12_and_13_workaround.xcconfig" // Common settings for fixtures CODE_SIGN_IDENTITY = diff --git a/Tests/Fixtures/TestProject/project.yml b/Tests/Fixtures/TestProject/project.yml index 10bbabffa..7b6d4ea59 100644 --- a/Tests/Fixtures/TestProject/project.yml +++ b/Tests/Fixtures/TestProject/project.yml @@ -27,6 +27,10 @@ projectReferences: path: ./AnotherProject/AnotherProject.xcodeproj configFiles: Test Debug: Configs/config.xcconfig +packages: + Swinject: + url: https://github.com/Swinject/Swinject + version: 2.8.0 targets: Legacy: type: "" @@ -57,6 +61,9 @@ targets: optional: true dependencies: - target: Framework_macOS + copy: + destination: plugins + subpath: "test" - target: XPC Service - target: NetworkSystemExtension - target: EndpointSecuritySystemExtension @@ -133,14 +140,23 @@ targets: - target: iMessageApp - sdk: Contacts.framework - bundle: BundleX.bundle + - { bundle: BundleY.bundle, codeSign: false } - target: AnotherProject/ExternalTarget - target: App_Clip + - package: Swinject + product: Swinject + platformFilter: iOS +# https://github.com/yonaskolb/XcodeGen/issues/1232 +# After GitHub Actions start supporting Xcode 14, an example for extensionKit should be added. +# - target: ExtensionKitExtension onlyCopyFilesOnInstall: true scheme: testTargets: - App_iOS_Tests - App_iOS_UITests gatherCoverageData: true + coverageTargets: + - App_iOS disableMainThreadChecker: true stopOnEveryMainThreadCheckerIssue: true configVariants: @@ -377,6 +393,12 @@ targets: sources: App_Clip_UITests dependencies: - target: App_Clip +# https://github.com/yonaskolb/XcodeGen/issues/1232 +# After GitHub Actions start supporting Xcode 14, an example for extensionKit should be added. +# ExtensionKitExtension: +# type: extensionkit-extension +# platform: iOS +# sources: ExtensionKit Extension schemes: Framework: @@ -415,7 +437,10 @@ schemes: - name: App_iOS_Tests parallelizable: true randomExecutionOrder: true + location: New York, NY, USA customLLDBInit: ${SRCROOT}/.lldbinit + testPlans: + - path: App_iOS/App_iOS.xctestplan targetTemplates: MyTemplate: scheme: {} diff --git a/Tests/Fixtures/TestProject/xcode12_workaround.xcconfig b/Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig similarity index 76% rename from Tests/Fixtures/TestProject/xcode12_workaround.xcconfig rename to Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig index 4585c89f8..d89990040 100644 --- a/Tests/Fixtures/TestProject/xcode12_workaround.xcconfig +++ b/Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig @@ -6,4 +6,5 @@ EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64=arm64 arm64e armv7 armv7s armv6 armv8 EXCLUDED_ARCHS_1200=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)) +EXCLUDED_ARCHS_1300=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)) EXCLUDED_ARCHS=$(inherited) $(EXCLUDED_ARCHS_$(XCODE_VERSION_MAJOR)) diff --git a/Tests/Fixtures/include_test.yml b/Tests/Fixtures/include_test.yml index 6affefa6d..1c24a731a 100644 --- a/Tests/Fixtures/include_test.yml +++ b/Tests/Fixtures/include_test.yml @@ -1,4 +1,11 @@ -include: [included.yml] +include: + - included.yml + - path: included_addtional.yml + enable: ${INCLUDE_ADDTIONAL_YAML} +packages: + Yams: + url: https://github.com/jpsim/Yams + majorVersion: 2.0.0 name: NewName settingGroups: test: @@ -19,3 +26,5 @@ targets: name: IncludedTargetNew platform: tvOS sources:REPLACE: NewSource + dependencies: + - package: Yams diff --git a/Tests/Fixtures/included_addtional.yml b/Tests/Fixtures/included_addtional.yml new file mode 100644 index 000000000..3623d2bea --- /dev/null +++ b/Tests/Fixtures/included_addtional.yml @@ -0,0 +1,12 @@ +name: Included_Addtional +settingGroups: + test: + MY_SETTING5: ADDTIONAL +packages: + SwiftPM: + url: https://github.com/apple/swift-package-manager + branch: swift-5.0-branch +targets: + IncludedTarget: + dependencies: + - package: SwiftPM diff --git a/Tests/Fixtures/paths_test/included_paths_test.yml b/Tests/Fixtures/paths_test/included_paths_test.yml index 75e084a63..f40bf5aed 100644 --- a/Tests/Fixtures/paths_test/included_paths_test.yml +++ b/Tests/Fixtures/paths_test/included_paths_test.yml @@ -20,11 +20,14 @@ targets: entitlements: path: entitlements preBuildScripts: - - path: preBuildScript + - path: preBuildScript postCompileScripts: - - path: postCompileScript + - path: postCompileScript postBuildScripts: - - path: postBuildScript + - path: postBuildScript + scheme: + testPlans: + - path: TestPlan.xctestplan aggregateTargets: IncludedAggregateTarget: targets: @@ -37,3 +40,11 @@ targetTemplates: Template1: sources: - template_source +schemes: + Scheme: + build: + targets: + NewTarget: all + test: + testPlans: + - path: TestPlan.xctestplan diff --git a/Tests/Fixtures/scheme_test/TestProject.xcodeproj/project.pbxproj b/Tests/Fixtures/scheme_test/TestProject.xcodeproj/project.pbxproj index 4cbb19508..2b667fe4d 100644 --- a/Tests/Fixtures/scheme_test/TestProject.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/scheme_test/TestProject.xcodeproj/project.pbxproj @@ -55,7 +55,7 @@ }; }; buildConfigurationList = E903F6E8184E2A86CEC31778 /* Build configuration list for PBXProject "TestProject" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 11.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( diff --git a/Tests/PerformanceTests/PerformanceTests.swift b/Tests/PerformanceTests/PerformanceTests.swift index 294b1b9ce..6d2c5a2c4 100644 --- a/Tests/PerformanceTests/PerformanceTests.swift +++ b/Tests/PerformanceTests/PerformanceTests.swift @@ -15,8 +15,8 @@ class GeneratedPerformanceTests: XCTestCase { try dumpYamlDictionary(project.toJSONDictionary(), path: specPath) measure { - let spec = try! SpecFile(path: specPath) - _ = spec.resolvedDictionary(variables: ProcessInfo.processInfo.environment) + let spec = try! SpecFile(path: specPath, variables: ProcessInfo.processInfo.environment) + _ = spec.resolvedDictionary() } } diff --git a/Tests/ProjectSpecTests/ProjectSpecTests.swift b/Tests/ProjectSpecTests/ProjectSpecTests.swift index cf41a13d7..0e74ecac2 100644 --- a/Tests/ProjectSpecTests/ProjectSpecTests.swift +++ b/Tests/ProjectSpecTests/ProjectSpecTests.swift @@ -126,7 +126,7 @@ class ProjectSpecTests: XCTestCase { project.settings = invalidSettings project.configFiles = ["invalidConfig": "invalidConfigFile"] project.fileGroups = ["invalidFileGroup"] - project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage")] + project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage", group: nil)] project.settingGroups = ["settingGroup1": Settings( configSettings: ["invalidSettingGroupConfig": [:]], groups: ["invalidSettingGroupSettingGroup"] @@ -142,6 +142,40 @@ class ProjectSpecTests: XCTestCase { try expectValidationError(project, .invalidBuildSettingConfig("invalidSettingGroupConfig")) } + $0.it("fails with duplicate dependencies") { + var project = baseProject + project.targets = [ + Target( + name: "target1", + type: .application, + platform: .iOS, + settings: invalidSettings, + dependencies: [ + Dependency(type: .target, reference: "dependency1"), + Dependency(type: .target, reference: "dependency1"), + Dependency(type: .framework, reference: "dependency2"), + Dependency(type: .framework, reference: "dependency2"), + ] + ), + Target( + name: "target2", + type: .framework, + platform: .iOS, + settings: invalidSettings, + dependencies: [ + Dependency(type: .framework, reference: "dependency3"), + Dependency(type: .target, reference: "dependency3"), + Dependency(type: .target, reference: "dependency4"), + Dependency(type: .target, reference: "dependency4"), + ] + ) + ] + + try expectValidationError(project, .duplicateDependencies(target: "target1", dependencyReference: "dependency1")) + try expectValidationError(project, .duplicateDependencies(target: "target1", dependencyReference: "dependency2")) + try expectValidationError(project, .duplicateDependencies(target: "target2", dependencyReference: "dependency4")) + } + $0.it("allows non-existent configurations") { var project = baseProject project.options = SpecOptions(disabledValidations: [.missingConfigs]) @@ -363,14 +397,321 @@ class ProjectSpecTests: XCTestCase { try expectVariant("Staging", for: Config(name: "Debug (Staging)", type: .debug), matches: true) try expectVariant("Production", for: Config(name: "Debug (Production)", type: .debug), matches: true) } + + $0.it("fails on missing test plan file") { + var project = baseProject + + project.configs = Config.defaultConfigs + + project.targets = [Target( + name: "target1", + type: .application, + platform: .iOS + )] + + let testPlan = try TestPlan(path: "does-not-exist.xctestplan") + + let scheme = Scheme( + name: "xctestplan-scheme", + build: Scheme.Build(targets: [ + Scheme.BuildTarget(target: "target1") + ]), + test: Scheme.Test(config: "Debug", + testPlans: [testPlan] + ) + ) + + project.schemes = [scheme] + + try expectValidationError(project, .invalidTestPlan(testPlan)) + } + + $0.it("fails on multiple default test plans") { + var project = baseProject + + project.configs = Config.defaultConfigs + + project.targets = [Target( + name: "target1", + type: .application, + platform: .iOS + )] + + let testPlan1 = try TestPlan(path: "\(fixturePath.string)/TestProject/App_iOS/App_iOS.xctestplan", defaultPlan: true) + let testPlan2 = try TestPlan(path: "\(fixturePath.string)/TestProject/App_iOS/App_iOS.xctestplan", defaultPlan: true) + + let scheme = Scheme( + name: "xctestplan-scheme", + build: Scheme.Build(targets: [ + Scheme.BuildTarget(target: "target1") + ]), + test: Scheme.Test(config: "Debug", + testPlans: [testPlan1, testPlan2] + ) + ) + + project.schemes = [scheme] + + try expectValidationError(project, .multipleDefaultTestPlans) + } } } func testJSONEncodable() { describe { $0.it("encodes to json") { - - let proj = testProject + let proj = Project(basePath: Path.current, + name: "ToJson", + configs: [Config(name: "DevelopmentConfig", type: .debug), Config(name: "ProductionConfig", type: .release)], + targets: [Target(name: "App", + type: .application, + platform: .iOS, + productName: "App", + deploymentTarget: Version(major: 0, minor: 1, patch: 2), + settings: Settings(buildSettings: ["foo": "bar"], + configSettings: ["foo": Settings(buildSettings: ["nested": "config"], + configSettings: [:], + groups: ["config-setting-group"])], + groups: ["setting-group"]), + configFiles: ["foo": "bar"], + sources: [TargetSource(path: "Source", + name: "Source", + compilerFlags: ["-Werror"], + excludes: ["foo", "bar"], + type: .folder, + optional: true, + buildPhase: .resources, + headerVisibility: .private, + createIntermediateGroups: true)], + dependencies: [Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), + reference: "reference", + embed: true, + codeSign: true, + link: true, + implicit: true, + weakLink: true, + copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "example", phaseOrder: .postCompile))], + info: Plist(path: "info.plist", attributes: ["foo": "bar"]), + entitlements: Plist(path: "entitlements.plist", attributes: ["foo": "bar"]), + transitivelyLinkDependencies: true, + directlyEmbedCarthageDependencies: true, + requiresObjCLinking: true, + preBuildScripts: [BuildScript(script: .script("pwd"), + name: "Foo script", + inputFiles: ["foo"], + outputFiles: ["bar"], + inputFileLists: ["foo.xcfilelist"], + outputFileLists: ["bar.xcfilelist"], + shell: "/bin/bash", + runOnlyWhenInstalling: true, + showEnvVars: true, + basedOnDependencyAnalysis: false)], + postCompileScripts: [BuildScript(script: .path("cmd.sh"), + name: "Bar script", + inputFiles: ["foo"], + outputFiles: ["bar"], + inputFileLists: ["foo.xcfilelist"], + outputFileLists: ["bar.xcfilelist"], + shell: "/bin/bash", + runOnlyWhenInstalling: true, + showEnvVars: true, + basedOnDependencyAnalysis: false)], + postBuildScripts: [BuildScript(script: .path("cmd.sh"), + name: "an another script", + inputFiles: ["foo"], + outputFiles: ["bar"], + inputFileLists: ["foo.xcfilelist"], + outputFileLists: ["bar.xcfilelist"], + shell: "/bin/bash", + runOnlyWhenInstalling: true, + showEnvVars: true, + basedOnDependencyAnalysis: false), + BuildScript(script: .path("cmd.sh"), + name: "Dependency script", + inputFiles: ["foo"], + outputFiles: ["bar"], + inputFileLists: ["foo.xcfilelist"], + outputFileLists: ["bar.xcfilelist"], + shell: "/bin/bash", + runOnlyWhenInstalling: true, + showEnvVars: true, + basedOnDependencyAnalysis: true, + discoveredDependencyFile: "dep.d")], + buildRules: [BuildRule(fileType: .pattern("*.xcassets"), + action: .script("pre_process_swift.py"), + name: "My Build Rule", + outputFiles: ["$(SRCROOT)/Generated.swift"], + outputFilesCompilerFlags: ["foo"], + runOncePerArchitecture: false), + BuildRule(fileType: .type("sourcecode.swift"), + action: .compilerSpec("com.apple.xcode.tools.swift.compiler"), + name: nil, + outputFiles: ["bar"], + outputFilesCompilerFlags: ["foo"], + runOncePerArchitecture: true)], + scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: "test target", + randomExecutionOrder: false, + parallelizable: false)], + configVariants: ["foo"], + gatherCoverageData: true, + coverageTargets: ["App"], + storeKitConfiguration: "Configuration.storekit", + disableMainThreadChecker: true, + stopOnEveryMainThreadCheckerIssue: false, + commandLineArguments: ["foo": true], + environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable", + value: "bar", + enabled: true)], + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")]), + legacy: LegacyTarget(toolPath: "foo", + passSettings: true, + arguments: "bar", + workingDirectory: "foo"), + attributes: ["foo": "bar"])], + aggregateTargets: [AggregateTarget(name: "aggregate target", + targets: ["App"], + settings: Settings(buildSettings: ["buildSettings": "bar"], + configSettings: ["configSettings": Settings(buildSettings: [:], + configSettings: [:], + groups: [])], + groups: ["foo"]), + configFiles: ["configFiles": "bar"], + buildScripts: [BuildScript(script: .path("script"), + name: "foo", + inputFiles: ["foo"], + outputFiles: ["bar"], + inputFileLists: ["foo.xcfilelist"], + outputFileLists: ["bar.xcfilelist"], + shell: "/bin/bash", + runOnlyWhenInstalling: true, + showEnvVars: false, + basedOnDependencyAnalysis: false)], + scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: "test target", + randomExecutionOrder: false, + parallelizable: false)], + configVariants: ["foo"], + gatherCoverageData: true, + disableMainThreadChecker: true, + commandLineArguments: ["foo": true], + environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable", + value: "bar", + enabled: true)], + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")]), + attributes: ["foo": "bar"])], + settings: Settings(buildSettings: ["foo": "bar"], + configSettings: ["foo": Settings(buildSettings: ["nested": "config"], + configSettings: [:], + groups: ["config-setting-group"])], + groups: ["setting-group"]), + settingGroups: ["foo": Settings(buildSettings: ["foo": "bar"], + configSettings: ["foo": Settings(buildSettings: ["nested": "config"], + configSettings: [:], + groups: ["config-setting-group"])], + groups: ["setting-group"])], + schemes: [Scheme(name: "scheme", + build: Scheme.Build(targets: [Scheme.BuildTarget(target: "foo", + buildTypes: [.archiving, .analyzing])], + parallelizeBuild: false, + buildImplicitDependencies: false, + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")]), + run: Scheme.Run(config: "run config", + commandLineArguments: ["foo": true], + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")], + environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo", + value: "bar", + enabled: false)], + launchAutomaticallySubstyle: "2", + storeKitConfiguration: "Configuration.storekit"), + test: Scheme.Test(config: "Config", + gatherCoverageData: true, + disableMainThreadChecker: true, + randomExecutionOrder: false, + parallelizable: false, + commandLineArguments: ["foo": true], + targets: [Scheme.Test.TestTarget(targetReference: "foo", + randomExecutionOrder: false, + parallelizable: false)], + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")], + environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo", + value: "bar", + enabled: false)]), + profile: Scheme.Profile(config: "profile config", + commandLineArguments: ["foo": true], + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")], + environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo", + value: "bar", + enabled: false)]), + analyze: Scheme.Analyze(config: "analyze config"), + archive: Scheme.Archive(config: "archive config", + customArchiveName: "customArchiveName", + revealArchiveInOrganizer: true, + preActions: [Scheme.ExecutionAction(name: "preAction", + script: "bar", + settingsTarget: "foo")], + postActions: [Scheme.ExecutionAction(name: "postAction", + script: "bar", + settingsTarget: "foo")]))], + packages: [ + "Yams": .remote( + url: "https://github.com/jpsim/Yams", + versionRequirement: .upToNextMajorVersion("2.0.0") + ), + ], + options: SpecOptions(minimumXcodeGenVersion: Version(major: 3, minor: 4, patch: 5), + carthageBuildPath: "carthageBuildPath", + carthageExecutablePath: "carthageExecutablePath", + createIntermediateGroups: true, + bundleIdPrefix: "bundleIdPrefix", + settingPresets: .project, + developmentLanguage: "developmentLanguage", + indentWidth: 123, + tabWidth: 456, + usesTabs: true, + xcodeVersion: "xcodeVersion", + deploymentTarget: DeploymentTarget(iOS: Version(major: 1, minor: 2, patch: 3), + tvOS: nil, + watchOS: Version(major: 4, minor: 5, patch: 6), + macOS: nil), + disabledValidations: [.missingConfigFiles], + defaultConfig: "defaultConfig", + transitivelyLinkDependencies: true, + groupSortPosition: .top, + generateEmptyDirectories: true, + findCarthageFrameworks: false), + fileGroups: ["foo", "bar"], + configFiles: ["configFiles": "bar"], + attributes: ["attributes": "bar"]) + let json = proj.toJSONDictionary() try expect(JSONSerialization.isValidJSONObject(json)).beTrue() diff --git a/Tests/ProjectSpecTests/SpecLoadingTests.swift b/Tests/ProjectSpecTests/SpecLoadingTests.swift index 2282a8fa1..0c9c96657 100644 --- a/Tests/ProjectSpecTests/SpecLoadingTests.swift +++ b/Tests/ProjectSpecTests/SpecLoadingTests.swift @@ -29,7 +29,7 @@ class SpecLoadingTests: XCTestCase { describe { $0.it("merges includes") { let path = fixturePath + "include_test.yml" - let project = try loadSpec(path: path) + let project = try loadSpec(path: path, variables: [:]) try expect(project.name) == "NewName" try expect(project.settingGroups) == [ @@ -38,7 +38,39 @@ class SpecLoadingTests: XCTestCase { "toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]), ] try expect(project.targets) == [ - Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]), + Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "Yams")]), + Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]), + ] + } + + $0.it("merges includes with addtional one") { + let path = fixturePath + "include_test.yml" + let project = try loadSpec(path: path, variables: ["INCLUDE_ADDTIONAL_YAML": "YES"]) + + try expect(project.name) == "NewName" + try expect(project.settingGroups) == [ + "test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}", "MY_SETTING5": "ADDTIONAL"]), + "new": Settings(dictionary: ["MY_SETTING": "VALUE"]), + "toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]), + ] + try expect(project.targets) == [ + Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "SwiftPM"), Dependency(type: .package(product: nil), reference: "Yams")]), + Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]), + ] + } + + $0.it("merges includes without addtional one by environemnt variable") { + let path = fixturePath + "include_test.yml" + let project = try loadSpec(path: path, variables: ["INCLUDE_ADDTIONAL_YAML": "NO"]) + + try expect(project.name) == "NewName" + try expect(project.settingGroups) == [ + "test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}"]), + "new": Settings(dictionary: ["MY_SETTING": "VALUE"]), + "toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]), + ] + try expect(project.targets) == [ + Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "Yams")]), Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]), ] } @@ -98,7 +130,8 @@ class SpecLoadingTests: XCTestCase { entitlements: Plist(path: "paths_test/entitlements"), preBuildScripts: [BuildScript(script: .path("paths_test/preBuildScript"))], postCompileScripts: [BuildScript(script: .path("paths_test/postCompileScript"))], - postBuildScripts: [BuildScript(script: .path("paths_test/postBuildScript"))] + postBuildScripts: [BuildScript(script: .path("paths_test/postBuildScript"))], + scheme: TargetScheme(testPlans: [.init(path: "paths_test/TestPlan.xctestplan")]) ), Target( name: "NewTarget", @@ -130,6 +163,14 @@ class SpecLoadingTests: XCTestCase { postBuildScripts: [BuildScript(script: .path("paths_test/recursive_test/postBuildScript"))] ), ] + + try expect(project.schemes) == [ + Scheme( + name: "Scheme", + build: .init(targets: [.init(target: "NewTarget")]), + test: .init(testPlans: [.init(path: "paths_test/TestPlan.xctestplan")]) + ) + ] } $0.it("respects directory expansion preference") { @@ -712,6 +753,7 @@ class SpecLoadingTests: XCTestCase { "ENV1": true, ], "gatherCoverageData": true, + "coverageTargets": ["t1"], "storeKitConfiguration": "Configuration.storekit", "language": "en", "region": "US", @@ -740,6 +782,7 @@ class SpecLoadingTests: XCTestCase { testTargets: ["t1", "t2"], configVariants: ["dev", "app-store"], gatherCoverageData: true, + coverageTargets: ["t1"], storeKitConfiguration: "Configuration.storekit", language: "en", region: "US", @@ -790,6 +833,7 @@ class SpecLoadingTests: XCTestCase { "name": "ExternalProject/Target2", "parallelizable": true, "skipped": true, + "location": "test.gpx", "randomExecutionOrder": true, "skippedTests": ["Test/testExample()"], ], @@ -797,6 +841,14 @@ class SpecLoadingTests: XCTestCase { "gatherCoverageData": true, "disableMainThreadChecker": true, "stopOnEveryMainThreadCheckerIssue": true, + "testPlans": [ + [ + "path": "Path/Plan.xctestplan" + ], + [ + "path": "Path/Plan2.xctestplan" + ] + ] ], ] let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary) @@ -836,9 +888,14 @@ class SpecLoadingTests: XCTestCase { targetReference: "ExternalProject/Target2", randomExecutionOrder: true, parallelizable: true, + location: "test.gpx", skipped: true, skippedTests: ["Test/testExample()"] ), + ], + testPlans: [ + .init(path: "Path/Plan.xctestplan"), + .init(path: "Path/Plan2.xctestplan") ] ) try expect(scheme.test) == expectedTest @@ -856,6 +913,7 @@ class SpecLoadingTests: XCTestCase { [ "name": "ExternalProject/Target2", "parallelizable": true, + "location": "New York, NY, USA", "randomExecutionOrder": true, "selectedTests": ["Test/testExample()"], ], @@ -877,6 +935,7 @@ class SpecLoadingTests: XCTestCase { targetReference: "ExternalProject/Target2", randomExecutionOrder: true, parallelizable: true, + location: "New York, NY, USA", selectedTests: ["Test/testExample()"] ), ] @@ -1221,9 +1280,10 @@ class SpecLoadingTests: XCTestCase { "package6": .remote(url: "package.git", versionRequirement: .range(from: "1.2.0", to: "1.2.5")), "package7": .remote(url: "package.git", versionRequirement: .exact("1.2.2")), "package8": .remote(url: "package.git", versionRequirement: .upToNextMajorVersion("4.0.0-beta.5")), - "package9": .local(path: "package/package"), + "package9": .local(path: "package/package", group: nil), "package10": .remote(url: "https://github.com/yonaskolb/XcodeGen", versionRequirement: .exact("1.2.2")), - "XcodeGen": .local(path: "../XcodeGen"), + "XcodeGen": .local(path: "../XcodeGen", group: nil), + "package11": .local(path: "../XcodeGen", group: "Packages/Feature"), ], options: .init(localPackagesGroup: "MyPackages")) let dictionary: [String: Any] = [ @@ -1242,6 +1302,7 @@ class SpecLoadingTests: XCTestCase { "package8": ["url": "package.git", "majorVersion": "4.0.0-beta.5"], "package9": ["path": "package/package"], "package10": ["github": "yonaskolb/XcodeGen", "exactVersion": "1.2.2"], + "package11": ["path": "../XcodeGen", "group": "Packages/Feature"], ], "localPackages": ["../XcodeGen"], ] @@ -1251,8 +1312,8 @@ class SpecLoadingTests: XCTestCase { $0.it("parses old local package format") { let project = Project(name: "spm", packages: [ - "XcodeGen": .local(path: "../XcodeGen"), - "Yams": .local(path: "Yams"), + "XcodeGen": .local(path: "../XcodeGen", group: nil), + "Yams": .local(path: "Yams", group: nil), ], options: .init(localPackagesGroup: "MyPackages")) let dictionary: [String: Any] = [ diff --git a/Tests/XcodeGenCoreTests/ArrayExtensionsTests.swift b/Tests/XcodeGenCoreTests/ArrayExtensionsTests.swift new file mode 100644 index 000000000..f15831477 --- /dev/null +++ b/Tests/XcodeGenCoreTests/ArrayExtensionsTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import XcodeGenCore + +class ArrayExtensionsTests: XCTestCase { + + func testSearchingForFirstIndex() { + let array = SortedArray([1, 2, 3, 4 ,5]) + XCTAssertEqual(array.firstIndex(where: { $0 > 2 }), 2) + } + + func testIndexCannotBeFound() { + let array = SortedArray([1, 2, 3, 4, 5]) + XCTAssertEqual(array.firstIndex(where: { $0 > 10 }), nil) + } + + func testEmptyArray() { + let array = SortedArray([Int]()) + XCTAssertEqual(array.firstIndex(where: { $0 > 0 }), nil) + } + + func testSearchingReturnsFirstIndexWhenMultipleElementsHaveSameValue() { + let array = SortedArray([1, 2, 3, 3 ,3]) + XCTAssertEqual(array.firstIndex(where: { $0 == 3 }), 2) + } +} + + +class SortedArrayTests: XCTestCase { + + func testSortingOnInitialization() { + let array = [1, 5, 4, 2] + let sortedArray = SortedArray(array) + XCTAssertEqual([1, 2, 4, 5], sortedArray.value) + } + + func testEmpty() { + XCTAssertEqual([Int](), SortedArray([Int]()).value) + } + +} diff --git a/Tests/XcodeGenCoreTests/AtomicTests.swift b/Tests/XcodeGenCoreTests/AtomicTests.swift new file mode 100644 index 000000000..470aeca35 --- /dev/null +++ b/Tests/XcodeGenCoreTests/AtomicTests.swift @@ -0,0 +1,39 @@ +// +// AtomicTests.swift +// +// +// Created by Vladislav Lisianskii on 27.03.2022. +// + +import XCTest +@testable import XcodeGenCore + +final class AtomicTests: XCTestCase { + + @Atomic private var atomicDictionary = [String: Int]() + + func testSimultaneousWriteOrder() { + let group = DispatchGroup() + + for index in (0..<100) { + group.enter() + DispatchQueue.global().async { + self.$atomicDictionary.with { atomicDictionary in + atomicDictionary["\(index)"] = index + } + group.leave() + } + } + + group.notify(queue: .main, execute: { + var expectedValue = [String: Int]() + for index in (0..<100) { + expectedValue["\(index)"] = index + } + XCTAssertEqual( + self.atomicDictionary, + expectedValue + ) + }) + } +} diff --git a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift index 359b6ce5f..f667ceb0e 100644 --- a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift @@ -357,18 +357,23 @@ class PBXProjGeneratorTests: XCTestCase { let dependency1 = Dependency(type: .target, reference: "TestAll", platformFilter: .all) let dependency2 = Dependency(type: .target, reference: "TestiOS", platformFilter: .iOS) let dependency3 = Dependency(type: .target, reference: "TestmacOS", platformFilter: .macOS) - let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3]) - let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3]) + let dependency4 = Dependency(type: .package(product: "Swinject"), reference: "Swinject", platformFilter: .iOS) + let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3, dependency4]) + let swinjectPackage = SwiftPackage.remote(url: "https://github.com/Swinject/Swinject", versionRequirement: .exact("2.8.0")) + let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3], packages: ["Swinject": swinjectPackage]) let pbxProj = try project.generatePbxProj() let targets = pbxProj.projects.first?.targets - let testTargetDependencies = pbxProj.projects.first?.targets.first(where: { $0.name == "Test" })?.dependencies + let testTarget = pbxProj.projects.first?.targets.first(where: { $0.name == "Test" }) + let testTargetDependencies = testTarget?.dependencies try expect(targets?.count) == 4 try expect(testTargetDependencies?.count) == 3 try expect(testTargetDependencies?[0].platformFilter).beNil() try expect(testTargetDependencies?[1].platformFilter) == "ios" try expect(testTargetDependencies?[2].platformFilter) == "maccatalyst" + try expect(testTarget?.frameworksBuildPhase()?.files?.count) == 1 + try expect(testTarget?.frameworksBuildPhase()?.files?[0].platformFilter) == "ios" } } } diff --git a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift index 9309c59aa..4ed5ae082 100644 --- a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift @@ -1271,7 +1271,7 @@ class ProjectGeneratorTests: XCTestCase { let project = Project(name: "test", targets: [app], packages: [ "XcodeGen": .remote(url: "http://github.com/yonaskolb/XcodeGen", versionRequirement: .branch("master")), "Codability": .remote(url: "http://github.com/yonaskolb/Codability", versionRequirement: .exact("1.0.0")), - "Yams": .local(path: "../Yams"), + "Yams": .local(path: "../Yams", group: nil), ], options: .init(localPackagesGroup: "MyPackages")) let pbxProject = try project.generatePbxProj(specValidate: false) @@ -1304,7 +1304,38 @@ class ProjectGeneratorTests: XCTestCase { ] ) - let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen")]) + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil)]) + + let pbxProject = try project.generatePbxProj(specValidate: false) + let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) + let localPackageFile = try unwrap(pbxProject.fileReferences.first(where: { $0.path == "../XcodeGen" })) + try expect(localPackageFile.lastKnownFileType) == "folder" + + let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase } + + guard let frameworkPhase = frameworkPhases.first else { + return XCTFail("frameworkPhases should have more than one") + } + + guard let file = frameworkPhase.files?.first else { + return XCTFail("frameworkPhase should have file") + } + + try expect(file.product?.productName) == "XcodeGen" + } + + + $0.it("generates local swift packages with custom xcode path") { + let app = Target( + name: "MyApp", + type: .application, + platform: .iOS, + dependencies: [ + Dependency(type: .package(product: nil), reference: "XcodeGen"), + ] + ) + + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: "Packages/Feature")]) let pbxProject = try project.generatePbxProj(specValidate: false) let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) @@ -1581,6 +1612,1467 @@ class ProjectGeneratorTests: XCTestCase { } } } + + func testGenerateXcodeProjectWithCustomDependencyDestinations() throws { + + describe("generateXcodeProject") { + + func generateProjectForApp(withDependencies: [Dependency], targets: [Target], packages: [String: SwiftPackage] = [:]) throws -> PBXProj { + + let app = Target( + name: "App", + type: .application, + platform: .macOS, + dependencies: withDependencies + ) + + let project = Project( + name: "test", + targets: targets + [app], + packages: packages + ) + + return try project.generatePbxProj() + } + + func expectCopyPhase(in project:PBXProj, withFilePaths: [String]? = nil, withProductPaths: [String]? = nil, toSubFolder subfolder: PBXCopyFilesBuildPhase.SubFolder, dstPath: String? = nil) throws { + + let phases = project.copyFilesBuildPhases + try expect(phases.count) == 1 + let phase = phases.first! + try expect(phase.dstSubfolderSpec) == subfolder + try expect(phase.dstPath) == dstPath + if let paths = withFilePaths { + try expect(phase.files?.count) == paths.count + let filePaths = phase.files!.map { $0.file!.path } + try expect(filePaths) == paths + } + if let paths = withProductPaths { + try expect(phase.files?.count) == paths.count + let filePaths = phase.files!.map { $0.product!.productName } + try expect(filePaths) == paths + } + } + + $0.context("with target dependencies") { + $0.context("application") { + + let appA = Target( + name: "appA", + type: .application, + platform: .macOS + ) + let appB = Target( + name: "appB", + type: .application, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true), + Dependency(type: .target, reference: appB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [ appA, appB ]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: appB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["appA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("framework") { + + let frameworkA = Target( + name: "frameworkA", + type: .framework, + platform: .macOS + ) + let frameworkB = Target( + name: "frameworkB", + type: .framework, + platform: .macOS + ) + + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true), + Dependency(type: .target, reference: frameworkB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: frameworkB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("staticFramework") { + + let frameworkA = Target( + name: "frameworkA", + type: .staticFramework, + platform: .macOS + ) + let frameworkB = Target( + name: "frameworkB", + type: .staticFramework, + platform: .macOS + ) + + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true), + Dependency(type: .target, reference: frameworkB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: frameworkB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("xcFramework") { + + let frameworkA = Target( + name: "frameworkA", + type: .xcFramework, + platform: .macOS + ) + let frameworkB = Target( + name: "frameworkB", + type: .xcFramework, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true), + Dependency(type: .target, reference: frameworkB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: frameworkA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: frameworkB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.xcframework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("Dynamic Library") { + + let libraryA = Target( + name: "libraryA", + type: .dynamicLibrary, + platform: .macOS + ) + let libraryB = Target( + name: "libraryB", + type: .dynamicLibrary, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true), + Dependency(type: .target, reference: libraryB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: libraryB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["libraryA.dylib"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("Static Library") { + + let libraryA = Target( + name: "libraryA", + type: .staticLibrary, + platform: .macOS + ) + let libraryB = Target( + name: "libraryB", + type: .staticLibrary, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true), + Dependency(type: .target, reference: libraryB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them to custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: libraryB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["liblibraryA.a"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("bundle") { + + let bundleA = Target( + name: "bundleA", + type: .bundle, + platform: .macOS + ) + let bundleB = Target( + name: "bundleB", + type: .bundle, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true), + Dependency(type: .target, reference: bundleB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: bundleB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.bundle"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("unitTestBundle") { + + let bundleA = Target( + name: "bundleA", + type: .unitTestBundle, + platform: .macOS + ) + let bundleB = Target( + name: "bundleB", + type: .unitTestBundle, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true), + Dependency(type: .target, reference: bundleB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: bundleB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.xctest"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("uitTestBundle") { + + let bundleA = Target( + name: "bundleA", + type: .uiTestBundle, + platform: .macOS + ) + let bundleB = Target( + name: "bundleB", + type: .uiTestBundle, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true), + Dependency(type: .target, reference: bundleB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: bundleB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.xctest"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("appExtension") { + + let extA = Target( + name: "extA", + type: .appExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .appExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .executables, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .executables, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .executables, dstPath: "test") + } + } + + $0.context("extensionKit") { + + let extA = Target( + name: "extA", + type: .extensionKitExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .extensionKitExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .productsDirectory, dstPath: "$(EXTENSIONS_FOLDER_PATH)") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .productsDirectory, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .productsDirectory, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .productsDirectory, dstPath: "test") + } + } + + $0.context("commandLineTool") { + + let toolA = Target( + name: "toolA", + type: .commandLineTool, + platform: .macOS + ) + let toolB = Target( + name: "toolB", + type: .commandLineTool, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: toolA.name, embed: true), + Dependency(type: .target, reference: toolB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [toolA, toolB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: toolA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: toolB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [toolA, toolB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["toolA"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("watchApp") { + + let appA = Target( + name: "appA", + type: .watchApp, + platform: .macOS + ) + let appB = Target( + name: "appB", + type: .watchApp, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true), + Dependency(type: .target, reference: appB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: appB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["appA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("watch2App") { + + let appA = Target( + name: "appA", + type: .watch2App, + platform: .macOS + ) + let appB = Target( + name: "appB", + type: .watch2App, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true), + Dependency(type: .target, reference: appB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: appB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["appA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("watch2AppContainer") { + + let appA = Target( + name: "appA", + type: .watch2AppContainer, + platform: .macOS + ) + let appB = Target( + name: "appB", + type: .watch2AppContainer, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true), + Dependency(type: .target, reference: appB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: appB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["appA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("watchExtension") { + + let extA = Target( + name: "extA", + type: .watchExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .watchExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("watch2Extension") { + + let extA = Target( + name: "extA", + type: .watch2Extension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .watch2Extension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("tvExtension") { + + let extA = Target( + name: "extA", + type: .tvExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .tvExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("messagesApplication") { + + let appA = Target( + name: "appA", + type: .messagesApplication, + platform: .macOS + ) + let appB = Target( + name: "appB", + type: .messagesApplication, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true), + Dependency(type: .target, reference: appB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: appA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: appB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [appA, appB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["appA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("messagesExtension") { + + let extA = Target( + name: "extA", + type: .messagesExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .messagesExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("stickerPack") { + + let extA = Target( + name: "extA", + type: .stickerPack, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .stickerPack, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("xpcService") { + + let xpcA = Target( + name: "xpcA", + type: .xpcService, + platform: .macOS + ) + let xpcB = Target( + name: "xpcB", + type: .xpcService, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: xpcA.name, embed: true), + Dependency(type: .target, reference: xpcB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [xpcA, xpcB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["xpcA.xpc"], toSubFolder: .productsDirectory, dstPath: "$(CONTENTS_FOLDER_PATH)/XPCServices") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: xpcA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: xpcB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [xpcA, xpcB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["xpcA.xpc"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("ocUnitTestBundle") { + + let bundleA = Target( + name: "bundleA", + type: .ocUnitTestBundle, + platform: .macOS + ) + let bundleB = Target( + name: "bundleB", + type: .ocUnitTestBundle, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true), + Dependency(type: .target, reference: bundleB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: bundleA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: bundleB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [bundleA, bundleB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.octest"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("xcodeExtension") { + + let extA = Target( + name: "extA", + type: .xcodeExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .xcodeExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("instrumentsPackage") { + + let pkgA = Target( + name: "pkgA", + type: .instrumentsPackage, + platform: .macOS + ) + let pkgB = Target( + name: "pkgB", + type: .instrumentsPackage, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: pkgA.name, embed: true), + Dependency(type: .target, reference: pkgB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [pkgA, pkgB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: pkgA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: pkgB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [pkgA, pkgB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["pkgA.instrpkg"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("intentsServiceExtension") { + + let extA = Target( + name: "extA", + type: .intentsServiceExtension, + platform: .macOS + ) + let extB = Target( + name: "extB", + type: .intentsServiceExtension, + platform: .macOS + ) + + $0.it("embeds them into plugins without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true), + Dependency(type: .target, reference: extB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .plugins, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: extA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: extB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [extA, extB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["extA.appex"], toSubFolder: .frameworks, dstPath: "test") + } + } + + $0.context("appClip") { + + let clipA = Target( + name: "clipA", + type: .onDemandInstallCapableApplication, + platform: .macOS + ) + let clipB = Target( + name: "clipB", + type: .onDemandInstallCapableApplication, + platform: .macOS + ) + + $0.it("does embed them into products directory without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: clipA.name, embed: true), + Dependency(type: .target, reference: clipB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [clipA, clipB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["clipA.app"], toSubFolder: .productsDirectory, dstPath: "$(CONTENTS_FOLDER_PATH)/AppClips") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: clipA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: clipB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [clipA, clipB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["clipA.app"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("Metal Library") { + + let libraryA = Target( + name: "libraryA", + type: .metalLibrary, + platform: .macOS + ) + let libraryB = Target( + name: "libraryB", + type: .metalLibrary, + platform: .macOS + ) + + $0.it("does not embed them without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true), + Dependency(type: .target, reference: libraryB.name, embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expect(pbxProject.copyFilesBuildPhases.count) == 0 + } + + $0.it("embeds them to custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .target, reference: libraryA.name, embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .target, reference: libraryB.name, embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [libraryA, libraryB]) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["libraryA.metallib"], toSubFolder: .plugins, dstPath: "test") + } + } + } + + $0.context("with framework dependencies") { + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .framework, reference: "frameworkA.framework", embed: true), + Dependency(type: .framework, reference: "frameworkB.framework", embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .framework, reference: "frameworkA.framework", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .framework, reference: "frameworkB.framework", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .plugins, dstPath: "test") + } + + $0.it("generates single copy phase for multiple frameworks with same copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .framework, reference: "frameworkA.framework", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .framework, reference: "frameworkB.framework", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework", "frameworkB.framework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("with sdk dependencies") { + + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .sdk(root: nil), reference: "sdkA.framework", embed: true), + Dependency(type: .sdk(root: nil), reference: "sdkB.framework", embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["System/Library/Frameworks/sdkA.framework"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .sdk(root: nil), reference: "sdkA.framework", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .sdk(root: nil), reference: "sdkB.framework", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["System/Library/Frameworks/sdkA.framework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("with package dependencies") { + + let packages: [String: SwiftPackage] = [ + "RxSwift": .remote(url: "https://github.com/ReactiveX/RxSwift", versionRequirement: .upToNextMajorVersion("5.1.1")), + ] + + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .package(product: "RxSwift"), reference: "RxSwift", embed: true), + Dependency(type: .package(product: "RxCocoa"), reference: "RxSwift", embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [], packages: packages) + + // then + try expectCopyPhase(in: pbxProject, withProductPaths: ["RxSwift"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .package(product: "RxSwift"), reference: "RxSwift", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .package(product: "RxCocoa"), reference: "RxSwift", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [], packages: packages) + + // then + try expectCopyPhase(in: pbxProject, withProductPaths: ["RxSwift"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("with carthage dependencies") { + + $0.it("embeds them into frameworks without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkA.framework", embed: true), + Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkB.framework", embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .frameworks, dstPath: "") + } + + $0.it("embeds them into custom location with copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkA.framework", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .carthage(findFrameworks: false, linkType: .static), reference: "frameworkB.framework", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + try expectCopyPhase(in: pbxProject, withFilePaths: ["frameworkA.framework"], toSubFolder: .plugins, dstPath: "test") + } + } + + $0.context("with bundle dependencies") { + $0.it("embeds them into resources without copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .bundle, reference: "bundleA.bundle", embed: true), + Dependency(type: .bundle, reference: "bundleB.bundle", embed: false), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + /// XcodeGen ignores embed: false for bundles + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.bundle", "bundleB.bundle"], toSubFolder: .resources) + } + + $0.it("ignores custom copy phase spec") { + + // given + let dependencies = [ + Dependency(type: .bundle, reference: "bundleA.bundle", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + Dependency(type: .bundle, reference: "bundleB.bundle", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)), + ] + + // when + let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: []) + + // then + /// XcodeGen ignores embed: false for bundles + try expectCopyPhase(in: pbxProject, withFilePaths: ["bundleA.bundle", "bundleB.bundle"], toSubFolder: .resources) + } + } + } + } } private extension PBXTarget { diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift index 3c27b8a91..e2c7ded6b 100644 --- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift @@ -49,11 +49,14 @@ class SchemeGeneratorTests: XCTestCase { let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: app.name) let simulateLocation = Scheme.SimulateLocation(allow: true, defaultLocation: "New York, NY, USA") let storeKitConfiguration = "Configuration.storekit" - let scheme = Scheme( + let scheme = try Scheme( name: "MyScheme", build: Scheme.Build(targets: [buildTarget], preActions: [preAction]), run: Scheme.Run(config: "Debug", askForAppToLaunch: true, launchAutomaticallySubstyle: "2", simulateLocation: simulateLocation, storeKitConfiguration: storeKitConfiguration, customLLDBInit: "/sample/.lldbinit"), - test: Scheme.Test(config: "Debug", customLLDBInit: "/test/.lldbinit"), + test: Scheme.Test(config: "Debug", targets: [ + Scheme.Test.TestTarget(targetReference: TestableTargetReference(framework.name), location: "test.gpx"), + Scheme.Test.TestTarget(targetReference: TestableTargetReference(framework.name), location: "New York, NY, USA") + ], customLLDBInit: "/test/.lldbinit"), profile: Scheme.Profile(config: "Release", askForAppToLaunch: true) ) let project = Project( @@ -109,6 +112,13 @@ class SchemeGeneratorTests: XCTestCase { try expect(xcscheme.launchAction?.customLLDBInitFile) == "/sample/.lldbinit" try expect(xcscheme.testAction?.customLLDBInitFile) == "/test/.lldbinit" try expect(xcscheme.testAction?.systemAttachmentLifetime).to.beNil() + + try expect(xcscheme.testAction?.testables[0].locationScenarioReference?.referenceType) == "0" + try expect(xcscheme.testAction?.testables[0].locationScenarioReference?.identifier) == "../test.gpx" + + try expect(xcscheme.testAction?.testables[1].locationScenarioReference?.referenceType) == "1" + try expect(xcscheme.testAction?.testables[1].locationScenarioReference?.identifier) == "New York, NY, USA" + } let frameworkTarget = Scheme.BuildTarget(target: .local(framework.name), buildTypes: [.archiving]) @@ -316,6 +326,25 @@ class SchemeGeneratorTests: XCTestCase { try expect(xcscheme.testAction?.postActions.count) == 0 } + $0.it("generates target schemes with code coverage options") { + var target = app + target.scheme = try TargetScheme( + gatherCoverageData: true, + coverageTargets: [ + TestableTargetReference(framework.name), + ] + ) + + let project = Project(name: "test", targets: [target, framework]) + let xcodeProject = try project.generateXcodeProject() + try expect(xcodeProject.sharedData?.schemes.count) == 1 + + let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) + try expect(xcscheme.testAction?.codeCoverageEnabled) == true + try expect(xcscheme.testAction?.codeCoverageTargets.count) == 1 + try expect(xcscheme.testAction?.codeCoverageTargets.first?.blueprintName) == framework.name + } + $0.it("generates scheme using external project file") { prepareXcodeProj: do { let project = try! Project(path: fixturePath + "scheme_test/test_project.yml") @@ -366,7 +395,8 @@ class SchemeGeneratorTests: XCTestCase { gatherCoverageData: true, coverageTargets: [ "TestProject/ExternalTarget", - TargetReference(framework.name), + TestableTargetReference(framework.name), + TestableTargetReference(name: "XcodeGenKitTests", location: .package("XcodeGen")) ] ) ) @@ -374,6 +404,7 @@ class SchemeGeneratorTests: XCTestCase { name: "test", targets: [framework], schemes: [scheme], + packages: ["XcodeGen": .local(path: "../", group: nil)], projectReferences: [ ProjectReference(name: "TestProject", path: externalProject.string), ] @@ -381,7 +412,7 @@ class SchemeGeneratorTests: XCTestCase { let xcodeProject = try project.generateXcodeProject() let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) try expect(xcscheme.testAction?.codeCoverageEnabled) == true - try expect(xcscheme.testAction?.codeCoverageTargets.count) == 2 + try expect(xcscheme.testAction?.codeCoverageTargets.count) == 3 let buildableReference = xcscheme.testAction?.codeCoverageTargets.first try expect(buildableReference?.blueprintName) == "ExternalTarget" try expect(buildableReference?.referencedContainer) == "container:\(externalProject.string)" @@ -446,6 +477,33 @@ class SchemeGeneratorTests: XCTestCase { let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) try expect(xcscheme.launchAction?.macroExpansion?.buildableName) == "MyApp.app" } + + $0.it("generates scheme with test target of local swift package") { + let targetScheme = TargetScheme( + testTargets: [Scheme.Test.TestTarget(targetReference: TestableTargetReference(name: "XcodeGenKitTests", location: .package("XcodeGen")))]) + let app = Target( + name: "MyApp", + type: .application, + platform: .iOS, + dependencies: [ + Dependency(type: .package(product: nil), reference: "XcodeGen") + ], + scheme: targetScheme + ) + let project = Project( + name: "ios_test", + targets: [app], + packages: ["XcodeGen": .local(path: "../", group: nil)] + ) + let xcodeProject = try project.generateXcodeProject() + let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) + let buildableReference = try unwrap(xcscheme.testAction?.testables.first?.buildableReference) + + try expect(buildableReference.blueprintIdentifier) == "XcodeGenKitTests" + try expect(buildableReference.blueprintName) == "XcodeGenKitTests" + try expect(buildableReference.buildableName) == "XcodeGenKitTests" + try expect(buildableReference.referencedContainer) == "container:../" + } $0.it("generates scheme capturing screenshots automatically and deleting on success") { let xcscheme = try self.makeSnapshotScheme( @@ -482,6 +540,33 @@ class SchemeGeneratorTests: XCTestCase { try expect(xcscheme.testAction?.systemAttachmentLifetime) == .keepNever } + + $0.it("generate test plans ") { + + let testPlanPath1 = "\(fixturePath.string)/TestProject/App_iOS/App_iOS.xctestplan" + let testPlanPath2 = "\(fixturePath.string)/TestProject/App_iOS/App_iOS.xctestplan" + + let scheme = Scheme( + name: "TestScheme", + build: Scheme.Build(targets: [buildTarget]), + test: Scheme.Test(config: "Debug", testPlans: [ + .init(path: testPlanPath1, defaultPlan: false), + .init(path: testPlanPath2, defaultPlan: true), + ]) + ) + let project = Project( + name: "test", + targets: [app, framework], + schemes: [scheme] + ) + let xcodeProject = try project.generateXcodeProject() + + let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) + try expect(xcscheme.testAction?.testPlans) == [ + .init(reference: "container:\(testPlanPath1)", default: false), + .init(reference: "container:\(testPlanPath2)", default: true), + ] + } } } diff --git a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift index 3ef691027..de033f509 100644 --- a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift @@ -589,6 +589,7 @@ class SourceGeneratorTests: XCTestCase { - file.xcassets - file.metal - file.mlmodel + - file.mlmodelc - Info.plist - Intent.intentdefinition - Configuration.storekit @@ -647,6 +648,7 @@ class SourceGeneratorTests: XCTestCase { try pbxProj.expectFile(paths: ["C", "Info.plist"], buildPhase: BuildPhaseSpec.none) try pbxProj.expectFile(paths: ["C", "file.metal"], buildPhase: .sources) try pbxProj.expectFile(paths: ["C", "file.mlmodel"], buildPhase: .sources) + try pbxProj.expectFile(paths: ["C", "file.mlmodelc"], buildPhase: .resources) try pbxProj.expectFile(paths: ["C", "Intent.intentdefinition"], buildPhase: .sources) try pbxProj.expectFile(paths: ["C", "Configuration.storekit"], buildPhase: .resources) try pbxProj.expectFile(paths: ["C", "Settings.bundle"], buildPhase: .resources) @@ -655,7 +657,7 @@ class SourceGeneratorTests: XCTestCase { try pbxProj.expectFileMissing(paths: ["C", "Settings.bundle", "Root.plist"]) try pbxProj.expectFileMissing(paths: ["C", "WithPeriod2.0"]) try pbxProj.expectFile(paths: ["C", "WithPeriod2.0", "file.swift"], buildPhase: .sources) - try pbxProj.expectFile(paths: ["C", "Documentation.docc"], buildPhase: BuildPhaseSpec.none) + try pbxProj.expectFile(paths: ["C", "Documentation.docc"], buildPhase: .sources) } $0.it("only omits the defined Info.plist from resource build phases but not other plists") { diff --git a/scripts/archive.sh b/scripts/archive.sh index ad3355072..0a9ebb585 100755 --- a/scripts/archive.sh +++ b/scripts/archive.sh @@ -10,7 +10,7 @@ LICENSE=LICENSE # copy mkdir -p $BINDIR -cp -f $1 $BINDIR +cp -f "$1" $BINDIR mkdir -p $SHAREDIR cp -R SettingPresets $SHAREDIR/SettingPresets