Merge branch 'master' into copy-files-ordering

This commit is contained in:
Brentley Jones
2018-11-05 10:38:21 -06:00
71 changed files with 7019 additions and 3758 deletions
+15 -11
View File
@@ -1,23 +1,27 @@
version: 2
jobs:
macOS_swift_4.1:
macos:
xcode: "9.3.0"
steps:
- checkout
- run: swift build
- run: swift test
- run: make build
macOS_swift_4.2:
macos:
xcode: "10.0.0"
steps:
- checkout
- run: swift build
- run: make build
- run:
name: Build
command: swift build
- run:
name: Test
command: swift test 2>&1 | xcpretty
- run:
name: Check Project Diff
command: ./diff-fixtures.sh
- run:
name: Build Test Project
command: ./build-fixtures.sh
- run:
name: Build release
command: make build
workflows:
version: 2
workflow:
jobs:
- macOS_swift_4.1
- macOS_swift_4.2
+1
View File
@@ -6,3 +6,4 @@ xcuserdata
*.xccheckout
*.xcuserstate
XcodeGen.xcodeproj
xcodegen.zip
+200 -173
View File
@@ -6,23 +6,50 @@
- Allow more granular phase ordering for Copy File and Run Script phases. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones
#### Fixed
- Fixed XPC Service package type [#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
#### Changed
- Changed spelling of build phases to **preBuildPhase** and **postBuildPhase**. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones
## 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
#### 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
#### 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)
## 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](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
#### Changed
- Added `en` as default value for knownRegions [390](https://github.com/yonaskolb/XcodeGen/pull/390) @Saik0s
- 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)
@@ -30,43 +57,43 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 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
- 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)
## 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
- 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
- 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
- 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
#### Internal
- Moved brew formula to homebrew core
@@ -77,7 +104,7 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 1.10.3
#### Fixed
- Fixed Mint installations finding `SettingPresets` [338](https://github.com/yonaskolb/XcodeGen/pull/338) @yonaskolb
- 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)
@@ -91,72 +118,72 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 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](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
#### Changed
- Code sign all dependencies by default except target executables [332](https://github.com/yonaskolb/XcodeGen/pull/332) @yonaskolb
- 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)
## 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](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
#### 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
- 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
- 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
- Fixed generated framework schemes from being executable [#328](https://github.com/yonaskolb/XcodeGen/pull/328) @brentleyjones
[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.9.0...1.10.0)
## 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](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
#### 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](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
#### 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
- 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)
## 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](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
#### 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](https://github.com/yonaskolb/XcodeGen/pull/272) @LinusU
#### Internal
- Make `LegacyTarget` init public [264](https://github.com/yonaskolb/XcodeGen/pull/264) @jerrymarino
- 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)
@@ -165,113 +192,113 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
#### 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](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
@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](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
- 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
- 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)
## 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](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
#### 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](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
- 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)
- 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)
## 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](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
#### 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](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
#### 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 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
- 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)
## 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](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
#### 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](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
#### 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](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
#### Internal
- refactoring
@@ -283,26 +310,26 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 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](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
#### 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](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
- 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
- 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)
@@ -322,8 +349,8 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 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](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
- Added some missing default settings for framework targets. `SKIP_INSTALL: YES` fixes archiving
- Filter out nulls from setting presets if specifying an empty string
@@ -345,17 +372,17 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
#### Added
- `include` now supports a single string as well as a list
- add support setting xcconfig files on a project with `configFiles` [PR#64](https://github.com/yonaskolb/XcodeGen/pull/64)
- add `fileGroups` to project spec for adding groups of files that aren't target source files [PR#64](https://github.com/yonaskolb/XcodeGen/pull/64)
- 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)
- better output (more info, emoji, colors)
- add `options.bundleIdPrefix` for autogenerating `PRODUCT_BUNDLE_IDENTIFIER` [PR#67](https://github.com/yonaskolb/XcodeGen/pull/67)
- add `:REPLACE` syntax when merging `include` [PR#68](https://github.com/yonaskolb/XcodeGen/pull/68)
- 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 `mint` installation support
#### Fixed
- fixed homebrew installation
- fixed target xcconfig files not working via `configFiles` [PR#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 [PR#64](https://github.com/yonaskolb/XcodeGen/pull/64)
- 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)
- exit with error on failure
[Commits](https://github.com/yonaskolb/XcodeGen/compare/1.1.0...1.2.0)
@@ -379,16 +406,16 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 1.0.0
#### Added
- Swift 4 support [PR#52](https://github.com/yonaskolb/XcodeGen/pull/52)
- Support for C and C++ files [PR#48](https://github.com/yonaskolb/XcodeGen/pull/48) by @antoniocasero
- 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
- Xcode 9 default settings
#### Fixed
- fixed empty string in YAML not being parsed properly [PR#50](https://github.com/yonaskolb/XcodeGen/pull/50) by @antoniocasero
- 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 [PR#56](https://github.com/yonaskolb/XcodeGen/pull/56)
- **BREAKING**: changed target definitions from list to map [PR#54](https://github.com/yonaskolb/XcodeGen/pull/54) See [Project Spec](docs/ProjectSpec.md)
- 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)
[Commits](https://github.com/yonaskolb/XcodeGen/compare/0.6.1...1.0.0)
@@ -396,18 +423,18 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 0.6.1
#### Added
- Ability to set PBXProject attributes [PR#45](https://github.com/yonaskolb/XcodeGen/pull/45)
- Ability to set PBXProject attributes [#45](https://github.com/yonaskolb/XcodeGen/pull/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 [PR#46](https://github.com/yonaskolb/XcodeGen/pull/46)
- 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)
## 0.6.0
#### Added
- Allow a project spec to include other project specs [PR#44](https://github.com/yonaskolb/XcodeGen/pull/44)
- Allow a project spec to include other project specs [#44](https://github.com/yonaskolb/XcodeGen/pull/44)
#### Changed
- Changed default spec path to `project.yml`
@@ -420,16 +447,16 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
#### Fixed
- Fix embedded framework dependencies
- Add `CODE_SIGN_IDENTITY[sdk=iphoneos*]` back to iOS targets
- Fix build scripts with "" generating invalid projects [PR#43](https://github.com/yonaskolb/XcodeGen/pull/43)
- 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)
## 0.5.0
#### Added
- Added multi platform targets [PR#35](https://github.com/yonaskolb/XcodeGen/pull/35)
- Automatically generate platform specific `FRAMEWORK_SEARCH_PATHS` for Carthage dependencies [PR#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 [PR#40](https://github.com/yonaskolb/XcodeGen/pull/40)
- Add options for controlling embedding of dependencies [PR#37](https://github.com/yonaskolb/XcodeGen/pull/37)
- 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)
#### Fixed
- Fixed localized files not being added to a target's resources
@@ -443,23 +470,23 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 0.4.0
##### Added
- Homebrew support [PR#16](https://github.com/yonaskolb/XcodeGen/pull/16) by @pepibumur
- Added `runOnlyWhenInstalling` to build scripts [PR#32](https://github.com/yonaskolb/XcodeGen/pull/32)
- Added `carthageBuildPath` option [PR#34](https://github.com/yonaskolb/XcodeGen/pull/34)
- 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)
#### 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 [PR#33](https://github.com/yonaskolb/XcodeGen/pull/33)
- 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)
## 0.3.0 - Extensions and Scheme Tests
#### Added
- Support for app extension dependencies, using the same `target: MyExtension` syntax [PR#19](https://github.com/yonaskolb/XcodeGen/pull/19)
- Added test targets to generated target schemes via `Target.scheme.testTargets` [PR#21](https://github.com/yonaskolb/XcodeGen/pull/21)
- 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)
#### Changed
- Updated xcodeproj to 0.0.9
@@ -475,7 +502,7 @@ If XcodeGen is compiled with Swift 4.2, then UUID's in the generated project wil
## 0.2.0 - Build scripts
#### Added
- Added Target build scripts with `Target.prebuildScripts` and `Target.postbuildScripts` [PR#17](https://github.com/yonaskolb/XcodeGen/pull/17)
- Added Target build scripts with `Target.prebuildScripts` and `Target.postbuildScripts` [#17](https://github.com/yonaskolb/XcodeGen/pull/17)
- Support for absolute paths in target sources, run script files, and config files
- Add validation for incorrect `Target.configFiles`
+41 -5
View File
@@ -171,6 +171,16 @@ Settings are merged in the following order: groups, base, configs.
- `FRAMEWORK_SEARCH_PATHS`: If carthage dependencies are used, the platform build path will be added to this setting
- `OTHER_LDFLAGS`: See `requiresObjCLinking` below
- [ ] **dependencies**: **[[Dependency](#dependency)]** - Dependencies for the target
- [ ] **info**: **[Plist](#plist)** - If defined, this will generate and write an `Info.plist` to the specified path and use it by setting the `INFOPLIST_FILE` build setting for every configuration. The following properties are generated automatically, the rest will have to be provided.
- `CFBundleIdentifier`
- `CFBundleInfoDictionaryVersion`
- `CFBundleExecutable`
- `CFBundleName`
- `CFBundleDevelopmentRegion`
- `CFBundleShortVersionString`
- `CFBundleVersion`
- `CFBundlePackageType`
- [ ] **entitlements**: **[Plist](#plist)** - If defined this will generate and write a `.entitlements` file, and use it by setting `CODE_SIGN_ENTITLEMENTS` build setting for every configuration. All properties must be provided
- [ ] **templates**: **[String]** - A list of target templates that will be merged in order
- [ ] **transitivelyLinkDependencies**: **Bool** - If this is not specified the value from the project set in [Options](#options)`.transitivelyLinkDependencies` will be used.
- [ ] **directlyEmbedCarthageDependencies**: **Bool** - If this is `true` Carthage dependencies will be embedded using an `Embed Frameworks` build phase instead of the `copy-frameworks` script. Defaults to `true` for all targets except iOS/tvOS/watchOS Applications.
@@ -323,15 +333,15 @@ A dependency can be one of a 3 types:
- `target: name` - links to another target
- `framework: path` - links to a framework
- `carthage: name` - helper for linking to a Carthage framework
- `sdk: name` - links to a dependency with the SDK. This can either be a relative path within the sdk root or a single filename that references a framework (.framework) or lib (.tbd)
**Embed options**:
These only applied to `target` and `framework` dependencies.
**Linking options**:
- [ ] **embed**: **Bool** - Whether to embed the dependency. Defaults to true for application target and false for non application targets.
- [ ] **link**: **Bool** - Whether to link the dependency. Defaults to `true` depending on the type of the dependency and the type of the target (e.g. static libraries will only link to executables by default).
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
**Implicit Framework options**:
@@ -356,6 +366,8 @@ targets:
- target: MyFramework
- framework: path/to/framework.framework
- carthage: Result
- sdk: Contacts.framework
- sdk: libc++.tbd
MyFramework:
type: framework
```
@@ -374,6 +386,25 @@ targets:
Debug: App/debug.xcconfig
Release: App/release.xcconfig
```
### Plist
Plists are created on disk on every generation of the project. They can be used as a way to define `Info.plist` or `.entitlement` files. Some `Info.plist` properties are generated automatically.
- [x] **path**: **String** - This is the path where the plist will be written to
- [x] **properties**: **[String: Any]** - This is a map of all the plist keys and values
```yml
targets:
App:
info:
path: App/Info.plist
properties:
UISupportedInterfaceOrientations: [UIInterfaceOrientationPortrait]
UILaunchStoryboardName: LaunchScreen
entitlements:
path: App/App.entitlements
properties:
com.apple.security.application-groups: group.com.app
```
### Build Script
@@ -437,7 +468,7 @@ targets:
This is a convenience used to automatically generate schemes for a target based on different configs or included tests. If you want more control check out the top level [Scheme](#scheme).
- [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**: **[String]** - a list of test targets that should be included in the scheme. These will be added to the build targets and the test entries
- [ ] **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
- [ ] **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.
@@ -561,7 +592,12 @@ 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
- [ ] **targets**: **[String]** - a list of targets to test
- [ ] **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)
#### Test Target
- [x] **name**: **String** - The name of the target
- [ ] **parallelizable**: **Bool** - Whether to run tests in parallel. Defaults to false
- [ ] **randomExecutionOrder**: **Bool** - Whether to run tests in a random order. Defaults to false
### Archive Action
+29 -3
View File
@@ -7,6 +7,7 @@
- [Use dependencies](#use-dependencies)
- [Cocoapods](#cocoapods)
- [Carthage](#carthage)
- [SDK](#sdk)
# Configuring build settings
There are various ways of configuring build settings
@@ -92,7 +93,7 @@ DEVELOPMENT_TEAM=XXXXXXXXX xcodebuild ...
# Use dependencies
Each target can declare one or more dependencies. See [Dependency](#dependency) for more info
Each target can declare one or more dependencies. See [Dependency](ProjectSpec.md#dependency) in the ProjectSpec for more info about all the properties
### Cocoapods
Use your `podfile` as normal. The pods themselves don't need to be referenced in the project spec. After you generate your project simply run `pod install` which will integrate with your project and create a workspace.
@@ -100,7 +101,7 @@ Use your `podfile` as normal. The pods themselves don't need to be referenced in
### Carthage
XcodeGen makes integrating Carthage dependencies super easy!
You simply reference them in each target that requires them and XcodeGen does the rest by automatically linking and embedding the carthage frameworks where neccessary.
You simply reference them in each target that requires them and XcodeGen does the rest by automatically linking and embedding the carthage frameworks where necessary.
```yaml
targets:
@@ -111,4 +112,29 @@ targets:
Framework:
dependencies:
- carthage: Alamofire
```
```
XcodeGen automatically creates the build phase that Carthage requires which lists all the files and runs `carthage copy-frameworks`. You can change the invocation of carthage to something different, for example if you are running it with [Mint](https://github.com/yonaskolb/mint). This is then prepended to ` copy frameworks`
```yaml
options:
carthageExecutablePath: mint run Carthage/Carthage carthage
```
By default XcodeGen looks for carthage frameworks in `Carthage/Build`. You can change this with the `carthageBuildPath` option
```yaml
options:
carthageBuildPath: ../../Carthage/Build
```
### SDK
System frameworks and libs can be linked by using the `sdk` dependency type. You can either specify frameworks or libs by using a `.framework` or `.tbd` filename, respectively
```yaml
targets:
App:
dependencies:
- sdk: Contacts.framework
- sdk: libc++.tbd
```
+1 -1
View File
@@ -1,6 +1,6 @@
TOOL_NAME = XcodeGen
export EXECUTABLE_NAME = xcodegen
VERSION = 1.11.2
VERSION = 2.0.0
PREFIX = /usr/local
INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME)
+17 -17
View File
@@ -3,11 +3,11 @@
"pins": [
{
"package": "AEXML",
"repositoryURL": "https://github.com/tadija/AEXML.git",
"repositoryURL": "https://github.com/tadija/AEXML",
"state": {
"branch": null,
"revision": "6eea665515d079c338690147082a8084a36484b0",
"version": "4.3.0"
"revision": "54bb8ea6fb693dd3f92a89e5fcc19e199fdeedd0",
"version": "4.3.3"
}
},
{
@@ -24,8 +24,8 @@
"repositoryURL": "https://github.com/yonaskolb/JSONUtilities.git",
"state": {
"branch": null,
"revision": "d9f957b1b2a078c93f96c723040d4cbffcb7d3f9",
"version": "4.0.2"
"revision": "db238f4858ac2ac3fff228b5b71ce90488a6a9b3",
"version": "4.1.0"
}
},
{
@@ -46,15 +46,6 @@
"version": "3.1.4"
}
},
{
"package": "ShellOut",
"repositoryURL": "https://github.com/JohnSundell/ShellOut.git",
"state": {
"branch": null,
"revision": "f1c253a34a40df4bfd268b09fdb101b059f6d52d",
"version": "2.1.0"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
@@ -65,12 +56,21 @@
}
},
{
"package": "xcproj",
"package": "SwiftShell",
"repositoryURL": "https://github.com/kareman/SwiftShell",
"state": {
"branch": null,
"revision": "beebe43c986d89ea5359ac3adcb42dac94e5e08a",
"version": "4.1.2"
}
},
{
"package": "xcodeproj",
"repositoryURL": "https://github.com/tuist/xcodeproj.git",
"state": {
"branch": null,
"revision": "5253c22f208558264e3a64a3a29f11537ca1b41a",
"version": "4.3.1"
"revision": "23da51abd3de3bedaad59a0afbb150b48504b5b0",
"version": "6.3.0"
}
},
{
+9 -5
View File
@@ -1,4 +1,4 @@
// swift-tools-version:4.0
// swift-tools-version:4.2
import PackageDescription
@@ -13,10 +13,10 @@ let package = Package(
.package(url: "https://github.com/kylef/PathKit.git", from: "0.9.0"),
.package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "1.0.0"),
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.0.0"),
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.1.0"),
.package(url: "https://github.com/kylef/Spectre.git", from: "0.9.0"),
.package(url: "https://github.com/onevcat/Rainbow.git", from: "3.0.0"),
.package(url: "https://github.com/tuist/xcodeproj.git", from: "4.3.1"),
.package(url: "https://github.com/tuist/xcodeproj.git", from: "6.3.0"),
],
targets: [
.target(name: "XcodeGen", dependencies: [
@@ -27,17 +27,21 @@ let package = Package(
.target(name: "XcodeGenKit", dependencies: [
"ProjectSpec",
"JSONUtilities",
"xcproj",
"xcodeproj",
"PathKit",
]),
.target(name: "ProjectSpec", dependencies: [
"JSONUtilities",
"xcproj",
"xcodeproj",
"Yams",
]),
.testTarget(name: "XcodeGenKitTests", dependencies: [
"XcodeGenKit",
"Spectre",
]),
.testTarget(name: "PerformanceTests", dependencies: [
"XcodeGenKit",
"Spectre",
])
]
)
+8 -4
View File
@@ -49,13 +49,17 @@ targets:
deploymentTarget: "10.0"
sources: [MyApp]
settings:
debug:
CUSTOM_BUILD_SETTING: my_debug_value
release:
CUSTOM_BUILD_SETTING: my_release_value
configs:
debug:
CUSTOM_BUILD_SETTING: my_debug_value
release:
CUSTOM_BUILD_SETTING: my_release_value
dependencies:
- target: MyFramework
- carthage: Alamofire
- framework: Vendor/MyFramework.framework
- sdk: Contacts.framework
- sdk: libc++.tbd
MyFramework:
type: framework
platform: iOS
@@ -1,2 +1,2 @@
CODE_SIGN_IDENTITY[sdk=iphoneos*]: iPhone Developer
CODE_SIGN_IDENTITY: iPhone Developer
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
+1 -1
View File
@@ -42,4 +42,4 @@ GCC_WARN_UNUSED_VARIABLE: YES
# Swift Settings
PRODUCT_NAME: $(TARGET_NAME)
SWIFT_VERSION: '4.0'
SWIFT_VERSION: '4.2'
-1
View File
@@ -1,6 +1,5 @@
import Foundation
import JSONUtilities
import xcproj
public struct Config: Equatable {
public var name: String
+11 -2
View File
@@ -1,6 +1,5 @@
import Foundation
import JSONUtilities
import xcproj
public struct Dependency: Equatable {
@@ -11,6 +10,7 @@ public struct Dependency: Equatable {
public var removeHeaders: Bool = true
public var link: Bool?
public var implicit: Bool = false
public var weakLink: Bool = false
public init(
type: DependencyType,
@@ -18,7 +18,8 @@ public struct Dependency: Equatable {
embed: Bool? = nil,
codeSign: Bool? = nil,
link: Bool? = nil,
implicit: Bool = false
implicit: Bool = false,
weakLink: Bool = false
) {
self.type = type
self.reference = reference
@@ -26,12 +27,14 @@ public struct Dependency: Equatable {
self.codeSign = codeSign
self.link = link
self.implicit = implicit
self.weakLink = weakLink
}
public enum DependencyType {
case target
case framework
case carthage
case sdk
}
}
@@ -47,6 +50,9 @@ extension Dependency: JSONObjectConvertible {
} else if let carthage: String = jsonDictionary.json(atKeyPath: "carthage") {
type = .carthage
reference = carthage
} else if let sdk: String = jsonDictionary.json(atKeyPath: "sdk") {
type = .sdk
reference = sdk
} else {
throw SpecParsingError.invalidDependency(jsonDictionary)
}
@@ -61,5 +67,8 @@ extension Dependency: JSONObjectConvertible {
if let bool: Bool = jsonDictionary.json(atKeyPath: "implicit") {
implicit = bool
}
if let bool: Bool = jsonDictionary.json(atKeyPath: "weak") {
weakLink = bool
}
}
}
+9 -1
View File
@@ -1,6 +1,5 @@
import Foundation
import JSONUtilities
import xcproj
public struct DeploymentTarget: Equatable {
@@ -41,6 +40,15 @@ extension Platform {
case .macOS: return "MACOSX_DEPLOYMENT_TARGET"
}
}
public var sdkRoot: String {
switch self {
case .iOS: return "iphoneos"
case .tvOS: return "appletvos"
case .watchOS: return "watchos"
case .macOS: return "macosx"
}
}
}
extension Version {
+26
View File
@@ -0,0 +1,26 @@
import Foundation
import JSONUtilities
public struct Plist: Equatable {
public let path: String
public let properties: [String: Any]
public init(path: String, attributes: [String: Any] = [:]) {
self.path = path
properties = attributes
}
public static func == (lhs: Plist, rhs: Plist) -> Bool {
return lhs.path == rhs.path &&
NSDictionary(dictionary: lhs.properties).isEqual(to: rhs.properties)
}
}
extension Plist: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
path = try jsonDictionary.json(atKeyPath: "path")
properties = jsonDictionary.json(atKeyPath: "properties") ?? [:]
}
}
+9 -2
View File
@@ -1,7 +1,6 @@
import Foundation
import JSONUtilities
import PathKit
import xcproj
import Yams
public struct Project: BuildSettingsContainer {
@@ -78,6 +77,10 @@ public struct Project: BuildSettingsContainer {
public func getConfig(_ configName: String) -> Config? {
return configs.first { $0.name == configName }
}
public var defaultProjectPath: Path {
return basePath + "\(name).xcodeproj"
}
}
extension Project: CustomDebugStringConvertible {
@@ -86,7 +89,7 @@ extension Project: CustomDebugStringConvertible {
var string = "Name: \(name)"
let indent = " "
if !include.isEmpty {
string += "\nInclude:\n\(indent)" + include.map { "📄 \($0)" }.joined(separator: "\n\(indent)")
string += "\nInclude:\n\(indent)" + include.map { $0.description }.joined(separator: "\n\(indent)")
}
if !settingGroups.isEmpty {
@@ -102,6 +105,10 @@ extension Project: CustomDebugStringConvertible {
if !aggregateTargets.isEmpty {
string += "\nAggregate Targets:\n\(indent)" + aggregateTargets.map { $0.description }.joined(separator: "\n\(indent)")
}
if !schemes.isEmpty {
let allSchemes = targets.filter { $0.scheme != nil }.map { $0.name } + schemes.map { $0.name }
string += "\nSchemes:\n\(indent)" + allSchemes.joined(separator: "\n\(indent)")
}
return string
}
+51 -11
View File
@@ -1,6 +1,6 @@
import Foundation
import JSONUtilities
import xcproj
import xcodeproj
public typealias BuildType = XCScheme.BuildAction.Entry.BuildFor
@@ -89,15 +89,40 @@ public struct Scheme: Equatable {
public var config: String?
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
public var targets: [String]
public var targets: [TestTarget]
public var preActions: [ExecutionAction]
public var postActions: [ExecutionAction]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public struct TestTarget: Equatable, ExpressibleByStringLiteral {
public let name: String
public var randomExecutionOrder: Bool
public var parallelizable: Bool
public init(
name: String,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false
) {
self.name = name
self.randomExecutionOrder = randomExecutionOrder
self.parallelizable = parallelizable
}
public init(stringLiteral value: String) {
name = value
randomExecutionOrder = false
parallelizable = false
}
}
public init(
config: String,
gatherCoverageData: Bool = false,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false,
commandLineArguments: [String: Bool] = [:],
targets: [String] = [],
targets: [TestTarget] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = [],
environmentVariables: [XCScheme.EnvironmentVariable] = []
@@ -210,13 +235,34 @@ extension Scheme.Test: JSONObjectConvertible {
config = jsonDictionary.json(atKeyPath: "config")
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
targets = jsonDictionary.json(atKeyPath: "targets") ?? []
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
if let string = target as? String {
return TestTarget(name: string)
} else if let dictionary = target as? JSONDictionary {
return try TestTarget(jsonDictionary: dictionary)
} else {
return nil
}
}
} else {
targets = []
}
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
}
}
extension Scheme.Test.TestTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? false
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? false
}
}
extension Scheme.Profile: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@@ -311,7 +357,7 @@ extension BuildType: JSONPrimitiveConvertible {
}
}
extension XCScheme.EnvironmentVariable: JSONObjectConvertible, Equatable {
extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
private static func parseValue(_ value: Any) -> String {
if let bool = value as? Bool {
@@ -346,10 +392,4 @@ extension XCScheme.EnvironmentVariable: JSONObjectConvertible, Equatable {
return []
}
}
public static func == (lhs: XCScheme.EnvironmentVariable, rhs: XCScheme.EnvironmentVariable) -> Bool {
return lhs.variable == rhs.variable &&
lhs.value == rhs.value &&
lhs.enabled == rhs.enabled
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Foundation
import JSONUtilities
import PathKit
import xcproj
import xcodeproj
public struct Settings: Equatable, JSONObjectConvertible, CustomStringConvertible {
-1
View File
@@ -1,6 +1,5 @@
import Foundation
import JSONUtilities
import xcproj
public struct SpecOptions: Equatable {
+37 -4
View File
@@ -22,6 +22,24 @@ extension Project {
}
}
}
if settings.buildSettings.count == configs.count {
var allConfigs = true
for buildSetting in settings.buildSettings.keys {
var isConfig = false
for config in configs {
if config.name.lowercased().contains(buildSetting.lowercased()) {
isConfig = true
}
}
if !isConfig {
allConfigs = false
}
}
if allConfigs {
errors.append(.invalidPerConfigSettings)
}
}
return errors
}
@@ -92,8 +110,8 @@ extension Project {
}
for testTarget in scheme.testTargets {
if getTarget(testTarget) == nil {
errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget))
if getTarget(testTarget.name) == nil {
errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget.name))
}
}
}
@@ -120,8 +138,23 @@ extension Project {
for target in targets {
for dependency in target.dependencies {
if dependency.type == .target, getProjectTarget(dependency.reference) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference))
switch dependency.type {
case .target:
if getProjectTarget(dependency.reference) == 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"?:
break
default:
errors.append(.invalidSDKDependency(target: target.name, dependency: dependency.reference))
}
}
default: break
}
}
@@ -6,6 +6,7 @@ public struct SpecValidationError: Error, CustomStringConvertible {
public enum ValidationError: Error, CustomStringConvertible {
case invalidXcodeGenVersion(minimumVersion: Version, version: Version)
case invalidSDKDependency(target: String, dependency: String)
case invalidTargetDependency(target: String, dependency: String)
case invalidTargetSource(target: String, source: String)
case invalidTargetConfigFile(target: String, configFile: String, config: String)
@@ -21,11 +22,14 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidConfigFileConfig(String)
case missingConfigForTargetScheme(target: String, configType: ConfigType)
case missingDefaultConfig(configName: String)
case invalidPerConfigSettings
public var description: String {
switch self {
case let .invalidXcodeGenVersion(minimumVersion, version):
return "XcodeGen version is \(version), but minimum required version specified as \(minimumVersion)"
case let .invalidSDKDependency(target, dependency):
return "Target \(target.quoted) has invalid sdk dependency: \(dependency.quoted). It must be a full path or have the following extensions: .framework, .dylib, .tbd"
case let .invalidTargetDependency(target, dependency):
return "Target \(target.quoted) has invalid dependency: \(dependency.quoted)"
case let .invalidTargetConfigFile(target, configFile, config):
@@ -56,6 +60,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme"
case let .missingDefaultConfig(name):
return "Default configuration \(name) doesn't exist"
case .invalidPerConfigSettings:
return "Settings that are for a specific config must go in \"configs\". \"base\" can be used for common settings"
}
}
}
+25 -6
View File
@@ -1,6 +1,6 @@
import Foundation
import JSONUtilities
import xcproj
import xcodeproj
public struct LegacyTarget: Equatable {
public var toolPath: String
@@ -28,6 +28,8 @@ public struct Target: ProjectTarget {
public var settings: Settings
public var sources: [TargetSource]
public var dependencies: [Dependency]
public var info: Plist?
public var entitlements: Plist?
public var transitivelyLinkDependencies: Bool?
public var directlyEmbedCarthageDependencies: Bool?
public var requiresObjCLinking: Bool?
@@ -40,14 +42,14 @@ public struct Target: ProjectTarget {
public var legacy: LegacyTarget?
public var deploymentTarget: Version?
public var attributes: [String: Any]
internal var productName: String?
public var productName: String
public var isLegacy: Bool {
return legacy != nil
}
public var filename: String {
var filename = productName ?? name
var filename = productName
if let fileExtension = type.fileExtension {
filename += ".\(fileExtension)"
}
@@ -58,11 +60,14 @@ public struct Target: ProjectTarget {
name: String,
type: PBXProductType,
platform: Platform,
productName: String? = nil,
deploymentTarget: Version? = nil,
settings: Settings = .empty,
configFiles: [String: String] = [:],
sources: [TargetSource] = [],
dependencies: [Dependency] = [],
info: Plist? = nil,
entitlements: Plist? = nil,
transitivelyLinkDependencies: Bool? = nil,
directlyEmbedCarthageDependencies: Bool? = nil,
requiresObjCLinking: Bool? = nil,
@@ -78,10 +83,13 @@ public struct Target: ProjectTarget {
self.type = type
self.platform = platform
self.deploymentTarget = deploymentTarget
self.productName = productName ?? name
self.settings = settings
self.configFiles = configFiles
self.sources = sources
self.dependencies = dependencies
self.info = info
self.entitlements = entitlements
self.transitivelyLinkDependencies = transitivelyLinkDependencies
self.directlyEmbedCarthageDependencies = directlyEmbedCarthageDependencies
self.requiresObjCLinking = requiresObjCLinking
@@ -98,7 +106,7 @@ public struct Target: ProjectTarget {
extension Target: CustomStringConvertible {
public var description: String {
return "\(platform.emoji) \(name): \(type)"
return "\(name): \(platform.rawValue) \(type)"
}
}
@@ -210,6 +218,8 @@ extension Target: Equatable {
lhs.settings == rhs.settings &&
lhs.configFiles == rhs.configFiles &&
lhs.sources == rhs.sources &&
lhs.info == rhs.info &&
lhs.entitlements == rhs.entitlements &&
lhs.dependencies == rhs.dependencies &&
lhs.preBuildScripts == rhs.preBuildScripts &&
lhs.postCompileScripts == rhs.postCompileScripts &&
@@ -234,8 +244,9 @@ extension LegacyTarget: JSONObjectConvertible {
extension Target: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = jsonDictionary.json(atKeyPath: "name") ?? name
productName = jsonDictionary.json(atKeyPath: "productName")
let resolvedName: String = jsonDictionary.json(atKeyPath: "name") ?? name
self.name = resolvedName
productName = jsonDictionary.json(atKeyPath: "productName") ?? resolvedName
let typeString: String = try jsonDictionary.json(atKeyPath: "type")
if let type = PBXProductType(string: typeString) {
self.type = type
@@ -279,6 +290,14 @@ extension Target: NamedJSONDictionaryConvertible {
} else {
dependencies = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
}
if jsonDictionary["info"] != nil {
info = try jsonDictionary.json(atKeyPath: "info") as Plist
}
if jsonDictionary["entitlements"] != nil {
entitlements = try jsonDictionary.json(atKeyPath: "entitlements") as Plist
}
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies")
directlyEmbedCarthageDependencies = jsonDictionary.json(atKeyPath: "directlyEmbedCarthageDependencies")
requiresObjCLinking = jsonDictionary.json(atKeyPath: "requiresObjCLinking")
+16 -4
View File
@@ -1,9 +1,9 @@
import Foundation
import JSONUtilities
import xcproj
import xcodeproj
public struct TargetScheme: Equatable {
public var testTargets: [String]
public var testTargets: [Scheme.Test.TestTarget]
public var configVariants: [String]
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
@@ -12,7 +12,7 @@ public struct TargetScheme: Equatable {
public var postActions: [Scheme.ExecutionAction]
public init(
testTargets: [String] = [],
testTargets: [Scheme.Test.TestTarget] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
commandLineArguments: [String: Bool] = [:],
@@ -33,7 +33,19 @@ public struct TargetScheme: Equatable {
extension TargetScheme: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
testTargets = jsonDictionary.json(atKeyPath: "testTargets") ?? []
if let targets = jsonDictionary["testTargets"] as? [Any] {
testTargets = try targets.compactMap { target in
if let string = target as? String {
return .init(name: string)
} else if let dictionary = target as? JSONDictionary {
return try .init(jsonDictionary: dictionary)
} else {
return nil
}
}
} else {
testTargets = []
}
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
+3 -3
View File
@@ -1,7 +1,7 @@
import Foundation
import JSONUtilities
import PathKit
import xcproj
import xcodeproj
public struct TargetSource: Equatable {
@@ -58,7 +58,7 @@ public struct TargetSource: Equatable {
case sharedSupport
case plugins
public var destination: xcproj.PBXCopyFilesBuildPhase.SubFolder? {
public var destination: xcodeproj.PBXCopyFilesBuildPhase.SubFolder? {
switch self {
case .absolutePath: return .absolutePath
case .productsDirectory: return .productsDirectory
@@ -96,7 +96,7 @@ public struct TargetSource: Equatable {
}
}
public var buildPhase: xcproj.BuildPhase? {
public var buildPhase: xcodeproj.BuildPhase? {
switch self {
case .sources: return .sources
case .headers: return .headers
+2 -2
View File
@@ -1,6 +1,6 @@
import Foundation
import PathKit
import xcproj
import xcodeproj
extension PBXProductType {
@@ -60,7 +60,7 @@ extension XCScheme.CommandLineArguments {
public convenience init(_ dict: [String: Bool]) {
let args = dict.map { tuple in
XCScheme.CommandLineArguments.CommandLineArgument(name: tuple.key, enabled: tuple.value)
}
}.sorted { $0.name < $1.name }
self.init(arguments: args)
}
}
+10 -17
View File
@@ -4,9 +4,9 @@ import JSONUtilities
import PathKit
import ProjectSpec
import XcodeGenKit
import xcproj
import xcodeproj
let version = try Version("1.11.2")
let version = try Version("2.0.0")
func generate(spec: String, project: String, isQuiet: Bool, justVersion: Bool) {
if justVersion {
@@ -22,7 +22,7 @@ func generate(spec: String, project: String, isQuiet: Bool, justVersion: Bool) {
}
let projectSpecPath = Path(spec).absolute()
let projectPath = project == "" ? projectSpecPath.parent() : Path(project).absolute()
var projectPath = project == "" ? projectSpecPath.parent() : Path(project).absolute()
if !projectSpecPath.exists {
fatalError("No project spec found at \(projectSpecPath.absolute())")
@@ -39,27 +39,20 @@ func generate(spec: String, project: String, isQuiet: Bool, justVersion: Bool) {
}
do {
logger.info("⚙️ Generating project...")
try project.validateMinimumXcodeGenVersion(version)
try project.validate()
logger.info("⚙️ Generating project...")
let projectGenerator = ProjectGenerator(project: project)
let xcodeProject = try projectGenerator.generateXcodeProject()
logger.info("⚙️ Writing project...")
let fileWriter = FileWriter(project: project)
projectPath = projectPath + "\(project.name).xcodeproj"
try fileWriter.writeXcodeProject(xcodeProject, to: projectPath)
try fileWriter.writePlists()
let projectFile = projectPath + "\(project.name).xcodeproj"
let tempPath = Path.temporary + "XcodeGen_\(Int(NSTimeIntervalSince1970))"
try? tempPath.delete()
if projectFile.exists {
try projectFile.copy(tempPath)
}
try xcodeProject.write(path: tempPath, override: true)
try? projectFile.delete()
try tempPath.copy(projectFile)
try? tempPath.delete()
logger.success("💾 Saved project to \(projectFile.string)")
logger.success("💾 Saved project to \(projectPath)")
} catch let error as SpecValidationError {
fatalError(error.description)
} catch {
+56
View File
@@ -0,0 +1,56 @@
import Foundation
import PathKit
import ProjectSpec
import xcodeproj
public class FileWriter {
let project: Project
public init(project: Project) {
self.project = project
}
public func writeXcodeProject(_ xcodeProject: XcodeProj, to projectPath: Path? = nil) throws {
let projectPath = project.defaultProjectPath
let tempPath = Path.temporary + "XcodeGen_\(Int(NSTimeIntervalSince1970))"
try? tempPath.delete()
if projectPath.exists {
try projectPath.copy(tempPath)
}
try xcodeProject.write(path: tempPath, override: true)
try? projectPath.delete()
try tempPath.copy(projectPath)
try? tempPath.delete()
}
public func writePlists() throws {
let infoPlistGenerator = InfoPlistGenerator()
for target in project.targets {
// write Info.plist
if let plist = target.info {
let properties = infoPlistGenerator.generateProperties(target: target).merged(plist.properties)
try writePlist(properties, path: plist.path)
}
// write entitlements
if let plist = target.entitlements {
try writePlist(plist.properties, path: plist.path)
}
}
}
private func writePlist(_ plist: [String: Any], path: String) throws {
let path = project.basePath + path
if path.exists, let data: Data = try? path.read(),
let existingPlist = (try? PropertyListSerialization.propertyList(from: data, format: nil)) as? [String: Any], NSDictionary(dictionary: plist).isEqual(to: existingPlist) {
// file is the same
return
}
let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try? path.delete()
try path.parent().mkpath()
try path.write(data)
}
}
@@ -0,0 +1,43 @@
import Foundation
import PathKit
import ProjectSpec
public class InfoPlistGenerator {
/**
Default info plist attributes taken from:
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_DefinitionsInfoPlist.xctemplate/TemplateInfo.plist
*/
var defaultInfoPlist: [String: Any] = {
var dictionary: [String: Any] = [:]
dictionary["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)"
dictionary["CFBundleInfoDictionaryVersion"] = "6.0"
dictionary["CFBundleExecutable"] = "$(EXECUTABLE_NAME)"
dictionary["CFBundleName"] = "$(PRODUCT_NAME)"
dictionary["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)"
dictionary["CFBundleShortVersionString"] = "1.0"
dictionary["CFBundleVersion"] = "1"
return dictionary
}()
public func generateProperties(target: Target) -> [String: Any] {
var targetInfoPlist = defaultInfoPlist
switch target.type {
case .uiTestBundle,
.unitTestBundle:
targetInfoPlist["CFBundlePackageType"] = "BNDL"
case .application,
.watch2App:
targetInfoPlist["CFBundlePackageType"] = "APPL"
case .framework:
targetInfoPlist["CFBundlePackageType"] = "FMWK"
case .bundle:
targetInfoPlist["CFBundlePackageType"] = "BNDL"
case .xpcService,
.appExtension:
targetInfoPlist["CFBundlePackageType"] = "XPC!"
default: break
}
return targetInfoPlist
}
}
File diff suppressed because it is too large Load Diff
+13 -228
View File
@@ -2,7 +2,7 @@ import Foundation
import JSONUtilities
import PathKit
import ProjectSpec
import xcproj
import xcodeproj
import Yams
public class ProjectGenerator {
@@ -13,21 +13,21 @@ public class ProjectGenerator {
self.project = project
}
var defaultDebugConfig: Config {
return project.configs.first { $0.type == .debug }!
}
var defaultReleaseConfig: Config {
return project.configs.first { $0.type == .release }!
}
public func generateXcodeProject() throws -> XcodeProj {
try project.validate()
// generate PBXProj
let pbxProjGenerator = PBXProjGenerator(project: project)
let pbxProject = try pbxProjGenerator.generate()
let pbxProj = try pbxProjGenerator.generate()
// generate Schemes
let schemeGenerator = SchemeGenerator(project: project, pbxProj: pbxProj)
let schemes = try schemeGenerator.generateSchemes()
// generate Workspace
let workspace = try generateWorkspace()
let sharedData = try generateSharedData(pbxProject: pbxProject)
return XcodeProj(workspace: workspace, pbxproj: pbxProject, sharedData: sharedData)
let sharedData = XCSharedData(schemes: schemes)
return XcodeProj(workspace: workspace, pbxproj: pbxProj, sharedData: sharedData)
}
func generateWorkspace() throws -> XCWorkspace {
@@ -35,219 +35,4 @@ public class ProjectGenerator {
let workspaceData = XCWorkspaceData(children: [dataElement])
return XCWorkspace(data: workspaceData)
}
func generateScheme(_ scheme: Scheme, pbxProject: PBXProj) throws -> XCScheme {
func getBuildEntry(_ buildTarget: Scheme.BuildTarget) -> XCScheme.BuildAction.Entry {
guard let targetReference = pbxProject.objects.targets(named: buildTarget.target).first else {
fatalError("Unable to find target named \"\(buildTarget.target)\" in \"PBXProj.objects.targets\"")
}
guard let buildableName =
project.getTarget(buildTarget.target)?.filename ??
project.getAggregateTarget(buildTarget.target)?.name else {
fatalError("Unable to determinate \"buildableName\" for build target: \(buildTarget.target)")
}
let buildableReference = XCScheme.BuildableReference(
referencedContainer: "container:\(project.name).xcodeproj",
blueprintIdentifier: targetReference.reference,
buildableName: buildableName,
blueprintName: buildTarget.target
)
return XCScheme.BuildAction.Entry(buildableReference: buildableReference, buildFor: buildTarget.buildTypes)
}
let testTargetNames = scheme.test?.targets ?? []
let testBuildTargets = testTargetNames.map {
Scheme.BuildTarget(target: $0, buildTypes: BuildType.testOnly)
}
let testBuildTargetEntries = testBuildTargets.map(getBuildEntry)
let buildActionEntries: [XCScheme.BuildAction.Entry] = scheme.build.targets.map(getBuildEntry)
func getExecutionAction(_ action: Scheme.ExecutionAction) -> XCScheme.ExecutionAction {
// ExecutionActions can require the use of build settings. Xcode allows the settings to come from a build or test target.
let environmentBuildable = action.settingsTarget.flatMap { settingsTarget in
return (buildActionEntries + testBuildTargetEntries)
.first { settingsTarget == $0.buildableReference.blueprintName }?
.buildableReference
}
return XCScheme.ExecutionAction(scriptText: action.script, title: action.name, environmentBuildable: environmentBuildable)
}
let target = project.getTarget(scheme.build.targets.first!.target)
let shouldExecuteOnLaunch = target?.type.isExecutable == true
let buildableReference = buildActionEntries.first!.buildableReference
let productRunable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference)
let buildAction = XCScheme.BuildAction(
buildActionEntries: buildActionEntries,
preActions: scheme.build.preActions.map(getExecutionAction),
postActions: scheme.build.postActions.map(getExecutionAction),
parallelizeBuild: scheme.build.parallelizeBuild,
buildImplicitDependencies: scheme.build.buildImplicitDependencies
)
let testables = testBuildTargetEntries.map {
XCScheme.TestableReference(skipped: false, buildableReference: $0.buildableReference)
}
let testCommandLineArgs = scheme.test.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let launchCommandLineArgs = scheme.run.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let profileCommandLineArgs = scheme.profile.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let testVariables = scheme.test.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let launchVariables = scheme.run.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let profileVariables = scheme.profile.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let testAction = XCScheme.TestAction(
buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name,
macroExpansion: buildableReference,
testables: testables,
preActions: scheme.test?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.test?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.test?.shouldUseLaunchSchemeArgsEnv ?? true,
codeCoverageEnabled: scheme.test?.gatherCoverageData ?? false,
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables
)
let launchAction = XCScheme.LaunchAction(
buildableProductRunnable: shouldExecuteOnLaunch ? productRunable : nil,
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
macroExpansion: shouldExecuteOnLaunch ? nil : buildableReference,
commandlineArguments: launchCommandLineArgs,
environmentVariables: launchVariables
)
let profileAction = XCScheme.ProfileAction(
buildableProductRunnable: productRunable,
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.profile?.shouldUseLaunchSchemeArgsEnv ?? true,
commandlineArguments: profileCommandLineArgs,
environmentVariables: profileVariables
)
let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: scheme.analyze?.config ?? defaultDebugConfig.name)
let archiveAction = XCScheme.ArchiveAction(
buildConfiguration: scheme.archive?.config ?? defaultReleaseConfig.name,
revealArchiveInOrganizer: scheme.archive?.revealArchiveInOrganizer ?? true,
customArchiveName: scheme.archive?.customArchiveName,
preActions: scheme.archive?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.archive?.postActions.map(getExecutionAction) ?? []
)
return XCScheme(
name: scheme.name,
lastUpgradeVersion: project.xcodeVersion,
version: project.schemeVersion,
buildAction: buildAction,
testAction: testAction,
launchAction: launchAction,
profileAction: profileAction,
analyzeAction: analyzeAction,
archiveAction: archiveAction
)
}
func generateSharedData(pbxProject: PBXProj) throws -> XCSharedData {
var xcschemes: [XCScheme] = []
for scheme in project.schemes {
let xcscheme = try generateScheme(scheme, pbxProject: pbxProject)
xcschemes.append(xcscheme)
}
for target in project.targets {
if let targetScheme = target.scheme {
if targetScheme.configVariants.isEmpty {
let schemeName = target.name
let debugConfig = project.configs.first { $0.type == .debug }!
let releaseConfig = project.configs.first { $0.type == .release }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme, pbxProject: pbxProject)
xcschemes.append(xcscheme)
} else {
for configVariant in targetScheme.configVariants {
let schemeName = "\(target.name) \(configVariant)"
let debugConfig = project.configs
.first { $0.type == .debug && $0.name.contains(configVariant) }!
let releaseConfig = project.configs
.first { $0.type == .release && $0.name.contains(configVariant) }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme, pbxProject: pbxProject)
xcschemes.append(xcscheme)
}
}
}
}
return XCSharedData(schemes: xcschemes)
}
}
extension Scheme {
public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(targets: [Scheme.BuildTarget(target: target.name)]),
run: .init(
config: debugConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
test: .init(
config: debugConfig,
gatherCoverageData: targetScheme.gatherCoverageData,
commandLineArguments: targetScheme.commandLineArguments,
targets: targetScheme.testTargets,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
profile: .init(
config: releaseConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
analyze: .init(
config: debugConfig
),
archive: .init(
config: releaseConfig,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions
)
)
}
}
+242
View File
@@ -0,0 +1,242 @@
import Foundation
import ProjectSpec
import xcodeproj
public class SchemeGenerator {
let project: Project
let pbxProj: PBXProj
var defaultDebugConfig: Config {
return project.configs.first { $0.type == .debug }!
}
var defaultReleaseConfig: Config {
return project.configs.first { $0.type == .release }!
}
public init(project: Project, pbxProj: PBXProj) {
self.project = project
self.pbxProj = pbxProj
}
public func generateSchemes() throws -> [XCScheme] {
var xcschemes: [XCScheme] = []
for scheme in project.schemes {
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
}
for target in project.targets {
if let targetScheme = target.scheme {
if targetScheme.configVariants.isEmpty {
let schemeName = target.name
let debugConfig = project.configs.first { $0.type == .debug }!
let releaseConfig = project.configs.first { $0.type == .release }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
} else {
for configVariant in targetScheme.configVariants {
let schemeName = "\(target.name) \(configVariant)"
let debugConfig = project.configs
.first { $0.type == .debug && $0.name.contains(configVariant) }!
let releaseConfig = project.configs
.first { $0.type == .release && $0.name.contains(configVariant) }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
}
}
}
}
return xcschemes
}
public func generateScheme(_ scheme: Scheme) throws -> XCScheme {
func getBuildEntry(_ buildTarget: Scheme.BuildTarget) -> XCScheme.BuildAction.Entry {
guard let pbxTarget = pbxProj.targets(named: buildTarget.target).first else {
fatalError("Unable to find target named \"\(buildTarget.target)\" in \"PBXProj.targets\"")
}
guard let buildableName =
project.getTarget(buildTarget.target)?.filename ??
project.getAggregateTarget(buildTarget.target)?.name else {
fatalError("Unable to determinate \"buildableName\" for build target: \(buildTarget.target)")
}
let buildableReference = XCScheme.BuildableReference(
referencedContainer: "container:\(project.name).xcodeproj",
blueprint: pbxTarget,
buildableName: buildableName,
blueprintName: buildTarget.target
)
return XCScheme.BuildAction.Entry(buildableReference: buildableReference, buildFor: buildTarget.buildTypes)
}
let testTargets = scheme.test?.targets ?? []
let testBuildTargets = testTargets.map {
Scheme.BuildTarget(target: $0.name, buildTypes: BuildType.testOnly)
}
let testBuildTargetEntries = testBuildTargets.map(getBuildEntry)
let buildActionEntries: [XCScheme.BuildAction.Entry] = scheme.build.targets.map(getBuildEntry)
func getExecutionAction(_ action: Scheme.ExecutionAction) -> XCScheme.ExecutionAction {
// ExecutionActions can require the use of build settings. Xcode allows the settings to come from a build or test target.
let environmentBuildable = action.settingsTarget.flatMap { settingsTarget in
return (buildActionEntries + testBuildTargetEntries)
.first { settingsTarget == $0.buildableReference.blueprintName }?
.buildableReference
}
return XCScheme.ExecutionAction(scriptText: action.script, title: action.name, environmentBuildable: environmentBuildable)
}
let target = project.getTarget(scheme.build.targets.first!.target)
let shouldExecuteOnLaunch = target?.type.isExecutable == true
let buildableReference = buildActionEntries.first!.buildableReference
let productRunable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference)
let buildAction = XCScheme.BuildAction(
buildActionEntries: buildActionEntries,
preActions: scheme.build.preActions.map(getExecutionAction),
postActions: scheme.build.postActions.map(getExecutionAction),
parallelizeBuild: scheme.build.parallelizeBuild,
buildImplicitDependencies: scheme.build.buildImplicitDependencies
)
let testables = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuilEntries in
XCScheme.TestableReference(
skipped: false,
parallelizable: testTarget.parallelizable,
randomExecutionOrdering: testTarget.randomExecutionOrder,
buildableReference: testBuilEntries.buildableReference
)
}
let testCommandLineArgs = scheme.test.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let launchCommandLineArgs = scheme.run.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let profileCommandLineArgs = scheme.profile.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let testVariables = scheme.test.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let launchVariables = scheme.run.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let profileVariables = scheme.profile.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let testAction = XCScheme.TestAction(
buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name,
macroExpansion: buildableReference,
testables: testables,
preActions: scheme.test?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.test?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.test?.shouldUseLaunchSchemeArgsEnv ?? true,
codeCoverageEnabled: scheme.test?.gatherCoverageData ?? false,
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables
)
let launchAction = XCScheme.LaunchAction(
buildableProductRunnable: shouldExecuteOnLaunch ? productRunable : nil,
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
macroExpansion: shouldExecuteOnLaunch ? nil : buildableReference,
commandlineArguments: launchCommandLineArgs,
environmentVariables: launchVariables
)
let profileAction = XCScheme.ProfileAction(
buildableProductRunnable: productRunable,
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.profile?.shouldUseLaunchSchemeArgsEnv ?? true,
commandlineArguments: profileCommandLineArgs,
environmentVariables: profileVariables
)
let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: scheme.analyze?.config ?? defaultDebugConfig.name)
let archiveAction = XCScheme.ArchiveAction(
buildConfiguration: scheme.archive?.config ?? defaultReleaseConfig.name,
revealArchiveInOrganizer: scheme.archive?.revealArchiveInOrganizer ?? true,
customArchiveName: scheme.archive?.customArchiveName,
preActions: scheme.archive?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.archive?.postActions.map(getExecutionAction) ?? []
)
return XCScheme(
name: scheme.name,
lastUpgradeVersion: project.xcodeVersion,
version: project.schemeVersion,
buildAction: buildAction,
testAction: testAction,
launchAction: launchAction,
profileAction: profileAction,
analyzeAction: analyzeAction,
archiveAction: archiveAction
)
}
}
extension Scheme {
public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(targets: [Scheme.BuildTarget(target: target.name)]),
run: .init(
config: debugConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
test: .init(
config: debugConfig,
gatherCoverageData: targetScheme.gatherCoverageData,
commandLineArguments: targetScheme.commandLineArguments,
targets: targetScheme.testTargets,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
profile: .init(
config: releaseConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
analyze: .init(
config: debugConfig
),
archive: .init(
config: releaseConfig,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions
)
)
}
}
+51 -30
View File
@@ -2,7 +2,7 @@ import Foundation
import JSONUtilities
import PathKit
import ProjectSpec
import xcproj
import xcodeproj
import Yams
extension Project {
@@ -10,6 +10,15 @@ extension Project {
public func getProjectBuildSettings(config: Config) -> BuildSettings {
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 type = config.type, options.settingPresets.applyProject {
buildSettings += SettingsPresetFile.base.getBuildSettings()
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
@@ -81,31 +90,27 @@ extension Project {
}
// combines all levels of a target's settings: target, target config, project, project config
public func getCombinedBuildSettings(basePath: Path, target: ProjectTarget, config: Config, includeProject: Bool = true) -> BuildSettings {
var buildSettings: BuildSettings = [:]
if includeProject {
if let configFilePath = configFiles[config.name] {
buildSettings += loadConfigFileBuildSettings(path: configFilePath)
}
buildSettings += getProjectBuildSettings(config: config)
public func getCombinedBuildSetting(_ setting: String, target: ProjectTarget, config: Config) -> Any? {
if let target = target as? Target,
let value = getTargetBuildSettings(target: target, config: config)[setting] {
return value
}
if let configFilePath = target.configFiles[config.name] {
buildSettings += loadConfigFileBuildSettings(path: configFilePath)
if let configFilePath = target.configFiles[config.name],
let value = loadConfigFileBuildSettings(path: configFilePath)?[setting] {
return value
}
if let target = target as? Target {
buildSettings += getTargetBuildSettings(target: target, config: config)
if let value = getProjectBuildSettings(config: config)[setting] {
return value
}
return buildSettings
if let configFilePath = configFiles[config.name],
let value = loadConfigFileBuildSettings(path: configFilePath)?[setting] {
return value
}
return nil
}
public func targetHasBuildSetting(_ setting: String, basePath: Path, target: Target, config: Config, includeProject: Bool = true) -> Bool {
let buildSettings = getCombinedBuildSettings(
basePath: basePath,
target: target,
config: config,
includeProject: includeProject
)
return buildSettings[setting] != nil
public func targetHasBuildSetting(_ setting: String, target: Target, config: Config) -> Bool {
return getCombinedBuildSetting(setting, target: target, config: config) != nil
}
/// Removes values from build settings if they are defined in an xcconfig file
@@ -126,28 +131,43 @@ extension Project {
/// Returns cached build settings from a config file
private func loadConfigFileBuildSettings(path: String) -> BuildSettings? {
let configFilePath = basePath + path
if let settings = configFileSettings[configFilePath.string] {
return settings
if let cached = configFileSettings[configFilePath.string] {
return cached.value
} else {
guard let configFile = try? XCConfig(path: configFilePath) else { return nil }
guard let configFile = try? XCConfig(path: configFilePath) else {
configFileSettings[configFilePath.string] = .nothing
return nil
}
let settings = configFile.flattenedBuildSettings()
configFileSettings[configFilePath.string] = settings
configFileSettings[configFilePath.string] = .cached(settings)
return settings
}
}
}
private enum Cached<T> {
case cached(T)
case nothing
var value: T? {
switch self {
case let .cached(value): return value
case .nothing: return nil
}
}
}
// cached flattened xcconfig file settings
private var configFileSettings: [String: BuildSettings] = [:]
private var configFileSettings: [String: Cached<BuildSettings>] = [:]
// cached setting preset settings
private var settingPresetSettings: [String: BuildSettings] = [:]
private var settingPresetSettings: [String: Cached<BuildSettings>] = [:]
extension SettingsPresetFile {
public func getBuildSettings() -> BuildSettings? {
if let group = settingPresetSettings[path] {
return group
if let cached = settingPresetSettings[path] {
return cached.value
}
let bundlePath = Path(Bundle.main.bundlePath)
let relativePath = Path("SettingPresets/\(path).yml")
@@ -171,6 +191,7 @@ extension SettingsPresetFile {
case .product, .productPlatform:
break
}
settingPresetSettings[path] = .nothing
return nil
}
@@ -178,7 +199,7 @@ extension SettingsPresetFile {
print("Error parsing \"\(name)\" settings")
return nil
}
settingPresetSettings[path] = buildSettings
settingPresetSettings[path] = .cached(buildSettings)
return buildSettings
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import Foundation
import ProjectSpec
import xcproj
import xcodeproj
public enum SettingsPresetFile {
case config(ConfigType)
+60 -66
View File
@@ -1,45 +1,41 @@
import Foundation
import PathKit
import ProjectSpec
import xcproj
import xcodeproj
struct SourceFile {
let path: Path
let fileReference: String
let fileReference: PBXFileElement
let buildFile: PBXBuildFile
let buildPhase: TargetSource.BuildPhase?
}
class SourceGenerator {
var rootGroups: Set<String> = []
private var fileReferencesByPath: [String: String] = [:]
private var groupsByPath: [Path: ObjectReference<PBXGroup>] = [:]
private var variantGroupsByPath: [Path: ObjectReference<PBXVariantGroup>] = [:]
var rootGroups: Set<PBXFileElement> = []
private var fileReferencesByPath: [String: PBXFileElement] = [:]
private var groupsByPath: [Path: PBXGroup] = [:]
private var variantGroupsByPath: [Path: PBXVariantGroup] = [:]
private let project: Project
var addObjectClosure: (String, PBXObject) -> String
let pbxProj: PBXProj
var targetSourceExcludePaths: Set<Path> = []
var defaultExcludedFiles = [
".DS_Store",
]
var targetName: String = ""
private(set) var knownRegions: Set<String> = []
init(project: Project, addObjectClosure: @escaping (String, PBXObject) -> String) {
init(project: Project, pbxProj: PBXProj) {
self.project = project
self.addObjectClosure = addObjectClosure
self.pbxProj = pbxProj
}
func addObject(id: String, _ object: PBXObject) -> String {
return addObjectClosure(id, object)
}
func createObject<T: PBXObject>(id: String, _ object: T) -> ObjectReference<T> {
let reference = addObject(id: id, object)
return ObjectReference(reference: reference, object: object)
func addObject<T: PBXObject>(_ object: T, context: String? = nil) -> T {
pbxProj.add(object: object)
object.context = context
return object
}
func getAllSourceFiles(targetType: PBXProductType, sources: [TargetSource]) throws -> [SourceFile] {
@@ -91,7 +87,7 @@ class SourceGenerator {
settings["COMPILER_FLAGS"] = targetSource.compilerFlags.joined(separator: " ")
}
let buildFile = PBXBuildFile(fileRef: fileReference, settings: settings.isEmpty ? nil : settings)
let buildFile = PBXBuildFile(file: fileReference, settings: settings.isEmpty ? nil : settings)
return SourceFile(
path: path,
fileReference: fileReference,
@@ -100,7 +96,7 @@ class SourceGenerator {
)
}
func getContainedFileReference(path: Path) -> String {
func getContainedFileReference(path: Path) -> PBXFileElement {
let createIntermediateGroups = project.options.createIntermediateGroups
let parentPath = path.parent()
@@ -113,12 +109,12 @@ class SourceGenerator {
)
if createIntermediateGroups {
createIntermediaGroups(for: parentGroup.reference, at: parentPath)
createIntermediaGroups(for: parentGroup, at: parentPath)
}
return fileReference
}
func getFileReference(path: Path, inPath: Path, name: String? = nil, sourceTree: PBXSourceTree = .group, lastKnownFileType: String? = nil) -> String {
func getFileReference(path: Path, inPath: Path, name: String? = nil, sourceTree: PBXSourceTree = .group, lastKnownFileType: String? = nil) -> PBXFileElement {
let fileReferenceKey = path.string.lowercased()
if let fileReference = fileReferencesByPath[fileReferenceKey] {
return fileReference
@@ -128,7 +124,7 @@ class SourceGenerator {
if fileReferencePath.string == fileReferenceName {
fileReferenceName = nil
}
let lastKnownFileType = lastKnownFileType ?? PBXFileReference.fileType(path: path)
let lastKnownFileType = lastKnownFileType ?? Xcode.fileType(path: path)
if path.extension == "xcdatamodeld" {
let versionedModels = (try? path.children()) ?? []
@@ -138,10 +134,9 @@ class SourceGenerator {
.filter { $0.extension == "xcdatamodel" }
.sorted { $0.string.localizedStandardCompare($1.string) == .orderedAscending }
let modelFileReference =
let modelFileReferences =
sortedPaths.map { path in
createObject(
id: path.byRemovingBase(path: project.basePath).string,
addObject(
PBXFileReference(
sourceTree: .group,
lastKnownFileType: "wrapper.xcdatamodel",
@@ -152,23 +147,22 @@ class SourceGenerator {
// If no current version path is found we fall back to alphabetical
// order by taking the last item in the sortedPaths array
let currentVersionPath = findCurrentCoreDataModelVersionPath(using: versionedModels) ?? sortedPaths.last
let currentVersion: ObjectReference<PBXFileReference>? = {
let currentVersion: PBXFileReference? = {
guard let indexOf = sortedPaths.index(where: { $0 == currentVersionPath }) else { return nil }
return modelFileReference[indexOf]
return modelFileReferences[indexOf]
}()
let versionGroup = addObject(id: fileReferencePath.string, XCVersionGroup(
currentVersion: currentVersion?.reference,
let versionGroup = addObject(XCVersionGroup(
currentVersion: currentVersion,
path: fileReferencePath.string,
sourceTree: sourceTree,
versionGroupType: "wrapper.xcdatamodel",
children: modelFileReference.map { $0.reference }
children: modelFileReferences
))
fileReferencesByPath[fileReferenceKey] = versionGroup
return versionGroup
} else {
// For all extensions other than `xcdatamodeld`
let fileReference = createObject(
id: path.byRemovingBase(path: project.basePath).string,
let fileReference = addObject(
PBXFileReference(
sourceTree: sourceTree,
name: fileReferenceName,
@@ -176,8 +170,8 @@ class SourceGenerator {
path: fileReferencePath.string
)
)
fileReferencesByPath[fileReferenceKey] = fileReference.reference
return fileReference.reference
fileReferencesByPath[fileReferenceKey] = fileReference
return fileReference
}
}
}
@@ -209,12 +203,16 @@ class SourceGenerator {
/// Create a group or return an existing one at the path.
/// Any merged children are added to a new group or merged into an existing one.
private func getGroup(path: Path, name: String? = nil, mergingChildren children: [String], createIntermediateGroups: Bool, isBaseGroup: Bool) -> ObjectReference<PBXGroup> {
let groupReference: ObjectReference<PBXGroup>
private func getGroup(path: Path, name: String? = nil, mergingChildren children: [PBXFileElement], createIntermediateGroups: Bool, isBaseGroup: Bool) -> PBXGroup {
let groupReference: PBXGroup
if let cachedGroup = groupsByPath[path] {
// only add the children that aren't already in the cachedGroup
cachedGroup.object.children = Array(Set(cachedGroup.object.children + children))
for child in children {
// only add the children that aren't already in the cachedGroup
if !cachedGroup.children.contains(child) {
cachedGroup.children.append(child)
}
}
groupReference = cachedGroup
} else {
@@ -237,28 +235,27 @@ class SourceGenerator {
name: groupName != groupPath ? groupName : nil,
path: groupPath
)
groupReference = createObject(id: path.byRemovingBase(path: project.basePath).string, group)
groupReference = addObject(group)
groupsByPath[path] = groupReference
if isTopLevelGroup {
rootGroups.insert(groupReference.reference)
rootGroups.insert(groupReference)
}
}
return groupReference
}
/// Creates a variant group or returns an existing one at the path
private func getVariantGroup(path: Path, inPath: Path) -> ObjectReference<PBXVariantGroup> {
let variantGroup: ObjectReference<PBXVariantGroup>
private func getVariantGroup(path: Path, inPath: Path) -> PBXVariantGroup {
let variantGroup: PBXVariantGroup
if let cachedGroup = variantGroupsByPath[path] {
variantGroup = cachedGroup
} else {
let group = PBXVariantGroup(
children: [],
sourceTree: .group,
name: path.lastComponent
)
variantGroup = createObject(id: path.byRemovingBase(path: project.basePath).string, group)
variantGroup = addObject(group)
variantGroupsByPath[path] = variantGroup
}
return variantGroup
@@ -307,27 +304,24 @@ class SourceGenerator {
/// creates all the source files and groups they belong to for a given targetSource
private func getGroupSources(targetType: PBXProductType, targetSource: TargetSource, path: Path, isBaseGroup: Bool)
throws -> (sourceFiles: [SourceFile], groups: [ObjectReference<PBXGroup>]) {
throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
let children = try getSourceChildren(targetSource: targetSource, dirPath: path)
let directories = children
.filter { $0.isDirectory && $0.extension == nil && $0.extension != "lproj" }
.sorted { $0.lastComponent < $1.lastComponent }
let filePaths = children
.filter { $0.isFile || $0.extension != nil && $0.extension != "lproj" }
.sorted { $0.lastComponent < $1.lastComponent }
let localisedDirectories = children
.filter { $0.extension == "lproj" }
.sorted { $0.lastComponent < $1.lastComponent }
var groupChildren: [String] = filePaths.map { getFileReference(path: $0, inPath: path) }
var groupChildren: [PBXFileElement] = filePaths.map { getFileReference(path: $0, inPath: path) }
var allSourceFiles: [SourceFile] = filePaths.map {
generateSourceFile(targetType: targetType, targetSource: targetSource, path: $0)
}
var groups: [ObjectReference<PBXGroup>] = []
var groups: [PBXGroup] = []
for path in directories {
let subGroups = try getGroupSources(targetType: targetType, targetSource: targetSource, path: path, isBaseGroup: false)
@@ -338,11 +332,11 @@ class SourceGenerator {
allSourceFiles += subGroups.sourceFiles
guard let first = subGroups.groups.first else {
guard let firstGroup = subGroups.groups.first else {
continue
}
groupChildren.append(first.reference)
groupChildren.append(firstGroup)
groups += subGroups.groups
}
@@ -365,13 +359,13 @@ class SourceGenerator {
.filter(isIncludedPath)
.sorted() {
let variantGroup = getVariantGroup(path: filePath, inPath: path)
groupChildren.append(variantGroup.reference)
baseLocalisationVariantGroups.append(variantGroup.object)
groupChildren.append(variantGroup)
baseLocalisationVariantGroups.append(variantGroup)
let sourceFile = SourceFile(
path: filePath,
fileReference: variantGroup.reference,
buildFile: PBXBuildFile(fileRef: variantGroup.reference),
fileReference: variantGroup,
buildFile: PBXBuildFile(file: variantGroup),
buildPhase: .resources
)
allSourceFiles.append(sourceFile)
@@ -409,7 +403,7 @@ class SourceGenerator {
let sourceFile = SourceFile(
path: filePath,
fileReference: fileReference,
buildFile: PBXBuildFile(fileRef: fileReference),
buildFile: PBXBuildFile(file: fileReference),
buildPhase: .resources
)
allSourceFiles.append(sourceFile)
@@ -425,7 +419,7 @@ class SourceGenerator {
isBaseGroup: isBaseGroup
)
if project.options.createIntermediateGroups {
createIntermediaGroups(for: group.reference, at: path)
createIntermediaGroups(for: group, at: path)
}
groups.insert(group, at: 0)
@@ -442,7 +436,7 @@ class SourceGenerator {
let createIntermediateGroups = project.options.createIntermediateGroups
var sourceFiles: [SourceFile] = []
let sourceReference: String
let sourceReference: PBXFileElement
var sourcePath = path
switch type {
case .folder:
@@ -483,7 +477,7 @@ class SourceGenerator {
} else {
let parentGroup = getGroup(path: parentPath, mergingChildren: [fileReference], createIntermediateGroups: createIntermediateGroups, isBaseGroup: true)
sourcePath = parentPath
sourceReference = parentGroup.reference
sourceReference = parentGroup
}
sourceFiles.append(sourceFile)
@@ -491,11 +485,11 @@ class SourceGenerator {
let (groupSourceFiles, groups) = try getGroupSources(targetType: targetType, targetSource: targetSource, path: path, isBaseGroup: true)
let group = groups.first!
if let name = targetSource.name {
group.object.name = name
group.name = name
}
sourceFiles += groupSourceFiles
sourceReference = group.reference
sourceReference = group
}
if createIntermediateGroups {
@@ -506,7 +500,7 @@ class SourceGenerator {
}
// Add groups for all parents recursively
private func createIntermediaGroups(for groupReference: String, at path: Path) {
private func createIntermediaGroups(for fileElement: PBXFileElement, at path: Path) {
let parentPath = path.parent()
guard parentPath != project.basePath && path.string.contains(project.basePath.string) else {
@@ -515,10 +509,10 @@ class SourceGenerator {
}
let hasParentGroup = groupsByPath[parentPath] != nil
let parentGroup = getGroup(path: parentPath, mergingChildren: [groupReference], createIntermediateGroups: true, isBaseGroup: false)
let parentGroup = getGroup(path: parentPath, mergingChildren: [fileElement], createIntermediateGroups: true, isBaseGroup: false)
if !hasParentGroup {
createIntermediaGroups(for: parentGroup.reference, at: parentPath)
createIntermediaGroups(for: parentGroup, at: parentPath)
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ import ProjectSpec
extension Project {
var xcodeVersion: String {
return XCodeVersion.parse(options.xcodeVersion ?? "9.3")
return XCodeVersion.parse(options.xcodeVersion ?? "10.0")
}
var schemeVersion: String {
+23 -8
View File
@@ -1,5 +1,6 @@
import Foundation
import xcproj
import PathKit
import xcodeproj
extension PBXFileElement {
@@ -11,8 +12,8 @@ extension PBXFileElement {
extension PBXProj {
public func printGroups() -> String {
guard let project = objects.projects.first?.value,
let mainGroup = objects.groups.getReference(project.mainGroup) else {
guard let project = projects.first,
let mainGroup = project.mainGroup else {
return ""
}
return printGroup(group: mainGroup)
@@ -20,17 +21,31 @@ extension PBXProj {
public func printGroup(group: PBXGroup) -> String {
var string = group.nameOrPath
for reference in group.children {
if let group = objects.groups.getReference(reference) {
for child in group.children {
if let group = child as? PBXGroup {
string += "\n 📁 " + printGroup(group: group).replacingOccurrences(of: "\n ", with: "\n ")
} else if let fileReference = objects.fileReferences.getReference(reference) {
} else if let fileReference = child as? PBXFileReference {
string += "\n 📄 " + fileReference.nameOrPath
} else if let variantGroup = objects.variantGroups.getReference(reference) {
} else if let variantGroup = child as? PBXVariantGroup {
string += "\n 🌎 " + variantGroup.nameOrPath
} else if let versionGroup = objects.versionGroups.getReference(reference) {
} else if let versionGroup = child as? XCVersionGroup {
string += "\n 🔢 " + versionGroup.nameOrPath
}
}
return string
}
}
extension Dictionary {
public var valueArray: Array<Value> {
return Array(values)
}
}
extension Xcode {
public static func fileType(path: Path) -> String? {
return path.extension.flatMap { Xcode.filetype(extension: $0) }
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<string>group.com.app</string>
</dict>
</plist>
@@ -6,7 +6,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
_ = FrameworkStruct()
// Standalone files added to project by path-to-file.
@@ -1,10 +1,11 @@
import Contacts
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
_ = CNContact()
}
override func didReceiveMemoryWarning() {
@@ -0,0 +1,13 @@
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
@@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,717 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="dfg" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="dfg" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About dfg" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide dfg" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit dfg" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
<connections>
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="dfg Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController id="B8D-0N-5wS" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<connections>
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
</connections>
</window>
<connections>
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
</connections>
</windowController>
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="250"/>
</scene>
<!--View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="655"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CustomSetting</key>
<string>$CUSTOM_SETTING</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
@@ -0,0 +1,16 @@
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
@@ -1,12 +1,12 @@
import Alamofire
import Foundation
import Result
import WatchKit
class InterfaceController: WKInterfaceController {
override func awake(withContext context: Any?) {
super.awake(withContext: context)
let method = HTTPMethod.get
_ = Result<String, NoError>.success("value")
// Configure interface objects here.
}
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
+1 -1
View File
@@ -1 +1 @@
github "Alamofire/Alamofire"
github "antitypical/Result"
+1 -1
View File
@@ -1 +1 @@
github "Alamofire/Alamofire" "4.7.2"
github "antitypical/Result" "4.0.0"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Production Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_D4BAEEEC88124103C8DFF41FCE206DCE"
BuildableName = "App_iOS_UITests.xctest"
BlueprintName = "App_iOS_UITests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_193BAF154270D1C21E269EDF2A1BD3F6"
BuildableName = "App_iOS_Tests.xctest"
BlueprintName = "App_iOS_Tests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Production Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Production Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Production Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Production Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_825232110500"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -33,7 +33,7 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_783122899910"
BlueprintIdentifier = "NT_193BAF154270D1C21E269EDF2A1BD3F6"
BuildableName = "App_iOS_Tests.xctest"
BlueprintName = "App_iOS_Tests"
ReferencedContainer = "container:Project.xcodeproj">
@@ -43,7 +43,7 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_123503999387"
BlueprintIdentifier = "NT_D4BAEEEC88124103C8DFF41FCE206DCE"
BuildableName = "App_iOS_UITests.xctest"
BlueprintName = "App_iOS_UITests"
ReferencedContainer = "container:Project.xcodeproj">
@@ -53,21 +53,21 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_825232110500"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
@@ -86,21 +86,21 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_825232110500"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
@@ -115,21 +115,21 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_825232110500"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Staging Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "NO">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_193BAF154270D1C21E269EDF2A1BD3F6"
BuildableName = "App_iOS_Tests.xctest"
BlueprintName = "App_iOS_Tests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_D4BAEEEC88124103C8DFF41FCE206DCE"
BuildableName = "App_iOS_UITests.xctest"
BlueprintName = "App_iOS_UITests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Staging Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Staging Release"
shouldUseLaunchSchemeArgsEnv = "NO"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Staging Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Staging Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Test Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "NO">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_193BAF154270D1C21E269EDF2A1BD3F6"
BuildableName = "App_iOS_Tests.xctest"
BlueprintName = "App_iOS_Tests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_D4BAEEEC88124103C8DFF41FCE206DCE"
BuildableName = "App_iOS_UITests.xctest"
BlueprintName = "App_iOS_UITests"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Test Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Test Release"
shouldUseLaunchSchemeArgsEnv = "NO"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_BEB0891E36797FE2214A0A9D516D408D"
BuildableName = "App_iOS.app"
BlueprintName = "App_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "MyDisabledArgument"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "MyEnabledArgument"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Test Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Test Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_324671077936"
BlueprintIdentifier = "NT_38A9FE87056942A2746E0FF025B52A91"
BuildableName = "App_watchOS.app"
BlueprintName = "App_watchOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -32,7 +32,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_324671077936"
BlueprintIdentifier = "NT_38A9FE87056942A2746E0FF025B52A91"
BuildableName = "App_watchOS.app"
BlueprintName = "App_watchOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -57,7 +57,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_324671077936"
BlueprintIdentifier = "NT_38A9FE87056942A2746E0FF025B52A91"
BuildableName = "App_watchOS.app"
BlueprintName = "App_watchOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -78,7 +78,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_324671077936"
BlueprintIdentifier = "NT_38A9FE87056942A2746E0FF025B52A91"
BuildableName = "App_watchOS.app"
BlueprintName = "App_watchOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
@@ -14,7 +14,7 @@
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_472296042419"
BlueprintIdentifier = "NT_7D108AE86BED8C9CCF52C2646FA4C5DE"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -32,7 +32,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_472296042419"
BlueprintIdentifier = "NT_7D108AE86BED8C9CCF52C2646FA4C5DE"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -44,18 +44,21 @@
buildConfiguration = "Production Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_472296042419"
BlueprintIdentifier = "NT_7D108AE86BED8C9CCF52C2646FA4C5DE"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
@@ -72,7 +75,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_472296042419"
BlueprintIdentifier = "NT_7D108AE86BED8C9CCF52C2646FA4C5DE"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -101,7 +104,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_472296042419"
BlueprintIdentifier = "NT_7D108AE86BED8C9CCF52C2646FA4C5DE"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_935153865209"
BlueprintIdentifier = "NT_6BD068FAAC6AA35C090C48147B94EC6E"
BuildableName = "iMessageApp.app"
BlueprintName = "iMessageApp"
ReferencedContainer = "container:Project.xcodeproj">
@@ -32,7 +32,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_935153865209"
BlueprintIdentifier = "NT_6BD068FAAC6AA35C090C48147B94EC6E"
BuildableName = "iMessageApp.app"
BlueprintName = "iMessageApp"
ReferencedContainer = "container:Project.xcodeproj">
@@ -57,7 +57,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_935153865209"
BlueprintIdentifier = "NT_6BD068FAAC6AA35C090C48147B94EC6E"
BuildableName = "iMessageApp.app"
BlueprintName = "iMessageApp"
ReferencedContainer = "container:Project.xcodeproj">
@@ -78,7 +78,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NT_935153865209"
BlueprintIdentifier = "NT_6BD068FAAC6AA35C090C48147B94EC6E"
BuildableName = "iMessageApp.app"
BlueprintName = "iMessageApp"
ReferencedContainer = "container:Project.xcodeproj">
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
set -e
carthage bootstrap --cache-builds
echo "
Building iOS app"
xcodebuild -project Project.xcodeproj -scheme "App_iOS Test" -configuration "Test Debug" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty
echo "
Building macOS app"
xcodebuild -project Project.xcodeproj -scheme "App_macOS" -configuration "Test Debug" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty
+51 -6
View File
@@ -26,18 +26,33 @@ targets:
App_macOS:
type: application
platform: macOS
info:
path: App_macOS/Info.plist
properties:
LSMinimumSystemVersion: $(MACOSX_DEPLOYMENT_TARGET)
NSMainStoryboardFile: Main
NSPrincipalClass: NSApplication
CFBundleIconFile: ""
CustomSetting: $CUSTOM_SETTING
attributes:
ProvisioningStyle: Automatic
sources:
- path: App_macOS
- path: StandaloneFiles/Standalone.swift
- path: Vendor/SomeXPCService.xpc
dependencies:
- target: Framework_macOS
- target: XPC Service
- sdk: Contacts.framework
- sdk: libc++.tbd
App_iOS:
type: application
platform: iOS
entitlements:
path: App_iOS/App.entitlements
properties:
com.apple.security.application-groups: group.com.app
attributes:
ProvisioningStyle: Automatic
sources:
@@ -70,16 +85,23 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: com.project.app
dependencies:
- target: Framework_iOS
- target: StaticLibrary_ObjC
- carthage: Alamofire
- target: StaticLibrary_ObjC_iOS
- carthage: Result
- target: Framework2_iOS
weak: true
- target: App_watchOS
- target: iMessageApp
- framework: Vendor/SomeFramework.framework
- sdk: Contacts.framework
scheme:
testTargets:
- App_iOS_Tests
- App_iOS_UITests
gatherCoverageData: true
configVariants:
- Test
- Staging
- Production
commandLineArguments:
MyEnabledArgument: true
MyDisabledArgument: false
@@ -111,7 +133,8 @@ targets:
settings:
PRODUCT_BUNDLE_IDENTIFIER: com.project.app.watch.extension
dependencies:
- carthage: Alamofire
- carthage: Result
link: false
iMessageApp:
type: application.messages
@@ -130,7 +153,7 @@ targets:
StaticLibrary_ObjC:
type: library.static
platform: iOS
platform: [iOS, tvOS, watchOS, macOS]
sources: StaticLibrary_ObjC
StaticLibrary_Swift:
@@ -153,8 +176,17 @@ targets:
- name: MyScript
path: scripts/script.sh
dependencies:
- carthage: Alamofire
- target: StaticLibrary_ObjC
- carthage: Result
- target: StaticLibrary_ObjC_$platform
Framework2:
type: framework
platform: [iOS, tvOS, watchOS, macOS]
sources:
- path: Framework
excludes:
- "*.xcodeproj"
App_iOS_Tests:
type: bundle.unit-test
platform: iOS
@@ -188,6 +220,19 @@ schemes:
commandLineArguments:
argument: YES
argument.with.dot: YES
test:
gatherCoverageData: true
App_Scheme:
build:
targets:
App_iOS: all
test:
gatherCoverageData: true
targets:
- App_iOS_UITests
- name: App_iOS_Tests
parallelizable: true
randomExecutionOrder: true
targetTemplates:
MyTemplate:
scheme: {}
@@ -0,0 +1,60 @@
import Foundation
import PathKit
import ProjectSpec
import XcodeGenKit
import xcodeproj
import XCTest
class GeneratedPerformanceTests: XCTestCase {
let basePath = Path.temporary + "XcodeGenPeformanceTests"
func testGeneration() throws {
let project = try Project.testProject(basePath: basePath)
measure {
let generator = ProjectGenerator(project: project)
_ = try! generator.generateXcodeProject()
}
}
func testWriting() throws {
let project = try Project.testProject(basePath: basePath)
let generator = ProjectGenerator(project: project)
let xcodeProject = try generator.generateXcodeProject()
measure {
xcodeProject.pbxproj.invalidateUUIDs()
try! xcodeProject.write(path: project.defaultProjectPath)
}
}
}
let fixturePath = Path(#file).parent().parent() + "Fixtures"
class FixturePerformanceTests: XCTestCase {
let specPath = fixturePath + "TestProject/project.yml"
func testFixtureDecoding() throws {
measure {
_ = try! Project(path: specPath)
}
}
func testFixtureGeneration() throws {
let project = try Project(path: specPath)
measure {
let generator = ProjectGenerator(project: project)
_ = try! generator.generateXcodeProject()
}
}
func testFixtureWriting() throws {
let project = try Project(path: specPath)
let generator = ProjectGenerator(project: project)
let xcodeProject = try generator.generateXcodeProject()
measure {
xcodeProject.pbxproj.invalidateUUIDs()
try! xcodeProject.write(path: project.defaultProjectPath)
}
}
}
+177
View File
@@ -0,0 +1,177 @@
import Foundation
import PathKit
import ProjectSpec
import xcodeproj
extension Project {
static func testProject(basePath: Path, createSources: Bool = true) throws -> Project {
if createSources {
try? basePath.delete()
try basePath.mkpath()
}
var paths: [Path] = []
var targets: [Target] = []
let scheme = TargetScheme(
testTargets: [],
configVariants: ["Test", "Staging", "Prod"],
gatherCoverageData: true,
commandLineArguments: [
"--command": true,
"--command2": false,
],
environmentVariables: [
XCScheme.EnvironmentVariable(variable: "ENV", value: "HELLO", enabled: true),
XCScheme.EnvironmentVariable(variable: "ENV2", value: "HELLO", enabled: false),
],
preActions: [Scheme.ExecutionAction(name: "run", script: "script")],
postActions: [Scheme.ExecutionAction(name: "run", script: "script")]
)
for platform in Platform.all {
let appTarget = Target(
name: "App_\(platform)",
type: .application,
platform: platform,
sources: [TargetSource(path: "App_\(platform)")],
dependencies: [
Dependency(type: .target, reference: "Framework_\(platform)"),
Dependency(type: .target, reference: "Framework2_\(platform)"),
Dependency(type: .carthage, reference: "Alamofire"),
Dependency(type: .carthage, reference: "BrightFutures"),
],
scheme: scheme
)
targets.append(appTarget)
if createSources {
paths += try createTargetSources(path: basePath, target: appTarget)
}
let testTarget = Target(
name: "App_Test_\(platform)",
type: .unitTestBundle,
platform: platform,
sources: [TargetSource(path: "App_Test_\(platform)")],
dependencies: [
Dependency(type: .target, reference: "App_\(platform)"),
Dependency(type: .target, reference: "Framework_\(platform)"),
Dependency(type: .target, reference: "Framework2_\(platform)"),
],
scheme: scheme
)
targets.append(testTarget)
if createSources {
paths += try createTargetSources(path: basePath, target: testTarget)
}
let frameworkTarget = Target(
name: "Framework_\(platform)",
type: .framework,
platform: platform,
sources: [
TargetSource(path: "Framework_\(platform)"),
],
dependencies: [
Dependency(type: .carthage, reference: "Alamofire"),
],
scheme: scheme
)
targets.append(frameworkTarget)
if createSources {
paths += try createTargetSources(path: basePath, target: frameworkTarget)
}
let frameworkTarget2 = Target(
name: "Framework2_\(platform)",
type: .framework,
platform: platform,
sources: [TargetSource(path: "Framework2_\(platform)")],
dependencies: [
Dependency(type: .target, reference: "Framework_\(platform)"),
Dependency(type: .carthage, reference: "Alamofire"),
Dependency(type: .carthage, reference: "BrightFutures"),
],
scheme: scheme
)
targets.append(frameworkTarget2)
if createSources {
paths += try createTargetSources(path: basePath, target: frameworkTarget2)
}
}
if createSources {
let files = paths.filter { $0.isFile }
let directories = paths.filter { $0.isDirectory }
print("Generated \(files.count) files and \(directories.count) directories")
}
return Project(
basePath: basePath,
name: "Project",
configs: [
Config(name: "Debug Test", type: .debug),
Config(name: "Release Test", type: .release),
Config(name: "Debug Staging", type: .debug),
Config(name: "Release Staging", type: .release),
Config(name: "Debug Production", type: .debug),
Config(name: "Release Production", type: .release),
],
targets: targets,
aggregateTargets: [],
settings: [:],
settingGroups: [:],
schemes: [],
options: SpecOptions(),
fileGroups: [],
configFiles: [:],
attributes: [:]
)
}
fileprivate static func createTargetSources(path: Path, target: Target) throws -> [Path] {
let levels = 3
let directoriesPerLevel = 1
let swiftFilesPerDirectory = 5
let objFilesPerDirectory = 2
let resourcesPerDirectory = 2
var paths: [Path] = []
func createDirectory(_ path: Path, depth: Int = 0) throws {
try path.mkpath()
paths.append(path)
for swiftFile in 1 ... swiftFilesPerDirectory {
let file = path + "file_\(swiftFile).swift"
try file.write("")
paths.append(file)
}
for resourceFile in 1 ... resourcesPerDirectory {
let file = path + "file_\(resourceFile).png"
try file.write("")
paths.append(file)
}
for objFile in 1 ... objFilesPerDirectory {
let header = path + "file_\(objFile).h"
try header.write("")
paths.append(header)
let implementation = path + "file_\(objFile).m"
try implementation.write("")
paths.append(implementation)
}
if depth < levels - 1 {
for directory in 1 ... directoriesPerLevel {
try createDirectory(path + "directory_\(directory)", depth: depth + 1)
}
}
}
for source in target.sources {
try createDirectory(path + source.path)
}
return paths
}
}
@@ -2,7 +2,7 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
import Yams
@@ -10,6 +10,7 @@ extension Project {
func generateXcodeProject(file: String = #file, line: Int = #line) throws -> XcodeProj {
return try doThrowing(file: file, line: line) {
try validate()
let generator = ProjectGenerator(project: self)
return try generator.generateXcodeProject()
}
@@ -32,12 +33,15 @@ extension PBXProj {
let mainGroup = try getMainGroup()
func validateGroup(_ group: PBXGroup) throws {
let hasDuplicatedChildren = group.children.count != Set(group.children).count
if hasDuplicatedChildren {
throw failure("Group \"\(group.nameOrPath)\" has duplicated children:\n - \(group.children.sorted().joined(separator: "\n - "))")
// check for duplicte children
let dictionary = Dictionary(grouping: group.children) { $0.hashValue }
let mostChildren = dictionary.sorted { $0.value.count > $1.value.count }
if let first = mostChildren.first, first.value.count > 1 {
throw failure("Group \"\(group.nameOrPath)\" has duplicated children:\n - \(group.children.map { $0.nameOrPath }.joined(separator: "\n - "))")
}
for child in group.children {
if let group = objects.groups.getReference(child) {
if let group = child as? PBXGroup {
try validateGroup(group)
}
}
@@ -2,81 +2,25 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
class ProjectFixtureTests: XCTestCase {
func testProjectFixture() {
describe {
var xcodeProject: XcodeProj?
$0.it("generates") {
xcodeProject = try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml", projectPath: fixturePath + "TestProject/Project.xcodeproj")
}
$0.it("generates variant group") {
guard let xcodeProject = xcodeProject else { return }
func getFileReferences(_ path: String) -> [ObjectReference<PBXFileReference>] {
return xcodeProject.pbxproj.objects.fileReferences.objectReferences.filter { $0.object.path == path }
}
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
return xcodeProject.pbxproj.objects.variantGroups.referenceValues.filter { $0.name == name }
}
let resourceName = "LocalizedStoryboard.storyboard"
let baseResource = "Base.lproj/LocalizedStoryboard.storyboard"
let localizedResource = "en.lproj/LocalizedStoryboard.strings"
guard let variableGroup = getVariableGroups(resourceName).first else { throw failure("Couldn't find the variable group") }
do {
let refs = getFileReferences(baseResource)
try expect(refs.count) == 1
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
}
do {
let refs = getFileReferences(localizedResource)
try expect(refs.count) == 1
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
}
}
$0.it("generates scheme execution actions") {
guard let xcodeProject = xcodeProject else { return }
let frameworkScheme = xcodeProject.sharedData?.schemes.first { $0.name == "Framework" }
try expect(frameworkScheme?.buildAction?.preActions.first?.scriptText) == "echo Starting Framework Build"
try expect(frameworkScheme?.buildAction?.preActions.first?.title) == "Run Script"
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.blueprintName) == "Framework_iOS"
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.buildableName) == "Framework.framework"
try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml")
}
}
}
}
fileprivate func generateXcodeProject(specPath: Path, projectPath: Path, file: String = #file, line: Int = #line) throws -> XcodeProj {
fileprivate func generateXcodeProject(specPath: Path, file: String = #file, line: Int = #line) throws {
let project = try Project(path: specPath)
let generator = ProjectGenerator(project: project)
let writer = FileWriter(project: project)
let xcodeProject = try generator.generateXcodeProject()
let oldProject = try XcodeProj(path: projectPath)
let pbxProjPath = projectPath + XcodeProj.pbxprojPath(projectPath)
let oldProjectString: String = try pbxProjPath.read()
try xcodeProject.write(path: projectPath, override: true)
let newProjectString: String = try pbxProjPath.read()
let newProject = try XcodeProj(path: projectPath)
let stringDiff = newProjectString != oldProjectString
if newProject != oldProject || stringDiff {
var message = "\(projectPath.string) has changed. If change is legitimate commit the change and run test again"
if stringDiff {
message += ":\n\n\(pbxProjPath):\n\(prettyFirstDifferenceBetweenStrings(oldProjectString, newProjectString))"
}
throw failure(message, file: file, line: line)
}
return newProject
try writer.writeXcodeProject(xcodeProject)
try writer.writePlists()
}
+129 -230
View File
@@ -2,7 +2,7 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
import Yams
@@ -21,6 +21,12 @@ fileprivate let framework = Target(
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
)
fileprivate let optionalFramework = Target(
name: "MyOptionalFramework",
type: .framework,
platform: .iOS
)
fileprivate let uiTest = Target(
name: "MyAppUITests",
type: .uiTestBundle,
@@ -29,7 +35,7 @@ fileprivate let uiTest = Target(
dependencies: [Dependency(type: .target, reference: "MyApp")]
)
fileprivate let targets = [app, framework, uiTest]
fileprivate let targets = [app, framework, optionalFramework, uiTest]
class ProjectGeneratorTests: XCTestCase {
@@ -41,11 +47,9 @@ class ProjectGeneratorTests: XCTestCase {
let options = SpecOptions(bundleIdPrefix: "com.test")
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
let pbxProj = try project.generatePbxProj()
guard let target = pbxProj.objects.nativeTargets.first?.value,
guard let target = pbxProj.nativeTargets.first,
let buildConfigList = target.buildConfigurationList,
let buildConfigs = pbxProj.objects.configurationLists.getReference(buildConfigList),
let buildConfigReference = buildConfigs.buildConfigurations.first,
let buildConfig = pbxProj.objects.buildConfigurations.getReference(buildConfigReference) else {
let buildConfig = buildConfigList.buildConfigurations.first else {
throw failure("Build Config not found")
}
try expect(buildConfig.buildSettings["PRODUCT_BUNDLE_IDENTIFIER"] as? String) == "com.test.MyFramework"
@@ -55,15 +59,15 @@ class ProjectGeneratorTests: XCTestCase {
let options = SpecOptions(settingPresets: .none)
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
let pbxProj = try project.generatePbxProj()
let allSettings = pbxProj.objects.buildConfigurations.referenceValues.reduce([:]) { $0.merged($1.buildSettings) }.keys.sorted()
try expect(allSettings) == ["SETTING_2"]
let allSettings = pbxProj.buildConfigurations.reduce([:]) { $0.merged($1.buildSettings) }.keys.sorted()
try expect(allSettings) == ["SDKROOT", "SETTING_2"]
}
$0.it("generates development language") {
let options = SpecOptions(developmentLanguage: "de")
let project = Project(basePath: "", name: "test", options: options)
let pbxProj = try project.generatePbxProj()
guard let pbxProject = pbxProj.objects.projects.first?.value else {
guard let pbxProject = pbxProj.projects.first else {
throw failure("Could't find PBXProject")
}
try expect(pbxProject.developmentRegion) == "de"
@@ -92,8 +96,8 @@ class ProjectGeneratorTests: XCTestCase {
let project = Project(basePath: "", name: "test", configs: [Config(name: "Aconfig"), Config(name: "Bconfig")], targets: [framework], options: options)
let pbxProject = try project.generatePbxProj()
guard let projectConfigListReference = pbxProject.objects.projects.values.first?.buildConfigurationList,
let defaultConfigurationName = pbxProject.objects.configurationLists[projectConfigListReference]?.defaultConfigurationName
guard let projectConfigList = pbxProject.projects.first?.buildConfigurationList,
let defaultConfigurationName = projectConfigList.defaultConfigurationName
else {
throw failure("Default configuration name not found")
}
@@ -109,7 +113,7 @@ class ProjectGeneratorTests: XCTestCase {
$0.it("generates config defaults") {
let project = Project(basePath: "", name: "test")
let pbxProj = try project.generatePbxProj()
let configs = pbxProj.objects.buildConfigurations.referenceValues
let configs = pbxProj.buildConfigurations
try expect(configs.count) == 2
try expect(configs).contains(name: "Debug")
try expect(configs).contains(name: "Release")
@@ -122,7 +126,7 @@ class ProjectGeneratorTests: XCTestCase {
configs: [Config(name: "config1"), Config(name: "config2")]
)
let pbxProj = try project.generatePbxProj()
let configs = pbxProj.objects.buildConfigurations.referenceValues
let configs = pbxProj.buildConfigurations
try expect(configs.count) == 2
try expect(configs).contains(name: "config1")
try expect(configs).contains(name: "config2")
@@ -135,7 +139,7 @@ class ProjectGeneratorTests: XCTestCase {
configs: [Config(name: "config")]
)
let pbxProj = try project.generatePbxProj()
guard let config = pbxProj.objects.buildConfigurations.first?.value else {
guard let config = pbxProj.buildConfigurations.first else {
throw failure("configuration not found")
}
try expect(config.buildSettings.isEmpty).to.beTrue()
@@ -150,6 +154,7 @@ class ProjectGeneratorTests: XCTestCase {
let targetDebugSettings = project.getTargetBuildSettings(target: target, config: config)
var buildSettings = BuildSettings()
buildSettings += ["SDKROOT": "iphoneos"]
buildSettings += SettingsPresetFile.base.getBuildSettings()
buildSettings += SettingsPresetFile.config(.debug).getBuildSettings()
@@ -184,6 +189,23 @@ class ProjectGeneratorTests: XCTestCase {
try expect(buildSettings["SETTING1"] as? String) == "VALUE1"
try expect(buildSettings["SETTING2"] as? String) == "VALUE2"
}
$0.it("sets project SDKROOT if there is only a single platform") {
var project = Project(
basePath: "",
name: "test",
targets: [
Target(name: "1", type: .application, platform: .iOS),
Target(name: "2", type: .framework, platform: .iOS),
]
)
var buildSettings = project.getProjectBuildSettings(config: project.configs.first!)
try expect(buildSettings["SDKROOT"] as? String) == "iphoneos"
project.targets.append(Target(name: "3", type: .application, platform: .tvOS))
buildSettings = project.getProjectBuildSettings(config: project.configs.first!)
try expect(buildSettings["SDKROOT"]).beNil()
}
}
}
@@ -198,8 +220,8 @@ class ProjectGeneratorTests: XCTestCase {
$0.it("generates aggregate targets") {
let pbxProject = try project.generatePbxProj()
let nativeTargets = pbxProject.objects.nativeTargets.referenceValues
let aggregateTargets = pbxProject.objects.aggregateTargets.referenceValues
let nativeTargets = pbxProject.nativeTargets.sorted { $0.name < $1.name }
let aggregateTargets = pbxProject.aggregateTargets.sorted { $0.name < $1.name }
try expect(nativeTargets.count) == 4
try expect(aggregateTargets.count) == 2
@@ -216,8 +238,7 @@ class ProjectGeneratorTests: XCTestCase {
let target2 = nativeTargets.first { $0.name == "Other2" }
try expect(target2?.dependencies.count) == 2
let targetDependencies = pbxProject.objects.targetDependencies.referenceValues
try expect(targetDependencies.count) == 7
try expect(pbxProject.targetDependencies.count) == 7
}
}
}
@@ -229,11 +250,12 @@ class ProjectGeneratorTests: XCTestCase {
$0.it("generates targets") {
let pbxProject = try project.generatePbxProj()
let nativeTargets = pbxProject.objects.nativeTargets.referenceValues
try expect(nativeTargets.count) == 3
let nativeTargets = pbxProject.nativeTargets
try expect(nativeTargets.count) == 4
try expect(nativeTargets.contains { $0.name == app.name }).beTrue()
try expect(nativeTargets.contains { $0.name == framework.name }).beTrue()
try expect(nativeTargets.contains { $0.name == uiTest.name }).beTrue()
try expect(nativeTargets.contains { $0.name == optionalFramework.name }).beTrue()
}
$0.it("generates target attributes") {
@@ -243,25 +265,25 @@ class ProjectGeneratorTests: XCTestCase {
var testTargetWithAttributes = uiTest
testTargetWithAttributes.settings.buildSettings["CODE_SIGN_STYLE"] = "Manual"
let project = Project(basePath: "", name: "test", targets: [appTargetWithAttributes, framework, testTargetWithAttributes])
let project = Project(basePath: "", name: "test", targets: [appTargetWithAttributes, framework, optionalFramework, testTargetWithAttributes])
let pbxProject = try project.generatePbxProj()
guard let targetAttributes = pbxProject.objects.projects.referenceValues.first?.attributes["TargetAttributes"] as? [String: [String: Any]] else {
guard let targetAttributes = pbxProject.projects.first?.targetAttributes else {
throw failure("Couldn't find Project TargetAttributes")
}
guard let appTarget = pbxProject.objects.targets(named: app.name).first else {
guard let appTarget = pbxProject.targets(named: app.name).first else {
throw failure("Couldn't find App Target")
}
guard let uiTestTarget = pbxProject.objects.targets(named: uiTest.name).first else {
guard let uiTestTarget = pbxProject.targets(named: uiTest.name).first else {
throw failure("Couldn't find UITest Target")
}
try expect(targetAttributes[uiTestTarget.reference]?["TestTargetID"] as? String) == appTarget.reference
try expect(targetAttributes[uiTestTarget.reference]?["ProvisioningStyle"] as? String) == "Manual"
try expect(targetAttributes[appTarget.reference]?["ProvisioningStyle"] as? String) == "Automatic"
try expect(targetAttributes[appTarget.reference]?["DevelopmentTeam"] as? String) == "123"
try expect((targetAttributes[uiTestTarget]?["TestTargetID"] as? PBXNativeTarget)?.name) == app.name
try expect(targetAttributes[uiTestTarget]?["ProvisioningStyle"] as? String) == "Manual"
try expect(targetAttributes[appTarget]?["ProvisioningStyle"] as? String) == "Automatic"
try expect(targetAttributes[appTarget]?["DevelopmentTeam"] as? String) == "123"
}
$0.it("generates platform version") {
@@ -270,16 +292,14 @@ class ProjectGeneratorTests: XCTestCase {
let pbxProject = try project.generatePbxProj()
guard let projectConfigListReference = pbxProject.objects.projects.values.first?.buildConfigurationList,
let projectConfigReference = pbxProject.objects.configurationLists[projectConfigListReference]?.buildConfigurations.first,
let projectConfig = pbxProject.objects.buildConfigurations[projectConfigReference]
guard let projectConfigList = pbxProject.projects.first?.buildConfigurationList,
let projectConfig = projectConfigList.buildConfigurations.first
else {
throw failure("Couldn't find Project config")
}
guard let targetConfigListReference = pbxProject.objects.nativeTargets.referenceValues.first?.buildConfigurationList,
let targetConfigReference = pbxProject.objects.configurationLists[targetConfigListReference]?.buildConfigurations.first,
let targetConfig = pbxProject.objects.buildConfigurations[targetConfigReference]
guard let targetConfigList = pbxProject.nativeTargets.first?.buildConfigurationList,
let targetConfig = targetConfigList.buildConfigurations.first
else {
throw failure("Couldn't find Target config")
}
@@ -296,14 +316,14 @@ class ProjectGeneratorTests: XCTestCase {
$0.it("generates dependencies") {
let pbxProject = try project.generatePbxProj()
let nativeTargets = pbxProject.objects.nativeTargets.objectReferences
let dependencies = pbxProject.objects.targetDependencies.objectReferences
let nativeTargets = pbxProject.nativeTargets
let dependencies = pbxProject.targetDependencies.sorted { $0.target?.name ?? "" < $1.target?.name ?? "" }
try expect(dependencies.count) == 2
let appTarget = nativeTargets.first { $0.object.name == app.name }
let frameworkTarget = nativeTargets.first { $0.object.name == framework.name }
let appTarget = nativeTargets.first { $0.name == app.name }
let frameworkTarget = nativeTargets.first { $0.name == framework.name }
try expect(dependencies).contains { $0.object.target == appTarget?.reference }
try expect(dependencies).contains { $0.object.target == frameworkTarget?.reference }
try expect(dependencies).contains { $0.target == appTarget }
try expect(dependencies).contains { $0.target == frameworkTarget }
}
$0.it("generates targets with correct transitive embeds") {
@@ -543,20 +563,21 @@ class ProjectGeneratorTests: XCTestCase {
let pbxProject = try project.generatePbxProj()
for target in targets {
guard let nativeTarget = pbxProject.objects.nativeTargets.referenceValues.first(where: { $0.name == target.name }) else {
guard let nativeTarget = pbxProject.nativeTargets.first(where: { $0.name == target.name }) else {
throw failure("PBXNativeTarget for \(target) not found")
}
let buildPhases = nativeTarget.buildPhases
let resourcesPhases = pbxProject.objects.resourcesBuildPhases.objectReferences.filter { buildPhases.contains($0.reference) }
let frameworkPhases = pbxProject.objects.frameworksBuildPhases.objectReferences.filter { buildPhases.contains($0.reference) }
let copyFilesPhases = pbxProject.objects.copyFilesBuildPhases.objectReferences.filter { buildPhases.contains($0.reference) }
let resourcesPhases = pbxProject.resourcesBuildPhases.filter { buildPhases.contains($0) }
let frameworkPhases = pbxProject.frameworksBuildPhases.filter { buildPhases.contains($0) }
let copyFilesPhases = pbxProject.copyFilesBuildPhases.filter { buildPhases.contains($0) }
// ensure only the right resources are copies, no more, no less
let expectedResourceFiles = expectedResourceFiles[target.name]!
try expect(resourcesPhases.count) == (expectedResourceFiles.isEmpty ? 0 : 1)
if !expectedResourceFiles.isEmpty {
let resourceFiles = resourcesPhases[0].object.files
.compactMap { pbxProject.objects.buildFiles[$0]?.fileRef.flatMap { pbxProject.objects.fileReferences[$0]?.nameOrPath } }
let resourceFiles = resourcesPhases[0].files
.compactMap { $0.file }
.map { $0.nameOrPath }
try expect(Set(resourceFiles)) == expectedResourceFiles
}
@@ -564,8 +585,8 @@ class ProjectGeneratorTests: XCTestCase {
let expectedLinkedFiles = expectedLinkedFiles[target.name]!
try expect(frameworkPhases.count) == (expectedLinkedFiles.isEmpty ? 0 : 1)
if !expectedLinkedFiles.isEmpty {
let linkFrameworks = frameworkPhases[0].object.files
.compactMap { pbxProject.objects.buildFiles[$0]?.fileRef.flatMap { pbxProject.objects.fileReferences[$0]?.nameOrPath } }
let linkFrameworks = frameworkPhases[0].files
.compactMap { $0.file?.nameOrPath }
try expect(Set(linkFrameworks)) == expectedLinkedFiles
}
@@ -573,8 +594,8 @@ class ProjectGeneratorTests: XCTestCase {
let expectedEmbeddedFrameworks = expectedEmbeddedFrameworks[target.name]!
try expect(copyFilesPhases.count) == (expectedEmbeddedFrameworks.isEmpty ? 0 : 1)
if !expectedEmbeddedFrameworks.isEmpty {
let copyFiles = copyFilesPhases[0].object.files
.compactMap { pbxProject.objects.buildFiles[$0]?.fileRef.flatMap { pbxProject.objects.fileReferences[$0]?.nameOrPath } }
let copyFiles = copyFilesPhases[0].files
.compactMap { $0.file?.nameOrPath }
try expect(Set(copyFiles)) == expectedEmbeddedFrameworks
}
}
@@ -641,11 +662,9 @@ class ProjectGeneratorTests: XCTestCase {
let pbxProj = try project.generatePbxProj()
func buildSettings(for target: Target) throws -> BuildSettings {
guard let nativeTarget = pbxProj.objects.targets(named: target.name).first?.object,
guard let nativeTarget = pbxProj.targets(named: target.name).first,
let buildConfigList = nativeTarget.buildConfigurationList,
let buildConfigs = pbxProj.objects.configurationLists.getReference(buildConfigList),
let buildConfigReference = buildConfigs.buildConfigurations.first,
let buildConfig = pbxProj.objects.buildConfigurations.getReference(buildConfigReference) else {
let buildConfig = buildConfigList.buildConfigurations.first else {
throw failure("XCBuildConfiguration not found for Target \(target.name.quoted)")
}
@@ -699,16 +718,15 @@ class ProjectGeneratorTests: XCTestCase {
let pbxProject = try project.generatePbxProj()
func scriptBuildPhases(target: Target) throws -> [PBXShellScriptBuildPhase] {
guard let nativeTarget = pbxProject.objects.nativeTargets.referenceValues.first(where: { $0.name == target.name }) else {
guard let nativeTarget = pbxProject.nativeTargets.first(where: { $0.name == target.name }) else {
throw failure("PBXNativeTarget for \(target) not found")
}
let buildPhases = nativeTarget.buildPhases
let scriptPhases = pbxProject.objects.shellScriptBuildPhases.objectReferences.filter({ buildPhases.contains($0.reference) }).map { $0.object }
let scriptPhases = buildPhases.compactMap { $0 as? PBXShellScriptBuildPhase }
return scriptPhases
}
let expectedScriptPhase = PBXShellScriptBuildPhase(
files: [],
name: "Copy Swift Objective-C Interface Header",
inputPaths: ["$(DERIVED_SOURCES_DIR)/$(SWIFT_OBJC_INTERFACE_HEADER_NAME)"],
outputPaths: ["$(BUILT_PRODUCTS_DIR)/include/$(PRODUCT_MODULE_NAME)/$(SWIFT_OBJC_INTERFACE_HEADER_NAME)"],
@@ -728,25 +746,20 @@ class ProjectGeneratorTests: XCTestCase {
scriptSpec.targets[0].postBuildScripts = [BuildScript(script: .script("script3"))]
let pbxProject = try scriptSpec.generatePbxProj()
guard let nativeTarget = pbxProject.objects.nativeTargets.referenceValues
guard let nativeTarget = pbxProject.nativeTargets
.first(where: { $0.buildPhases.count >= 3 }) else {
throw failure("Target with build phases not found")
}
let buildPhases = nativeTarget.buildPhases
let scripts = pbxProject.objects.shellScriptBuildPhases.objectReferences
let script1 = scripts[0]
let script2 = scripts[1]
let script3 = scripts[2]
let scripts = pbxProject.shellScriptBuildPhases
try expect(scripts.count) == 3
try expect(buildPhases.contains(script1.reference)) == true
try expect(buildPhases.contains(script2.reference)) == true
try expect(buildPhases.contains(script3.reference)) == true
let scriptTexts = Set<String>(["script1", "script2", "script3"])
try expect(scriptTexts.contains(script1.object.shellScript!)) == true
try expect(scriptTexts.contains(script2.object.shellScript!)) == true
try expect(scriptTexts.contains(script3.object.shellScript!)) == true
let script1 = scripts.first { $0.shellScript == "script1" }!
let script2 = scripts.first { $0.shellScript == "script2" }!
let script3 = scripts.first { $0.shellScript == "script3" }!
try expect(buildPhases.contains(script1)) == true
try expect(buildPhases.contains(script2)) == true
try expect(buildPhases.contains(script3)) == true
}
$0.it("generates targets with cylical dependencies") {
@@ -788,7 +801,7 @@ class ProjectGeneratorTests: XCTestCase {
]
let pbxProject = try scriptSpec.generatePbxProj()
let buildRules = pbxProject.objects.buildRules.referenceValues
let buildRules = pbxProject.buildRules
try expect(buildRules.count) == 2
let first = buildRules.first { $0.name == "My Rule" }!
let second = buildRules.first { $0.name != "My Rule" }!
@@ -810,178 +823,64 @@ class ProjectGeneratorTests: XCTestCase {
try expect(second.outputFiles) == []
try expect(second.outputFilesCompilerFlags) == []
}
}
}
func testSchemes() {
describe {
let buildTarget = Scheme.BuildTarget(target: app.name)
$0.it("generates scheme") {
let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: app.name)
let scheme = Scheme(
name: "MyScheme",
build: Scheme.Build(targets: [buildTarget], preActions: [preAction])
$0.it("generates dependency build file settings") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .target, reference: "MyFramework"),
Dependency(type: .target, reference: "MyOptionalFramework", weakLink: true),
]
)
let project = Project(
basePath: "",
name: "test",
targets: [app, framework],
schemes: [scheme]
)
let xcodeProject = try project.generateXcodeProject()
guard let target = xcodeProject.pbxproj.objects.nativeTargets.objectReferences
.first(where: { $0.object.name == app.name }) else {
throw failure("Target not found")
}
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(scheme.name) == "MyScheme"
try expect(xcscheme.buildAction?.buildImplicitDependencies) == true
try expect(xcscheme.buildAction?.parallelizeBuild) == true
try expect(xcscheme.buildAction?.preActions.first?.title) == "Script"
try expect(xcscheme.buildAction?.preActions.first?.scriptText) == "echo Starting"
try expect(xcscheme.buildAction?.preActions.first?.environmentBuildable?.buildableName) == "MyApp.app"
try expect(xcscheme.buildAction?.preActions.first?.environmentBuildable?.blueprintName) == "MyApp"
guard let buildActionEntry = xcscheme.buildAction?.buildActionEntries.first else {
throw failure("Build Action entry not found")
}
try expect(buildActionEntry.buildFor) == BuildType.all
let buildableReferences: [XCScheme.BuildableReference] = [
buildActionEntry.buildableReference,
xcscheme.launchAction?.buildableProductRunnable?.buildableReference,
xcscheme.profileAction?.buildableProductRunnable?.buildableReference,
xcscheme.testAction?.macroExpansion,
].compactMap { $0 }
let project = Project(basePath: "", name: "test", targets: [app, framework, optionalFramework, uiTest])
let pbxProject = try project.generatePbxProj()
for buildableReference in buildableReferences {
try expect(buildableReference.blueprintIdentifier) == target.reference
try expect(buildableReference.blueprintName) == target.object.name
try expect(buildableReference.buildableName) == "\(target.object.name).\(target.object.productType!.fileExtension!)"
guard let nativeTarget = pbxProject.nativeTargets.first(where: { $0.name == app.name }) else {
throw failure("PBXNativeTarget for \(app.name) not found")
}
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
try expect(xcscheme.launchAction?.buildConfiguration) == "Debug"
try expect(xcscheme.testAction?.buildConfiguration) == "Debug"
try expect(xcscheme.profileAction?.buildConfiguration) == "Release"
try expect(xcscheme.analyzeAction?.buildConfiguration) == "Debug"
try expect(xcscheme.archiveAction?.buildConfiguration) == "Release"
let frameworkBuildFiles = frameworkPhases[0].files
let buildFileSettings = frameworkBuildFiles.map { $0.settings }
try expect(frameworkBuildFiles.count) == 2
try expect(buildFileSettings.compactMap({ $0 }).count) == 1
try expect(buildFileSettings.compactMap({ $0?["ATTRIBUTES"] }).count) == 1
try expect(buildFileSettings.compactMap({ $0?["ATTRIBUTES"] as? [String] }).first) == ["Weak"]
}
$0.it("sets environment variables for a scheme") {
let runVariables: [XCScheme.EnvironmentVariable] = [
XCScheme.EnvironmentVariable(variable: "RUN_ENV", value: "ENABLED", enabled: true),
XCScheme.EnvironmentVariable(variable: "OTHER_RUN_ENV", value: "DISABLED", enabled: false),
]
$0.it("generates info.plist") {
let plist = Plist(path: "Info.plist", attributes: ["UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]])
let tempPath = Path.temporary + "info"
let project = Project(basePath: tempPath, name: "", targets: [Target(name: "", type: .application, platform: .iOS, info: plist)])
let pbxProject = try project.generatePbxProj()
let writer = FileWriter(project: project)
try writer.writePlists()
let scheme = Scheme(
name: "EnvironmentVariablesScheme",
build: Scheme.Build(targets: [buildTarget]),
run: Scheme.Run(config: "Debug", environmentVariables: runVariables),
test: Scheme.Test(config: "Debug"),
profile: Scheme.Profile(config: "Debug")
)
let project = Project(
basePath: "",
name: "test",
targets: [app, framework],
schemes: [scheme]
)
let xcodeProject = try project.generateXcodeProject()
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
guard let targetConfig = pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first else {
throw failure("Couldn't find Target config")
}
try expect(
xcodeProject.pbxproj.objects.nativeTargets.objectReferences
.contains(where: { $0.object.name == app.name })
).beTrue()
try expect(xcscheme.launchAction?.environmentVariables) == runVariables
try expect(xcscheme.testAction?.environmentVariables).to.beNil()
try expect(xcscheme.profileAction?.environmentVariables).to.beNil()
}
try expect(targetConfig.buildSettings["INFOPLIST_FILE"] as? String) == plist.path
$0.it("generates target schemes from config variant") {
let configVariants = ["Test", "Production"]
var target = app
target.scheme = TargetScheme(configVariants: configVariants)
let configs: [Config] = [
Config(name: "Test Debug", type: .debug),
Config(name: "Production Debug", type: .debug),
Config(name: "Test Release", type: .release),
Config(name: "Production Release", type: .release),
]
let infoPlistFile = tempPath + plist.path
let data: Data = try infoPlistFile.read()
let infoPlist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [String: Any]
var expectedInfoPlist: [String: Any] = [:]
expectedInfoPlist["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)"
expectedInfoPlist["CFBundleInfoDictionaryVersion"] = "6.0"
expectedInfoPlist["CFBundleExecutable"] = "$(EXECUTABLE_NAME)"
expectedInfoPlist["CFBundleName"] = "$(PRODUCT_NAME)"
expectedInfoPlist["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)"
expectedInfoPlist["CFBundleShortVersionString"] = "1.0"
expectedInfoPlist["CFBundleVersion"] = "1"
expectedInfoPlist["CFBundlePackageType"] = "APPL"
expectedInfoPlist["UISupportedInterfaceOrientations"] = ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]
let project = Project(basePath: "", name: "test", configs: configs, targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 2
guard let nativeTarget = xcodeProject.pbxproj.objects.nativeTargets.objectReferences
.first(where: { $0.object.name == app.name }) else {
throw failure("Target not found")
}
guard let xcscheme = xcodeProject.sharedData?.schemes
.first(where: { $0.name == "\(target.name) Test" }) else {
throw failure("Scheme not found")
}
guard let buildActionEntry = xcscheme.buildAction?.buildActionEntries.first else {
throw failure("Build Action entry not found")
}
try expect(buildActionEntry.buildableReference.blueprintIdentifier) == nativeTarget.reference
try expect(xcscheme.launchAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.testAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.profileAction?.buildConfiguration) == "Test Release"
try expect(xcscheme.analyzeAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.archiveAction?.buildConfiguration) == "Test Release"
}
$0.it("generates environment variables for target schemes") {
let variables: [XCScheme.EnvironmentVariable] = [XCScheme.EnvironmentVariable(variable: "env", value: "var", enabled: false)]
var target = app
target.scheme = TargetScheme(environmentVariables: variables)
let project = Project(basePath: "", name: "test", targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 1
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.environmentVariables) == variables
try expect(xcscheme.testAction?.environmentVariables) == variables
try expect(xcscheme.profileAction?.environmentVariables) == variables
}
$0.it("generates pre and post actions for target schemes") {
var target = app
target.scheme = TargetScheme(
preActions: [.init(name: "Run", script: "do")],
postActions: [.init(name: "Run2", script: "post", settingsTarget: "MyApp")]
)
let project = Project(basePath: "", name: "test", targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 1
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.preActions.count) == 1
try expect(xcscheme.launchAction?.preActions.first?.title) == "Run"
try expect(xcscheme.launchAction?.preActions.first?.scriptText) == "do"
try expect(xcscheme.testAction?.postActions.count) == 1
try expect(xcscheme.testAction?.postActions.first?.title) == "Run2"
try expect(xcscheme.testAction?.postActions.first?.scriptText) == "post"
try expect(xcscheme.testAction?.postActions.first?.environmentBuildable?.blueprintName) == "MyApp"
try expect(NSDictionary(dictionary: expectedInfoPlist).isEqual(to: infoPlist)).beTrue()
}
}
}
+23 -1
View File
@@ -2,7 +2,7 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
class ProjectSpecTests: XCTestCase {
@@ -176,6 +176,20 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .invalidTargetSchemeConfigVariant(target: "target1", configVariant: "invalidVariant", configType: .debug))
}
$0.it("fails with invalid sdk dependency") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .iOS,
dependencies: [Dependency(type: .sdk, reference: "invalidDependency")]
),
]
try expectValidationError(project, .invalidSDKDependency(target: "target1", dependency: "invalidDependency"))
}
$0.it("fails with invalid scheme") {
var project = baseProject
project.schemes = [Scheme(
@@ -207,6 +221,14 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .missingDefaultConfig(configName: "foo"))
}
$0.it("validates config settings format") {
var project = baseProject
project.configs = Config.defaultConfigs
project.settings.buildSettings = ["Debug": ["SETTING": "VALUE"], "Release": ["SETTING": "VALUE"]]
try expectValidationError(project, .invalidPerConfigSettings)
}
$0.it("allows custom scheme for aggregated target") {
var project = baseProject
let buildScript = BuildScript(script: .path(#file), name: "buildScript1")
@@ -0,0 +1,206 @@
import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcodeproj
import XCTest
import Yams
fileprivate let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [Dependency(type: .target, reference: "MyFramework")]
)
fileprivate let framework = Target(
name: "MyFramework",
type: .framework,
platform: .iOS
)
fileprivate let optionalFramework = Target(
name: "MyOptionalFramework",
type: .framework,
platform: .iOS
)
fileprivate let uiTest = Target(
name: "MyAppUITests",
type: .uiTestBundle,
platform: .iOS,
dependencies: [Dependency(type: .target, reference: "MyApp")]
)
class SchemeGeneratorTests: XCTestCase {
func testSchemes() {
describe {
let buildTarget = Scheme.BuildTarget(target: app.name)
$0.it("generates scheme") {
let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: app.name)
let scheme = Scheme(
name: "MyScheme",
build: Scheme.Build(targets: [buildTarget], preActions: [preAction])
)
let project = Project(
basePath: "",
name: "test",
targets: [app, framework],
schemes: [scheme]
)
let xcodeProject = try project.generateXcodeProject()
guard let target = xcodeProject.pbxproj.nativeTargets
.first(where: { $0.name == app.name }) else {
throw failure("Target not found")
}
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(scheme.name) == "MyScheme"
try expect(xcscheme.buildAction?.buildImplicitDependencies) == true
try expect(xcscheme.buildAction?.parallelizeBuild) == true
try expect(xcscheme.buildAction?.preActions.first?.title) == "Script"
try expect(xcscheme.buildAction?.preActions.first?.scriptText) == "echo Starting"
try expect(xcscheme.buildAction?.preActions.first?.environmentBuildable?.buildableName) == "MyApp.app"
try expect(xcscheme.buildAction?.preActions.first?.environmentBuildable?.blueprintName) == "MyApp"
guard let buildActionEntry = xcscheme.buildAction?.buildActionEntries.first else {
throw failure("Build Action entry not found")
}
try expect(buildActionEntry.buildFor) == BuildType.all
let buildableReferences: [XCScheme.BuildableReference] = [
buildActionEntry.buildableReference,
xcscheme.launchAction?.buildableProductRunnable?.buildableReference,
xcscheme.profileAction?.buildableProductRunnable?.buildableReference,
xcscheme.testAction?.macroExpansion,
].compactMap { $0 }
for buildableReference in buildableReferences {
// FIXME: try expect(buildableReference.blueprintIdentifier) == target.reference
try expect(buildableReference.blueprintName) == target.name
try expect(buildableReference.buildableName) == "\(target.name).\(target.productType!.fileExtension!)"
}
try expect(xcscheme.launchAction?.buildConfiguration) == "Debug"
try expect(xcscheme.testAction?.buildConfiguration) == "Debug"
try expect(xcscheme.profileAction?.buildConfiguration) == "Release"
try expect(xcscheme.analyzeAction?.buildConfiguration) == "Debug"
try expect(xcscheme.archiveAction?.buildConfiguration) == "Release"
}
$0.it("sets environment variables for a scheme") {
let runVariables: [XCScheme.EnvironmentVariable] = [
XCScheme.EnvironmentVariable(variable: "RUN_ENV", value: "ENABLED", enabled: true),
XCScheme.EnvironmentVariable(variable: "OTHER_RUN_ENV", value: "DISABLED", enabled: false),
]
let scheme = Scheme(
name: "EnvironmentVariablesScheme",
build: Scheme.Build(targets: [buildTarget]),
run: Scheme.Run(config: "Debug", environmentVariables: runVariables),
test: Scheme.Test(config: "Debug"),
profile: Scheme.Profile(config: "Debug")
)
let project = Project(
basePath: "",
name: "test",
targets: [app, framework],
schemes: [scheme]
)
let xcodeProject = try project.generateXcodeProject()
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(
xcodeProject.pbxproj.nativeTargets
.contains(where: { $0.name == app.name })
).beTrue()
try expect(xcscheme.launchAction?.environmentVariables) == runVariables
try expect(xcscheme.testAction?.environmentVariables).to.beNil()
try expect(xcscheme.profileAction?.environmentVariables).to.beNil()
}
$0.it("generates target schemes from config variant") {
let configVariants = ["Test", "Production"]
var target = app
target.scheme = TargetScheme(configVariants: configVariants)
let configs: [Config] = [
Config(name: "Test Debug", type: .debug),
Config(name: "Production Debug", type: .debug),
Config(name: "Test Release", type: .release),
Config(name: "Production Release", type: .release),
]
let project = Project(basePath: "", name: "test", configs: configs, targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 2
guard let xcscheme = xcodeProject.sharedData?.schemes
.first(where: { $0.name == "\(target.name) Test" }) else {
throw failure("Scheme not found")
}
guard let buildActionEntry = xcscheme.buildAction?.buildActionEntries.first else {
throw failure("Build Action entry not found")
}
try expect(buildActionEntry.buildableReference.blueprintIdentifier.count > 0) == true
try expect(xcscheme.launchAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.testAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.profileAction?.buildConfiguration) == "Test Release"
try expect(xcscheme.analyzeAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.archiveAction?.buildConfiguration) == "Test Release"
}
$0.it("generates environment variables for target schemes") {
let variables: [XCScheme.EnvironmentVariable] = [XCScheme.EnvironmentVariable(variable: "env", value: "var", enabled: false)]
var target = app
target.scheme = TargetScheme(environmentVariables: variables)
let project = Project(basePath: "", name: "test", targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 1
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.environmentVariables) == variables
try expect(xcscheme.testAction?.environmentVariables) == variables
try expect(xcscheme.profileAction?.environmentVariables) == variables
}
$0.it("generates pre and post actions for target schemes") {
var target = app
target.scheme = TargetScheme(
preActions: [.init(name: "Run", script: "do")],
postActions: [.init(name: "Run2", script: "post", settingsTarget: "MyApp")]
)
let project = Project(basePath: "", name: "test", targets: [target, framework])
let xcodeProject = try project.generateXcodeProject()
try expect(xcodeProject.sharedData?.schemes.count) == 1
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.preActions.count) == 1
try expect(xcscheme.launchAction?.preActions.first?.title) == "Run"
try expect(xcscheme.launchAction?.preActions.first?.scriptText) == "do"
try expect(xcscheme.testAction?.postActions.count) == 1
try expect(xcscheme.testAction?.postActions.first?.title) == "Run2"
try expect(xcscheme.testAction?.postActions.first?.scriptText) == "post"
try expect(xcscheme.testAction?.postActions.first?.environmentBuildable?.blueprintName) == "MyApp"
}
}
}
}
@@ -2,7 +2,7 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
import Yams
@@ -85,14 +85,16 @@ class SourceGeneratorTests: XCTestCase {
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
let pbxProj = try project.generatePbxProj()
try pbxProj.expectFile(paths: ["Sources", "Bar.swift"], buildPhase: .sources)
let buildPhase = pbxProj.objects.copyFilesBuildPhases.referenceValues.first
let buildPhase = pbxProj.copyFilesBuildPhases.first
try expect(buildPhase?.dstSubfolderSpec) == .frameworks
let fileReference = pbxProj.getFileReference(
paths: ["Sources", "Foo.framework"],
names: ["Sources", "Foo.framework"]
)?.reference ?? ""
let buildFile = pbxProj.objects.buildFiles.objectReferences
.first(where: { $0.object.fileRef == fileReference })?.reference ?? ""
)
guard let buildFile = pbxProj.buildFiles
.first(where: { $0.file == fileReference }) else {
throw failure("Cant find build file")
}
try expect(buildPhase?.files.count) == 1
try expect(buildPhase?.files.contains(buildFile)) == true
}
@@ -112,16 +114,58 @@ class SourceGeneratorTests: XCTestCase {
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
let pbxProj = try project.generatePbxProj()
guard let fileReference = pbxProj.objects.fileReferences.first(where: { $0.value.nameOrPath == "model2.xcdatamodel" }) else {
guard let fileReference = pbxProj.fileReferences.first(where: { $0.nameOrPath == "model2.xcdatamodel" }) else {
throw failure("Couldn't find model file reference")
}
guard let versionGroup = pbxProj.objects.versionGroups.values.first else {
guard let versionGroup = pbxProj.versionGroups.first else {
throw failure("Couldn't find version group")
}
try expect(versionGroup.currentVersion) == fileReference.key
try expect(versionGroup.currentVersion) == fileReference
try expect(versionGroup.children.count) == 3
try expect(versionGroup.path) == "model.xcdatamodeld"
try expect(fileReference.value.path) == "model2.xcdatamodel"
try expect(fileReference.path) == "model2.xcdatamodel"
}
$0.it("generates variant groups") {
let directories = """
Sources:
Base.lproj:
- LocalizedStoryboard.storyboard
en.lproj:
- LocalizedStoryboard.strings
"""
try createDirectories(directories)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
let pbxProj = try project.generatePbxProj()
func getFileReferences(_ path: String) -> [PBXFileReference] {
return pbxProj.fileReferences.filter { $0.path == path }
}
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
return pbxProj.variantGroups.filter { $0.name == name }
}
let resourceName = "LocalizedStoryboard.storyboard"
let baseResource = "Base.lproj/LocalizedStoryboard.storyboard"
let localizedResource = "en.lproj/LocalizedStoryboard.strings"
guard let variableGroup = getVariableGroups(resourceName).first else { throw failure("Couldn't find the variable group") }
do {
let refs = getFileReferences(baseResource)
try expect(refs.count) == 1
try expect(variableGroup.children.filter { $0 == refs.first }.count) == 1
}
do {
let refs = getFileReferences(localizedResource)
try expect(refs.count) == 1
try expect(variableGroup.children.filter { $0 == refs.first }.count) == 1
}
}
$0.it("handles duplicate names") {
@@ -458,9 +502,7 @@ class SourceGeneratorTests: XCTestCase {
let pbxProj = try project.generatePbxProj()
try pbxProj.expectFile(paths: ["Sources/A", "a.swift"], names: ["A", "a.swift"], buildPhase: .sources)
let sourcesBuildPhase = pbxProj.objects.buildPhases
.first(where: { $0.1.buildPhase == BuildPhase.sources })!
.value
let sourcesBuildPhase = pbxProj.buildPhases.first(where: { $0.buildPhase == BuildPhase.sources })!
try expect(sourcesBuildPhase.files.count) == 1
}
@@ -480,7 +522,7 @@ class SourceGeneratorTests: XCTestCase {
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
let pbxProj = try project.generatePbxProj()
let groups = try pbxProj.getMainGroup().children.compactMap { pbxProj.objects.getFileElement(reference: $0)?.nameOrPath }
let groups = try pbxProj.getMainGroup().children.map { $0.nameOrPath }
try expect(groups) == ["A", "P", "S", "Frameworks", "Products"]
}
@@ -503,8 +545,9 @@ class SourceGeneratorTests: XCTestCase {
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
let pbxProj = try project.generatePbxProj()
let group = pbxProj.objects.group(named: "Sources", inGroup: try pbxProj.getMainGroup())!.object
let names = group.children.compactMap { pbxProj.objects.getFileElement(reference: $0)?.nameOrPath }
let mainGroup = try pbxProj.getMainGroup()
let group = mainGroup.children.compactMap { $0 as? PBXGroup }.first { $0.nameOrPath == "Sources" }!
let names = group.children.compactMap { $0.nameOrPath }
try expect(names) == [
"1file.a",
"10file.a",
@@ -532,10 +575,10 @@ extension PBXProj {
}
if let buildPhase = buildPhase {
let buildFile = objects.buildFiles.objectReferences
.first(where: { $0.object.fileRef == fileReference.reference })
let buildFile = buildFiles
.first(where: { $0.file === fileReference })
let actualBuildPhase = buildFile
.flatMap { buildFile in objects.buildPhases.referenceValues.first { $0.files.contains(buildFile.reference) } }?.buildPhase
.flatMap { buildFile in buildPhases.first { $0.files.contains(buildFile) } }?.buildPhase
var error: String?
if let buildPhase = buildPhase.buildPhase {
@@ -563,24 +606,20 @@ extension PBXProj {
}
}
func getFileReference(paths: [String], names: [String], file: String = #file, line: Int = #line) -> ObjectReference<PBXFileReference>? {
guard let project = objects.projects.first?.value else { return nil }
guard let mainGroup = objects.groups.getReference(project.mainGroup) else { return nil }
func getFileReference(paths: [String], names: [String], file: String = #file, line: Int = #line) -> PBXFileReference? {
guard let mainGroup = projects.first?.mainGroup else { return nil }
return getFileReference(group: mainGroup, paths: paths, names: names)
}
func getMainGroup(function: String = #function, file: String = #file, line: Int = #line) throws -> PBXGroup {
guard let project = objects.projects.first?.value else {
throw failure("Couldn't find project", file: file, line: line)
}
guard let mainGroup = objects.groups.getReference(project.mainGroup) else {
guard let mainGroup = projects.first?.mainGroup else {
throw failure("Couldn't find main group", file: file, line: line)
}
return mainGroup
}
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> ObjectReference<PBXFileReference>? {
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> PBXFileReference? {
guard !paths.isEmpty else { return nil }
let path = paths.first!
@@ -588,16 +627,10 @@ extension PBXProj {
let restOfPath = Array(paths.dropFirst())
let restOfName = Array(names.dropFirst())
if restOfPath.isEmpty {
let fileReferences: [ObjectReference<PBXFileReference>] = group.children.compactMap { reference in
if let fileReference = self.objects.fileReferences.getReference(reference) {
return ObjectReference(reference: reference, object: fileReference)
} else {
return nil
}
}
return fileReferences.first { $0.object.path == path && $0.object.nameOrPath == name }
let fileReferences: [PBXFileReference] = group.children.compactMap { $0 as? PBXFileReference }
return fileReferences.first { $0.path == path && $0.nameOrPath == name }
} else {
let groups = group.children.compactMap { self.objects.groups.getReference($0) }
let groups = group.children.compactMap { $0 as? PBXGroup }
guard let group = groups.first(where: { $0.path == path && $0.nameOrPath == name }) else { return nil }
return getFileReference(group: group, paths: restOfPath, names: restOfName)
}
+65 -5
View File
@@ -3,7 +3,7 @@ import PathKit
import ProjectSpec
import Spectre
import XcodeGenKit
import xcproj
import xcodeproj
import XCTest
class SpecLoadingTests: XCTestCase {
@@ -146,13 +146,47 @@ class SpecLoadingTests: XCTestCase {
targetDictionary["dependencies"] = [
["target": "name", "embed": false],
["carthage": "name"],
["framework": "path"],
["framework": "path", "weak": true],
["sdk": "Contacts.framework"],
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
try expect(target.dependencies.count) == 3
try expect(target.dependencies.count) == 4
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false)
try expect(target.dependencies[1]) == Dependency(type: .carthage, reference: "name")
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path")
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path", weakLink: true)
try expect(target.dependencies[3]) == Dependency(type: .sdk, reference: "Contacts.framework")
}
$0.it("parses info plist") {
var targetDictionary = validTarget
targetDictionary["info"] = [
"path": "Info.plist",
"properties": [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"],
],
]
let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.info) == Plist(path: "Info.plist", attributes: [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"],
])
}
$0.it("parses entitlement plist") {
var targetDictionary = validTarget
targetDictionary["entitlements"] = [
"path": "app.entitlements",
"properties": [
"com.apple.security.application-groups": "com.group",
],
]
let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.entitlements) == Plist(path: "app.entitlements", attributes: [
"com.apple.security.application-groups": "com.group",
])
}
$0.it("parses cross platform targets") {
@@ -221,7 +255,7 @@ class SpecLoadingTests: XCTestCase {
$0.it("parses target schemes") {
var targetDictionary = validTarget
targetDictionary["scheme"] = [
"testTargets": ["t1", "t2"],
"testTargets": ["t1", ["name": "t2"]],
"configVariants": ["dev", "app-store"],
"commandLineArguments": [
"ENV1": true,
@@ -280,6 +314,18 @@ class SpecLoadingTests: XCTestCase {
],
],
],
"test": [
"config": "debug",
"targets": [
"Target1",
[
"name": "Target2",
"parallelizable": true,
"randomExecutionOrder": true,
],
],
"gatherCoverageData": true,
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedTargets: [Scheme.BuildTarget] = [
@@ -298,6 +344,20 @@ class SpecLoadingTests: XCTestCase {
try expect(scheme.build.parallelizeBuild) == false
try expect(scheme.build.buildImplicitDependencies) == false
let expectedTest = Scheme.Test(
config: "debug",
gatherCoverageData: true,
targets: [
"Target1",
Scheme.Test.TestTarget(
name: "Target2",
randomExecutionOrder: true,
parallelizable: true
),
]
)
try expect(scheme.test) == expectedTest
}
$0.it("parses schemes variables") {
+1 -1
View File
@@ -2,7 +2,7 @@ import Foundation
import PathKit
import ProjectSpec
import Spectre
import xcproj
import xcodeproj
import XCTest
let fixturePath = Path(#file).parent().parent() + "Fixtures"
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
set -e
cd Tests/Fixtures/TestProject
./build.sh
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
set -e
if [[ `git status --porcelain Tests/Fixtures/TestProject` ]]; then
echo ""
echo "⚠️ Generated TestProject has changed."
echo "⚠️ If this is a valid change please run the tests and commit the updated TestProject."
echo ""
git --no-pager diff --color=always Tests/Fixtures/TestProject
exit 1
else
echo "✅ Generated TestProject has not changed."
fi