Merge branch 'external-target-ref' into test-coverage

This commit is contained in:
Yuta Saito 2019-10-27 12:57:23 +09:00
commit f81adfe291
79 changed files with 2697 additions and 555 deletions

View File

@ -1,13 +1,25 @@
name: CI
on: [push, pull_request]
on:
push: {}
pull_request: {}
jobs:
swift_5:
runs-on: macos-latest
name: Swift 5.0
run:
runs-on: macOS-latest
name: Xcode ${{ matrix.xcode }}
strategy:
matrix:
xcode: ["10.3", "11"]
steps:
- uses: actions/checkout@master
- name: Info
run: swift --version; swift package --version
- name: Set Xcode
run: |
echo "Available Xcode versions:"
ls /Applications | grep Xcode
echo "Choosing Xcode_${{ matrix.xcode }}.app"
sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
xcodebuild -version
swift --version
swift package --version
- name: Resolve
run: swift package resolve
- name: Build
@ -20,25 +32,3 @@ jobs:
run: scripts/diff-fixtures.sh
- name: Build fixtures
run: scripts/build-fixtures.sh
# swift_5.1:
# runs-on: macos-latest
# name: Swift 5.1
# steps:
# - uses: actions/checkout@master
# - name: Xcode version
# run: sudo xcode-select -s /Applications/Xcode_11_beta.app
# - name: Info
# run: swift --version; swift package --version
# - name: Resolve
# run: swift package resolve
# - name: Build
# run: swift build
# - name: Test
# run: swift test
# - name: Gen fixtures
# run: scripts/gen-fixtures.sh
# - name: Check fixtures
# run: scripts/diff-fixtures.sh
# - name: Build fixtures
# run: scripts/build-fixtures.sh

View File

@ -3,23 +3,53 @@
## Next Version
#### Added
- Added `includes` to `sources` for a Target. This follows the same glob-style as `excludes` but functions as a way to only include files that match a specified pattern. Useful if you only want a certain file type, for example specifying `**/*.swift`. [#637](https://github.com/yonaskolb/XcodeGen/pull/637) @bclymer
- Support `dylib` SDK. [#650](https://github.com/yonaskolb/XcodeGen/pull/650) @kateinoigakukun
- Added `language` and `region` options for `run` and `test` scheme [#654](https://github.com/yonaskolb/XcodeGen/pull/654) @kateinoigakukun
- Added `debugEnabled` option for `run` and `test` scheme [#657](https://github.com/yonaskolb/XcodeGen/pull/657) @kateinoigakukun
- Support External Target Reference. [#655](https://github.com/yonaskolb/XcodeGen/pull/655) @kateinoigakukun
- Support Target Reference to another project. [#655](https://github.com/yonaskolb/XcodeGen/pull/655) @kateinoigakukun
- Added `enableCoverage` for each test target. This enables to gather code coverage for specific targets. [#656](https://github.com/yonaskolb/XcodeGen/pull/656) @kateinoigakukun
#### Fixed
- Add base localisation by default even if no base localised files were found. Fixes warning in Xcode 11 [#685](https://github.com/yonaskolb/XcodeGen/pull/685) @yonaskolb
## 2.9.0
#### Added
- Added Scheme Templates [#672](https://github.com/yonaskolb/XcodeGen/pull/672) @bclymer
#### Fixed
- Fixed macOS unit test setting preset [#665](https://github.com/yonaskolb/XcodeGen/pull/665) @yonaskolb
- Add `rcproject` files to sources build phase instead of resources [#669](https://github.com/yonaskolb/XcodeGen/pull/669) @Qusic
- Prefer default configuration names for generated schemes [#673](https://github.com/yonaskolb/XcodeGen/pull/673) @giginet
- Fixed some resource files being placed to "Recovered References" group [#679](https://github.com/yonaskolb/XcodeGen/pull/679) @nivanchikov
#### Internal
- Updated to SwiftCLI 5.3.2 [#667](https://github.com/yonaskolb/XcodeGen/pull/667) @giginet
- Fixed tests in case-sensitive file system [#670](https://github.com/yonaskolb/XcodeGen/pull/670) @Qusic
[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.8.0...2.9.0)
## 2.8.0
#### Added
- Added support for Swift Package dependencies [#624](https://github.com/yonaskolb/XcodeGen/pull/624) @yonaskolb
- Added `includes` to `sources` for a Target. This follows the same glob-style as `excludes` but functions as a way to only include files that match a specified pattern. Useful if you only want a certain file type, for example specifying `**/*.swift`. [#637](https://github.com/yonaskolb/XcodeGen/pull/637) @bclymer
- Support `dylib` SDK. [#650](https://github.com/yonaskolb/XcodeGen/pull/650) @kateinoigakukun
- Added `language` and `region` options for `run` and `test` scheme [#654](https://github.com/yonaskolb/XcodeGen/pull/654) @kateinoigakukun
- Added `debugEnabled` option for `run` and `test` scheme [#657](https://github.com/yonaskolb/XcodeGen/pull/657) @kateinoigakukun
#### Fixed
- Expand template variable in Array of Any [#651](https://github.com/yonaskolb/XcodeGen/pull/651) @kateinoigakukun
- Significantly improve performance when running with a large number files. [#658](https://github.com/yonaskolb/XcodeGen/pull/658) @kateinoigakukun
- Removed some more diffs between the generated .pbxproj and when Xcode resaves it [#663](https://github.com/yonaskolb/XcodeGen/pull/663) @yonaskolb
#### Internal
- Removed needless `Array` initialization. [#661](https://github.com/yonaskolb/XcodeGen/pull/661) @RomanPodymov
- Updated to XcodeProj 7.1.0 [#624](https://github.com/yonaskolb/XcodeGen/pull/624) @yonaskolb
[Commits](https://github.com/yonaskolb/XcodeGen/compare/2.7.0...2.8.0)
## 2.7.0
#### Added
- Added Bash 4 style recursive globbing (`**/*`) in target sources `excludes` [#636](https://github.com/yonaskolb/XcodeGen/pull/636) @bclymer
- Added ability to disable main thread checker in Schemes [#601](https://github.com/yonaskolb/XcodeGen/pull/601) @wag-miles

View File

@ -23,6 +23,8 @@
- [Aggregate Target](#aggregate-target)
- [Target Template](#target-template)
- [Scheme](#scheme)
- [Scheme Template](#scheme-template)
- [Swift Package](#swift-package)
## General
@ -46,6 +48,9 @@ You can also use environment variables in your configuration file, by using `${S
- [ ] **fileGroups**: **[String]** - A list of paths to add to the root of the project. These aren't files that will be included in your targets, but that you'd like to include in the project hierachy anyway. For example a folder of xcconfig files that aren't already added by any target sources, or a Readme file.
- [ ] **schemes**: **[Scheme](#scheme)** - A list of schemes by name. This allows more control over what is found in [Target Scheme](#target-scheme)
- [ ] **targetTemplates**: **[String: [Target Template](#target-template)]** - a list of targets that can be used as templates for actual targets which reference them via a `template` property. They can be used to extract common target settings. Works great in combination with `include`.
- [ ] **packages**: **[String: [Swift Package](#swift-package)]** - a map of Swift packages by name
- [ ] **localPackages**: **[String]** - A list of paths to local Swift Packages. The paths must be directories with a `Package.swift` file in them. This is used to override `packages` with a local version for development purposes.
- [ ] **projectReferences**: **[String: [Project Reference](#project-reference)]** - a map of project references by name
### Include
@ -117,6 +122,7 @@ Note that target names can also be changed by adding a `name` property to a targ
- [ ] **transitivelyLinkDependencies**: **Bool** - If this is `true` then targets will link to the dependencies of their target dependencies. If a target should embed its dependencies, such as application and test bundles, it will embed these transitive dependencies as well. Some complex setups might want to set this to `false` and explicitly specify dependencies at every level. Targets can override this with [Target](#target).transitivelyLinkDependencies. Defaults to `false`.
- [ ] **generateEmptyDirectories**: **Bool** - If this is `true` then empty directories will be added to project too else will be missed. Defaults to `false`.
- [ ] **findCarthageFrameworks**: **Bool** - When this is set to `true`, all the invididual frameworks for Carthage dependencies will automatically be found. This property can be overriden individually for each carthage dependency - for more details see See **findFrameworks** in the [Dependency](#dependency) section. Defaults to `false`.
- [ ] **localPackagesGroup**: **String** - The group name that local packages are put into. This defaults to `Packages`
```yaml
options:
@ -373,6 +379,7 @@ A dependency can be one of a 3 types:
- `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)
- `package: name` - links to a Swift Package. The name must match the name of a package defined in the top level `packages`
**Linking options**:
@ -396,7 +403,7 @@ Carthage frameworks are expected to be in `CARTHAGE_BUILD_PATH/PLATFORM/FRAMEWOR
- `PLATFORM` = the target's platform
- `FRAMEWORK` = the specified name.
All the invididual frameworks of a Carthage dependency can be automatically found via `findFrameworks: true`. This overrides the value of [Options](#options).findCarthageFrameworks. Otherwise each one will have to be listed invididually.
All the individual frameworks of a Carthage dependency can be automatically found via `findFrameworks: true`. This overrides the value of [Options](#options).findCarthageFrameworks. Otherwise each one will have to be listed individually.
Xcodegen uses `.version` files generated by Carthage in order for this framework lookup to work, so the Carthage dependencies will need to have already been built at the time XcodeGen is run.
If any applications contain carthage dependencies within itself or any dependent targets, a carthage copy files script is automatically added to the application containing all the relevant frameworks. A `FRAMEWORK_SEARCH_PATHS` setting is also automatically added
@ -433,6 +440,25 @@ targets:
type: framework
```
**Package dependency**
- [ ] **product**: **String** - The product to use from the package. This defaults to the package name, so is only required if a Package has multiple libraries or a library with a differing name
```yaml
packages:
Yams:
url: https://github.com/jpsim/Yams
majorVersion: 2.0.0
SwiftPM:
url: https://github.com/apple/swift-package-manager
branch: swift-5.0-branch
targets:
App:
dependencies:
- package: Yams
- package: SwiftPM
product: SPMUtility
```
### Config Files
Specifies `.xcconfig` files for each configuration.
@ -650,7 +676,7 @@ Schemes allows for more control than the convenience [Target Scheme](#target-sch
### Build
- [x] **targets**: **[String:String]**, **[String:[String]]** or **[String: [String: [Build Target](#build-target)]]** - A map of target names to build and which build types they should be enabled for. The build types can be `all`, `none`, or an array of the following types:
- [x] **targets**: **[String:String]** or **[String:[String]]** - A map of target names to build and which build types they should be enabled for. The build types can be `all`, `none`, or an array of the following types:
- `run` or `running`
- `test` or `testing`
- `profile` or `profiling`
@ -673,10 +699,6 @@ parallelizeBuild: true
buildImplicitDependencies: true
```
### Build Target
- [ ] **types** Build types they should be enabled for.
- [ ] **externalProject**: **String** - `xcodeproj` file to reference target.
### Common Build Action options
The different actions share some properties:
@ -714,7 +736,6 @@ A multiline script can be written using the various YAML multiline methods, for
- [ ] **parallelizable**: **Bool** - Whether to run tests in parallel. Defaults to false
- [ ] **randomExecutionOrder**: **Bool** - Whether to run tests in a random order. Defaults to false
- [ ] **skippedTests**: **[String]** - List of tests in the test target to skip. Defaults to empty.
- [ ] **externalProject**: **String** - `xcodeproj` file to reference target.
#### Coverage Target
- [x] **name**: **String** - The name of the target
@ -738,9 +759,6 @@ schemes:
targets:
MyTarget1: all
MyTarget2: [run, archive]
MyTarget3:
types: [run, test]
externalProject: ./Submodules/OtherProject.xcodeproj
run:
config: prod-debug
commandLineArguments: "--option value"
@ -757,7 +775,6 @@ schemes:
targets:
- Tester1
- name: Tester2
externalProject: ./Submodules/OtherProject.xcodeproj
parallelizable: true
randomExecutionOrder: true
skippedTests: [Test/testExample()]
@ -774,3 +791,79 @@ schemes:
customArchiveName: MyTarget
revealArchiveInOrganizer: false
```
### Scheme Template
This is a template that can be referenced from a normal scheme using the `templates` property. The properties of this template are the same as a [Scheme](#scheme). This functions identically in practice to [Target Template](#target-template).
Any instances of `${scheme_name}` within each template will be replaced by the final scheme name which references the template.
Any attributes defined within a scheme's `templateAttributes` will be used to replace any attribute references in the template using the syntax `${attribute_name}`.
```yaml
schemes:
MyModule:
templates:
- FeatureModuleScheme
templateAttributes:
testTargetName: MyModuleTests
schemeTemplates:
FeatureModuleScheme:
templates:
- TestScheme
build:
targets:
${scheme_name}: build
TestScheme:
test:
gatherCoverageData: true
targets:
- name: ${testTargetName}
parallelizable: true
randomExecutionOrder: true
```
The result will be a scheme that builds `MyModule` when you request a build, and will test against `MyModuleTests` when you request to run tests. This is particularly useful when you work in a very modular application and each module has a similar structure.
## Swift Package
Swift packages are defined at a project level, and then linked to individual targets via a [Dependency](#dependency).
> Note that Swift Packages don't work in projects with configurations other than `Debug` and `Release`. That limitation is tracked here bugs.swift.org/browse/SR-10927
- [x] **url**: **URL** - the url to the package
- [x] **version**: **String** - the version of the package to use. It can take a few forms:
- `majorVersion: 1.2.0` or `from: 1.2.0`
- `minorVersion: 1.2.1`
- `exactVersion: 1.2.1` or `version: 1.2.1`
- `minVersion: 1.0.0, maxVersion: 1.2.9`
- `branch: master`
- `revision: xxxxxx`
```yml
packages:
Yams:
url: https://github.com/jpsim/Yams
from: 2.0.0
targets:
App:
dependencies:
- package: Yams
```
## Project Reference
Project References are defined at a project level, and then you can use the project name to refer its target via a [Scheme](#scheme)
- [x] **path**: **String** - The path to the `xcodeproj` file to reference.
```yml
projectReferences:
YamsProject:
path: ./Carthage/Checkouts/Yams/Yams.xcodeproj
schemes:
TestTarget:
build:
targets:
YamsProject/Yams: ["run"]
```

View File

@ -4,9 +4,10 @@
- [Settings](#settings)
- [Setting Groups](#setting-groups)
- [xcconfig files](#xcconfig-files)
- [Use dependencies](#use-dependencies)
- [Dependencies](#dependencies)
- [CocoaPods](#cocoapods)
- [Carthage](#carthage)
- [Swift Package](#swift-package)
- [SDK](#sdk)
- [Framework](#framework)
@ -92,7 +93,7 @@ You can also always overide any build settings on CI when building by passing sp
DEVELOPMENT_TEAM=XXXXXXXXX xcodebuild ...
```
# Use dependencies
# Dependencies
Each target can declare one or more dependencies. See [Dependency](ProjectSpec.md#dependency) in the ProjectSpec for more info about all the properties
@ -153,6 +154,41 @@ options:
carthageBuildPath: ../../Carthage/Build
```
### Swift Package
Swift Packages can be integrated by defining them at the project level and then referencing them in targets
```yaml
packages:
Yams:
url: https://github.com/jpsim/Yams
from: 2.0.0
SwiftPM:
url: https://github.com/apple/swift-package-manager
branch: swift-5.0-branch
targets:
App:
dependencies:
# by default the package product that is linked to is the same as the package name
- package: Yams
- package: SwiftPM
- package: SwiftPM
product: SPMUtility # specify a specific product
```
If you want to check in the `Package.resolved` file so that everyone is on the same versions, you need to check in `ProjectName.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved`
> Note that Swift Packages don't work in projects with configurations other than `Debug` and `Release`. That limitation is tracked here bugs.swift.org/browse/SR-10927
You can also include local Swift Packages by referencing them by paths in `localPackages`. When these have the same name as `packages` they will be used instead of the remote repos. This is useful for local development.
```yml
localPackages:
- ../../Yams
- ~/Developer/MyPackage
```
These local packages get put into a `Packages` group in the root of the project by default. This can be changed with `options.localPackagesGroup`
> For now local packages that don't mirror remote packages aren't able to be linked to
### 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`, `.tbd` or `dylib` filename, respectively

View File

@ -1,6 +1,6 @@
TOOL_NAME = XcodeGen
export EXECUTABLE_NAME = xcodegen
VERSION = 2.7.0
VERSION = 2.9.0
PREFIX = /usr/local
INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME)

View File

@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/jakeheis/SwiftCLI.git",
"state": {
"branch": null,
"revision": "5318c37d3cacc8780f50b87a8840a6774320ebdf",
"version": "5.2.2"
"revision": "ba2268e67c07b9f9cfbc0801385e6238b36255eb",
"version": "5.3.2"
}
},
{
@ -69,8 +69,8 @@
"repositoryURL": "https://github.com/tuist/xcodeproj.git",
"state": {
"branch": null,
"revision": "b951777f42e9acbfb8f19da623b43aaa604422f9",
"version": "7.0.0"
"revision": "aefcbf59cea5b0831837ed2f385e6dfd80d82cc9",
"version": "7.1.0"
}
},
{

View File

@ -4,6 +4,7 @@ import PackageDescription
let package = Package(
name: "XcodeGen",
platforms: [.macOS(.v10_13)],
products: [
.executable(name: "xcodegen", targets: ["XcodeGen"]),
.library(name: "XcodeGenKit", targets: ["XcodeGenKit"]),
@ -15,8 +16,8 @@ let package = Package(
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.2.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", .exact("7.0.0")),
.package(url: "https://github.com/jakeheis/SwiftCLI.git", .exact("5.2.2")),
.package(url: "https://github.com/tuist/xcodeproj.git", .exact("7.1.0")),
.package(url: "https://github.com/jakeheis/SwiftCLI.git", .upToNextMinor(from: "5.3.2")),
],
targets: [
.target(name: "XcodeGen", dependencies: [

View File

@ -39,6 +39,10 @@ Given a very simple project spec file like this:
name: MyProject
options:
bundleIdPrefix: com.myapp
packages:
Yams:
url: https://github.com/jpsim/Yams
from: 2.0.0
targets:
MyApp:
type: application
@ -57,7 +61,7 @@ targets:
- framework: Vendor/MyFramework.framework
- sdk: Contacts.framework
- sdk: libc++.tbd
- sdk: libz.dylib
- package: Yams
MyFramework:
type: framework
platform: iOS
@ -103,7 +107,7 @@ swift run xcodegen
Add the following to your Package.swift file's dependencies:
```swift
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.7.0"),
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.9.0"),
```
And then import wherever needed: `import XcodeGenKit`

View File

@ -1,3 +1,3 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: iphoneos
TARGETED_DEVICE_FAMILY: '1,2'

View File

@ -1,3 +1,3 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/../Frameworks"]
SDKROOT: macosx
COMBINE_HIDPI_IMAGES: 'YES'

View File

@ -1,3 +1,3 @@
TARGETED_DEVICE_FAMILY: 3
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: appletvos

View File

@ -1 +1 @@
LD_RUNPATH_SEARCH_PATHS: $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks"]

View File

@ -1 +1 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks @executable_path/../../../../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../../../../Frameworks"]

View File

@ -1,2 +1,2 @@
ASSETCATALOG_COMPILER_APPICON_NAME: iMessage App Icon
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"]

View File

@ -1 +1 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"]

View File

@ -1,2 +1,2 @@
BUNDLE_LOADER: $(TEST_HOST)
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]

View File

@ -1,2 +1,2 @@
BUNDLE_LOADER: $(TEST_HOST)
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks"]

View File

@ -1,2 +1,2 @@
SKIP_INSTALL: 'YES'
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"]

View File

@ -1,2 +1,2 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks"]
ASSETCATALOG_COMPILER_COMPLICATION_NAME: Complication

View File

@ -38,6 +38,7 @@ public struct Dependency: Equatable {
case framework
case carthage(findFrameworks: Bool?)
case sdk(root: String?)
case package(product: String?)
}
}
@ -64,6 +65,10 @@ extension Dependency: JSONObjectConvertible {
let sdkRoot: String? = jsonDictionary.json(atKeyPath: "root")
type = .sdk(root: sdkRoot)
reference = sdk
} else if let package: String = jsonDictionary.json(atKeyPath: "package") {
let product: String? = jsonDictionary.json(atKeyPath: "product")
type = .package(product: product)
reference = package
} else {
throw SpecParsingError.invalidDependency(jsonDictionary)
}
@ -114,6 +119,8 @@ extension Dependency: JSONEncodable {
}
case .sdk:
dict["sdk"] = reference
case .package:
dict["package"] = reference
}
return dict

View File

@ -19,6 +19,9 @@ public struct Project: BuildSettingsContainer {
}
}
public var packages: [String: SwiftPackage]
public var localPackages: [String]
public var settings: Settings
public var settingGroups: [String: Settings]
public var configs: [Config]
@ -28,15 +31,15 @@ public struct Project: BuildSettingsContainer {
public var fileGroups: [String]
public var configFiles: [String: String]
public var include: [String] = []
public var externalProjects: [ExternalProject] = [] {
public var projectReferences: [ProjectReference] = [] {
didSet {
externalProjectsMap = Dictionary(uniqueKeysWithValues: externalProjects.map { ($0.name, $0) })
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}
}
private var targetsMap: [String: Target]
private var aggregateTargetsMap: [String: AggregateTarget]
private var externalProjectsMap: [String: ExternalProject]
private var projectReferencesMap: [String: ProjectReference]
public init(
basePath: Path = "",
@ -47,11 +50,13 @@ public struct Project: BuildSettingsContainer {
settings: Settings = .empty,
settingGroups: [String: Settings] = [:],
schemes: [Scheme] = [],
packages: [String: SwiftPackage] = [:],
localPackages: [String] = [],
options: SpecOptions = SpecOptions(),
fileGroups: [String] = [],
configFiles: [String: String] = [:],
attributes: [String: Any] = [:],
externalProjects: [ExternalProject] = []
projectReferences: [ProjectReference] = []
) {
self.basePath = basePath
self.name = name
@ -63,16 +68,18 @@ public struct Project: BuildSettingsContainer {
self.settings = settings
self.settingGroups = settingGroups
self.schemes = schemes
self.packages = packages
self.localPackages = localPackages
self.options = options
self.fileGroups = fileGroups
self.configFiles = configFiles
self.attributes = attributes
self.externalProjects = externalProjects
externalProjectsMap = Dictionary(uniqueKeysWithValues: self.externalProjects.map { ($0.name, $0) })
self.projectReferences = projectReferences
projectReferencesMap = Dictionary(uniqueKeysWithValues: self.projectReferences.map { ($0.name, $0) })
}
public func getExternalProject(_ projectName: String) -> ExternalProject? {
return externalProjectsMap[projectName]
public func getProjectReference(_ projectName: String) -> ProjectReference? {
return projectReferencesMap[projectName]
}
public func getTarget(_ targetName: String) -> Target? {
@ -139,6 +146,8 @@ extension Project: Equatable {
lhs.fileGroups == rhs.fileGroups &&
lhs.configFiles == rhs.configFiles &&
lhs.options == rhs.options &&
lhs.packages == rhs.packages &&
lhs.localPackages == rhs.localPackages &&
NSDictionary(dictionary: lhs.attributes).isEqual(to: rhs.attributes)
}
}
@ -157,7 +166,7 @@ extension Project {
public init(basePath: Path = "", jsonDictionary: JSONDictionary) throws {
self.basePath = basePath
let jsonDictionary = try Project.resolveProject(jsonDictionary: jsonDictionary)
let jsonDictionary = Project.resolveProject(jsonDictionary: jsonDictionary)
name = try jsonDictionary.json(atKeyPath: "name")
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
@ -168,12 +177,18 @@ extension Project {
configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
targets = try jsonDictionary.json(atKeyPath: "targets").sorted { $0.name < $1.name }
aggregateTargets = try jsonDictionary.json(atKeyPath: "aggregateTargets").sorted { $0.name < $1.name }
externalProjects = try jsonDictionary.json(atKeyPath: "externalProjects").sorted { $0.name < $1.name }
projectReferences = try jsonDictionary.json(atKeyPath: "projectReferences").sorted { $0.name < $1.name }
schemes = try jsonDictionary.json(atKeyPath: "schemes")
fileGroups = jsonDictionary.json(atKeyPath: "fileGroups") ?? []
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
include = jsonDictionary.json(atKeyPath: "include") ?? []
if jsonDictionary["packages"] != nil {
packages = try jsonDictionary.json(atKeyPath: "packages", invalidItemBehaviour: .fail)
} else {
packages = [:]
}
localPackages = jsonDictionary.json(atKeyPath: "localPackages") ?? []
if jsonDictionary["options"] != nil {
options = try jsonDictionary.json(atKeyPath: "options")
} else {
@ -181,17 +196,18 @@ extension Project {
}
targetsMap = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
aggregateTargetsMap = Dictionary(uniqueKeysWithValues: aggregateTargets.map { ($0.name, $0) })
externalProjectsMap = Dictionary(uniqueKeysWithValues: externalProjects.map { ($0.name, $0) })
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}
static func resolveProject(jsonDictionary: JSONDictionary) throws -> JSONDictionary {
static func resolveProject(jsonDictionary: JSONDictionary) -> JSONDictionary {
var jsonDictionary = jsonDictionary
// resolve multiple times so that we support both multi-platform templates,
// as well as platform specific templates in multi-platform targets
jsonDictionary = try Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
jsonDictionary = try Target.resolveTargetTemplates(jsonDictionary: jsonDictionary)
jsonDictionary = try Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
jsonDictionary = Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
jsonDictionary = Target.resolveTargetTemplates(jsonDictionary: jsonDictionary)
jsonDictionary = Scheme.resolveSchemeTemplates(jsonDictionary: jsonDictionary)
jsonDictionary = Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
return jsonDictionary
}
@ -202,6 +218,7 @@ extension Project: PathContainer {
static var pathProperties: [PathProperty] {
return [
.string("configFiles"),
.string("localPackages"),
.object("options", SpecOptions.pathProperties),
.object("targets", Target.pathProperties),
.object("targetTemplates", Target.pathProperties),
@ -256,48 +273,25 @@ extension Project: JSONEncodable {
let configsPairs = configs.map { ($0.name, $0.type?.rawValue) }
let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) }
let schemesPairs = schemes.map { ($0.name, $0.toJSONValue()) }
let externalProjectsPairs = externalProjects.map { ($0.name, $0.toJSONValue()) }
let projectReferencesPairs = projectReferences.map { ($0.name, $0.toJSONValue()) }
return [
"name": name,
"options": options.toJSONValue(),
"settings": settings.toJSONValue(),
"fileGroups": fileGroups,
"configFiles": configFiles,
"include": include,
"attributes": attributes,
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
"configs": Dictionary(uniqueKeysWithValues: configsPairs),
"aggregateTargets": Dictionary(uniqueKeysWithValues: aggregateTargetsPairs),
"schemes": Dictionary(uniqueKeysWithValues: schemesPairs),
"settingGroups": settingGroups.mapValues { $0.toJSONValue() },
"externalProjects": externalProjectsPairs,
]
}
}
public struct ExternalProject {
public let name: String
public let path: String
public init(name: String, path: String) {
self.name = name
self.path = path
}
}
extension ExternalProject: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
self.path = try jsonDictionary.json(atKeyPath: "path")
}
}
extension ExternalProject: JSONEncodable {
public func toJSONValue() -> Any {
return [
"path": path,
]
var dictionary: JSONDictionary = [:]
dictionary["name"] = name
dictionary["options"] = options.toJSONValue()
dictionary["settings"] = settings.toJSONValue()
dictionary["fileGroups"] = fileGroups
dictionary["configFiles"] = configFiles
dictionary["include"] = include
dictionary["attributes"] = attributes
dictionary["packages"] = packages.mapValues { $0.toJSONValue() }
dictionary["localPackages"] = localPackages
dictionary["targets"] = Dictionary(uniqueKeysWithValues: targetPairs)
dictionary["configs"] = Dictionary(uniqueKeysWithValues: configsPairs)
dictionary["aggregateTargets"] = Dictionary(uniqueKeysWithValues: aggregateTargetsPairs)
dictionary["schemes"] = Dictionary(uniqueKeysWithValues: schemesPairs)
dictionary["settingGroups"] = settingGroups.mapValues { $0.toJSONValue() }
dictionary["projectReferences"] = projectReferencesPairs
return dictionary
}
}

View File

@ -0,0 +1,27 @@
import Foundation
import JSONUtilities
public struct ProjectReference: Hashable {
public var name: String
public var path: String
public init(name: String, path: String) {
self.name = name
self.path = path
}
}
extension ProjectReference: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
self.path = try jsonDictionary.json(atKeyPath: "path")
}
}
extension ProjectReference: JSONEncodable {
public func toJSONValue() -> Any {
return [
"path": path,
]
}
}

View File

@ -146,7 +146,7 @@ public struct Scheme: Equatable {
public init(stringLiteral value: String) {
do {
targetReference = try TargetReference(string: value)
targetReference = try TargetReference(value)
randomExecutionOrder = false
parallelizable = false
skippedTests = []
@ -246,7 +246,7 @@ public struct Scheme: Equatable {
}
}
public struct BuildTarget: Equatable {
public struct BuildTarget: Equatable, Hashable {
public var target: TargetReference
public var buildTypes: [BuildType]
@ -257,50 +257,6 @@ public struct Scheme: Equatable {
}
}
public struct TargetReference: Equatable {
public let name: String
public let location: Location
public enum Location: Equatable {
case local
case project(String)
}
public init(name: String, location: Location = .local) {
self.name = name
self.location = location
}
}
extension TargetReference {
public init(string: String) throws {
let paths = string.split(separator: "/")
guard paths.count <= 2 && !paths.isEmpty else {
throw SpecParsingError.invalidTargetReference(string)
}
switch paths.count {
case 2:
location = .project(String(paths[0]))
name = String(paths[1])
case 1:
location = .local
name = String(paths[0])
default: fatalError("unreachable")
}
}
}
extension TargetReference {
public func toString() -> String {
switch location {
case .local: return name
case .project(let projectPath):
return "\(projectPath)/\(name)"
}
}
}
protocol BuildAction: Equatable {
var config: String? { get }
}
@ -367,13 +323,13 @@ extension Scheme.Test: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
config = jsonDictionary.json(atKeyPath: "config")
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? Scheme.Test.gatherCoverageDataDefault
coverageTargets = try (jsonDictionary.json(atKeyPath: "coverageTargets") ?? []).map { try TargetReference(string: $0) }
coverageTargets = try (jsonDictionary.json(atKeyPath: "coverageTargets") ?? []).map { try TargetReference($0) }
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Test.disableMainThreadCheckerDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
if let string = target as? String {
return TestTarget(stringLiteral: string)
return try TestTarget(targetReference: TargetReference(string))
} else if let dictionary = target as? JSONDictionary {
return try TestTarget(jsonDictionary: dictionary)
} else {
@ -403,7 +359,7 @@ extension Scheme.Test: JSONEncodable {
"config": config,
"language": language,
"region": region,
"coverageTargets": coverageTargets.map { $0.toString() },
"coverageTargets": coverageTargets.map { $0.reference },
]
if gatherCoverageData != Scheme.Test.gatherCoverageDataDefault {
@ -425,7 +381,7 @@ extension Scheme.Test: JSONEncodable {
extension Scheme.Test.TestTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
targetReference = try TargetReference(string: jsonDictionary.json(atKeyPath: "name"))
targetReference = try TargetReference(jsonDictionary.json(atKeyPath: "name"))
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? Scheme.Test.TestTarget.randomExecutionOrderDefault
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? Scheme.Test.TestTarget.parallelizableDefault
skippedTests = jsonDictionary.json(atKeyPath: "skippedTests") ?? []
@ -436,11 +392,11 @@ extension Scheme.Test.TestTarget: JSONEncodable {
public func toJSONValue() -> Any {
if randomExecutionOrder == Scheme.Test.TestTarget.randomExecutionOrderDefault,
parallelizable == Scheme.Test.TestTarget.parallelizableDefault {
return targetReference.toString()
return targetReference.reference
}
var dict: JSONDictionary = [
"name": targetReference.toString(),
"name": targetReference.reference,
]
if randomExecutionOrder != Scheme.Test.TestTarget.randomExecutionOrderDefault {
@ -568,7 +524,7 @@ extension Scheme.Build: JSONObjectConvertible {
} else {
buildTypes = BuildType.all
}
let target = try TargetReference(string: targetRepr)
let target = try TargetReference(targetRepr)
targets.append(Scheme.BuildTarget(target: target, buildTypes: buildTypes))
}
self.targets = targets.sorted { $0.target.name < $1.target.name }
@ -581,7 +537,7 @@ extension Scheme.Build: JSONObjectConvertible {
extension Scheme.Build: JSONEncodable {
public func toJSONValue() -> Any {
let targetPairs = targets.map { ($0.target.toString(), $0.buildTypes.map { $0.toJSONValue() }) }
let targetPairs = targets.map { ($0.target.reference, $0.buildTypes.map { $0.toJSONValue() }) }
var dict: JSONDictionary = [
"targets": Dictionary(uniqueKeysWithValues: targetPairs),

View File

@ -158,7 +158,11 @@ extension Dictionary where Key == String, Value: Any {
func replaceString(_ template: String, with replacement: String) -> JSONDictionary {
var replaced: JSONDictionary = self
for (key, value) in self {
replaced[key] = replace(value: value, template, with: replacement)
let newKey = key.replacingOccurrences(of: template, with: replacement)
if newKey != key {
replaced.removeValue(forKey: key)
}
replaced[newKey] = replace(value: value, template, with: replacement)
}
return replaced
}

View File

@ -27,6 +27,7 @@ public struct SpecOptions: Equatable {
public var groupSortPosition: GroupSortPosition
public var generateEmptyDirectories: Bool
public var findCarthageFrameworks: Bool
public var localPackagesGroup: String?
public enum ValidationType: String {
case missingConfigs
@ -82,7 +83,8 @@ public struct SpecOptions: Equatable {
transitivelyLinkDependencies: Bool = transitivelyLinkDependenciesDefault,
groupSortPosition: GroupSortPosition = groupSortPositionDefault,
generateEmptyDirectories: Bool = generateEmptyDirectoriesDefault,
findCarthageFrameworks: Bool = findCarthageFrameworksDefault
findCarthageFrameworks: Bool = findCarthageFrameworksDefault,
localPackagesGroup: String? = nil
) {
self.minimumXcodeGenVersion = minimumXcodeGenVersion
self.carthageBuildPath = carthageBuildPath
@ -102,6 +104,7 @@ public struct SpecOptions: Equatable {
self.groupSortPosition = groupSortPosition
self.generateEmptyDirectories = generateEmptyDirectories
self.findCarthageFrameworks = findCarthageFrameworks
self.localPackagesGroup = localPackagesGroup
}
}
@ -129,6 +132,7 @@ extension SpecOptions: JSONObjectConvertible {
groupSortPosition = jsonDictionary.json(atKeyPath: "groupSortPosition") ?? SpecOptions.groupSortPositionDefault
generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? SpecOptions.generateEmptyDirectoriesDefault
findCarthageFrameworks = jsonDictionary.json(atKeyPath: "findCarthageFrameworks") ?? SpecOptions.findCarthageFrameworksDefault
localPackagesGroup = jsonDictionary.json(atKeyPath: "localPackagesGroup")
}
}
@ -149,6 +153,7 @@ extension SpecOptions: JSONEncodable {
"indentWidth": indentWidth.flatMap { Int($0) },
"tabWidth": tabWidth.flatMap { Int($0) },
"defaultConfig": defaultConfig,
"localPackagesGroup": localPackagesGroup,
]
if settingPresets != SpecOptions.settingPresetsDefault {

View File

@ -4,6 +4,7 @@ public enum SpecParsingError: Error, CustomStringConvertible {
case unknownTargetType(String)
case unknownTargetPlatform(String)
case invalidDependency([String: Any])
case unknownPackageRequirement([String: Any])
case invalidSourceBuildPhase(String)
case invalidTargetReference(String)
case invalidVersion(String)
@ -22,6 +23,8 @@ public enum SpecParsingError: Error, CustomStringConvertible {
return "Invalid Target Reference Syntax: \(targetReference)"
case let .invalidVersion(version):
return "Invalid version: \(version)"
case let .unknownPackageRequirement(package):
return "Unknown package requirement: \(package)"
}
}
}

View File

@ -52,6 +52,12 @@ extension Project {
}
}
for package in localPackages {
if !(basePath + Path(package).normalize()).exists {
errors.append(.invalidLocalPackage(package))
}
}
for (config, configFile) in configFiles {
if !options.disabledValidations.contains(.missingConfigFiles) && !(basePath + configFile).exists {
errors.append(.invalidConfigFile(configFile: configFile, config: config))
@ -156,6 +162,10 @@ extension Project {
errors.append(.invalidSDKDependency(target: target.name, dependency: dependency.reference))
}
}
case .package:
if packages[dependency.reference] == nil {
errors.append(.invalidSwiftPackage(name: dependency.reference, target: target.name))
}
default: break
}
}
@ -170,9 +180,15 @@ extension Project {
for scheme in schemes {
for buildTarget in scheme.build.targets {
guard buildTarget.target.location == .local else { continue }
if getProjectTarget(buildTarget.target.name) == nil {
errors.append(.invalidSchemeTarget(scheme: scheme.name, target: buildTarget.target.name))
switch buildTarget.target.location {
case .local:
if getProjectTarget(buildTarget.target.name) == nil {
errors.append(.invalidSchemeTarget(scheme: scheme.name, target: buildTarget.target.name))
}
case .project(let project):
if getProjectReference(project) == nil {
errors.append(.invalidProjectReference(scheme: scheme.name, reference: project))
}
}
}
if let action = scheme.run, let config = action.config, getConfig(config) == nil {

View File

@ -18,6 +18,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidTargetSchemeTest(target: String, testTarget: String)
case invalidSchemeTarget(scheme: String, target: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidSwiftPackage(name: String, target: String)
case invalidLocalPackage(String)
case invalidConfigFile(configFile: String, config: String)
case invalidBuildSettingConfig(String)
case invalidSettingsGroup(String)
@ -27,6 +29,7 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case missingConfigForTargetScheme(target: String, configType: ConfigType)
case missingDefaultConfig(configName: String)
case invalidPerConfigSettings
case invalidProjectReference(scheme: String, reference: String)
case deprecatedUsageOfPlaceholder(placeholderName: String)
public var description: String {
@ -61,12 +64,18 @@ public struct SpecValidationError: Error, CustomStringConvertible {
return "Invalid file group \(group.quoted)"
case let .invalidConfigFileConfig(config):
return "Config file has invalid config \(config.quoted)"
case let .invalidSwiftPackage(name, target):
return "Target \(target.quoted) has an invalid package dependency \(name.quoted)"
case let .invalidLocalPackage(path):
return "Invalid local package \(path.quoted)"
case let .missingConfigForTargetScheme(target, configType):
return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme"
case let .missingDefaultConfig(name):
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"
case let .invalidProjectReference(scheme, project):
return "Scheme \(scheme.quoted) has invalid project reference \(project.quoted)"
case let .deprecatedUsageOfPlaceholder(placeholderName: placeholderName):
return "Usage of $\(placeholderName) is deprecated and will stop working in an upcoming version. Use ${\(placeholderName)} instead."
}

View File

@ -0,0 +1,77 @@
import Foundation
import XcodeProj
import JSONUtilities
public struct SwiftPackage: Equatable {
public typealias VersionRequirement = XCRemoteSwiftPackageReference.VersionRequirement
public let url: String
public let versionRequirement: VersionRequirement
public init(url: String, versionRequirement: VersionRequirement) {
self.url = url
self.versionRequirement = versionRequirement
}
}
extension SwiftPackage: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
url = try jsonDictionary.json(atKeyPath: "url")
versionRequirement = try VersionRequirement(jsonDictionary: jsonDictionary)
}
}
extension SwiftPackage: JSONEncodable {
public func toJSONValue() -> Any {
var dictionary: JSONDictionary = [:]
dictionary["url"] = url
switch versionRequirement {
case .upToNextMajorVersion(let version):
dictionary["majorVersion"] = version
case .upToNextMinorVersion(let version):
dictionary["minorVersion"] = version
case .range(let from, let to):
dictionary["minVersion"] = from
dictionary["maxVersion"] = to
case .exact(let version):
dictionary["exactVersion"] = version
case .branch(let branch):
dictionary["branch"] = branch
case .revision(let revision):
dictionary["revision"] = revision
}
return dictionary
}
}
extension SwiftPackage.VersionRequirement: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
if jsonDictionary["exactVersion"] != nil {
self = try .exact(jsonDictionary.json(atKeyPath: "exactVersion"))
} else if jsonDictionary["version"] != nil {
self = try .exact(jsonDictionary.json(atKeyPath: "version"))
} else if jsonDictionary["revision"] != nil {
self = try .revision(jsonDictionary.json(atKeyPath: "revision"))
} else if jsonDictionary["branch"] != nil {
self = try .branch(jsonDictionary.json(atKeyPath: "branch"))
} else if jsonDictionary["minVersion"] != nil && jsonDictionary["maxVersion"] != nil {
let minimum: String = try jsonDictionary.json(atKeyPath: "minVersion")
let maximum: String = try jsonDictionary.json(atKeyPath: "maxVersion")
self = .range(from: minimum, to: maximum)
} else if jsonDictionary["minorVersion"] != nil {
self = try .upToNextMinorVersion(jsonDictionary.json(atKeyPath: "minorVersion"))
} else if jsonDictionary["majorVersion"] != nil {
self = try .upToNextMajorVersion(jsonDictionary.json(atKeyPath: "majorVersion"))
} else if jsonDictionary["from"] != nil {
self = try .upToNextMajorVersion(jsonDictionary.json(atKeyPath: "from"))
} else {
throw SpecParsingError.unknownPackageRequirement(jsonDictionary)
}
}
}

View File

@ -134,59 +134,7 @@ extension Target: PathContainer {
extension Target {
static func resolveTargetTemplates(jsonDictionary: JSONDictionary) throws -> JSONDictionary {
guard var targetsDictionary: [String: JSONDictionary] = jsonDictionary["targets"] as? [String: JSONDictionary] else {
return jsonDictionary
}
let targetTemplatesDictionary: [String: JSONDictionary] = jsonDictionary["targetTemplates"] as? [String: JSONDictionary] ?? [:]
// Recursively collects all nested template names of a given dictionary.
func collectTemplates(of jsonDictionary: JSONDictionary,
into allTemplates: inout [String],
insertAt insertionIndex: inout Int) {
guard let templates = jsonDictionary["templates"] as? [String] else {
return
}
for template in templates where !allTemplates.contains(template) {
guard let templateDictionary = targetTemplatesDictionary[template] else {
continue
}
allTemplates.insert(template, at: insertionIndex)
collectTemplates(of: templateDictionary, into: &allTemplates, insertAt: &insertionIndex)
insertionIndex += 1
}
}
for (targetName, var target) in targetsDictionary {
var templates: [String] = []
var index: Int = 0
collectTemplates(of: target, into: &templates, insertAt: &index)
if !templates.isEmpty {
var mergedDictionary: JSONDictionary = [:]
for template in templates {
if let templateDictionary = targetTemplatesDictionary[template] {
mergedDictionary = templateDictionary.merged(onto: mergedDictionary)
}
}
target = target.merged(onto: mergedDictionary)
target = target.replaceString("$target_name", with: targetName) // Will be removed in upcoming version
target = target.replaceString("${target_name}", with: targetName)
if let templateAttributes = target["templateAttributes"] as? [String: String] {
for (templateAttribute, value) in templateAttributes {
target = target.replaceString("${\(templateAttribute)}", with: value)
}
}
}
targetsDictionary[targetName] = target
}
var jsonDictionary = jsonDictionary
jsonDictionary["targets"] = targetsDictionary
return jsonDictionary
}
static func resolveMultiplatformTargets(jsonDictionary: JSONDictionary) throws -> JSONDictionary {
static func resolveMultiplatformTargets(jsonDictionary: JSONDictionary) -> JSONDictionary {
guard let targetsDictionary: [String: JSONDictionary] = jsonDictionary["targets"] as? [String: JSONDictionary] else {
return jsonDictionary
}

View File

@ -0,0 +1,57 @@
import Foundation
import JSONUtilities
public struct TargetReference: Hashable {
public var name: String
public var location: Location
public enum Location: Hashable {
case local
case project(String)
}
public init(name: String, location: Location) {
self.name = name
self.location = location
}
}
extension TargetReference {
public init(_ string: String) throws {
let paths = string.split(separator: "/")
switch paths.count {
case 2:
location = .project(String(paths[0]))
name = String(paths[1])
case 1:
location = .local
name = String(paths[0])
default:
throw SpecParsingError.invalidTargetReference(string)
}
}
public static func local(_ name: String) -> TargetReference {
return TargetReference(name: name, location: .local)
}
}
extension TargetReference: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
try! self.init(value)
}
}
extension TargetReference: CustomStringConvertible {
public var reference: String {
switch location {
case .local: return name
case .project(let projectPath):
return "\(projectPath)/\(name)"
}
}
public var description: String {
return reference
}
}

View File

@ -42,7 +42,7 @@ extension TargetScheme: JSONObjectConvertible {
if let targets = jsonDictionary["testTargets"] as? [Any] {
testTargets = try targets.compactMap { target in
if let string = target as? String {
return .init(stringLiteral: string)
return .init(targetReference: try TargetReference(string))
} else if let dictionary = target as? JSONDictionary {
return try .init(jsonDictionary: dictionary)
} else {

View File

@ -0,0 +1,78 @@
import Foundation
import JSONUtilities
struct TemplateStructure {
let baseKey: String
let templatesKey: String
let nameToReplace: String
}
extension Target {
static func resolveTargetTemplates(jsonDictionary: JSONDictionary) -> JSONDictionary {
return resolveTemplates(jsonDictionary: jsonDictionary,
templateStructure: TemplateStructure(baseKey: "targets",
templatesKey: "targetTemplates",
nameToReplace: "target_name"))
}
}
extension Scheme {
static func resolveSchemeTemplates(jsonDictionary: JSONDictionary) -> JSONDictionary {
return resolveTemplates(jsonDictionary: jsonDictionary,
templateStructure: TemplateStructure(baseKey: "schemes",
templatesKey: "schemeTemplates",
nameToReplace: "scheme_name"))
}
}
private func resolveTemplates(jsonDictionary: JSONDictionary, templateStructure: TemplateStructure) -> JSONDictionary {
guard var baseDictionary: [String: JSONDictionary] = jsonDictionary[templateStructure.baseKey] as? [String: JSONDictionary] else {
return jsonDictionary
}
let templatesDictionary: [String: JSONDictionary] = jsonDictionary[templateStructure.templatesKey] as? [String: JSONDictionary] ?? [:]
// Recursively collects all nested template names of a given dictionary.
func collectTemplates(of jsonDictionary: JSONDictionary,
into allTemplates: inout [String],
insertAt insertionIndex: inout Int) {
guard let templates = jsonDictionary["templates"] as? [String] else {
return
}
for template in templates where !allTemplates.contains(template) {
guard let templateDictionary = templatesDictionary[template] else {
continue
}
allTemplates.insert(template, at: insertionIndex)
collectTemplates(of: templateDictionary, into: &allTemplates, insertAt: &insertionIndex)
insertionIndex += 1
}
}
for (referenceName, var reference) in baseDictionary {
var templates: [String] = []
var index: Int = 0
collectTemplates(of: reference, into: &templates, insertAt: &index)
if !templates.isEmpty {
var mergedDictionary: JSONDictionary = [:]
for template in templates {
if let templateDictionary = templatesDictionary[template] {
mergedDictionary = templateDictionary.merged(onto: mergedDictionary)
}
}
reference = reference.merged(onto: mergedDictionary)
reference = reference.replaceString("$\(templateStructure.nameToReplace)", with: referenceName) // Will be removed in upcoming version
reference = reference.replaceString("${\(templateStructure.nameToReplace)}", with: referenceName)
if let templateAttributes = reference["templateAttributes"] as? [String: String] {
for (templateAttribute, value) in templateAttributes {
reference = reference.replaceString("${\(templateAttribute)}", with: value)
}
}
}
baseDictionary[referenceName] = reference
}
var jsonDictionary = jsonDictionary
jsonDictionary[templateStructure.baseKey] = baseDictionary
return jsonDictionary
}

View File

@ -2,6 +2,6 @@ import Foundation
import ProjectSpec
import XcodeGenCLI
let version = Version("2.7.0")
let version = Version("2.9.0")
let cli = XcodeGenCLI(version: version)
cli.execute()

View File

@ -1,20 +0,0 @@
import Foundation
import SwiftCLI
class CommandRouter: Router {
let defaultCommand: Command
init(defaultCommand: Command) {
self.defaultCommand = defaultCommand
}
func parse(commandGroup: CommandGroup, arguments: ArgumentList) throws -> (CommandPath, OptionRegistry) {
if !arguments.hasNext() || arguments.nextIsOption() {
arguments.manipulate { existing in
[defaultCommand.name] + existing
}
}
return try DefaultRouter().parse(commandGroup: commandGroup, arguments: arguments)
}
}

View File

@ -3,7 +3,6 @@ import ProjectSpec
import SwiftCLI
public class XcodeGenCLI {
let cli: CLI
public init(version: Version) {
@ -15,7 +14,7 @@ public class XcodeGenCLI {
description: "Generates Xcode projects",
commands: [generateCommand]
)
cli.parser = Parser(router: CommandRouter(defaultCommand: generateCommand))
cli.parser.routeBehavior = .searchWithFallback(generateCommand)
}
public func execute(arguments: [String]? = nil) {

View File

@ -9,7 +9,7 @@ public class CacheFile {
guard #available(OSX 10.13, *) else { return nil }
let files = Array(Set(project.allFiles))
let files = Set(project.allFiles)
.map { ((try? $0.relativePath(from: project.basePath)) ?? $0).string }
.sorted { $0.localizedStandardCompare($1) == .orderedAscending }
.joined(separator: "\n")

View File

@ -43,7 +43,7 @@ struct CarthageVersionFile: Decodable {
let container = try decoder.container(keyedBy: Platform.self)
data = try Platform.allCases.reduce(into: [:]) { data, platform in
let references = try container.decodeIfPresent([Reference].self, forKey: platform) ?? []
let frameworks = Array(Set(references.map { $0.name })).sorted()
let frameworks = Set(references.map { $0.name }).sorted()
data[platform] = frameworks
}
}

View File

@ -17,6 +17,7 @@ public class PBXProjGenerator {
var targetAggregateObjects: [String: PBXAggregateTarget] = [:]
var targetFileReferences: [String: PBXFileReference] = [:]
var sdkFileReferences: [String: PBXFileReference] = [:]
var packageReferences: [String: XCRemoteSwiftPackageReference] = [:]
var carthageFrameworksByPlatform: [String: Set<PBXFileElement>] = [:]
var frameworkFiles: [PBXFileElement] = []
@ -30,6 +31,7 @@ public class PBXProjGenerator {
sourceGenerator = SourceGenerator(project: project, pbxProj: pbxProj)
}
@discardableResult
func addObject<T: PBXObject>(_ object: T, context: String? = nil) -> T {
pbxProj.add(object: object)
object.context = context
@ -46,6 +48,12 @@ public class PBXProjGenerator {
try sourceGenerator.getFileGroups(path: group)
}
let localPackages = Set(project.localPackages)
for package in localPackages {
let path = project.basePath + Path(package).normalize()
try sourceGenerator.createLocalPackage(path: path)
}
let buildConfigs: [XCBuildConfiguration] = project.configs.map { config in
let buildSettings = project.getProjectBuildSettings(config: config)
var baseConfiguration: PBXFileReference?
@ -149,6 +157,12 @@ public class PBXProjGenerator {
targetAggregateObjects[target.name] = aggregateTarget
}
for (name, package) in project.packages {
let packageReference = XCRemoteSwiftPackageReference(repositoryURL: package.url, versionRequirement: package.versionRequirement)
packageReferences[name] = packageReference
addObject(packageReference)
}
try project.targets.forEach(generateTarget)
try project.aggregateTargets.forEach(generateAggregateTarget)
@ -208,8 +222,9 @@ public class PBXProjGenerator {
let knownRegions = sourceGenerator.knownRegions.sorted()
pbxProject.knownRegions = knownRegions.isEmpty ? ["en"] : knownRegions
pbxProject.packages = packageReferences.sorted { $0.key < $1.key }.map { $1 }
let allTargets: [PBXTarget] = Array(targetObjects.values) + Array(targetAggregateObjects.values)
let allTargets: [PBXTarget] = targetObjects.valueArray + targetAggregateObjects.valueArray
pbxProject.targets = allTargets
.sorted { $0.name < $1.name }
pbxProject.attributes = projectAttributes
@ -415,6 +430,7 @@ public class PBXProjGenerator {
var copyFrameworksReferences: [PBXBuildFile] = []
var copyResourcesReferences: [PBXBuildFile] = []
var copyWatchReferences: [PBXBuildFile] = []
var packageDependencies: [XCSwiftPackageProductDependency] = []
var extensions: [PBXBuildFile] = []
var carthageFrameworksToEmbed: [String] = []
@ -457,10 +473,10 @@ public class PBXProjGenerator {
guard let dependencyTarget = project.getTarget(dependencyTargetName) else { continue }
let dependecyLinkage = dependencyTarget.defaultLinkage
let link = dependency.link ?? (
(dependecyLinkage == .dynamic && target.type != .staticLibrary)
|| (dependecyLinkage == .static && target.type.isExecutable)
)
let link = dependency.link ??
(dependecyLinkage == .dynamic && target.type != .staticLibrary) ||
(dependecyLinkage == .static && target.type.isExecutable)
if link {
let dependencyFile = targetFileReferences[dependencyTarget.name]!
let buildFile = addObject(
@ -604,6 +620,29 @@ public class PBXProjGenerator {
}
}
// Embedding handled by iterating over `carthageDependencies` below
case .package(let product):
guard let packageReference = packageReferences[dependency.reference] else {
return
}
let productName = product ?? dependency.reference
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)
packageDependencies.append(packageDependency)
let link = dependency.link ?? (target.type != .staticLibrary)
if link {
let buildFile = addObject(
PBXBuildFile(product: packageDependency)
)
targetFrameworkBuildFiles.append(buildFile)
}
let targetDependency = addObject(
PBXTargetDependency(product: packageDependency)
)
dependencies.append(targetDependency)
}
}
@ -879,9 +918,9 @@ public class PBXProjGenerator {
let configFrameworkBuildPaths: [String]
if !carthageDependencies.isEmpty {
let carthagePlatformBuildPath = "$(PROJECT_DIR)/" + carthageResolver.buildPath(for: target.platform)
configFrameworkBuildPaths = [carthagePlatformBuildPath] + Array(frameworkBuildPaths).sorted()
configFrameworkBuildPaths = [carthagePlatformBuildPath] + frameworkBuildPaths.sorted()
} else {
configFrameworkBuildPaths = Array(frameworkBuildPaths).sorted()
configFrameworkBuildPaths = frameworkBuildPaths.sorted()
}
// set framework search paths
@ -925,6 +964,7 @@ public class PBXProjGenerator {
targetObject.dependencies = dependencies
targetObject.productName = target.name
targetObject.buildRules = buildRules
targetObject.packageProductDependencies = packageDependencies
targetObject.product = targetFileReference
if !target.isLegacy {
targetObject.productType = target.type
@ -969,7 +1009,7 @@ public class PBXProjGenerator {
switch dependency.type {
case .sdk:
dependencies[dependency.reference] = dependency
case .framework, .carthage:
case .framework, .carthage, .package:
if isTopLevel || dependency.embed == nil {
dependencies[dependency.reference] = dependency
}

View File

@ -2,6 +2,14 @@ import Foundation
import ProjectSpec
import XcodeProj
private func suitableConfig(for type: ConfigType, in project: Project) -> Config {
if let defaultConfig = Config.defaultConfigs.first(where: { $0.type == type }),
project.configs.contains(defaultConfig) {
return defaultConfig
}
return project.configs.first { $0.type == type }!
}
public class SchemeGenerator {
let project: Project
@ -20,6 +28,17 @@ public class SchemeGenerator {
self.pbxProj = pbxProj
}
private var projects: [ProjectReference: PBXProj] = [:]
func getPBXProj(from reference: ProjectReference) throws -> PBXProj {
if let cachedProject = projects[reference] {
return cachedProject
}
let pbxproj = try XcodeProj(pathString: reference.path).pbxproj
projects[reference] = pbxproj
return pbxproj
}
public func generateSchemes() throws -> [XCScheme] {
var xcschemes: [XCScheme] = []
@ -34,8 +53,8 @@ public class SchemeGenerator {
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 debugConfig = suitableConfig(for: .debug, in: project)
let releaseConfig = suitableConfig(for: .release, in: project)
let scheme = Scheme(
name: schemeName,
@ -80,27 +99,39 @@ public class SchemeGenerator {
let projectFilePath: String
switch target.location {
case .project(let project):
guard let externalProject = self.project.getExternalProject(project) else {
fatalError("Unable to find external project named \"\(project)\" in project.yml")
guard let projectReference = self.project.getProjectReference(project) else {
throw SchemeGenerationError.missingProject(project)
}
pbxProj = try XcodeProj(pathString: externalProject.path).pbxproj
projectFilePath = externalProject.path
pbxProj = try getPBXProj(from: projectReference)
projectFilePath = projectReference.path
case .local:
pbxProj = self.pbxProj
projectFilePath = "\(self.project.name).xcodeproj"
}
guard let pbxTarget = pbxProj.targets(named: target.name).first else {
fatalError("Unable to find target named \"\(target.name)\" in \"PBXProj.targets\"")
throw SchemeGenerationError.missingTarget(target, projectPath: projectFilePath)
}
let buildableName: String
switch target.location {
case .project:
buildableName = pbxTarget.productNameWithExtension() ?? pbxTarget.name
case .local:
guard let _buildableName =
project.getTarget(target.name)?.filename ??
project.getAggregateTarget(target.name)?.name else {
fatalError("Unable to determinate \"buildableName\" for build target: \(target)")
}
buildableName = _buildableName
}
let buildableName = pbxTarget.productNameWithExtension() ?? pbxTarget.name
return XCScheme.BuildableReference(
referencedContainer: "container:\(projectFilePath)",
blueprint: pbxTarget,
buildableName: buildableName,
blueprintName: target.name
)
referencedContainer: "container:\(projectFilePath)",
blueprint: pbxTarget,
buildableName: buildableName,
blueprintName: target.name
)
}
func getBuildEntry(_ buildTarget: Scheme.BuildTarget) throws -> XCScheme.BuildAction.Entry {
@ -229,11 +260,26 @@ public class SchemeGenerator {
}
}
enum SchemeGenerationError: Error, CustomStringConvertible {
case missingTarget(TargetReference, projectPath: String)
case missingProject(String)
var description: String {
switch self {
case .missingTarget(let target, let projectPath):
return "Unable to find target named \"\(target)\" in \"\(projectPath)\""
case .missingProject(let project):
return "Unable to find project reference named \"\(project)\" in project.yml"
}
}
}
extension Scheme {
public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(targets: [Scheme.BuildTarget(target: TargetReference(name: target.name, location: .local))]),
build: .init(targets: [Scheme.BuildTarget(target: TargetReference.local(target.name))]),
run: .init(
config: debugConfig,
commandLineArguments: targetScheme.commandLineArguments,

View File

@ -16,6 +16,7 @@ class SourceGenerator {
private var fileReferencesByPath: [String: PBXFileElement] = [:]
private var groupsByPath: [Path: PBXGroup] = [:]
private var variantGroupsByPath: [Path: PBXVariantGroup] = [:]
private var localPackageGroup: PBXGroup?
private let project: Project
let pbxProj: PBXProj
@ -34,12 +35,32 @@ class SourceGenerator {
self.pbxProj = pbxProj
}
@discardableResult
func addObject<T: PBXObject>(_ object: T, context: String? = nil) -> T {
pbxProj.add(object: object)
object.context = context
return object
}
func createLocalPackage(path: Path) throws {
if localPackageGroup == nil {
let groupName = project.options.localPackagesGroup ?? "Packages"
localPackageGroup = addObject(PBXGroup(sourceTree: .sourceRoot, name: groupName))
rootGroups.insert(localPackageGroup!)
}
let fileReference = addObject(
PBXFileReference(
sourceTree: .sourceRoot,
name: path.lastComponent,
lastKnownFileType: "folder",
path: try path.relativePath(from: project.basePath).string
)
)
localPackageGroup!.children.append(fileReference)
}
func getAllSourceFiles(targetType: PBXProductType, sources: [TargetSource]) throws -> [SourceFile] {
return try sources.flatMap { try getSourceFiles(targetType: targetType, targetSource: $0, path: project.basePath + $0.path) }
}
@ -200,7 +221,8 @@ class SourceGenerator {
"xcdatamodeld",
"intentdefinition",
"metal",
"mlmodel":
"mlmodel",
"rcproject":
return .sources
case "h",
"hh",
@ -246,7 +268,7 @@ class SourceGenerator {
for child in children {
// only add the children that aren't already in the cachedGroup
// Check equality by path and sourceTree because XcodeProj.PBXObject.== is very slow.
if !cachedGroupChildren.contains(where: { $0.path == child.path && $0.sourceTree == child.sourceTree }) {
if !cachedGroupChildren.contains(where: { $0.name == child.name && $0.path == child.path && $0.sourceTree == child.sourceTree }) {
cachedGroupChildren.append(child)
}
}

View File

@ -46,6 +46,17 @@ extension Dictionary {
extension Xcode {
public static func fileType(path: Path) -> String? {
return path.extension.flatMap { Xcode.filetype(extension: $0) }
guard let fileExtension = path.extension else { return nil}
switch fileExtension {
// cases that aren't handled (yet) in XcodeProj.
// they can be removed once XcodeProj supports them
case "stringsdict": return "text.plist.stringsdict"
case "tbd": return "sourcecode.text-based-dylib-definition"
case "xpc": return "wrapper.xpc-service"
case "xcfilelist": return "text.xcfilelist"
default:
// fallback to XcodeProj defaults
return Xcode.filetype(extension: fileExtension)
}
}
}

View File

@ -429,7 +429,10 @@
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
@ -474,7 +477,10 @@
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
@ -519,7 +525,10 @@
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
@ -601,7 +610,10 @@
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = macosx;
SKIP_INSTALL = YES;
@ -624,7 +636,10 @@
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = macosx;
SKIP_INSTALL = YES;
@ -646,7 +661,10 @@
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_NAME = Framework;
SDKROOT = appletvos;
SKIP_INSTALL = YES;

View File

@ -0,0 +1,469 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */; };
578E78BC3627CF48FB2CE129 /* App.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = A9601593D0AD02931266A4E5 /* App.xctestplan */; };
78E2E1F9F271C0C4CDA04BD9 /* StaticLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F7AFEF8ECCC678519EA643C /* StaticLibrary.a */; };
9C4AD0711D706FD3ED0E436D /* StaticLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */; };
CE46CBA5671B951B546C8673 /* Codability in Frameworks */ = {isa = PBXBuildFile; productRef = 16E6FE01D5BD99F78D4A17E2 /* Codability */; };
E368431019ABC696E4FFC0CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
29147E1DDAEB1AAC20CB0CF9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F7B09D77DB7447B17DCDA3C4 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3F8D94C4EFC431F646AAFB28;
remoteInfo = StaticLibrary;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
097F2DB5622B591E21BC3C73 /* App.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
26F7EFEE613987D1E1258A60 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3F7AFEF8ECCC678519EA643C /* StaticLibrary.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = StaticLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; };
464ACF8D8F2D9F219BCFD3E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLibrary.swift; sourceTree = "<group>"; };
A9601593D0AD02931266A4E5 /* App.xctestplan */ = {isa = PBXFileReference; path = App.xctestplan; sourceTree = "<group>"; };
C1DE9A872F470EAA65B9B0B0 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
796E7DE873F16699BD82FFEA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CE46CBA5671B951B546C8673 /* Codability in Frameworks */,
78E2E1F9F271C0C4CDA04BD9 /* StaticLibrary.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
17DD374CC81D710476AFF41C /* SPM */ = {
isa = PBXGroup;
children = (
A9601593D0AD02931266A4E5 /* App.xctestplan */,
26F7EFEE613987D1E1258A60 /* AppDelegate.swift */,
4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */,
464ACF8D8F2D9F219BCFD3E7 /* Info.plist */,
);
path = SPM;
sourceTree = "<group>";
};
1FA59BFD192FB5A68D5F587C /* StaticLibrary */ = {
isa = PBXGroup;
children = (
61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */,
);
path = StaticLibrary;
sourceTree = "<group>";
};
218F6C96DF9E182F526258CF = {
isa = PBXGroup;
children = (
AD0F3623091EEA8D1EA3DFF8 /* Packages */,
17DD374CC81D710476AFF41C /* SPM */,
1FA59BFD192FB5A68D5F587C /* StaticLibrary */,
5D68FDDE55EE935627A1B376 /* Products */,
);
sourceTree = "<group>";
};
5D68FDDE55EE935627A1B376 /* Products */ = {
isa = PBXGroup;
children = (
097F2DB5622B591E21BC3C73 /* App.app */,
3F7AFEF8ECCC678519EA643C /* StaticLibrary.a */,
);
name = Products;
sourceTree = "<group>";
};
AD0F3623091EEA8D1EA3DFF8 /* Packages */ = {
isa = PBXGroup;
children = (
C1DE9A872F470EAA65B9B0B0 /* XcodeGen */,
);
name = Packages;
sourceTree = SOURCE_ROOT;
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
3F8D94C4EFC431F646AAFB28 /* StaticLibrary */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3B861439E878E4B7EE6EE131 /* Build configuration list for PBXNativeTarget "StaticLibrary" */;
buildPhases = (
B070E114B1D62BD5A07B61DF /* Sources */,
723C19B61A1AD980BD7C9DF0 /* Copy Swift Objective-C Interface Header */,
);
buildRules = (
);
dependencies = (
D85FFB99444DD260A72DDDA7 /* PBXTargetDependency */,
);
name = StaticLibrary;
packageProductDependencies = (
AF233B61592982A7F6431FC6 /* Codability */,
);
productName = StaticLibrary;
productReference = 3F7AFEF8ECCC678519EA643C /* StaticLibrary.a */;
productType = "com.apple.product-type.library.static";
};
C99E3C420D63D5219CE57E33 /* App */ = {
isa = PBXNativeTarget;
buildConfigurationList = 091BBC493EA2F0A446682C48 /* Build configuration list for PBXNativeTarget "App" */;
buildPhases = (
460F52476B5219D2CDA494C2 /* Sources */,
F77D37B94534F63D9B461F30 /* Resources */,
796E7DE873F16699BD82FFEA /* Frameworks */,
);
buildRules = (
);
dependencies = (
E157C6348B8AD6A28C706801 /* PBXTargetDependency */,
078202CF7B08B66ACF7FEC23 /* PBXTargetDependency */,
);
name = App;
packageProductDependencies = (
16E6FE01D5BD99F78D4A17E2 /* Codability */,
);
productName = App;
productReference = 097F2DB5622B591E21BC3C73 /* App.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
F7B09D77DB7447B17DCDA3C4 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
};
buildConfigurationList = 425866ADA259DB93FC4AF1E3 /* Build configuration list for PBXProject "SPM" */;
compatibilityVersion = "Xcode 10.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 218F6C96DF9E182F526258CF;
packageReferences = (
5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */,
);
projectDirPath = "";
projectRoot = "";
targets = (
C99E3C420D63D5219CE57E33 /* App */,
3F8D94C4EFC431F646AAFB28 /* StaticLibrary */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
F77D37B94534F63D9B461F30 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
578E78BC3627CF48FB2CE129 /* App.xctestplan in Resources */,
E368431019ABC696E4FFC0CF /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
723C19B61A1AD980BD7C9DF0 /* Copy Swift Objective-C Interface Header */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$(DERIVED_SOURCES_DIR)/$(SWIFT_OBJC_INTERFACE_HEADER_NAME)",
);
name = "Copy Swift Objective-C Interface Header";
outputPaths = (
"$(BUILT_PRODUCTS_DIR)/include/$(PRODUCT_MODULE_NAME)/$(SWIFT_OBJC_INTERFACE_HEADER_NAME)",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "ditto \"${SCRIPT_INPUT_FILE_0}\" \"${SCRIPT_OUTPUT_FILE_0}\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
460F52476B5219D2CDA494C2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B070E114B1D62BD5A07B61DF /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9C4AD0711D706FD3ED0E436D /* StaticLibrary.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
078202CF7B08B66ACF7FEC23 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3F8D94C4EFC431F646AAFB28 /* StaticLibrary */;
targetProxy = 29147E1DDAEB1AAC20CB0CF9 /* PBXContainerItemProxy */;
};
D85FFB99444DD260A72DDDA7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = AF233B61592982A7F6431FC6 /* Codability */;
};
E157C6348B8AD6A28C706801 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 16E6FE01D5BD99F78D4A17E2 /* Codability */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
0CCC06807E5CD8361D899B7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
1640ABF22E84A6AB9FFFB0D9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
41B31B6C4A1D9194EC6FFF6B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = SPM/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
);
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7A384B9B9CF42FCF9EF02057 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = SPM/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
);
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
B4F2839AD4756B475B2005F2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
BC33B43DF18620E6CCC43E96 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
091BBC493EA2F0A446682C48 /* Build configuration list for PBXNativeTarget "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
41B31B6C4A1D9194EC6FFF6B /* Debug */,
7A384B9B9CF42FCF9EF02057 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
};
3B861439E878E4B7EE6EE131 /* Build configuration list for PBXNativeTarget "StaticLibrary" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0CCC06807E5CD8361D899B7F /* Debug */,
1640ABF22E84A6AB9FFFB0D9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
};
425866ADA259DB93FC4AF1E3 /* Build configuration list for PBXProject "SPM" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B4F2839AD4756B475B2005F2 /* Debug */,
BC33B43DF18620E6CCC43E96 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/yonaskolb/Codability";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.2.1;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
16E6FE01D5BD99F78D4A17E2 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
productName = Codability;
};
AF233B61592982A7F6431FC6 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
productName = Codability;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = F7B09D77DB7447B17DCDA3C4 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,97 @@
{
"object": {
"pins": [
{
"package": "AEXML",
"repositoryURL": "https://github.com/tadija/AEXML",
"state": {
"branch": null,
"revision": "e4d517844dd03dac557e35d77a8e9ab438de91a6",
"version": "4.4.0"
}
},
{
"package": "Codability",
"repositoryURL": "https://github.com/yonaskolb/Codability",
"state": {
"branch": null,
"revision": "eb5bac78e0679f521c3f058c1eb9be0dd657dadd",
"version": "0.2.1"
}
},
{
"package": "JSONUtilities",
"repositoryURL": "https://github.com/yonaskolb/JSONUtilities.git",
"state": {
"branch": null,
"revision": "128d2ffc22467f69569ef8ff971683e2393191a0",
"version": "4.2.0"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511",
"version": "1.0.0"
}
},
{
"package": "Rainbow",
"repositoryURL": "https://github.com/onevcat/Rainbow.git",
"state": {
"branch": null,
"revision": "9c52c1952e9b2305d4507cf473392ac2d7c9b155",
"version": "3.1.5"
}
},
{
"package": "Shell",
"repositoryURL": "https://github.com/tuist/Shell",
"state": {
"branch": null,
"revision": "d38121f89401db902b0d0bfc30b987e2c84c254e",
"version": "2.0.3"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
"state": {
"branch": null,
"revision": "f14ff47f45642aa5703900980b014c2e9394b6e5",
"version": "0.9.0"
}
},
{
"package": "SwiftCLI",
"repositoryURL": "https://github.com/jakeheis/SwiftCLI.git",
"state": {
"branch": null,
"revision": "5318c37d3cacc8780f50b87a8840a6774320ebdf",
"version": "5.2.2"
}
},
{
"package": "XcodeProj",
"repositoryURL": "https://github.com/tuist/xcodeproj.git",
"state": {
"branch": null,
"revision": "aefcbf59cea5b0831837ed2f385e6dfd80d82cc9",
"version": "7.1.0"
}
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f",
"version": "2.0.0"
}
}
]
},
"version": 1
}

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C99E3C420D63D5219CE57E33"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:SPM.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C99E3C420D63D5219CE57E33"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:SPM.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
</CodeCoverageTargets>
</TestAction>
<LaunchAction
buildConfiguration = "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 = "C99E3C420D63D5219CE57E33"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:SPM.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C99E3C420D63D5219CE57E33"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:SPM.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,23 @@
{
"configurations" : [
{
"id" : "521B6958-2D62-4961-B353-91EF8F252F4B",
"name" : "Configuration 1",
"options" : {
}
}
],
"defaultOptions" : {
"codeCoverage" : false,
"targetForVariableExpansion" : {
"containerPath" : "container:SPM.xcodeproj",
"identifier" : "C99E3C420D63D5219CE57E33",
"name" : "App"
}
},
"testTargets" : [
],
"version" : 1
}

View File

@ -0,0 +1,22 @@
//
// AppDelegate.swift
// SPM
//
// Created by Yonas Kolb on 13/8/19.
// Copyright © 2019 BeemIt. All rights reserved.
//
import UIKit
import Codability
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
}

View File

@ -0,0 +1,64 @@
<?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>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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
import Codability
func doThing() {
_ = AnyCodable.self
}

View File

@ -0,0 +1,22 @@
name: SPM
packages:
Codability:
url: https://github.com/yonaskolb/Codability
majorVersion: 0.2.1
localPackages:
- ../../.. #XcodeGen itself
targets:
App:
type: application
platform: iOS
sources: [SPM]
scheme: {}
dependencies:
- package: Codability
- target: StaticLibrary
StaticLibrary:
type: library.static
platform: iOS
sources: StaticLibrary
dependencies:
- package: Codability

View File

@ -2,26 +2,30 @@
"Mac" : [
{
"name" : "Result",
"hash" : "f14569d60b4947d7467d06912c9969bac4caa5c2b62e19dec21bfd37ae146a9f"
"hash" : "62e4384d0cce7e469b9f0dc7f736c274e671cce26504e299f77918ed0a5233f6",
"swiftToolchainVersion" : "5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
}
],
"watchOS" : [
{
"name" : "Result",
"hash" : "80ed6de84fe4e9a468a10ca021db53f5a91edfa70874fdab0b5c1e61487604ea"
"hash" : "ffdf4671a69099751c25a1d75458a167d80fa6a4a3da54411baa2ae3d27da37d",
"swiftToolchainVersion" : "5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
}
],
"tvOS" : [
{
"name" : "Result",
"hash" : "28ba58a2f0caf9db17d544ea3f01908b3f100bca5777a10c8238386be054b9ce"
"hash" : "c14d1468b641dd44eaa03cd1802dbdd5e428a9529456915e49f0d49208eee73a",
"swiftToolchainVersion" : "5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
}
],
"commitish" : "4.1.0",
"iOS" : [
{
"name" : "Result",
"hash" : "328ec56ff90373242dc848bda9f214eeb58c456ea5e4703855b3f7f207861a24"
"hash" : "78dba477b2c36e23b8578d032c469e95970db392f8ededd1ec1c7dec712f2cdf",
"swiftToolchainVersion" : "5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AE3F93DB94E7208F2F1D9A78"
BuildableName = "Framework_iOS.framework"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
@ -33,7 +33,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AE3F93DB94E7208F2F1D9A78"
BuildableName = "Framework_iOS.framework"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
@ -54,7 +54,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AE3F93DB94E7208F2F1D9A78"
BuildableName = "Framework_iOS.framework"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
@ -80,7 +80,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AE3F93DB94E7208F2F1D9A78"
BuildableName = "Framework_iOS.framework"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>
@ -109,7 +109,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AE3F93DB94E7208F2F1D9A78"
BuildableName = "Framework_iOS.framework"
BuildableName = "Framework.framework"
BlueprintName = "Framework_iOS"
ReferencedContainer = "container:Project.xcodeproj">
</BuildableReference>

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -128,7 +128,7 @@ targets:
App_watchOS:
type: application.watchapp2
platform: watchOS
deploymentTarget: 3.0
deploymentTarget: 4.0
sources:
App_watchOS
settings:
@ -151,7 +151,7 @@ targets:
iMessageApp:
type: application.messages
platform: iOS
sources: iMessage
sources: iMessageApp
scheme: {}
dependencies:
- target: iMessageExtension
@ -159,7 +159,7 @@ targets:
iMessageExtension:
type: app-extension.messages
platform: iOS
sources: iMessage MessagesExtension
sources: iMessageExtension
settings:
PRODUCT_BUNDLE_IDENTIFIER: com.project.iMessageApp.extension
@ -167,7 +167,7 @@ targets:
type: app-extension.messages-sticker-pack
platform: iOS
sources:
- path: iMessage Stickers
- path: iMessageStickers
StaticLibrary_ObjC:
type: library.static

View File

@ -140,7 +140,10 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
@ -158,7 +161,10 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";

View File

@ -8,18 +8,22 @@ import Yams
extension Project {
func generateXcodeProject(file: String = #file, line: Int = #line) throws -> XcodeProj {
func generateXcodeProject(validate: Bool = true, file: String = #file, line: Int = #line) throws -> XcodeProj {
return try doThrowing(file: file, line: line) {
try validate()
if validate {
try self.validate()
}
let generator = ProjectGenerator(project: self)
return try generator.generateXcodeProject()
}
}
func generatePbxProj(file: String = #file, line: Int = #line) throws -> PBXProj {
func generatePbxProj(specValidate: Bool = true, projectValidate: Bool = true, file: String = #file, line: Int = #line) throws -> PBXProj {
return try doThrowing(file: file, line: line) {
let xcodeProject = try generateXcodeProject().pbxproj
try xcodeProject.validate()
let xcodeProject = try generateXcodeProject(validate: specValidate).pbxproj
if projectValidate {
try xcodeProject.validate()
}
return xcodeProject
}
}

View File

@ -9,8 +9,10 @@ class ProjectFixtureTests: XCTestCase {
func testProjectFixture() {
describe {
$0.it("generates") {
$0.it("generates fixtures") {
try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml")
try generateXcodeProject(specPath: fixturePath + "CarthageProject/project.yml")
try generateXcodeProject(specPath: fixturePath + "SPM/project.yml")
}
}
}

View File

@ -907,6 +907,53 @@ class ProjectGeneratorTests: XCTestCase {
try expect(buildFileSettings.compactMap { $0?["ATTRIBUTES"] as? [String] }.first) == ["Weak"]
}
$0.it("generates swift packages") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: "ProjectSpec"), reference: "XcodeGen"),
Dependency(type: .package(product: nil), reference: "Codability"),
]
)
let project = Project(name: "test", targets: [app], packages: [
"XcodeGen": SwiftPackage(url: "http://github.com/yonaskolb/XcodeGen", versionRequirement: .branch("master")),
"Codability": SwiftPackage(url: "http://github.com/yonaskolb/Codability", versionRequirement: .exact("1.0.0")),
], localPackages: ["../XcodeGen"], options: .init(localPackagesGroup: "MyPackages"))
let pbxProject = try project.generatePbxProj(specValidate: false)
guard let nativeTarget = pbxProject.nativeTargets.first(where: { $0.name == app.name }) else {
throw failure("PBXNativeTarget for \(app.name) not found")
}
guard let projectSpecDependency = nativeTarget.packageProductDependencies.first(where: { $0.productName == "ProjectSpec" }) else {
throw failure("XCSwiftPackageProductDependency for \(app.name) not found")
}
try expect(projectSpecDependency.package?.name) == "XcodeGen"
try expect(projectSpecDependency.package?.versionRequirement) == .branch("master")
guard let codabilityDependency = nativeTarget.packageProductDependencies.first(where: { $0.productName == "Codability" }) else {
throw failure("XCSwiftPackageProductDependency for \(app.name) not found")
}
try expect(codabilityDependency.package?.name) == "Codability"
try expect(codabilityDependency.package?.versionRequirement) == .exact("1.0.0")
guard let localPackagesGroup = try pbxProject.getMainGroup().children.first(where: { $0.name == "MyPackages" }) as? PBXGroup else {
throw failure("Group not found")
}
guard let localPackageFile = pbxProject.fileReferences.first(where: { $0.path == "../XcodeGen" }) else {
throw failure("FileReference not found")
}
try expect(localPackagesGroup.children.contains(localPackageFile)) == true
try expect(localPackageFile.lastKnownFileType) == "folder"
}
$0.it("generates info.plist") {
let plist = Plist(path: "Info.plist", attributes: ["UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]])
let tempPath = Path.temporary + "info"

View File

@ -96,6 +96,7 @@ class ProjectSpecTests: XCTestCase {
project.settings = invalidSettings
project.configFiles = ["invalidConfig": "invalidConfigFile"]
project.fileGroups = ["invalidFileGroup"]
project.localPackages = ["invalidLocalPackage"]
project.settingGroups = ["settingGroup1": Settings(
configSettings: ["invalidSettingGroupConfig": [:]],
groups: ["invalidSettingGroupSettingGroup"]
@ -106,6 +107,7 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .invalidConfigFile(configFile: "invalidConfigFile", config: "invalidConfig"))
try expectValidationError(project, .invalidSettingsGroup("invalidSettingGroup"))
try expectValidationError(project, .invalidFileGroup("invalidFileGroup"))
try expectValidationError(project, .invalidLocalPackage("invalidLocalPackage"))
try expectValidationError(project, .invalidSettingsGroup("invalidSettingGroupSettingGroup"))
try expectValidationError(project, .invalidBuildSettingConfig("invalidSettingGroupConfig"))
}
@ -134,7 +136,10 @@ class ProjectSpecTests: XCTestCase {
settings: invalidSettings,
configFiles: ["invalidConfig": "invalidConfigFile"],
sources: ["invalidSource"],
dependencies: [Dependency(type: .target, reference: "invalidDependency")],
dependencies: [
Dependency(type: .target, reference: "invalidDependency"),
Dependency(type: .package(product: nil), reference: "invalidPackage")
],
preBuildScripts: [BuildScript(script: .path("invalidPreBuildScript"), name: "preBuildScript1")],
postCompileScripts: [BuildScript(script: .path("invalidPostCompileScript"))],
postBuildScripts: [BuildScript(script: .path("invalidPostBuildScript"))],
@ -142,6 +147,7 @@ class ProjectSpecTests: XCTestCase {
)]
try expectValidationError(project, .invalidTargetDependency(target: "target1", dependency: "invalidDependency"))
try expectValidationError(project, .invalidSwiftPackage(name: "invalidPackage", target: "target1"))
try expectValidationError(project, .invalidTargetConfigFile(target: "target1", configFile: "invalidConfigFile", config: "invalidConfig"))
try expectValidationError(project, .invalidTargetSchemeTest(target: "target1", testTarget: "invalidTarget"))
try expectValidationError(project, .invalidTargetSource(target: "target1", source: "invalidSource"))
@ -201,7 +207,7 @@ class ProjectSpecTests: XCTestCase {
var project = baseProject
project.schemes = [Scheme(
name: "scheme1",
build: .init(targets: [.init(target: .init(name: "invalidTarget", location: .local))]),
build: .init(targets: [.init(target: "invalidTarget")]),
run: .init(config: "debugInvalid"),
archive: .init(config: "releaseInvalid")
)]
@ -211,6 +217,15 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .invalidSchemeConfig(scheme: "scheme1", config: "releaseInvalid"))
}
$0.it("fails with invalid project reference") {
var project = baseProject
project.schemes = [Scheme(
name: "scheme1",
build: .init(targets: [.init(target: "invalidProjectRef/target1")])
)]
try expectValidationError(project, .invalidProjectReference(scheme: "scheme1", reference: "invalidProjectRef"))
}
$0.it("allows missing optional file") {
var project = baseProject
project.targets = [Target(
@ -249,7 +264,7 @@ class ProjectSpecTests: XCTestCase {
attributes: [:]
)
project.aggregateTargets = [aggregatedTarget]
let buildTarget = Scheme.BuildTarget(target: .init(name: "target1"))
let buildTarget = Scheme.BuildTarget(target: "target1")
let scheme = Scheme(name: "target1-Scheme", build: Scheme.Build(targets: [buildTarget]))
project.schemes = [scheme]
try project.validate()
@ -332,7 +347,7 @@ class ProjectSpecTests: XCTestCase {
name: nil,
outputFiles: ["bar"],
outputFilesCompilerFlags: ["foo"])],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: .init(name: "test target"),
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
@ -370,7 +385,7 @@ class ProjectSpecTests: XCTestCase {
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: false)],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: .init(name: "test target"),
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(targetReference: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
@ -398,7 +413,7 @@ class ProjectSpecTests: XCTestCase {
groups: ["config-setting-group"])],
groups: ["setting-group"])],
schemes: [Scheme(name: "scheme",
build: Scheme.Build(targets: [Scheme.BuildTarget(target: .init(name: "foo"),
build: Scheme.Build(targets: [Scheme.BuildTarget(target: "foo",
buildTypes: [.archiving, .analyzing])],
parallelizeBuild: false,
buildImplicitDependencies: false,
@ -425,7 +440,7 @@ class ProjectSpecTests: XCTestCase {
randomExecutionOrder: false,
parallelizable: false,
commandLineArguments: ["foo": true],
targets: [Scheme.Test.TestTarget(targetReference: .init(name: "foo"),
targets: [Scheme.Test.TestTarget(targetReference: "foo",
randomExecutionOrder: false,
parallelizable: false)],
preActions: [Scheme.ExecutionAction(name: "preAction",
@ -458,6 +473,12 @@ class ProjectSpecTests: XCTestCase {
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]))],
packages: [
"Yams": SwiftPackage(
url: "https://github.com/jpsim/Yams",
versionRequirement: .upToNextMajorVersion("2.0.0"))
],
localPackages: ["../../Package"],
options: SpecOptions(minimumXcodeGenVersion: Version(major: 3, minor: 4, patch: 5),
carthageBuildPath: "carthageBuildPath",
carthageExecutablePath: "carthageExecutablePath",
@ -496,6 +517,7 @@ class ProjectSpecTests: XCTestCase {
try expect(proj.options) == restoredProj.options
try expect(proj.settingGroups) == restoredProj.settingGroups
try expect(proj.targets) == restoredProj.targets
try expect(proj.packages) == restoredProj.packages
try expect(proj) == restoredProj
}

View File

@ -19,6 +19,12 @@ private let framework = Target(
platform: .iOS
)
private let frameworkTest = Target(
name: "MyFrameworkTests",
type: .unitTestBundle,
platform: .iOS
)
private let optionalFramework = Target(
name: "MyOptionalFramework",
type: .framework,
@ -37,7 +43,7 @@ class SchemeGeneratorTests: XCTestCase {
func testSchemes() {
describe {
let buildTarget = Scheme.BuildTarget(target: .init(name: app.name, location: .local))
let buildTarget = Scheme.BuildTarget(target: .local(app.name))
$0.it("generates scheme") {
let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: app.name)
let scheme = Scheme(
@ -92,6 +98,36 @@ class SchemeGeneratorTests: XCTestCase {
try expect(xcscheme.testAction?.selectedDebuggerIdentifier) == XCScheme.defaultDebugger
}
$0.it("generates scheme with multiple configs") {
let configs: [Config] = [
Config(name: "Beta", type: .debug),
Config(name: "Debug", type: .debug),
Config(name: "Production", type: .release),
Config(name: "Release", type: .release),
]
let framework = Target(
name: "MyFramework",
type: .application,
platform: .iOS,
scheme: TargetScheme(testTargets: ["MyFrameworkTests"])
)
let project = Project(
name: "test",
configs: configs,
targets: [framework, frameworkTest]
)
let xcodeProject = try project.generateXcodeProject()
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
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),
@ -234,7 +270,7 @@ class SchemeGeneratorTests: XCTestCase {
try! writer.writePlists()
}
let externalProjectPath = fixturePath + "scheme_test/TestProject.xcodeproj"
let externalProject = ExternalProject(name: "ExternalProject", path: externalProjectPath.string)
let projectReference = ProjectReference(name: "ExternalProject", path: externalProjectPath.string)
let target = Scheme.BuildTarget(target: .init(name: "ExternalTarget", location: .project("ExternalProject")))
let scheme = Scheme(
name: "ExternalProjectScheme",
@ -244,7 +280,7 @@ class SchemeGeneratorTests: XCTestCase {
name: "test",
targets: [],
schemes: [scheme],
externalProjects: [externalProject]
projectReferences: [projectReference]
)
let xcodeProject = try project.generateXcodeProject()
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {

View File

@ -167,6 +167,69 @@ class SourceGeneratorTests: XCTestCase {
try expect(variableGroup.children.filter { $0 == refs.first }.count) == 1
}
}
$0.it("handles localized resources") {
let directories = """
App:
Resources:
en-CA.lproj:
- empty.json
- Localizable.strings
en-US.lproj:
- empty.json
- Localizable.strings
en.lproj:
- empty.json
- Localizable.strings
fonts:
SFUI:
- SFUILight.ttf
"""
try createDirectories(directories)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "App/Resources")])
let options = SpecOptions(createIntermediateGroups: true)
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
let outputXcodeProj = try project.generateXcodeProject()
try outputXcodeProj.write(path: directoryPath)
let inputXcodeProj = try XcodeProj(path: directoryPath)
let pbxProj = inputXcodeProj.pbxproj
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 stringsResourceName = "Localizable.strings"
let jsonResourceName = "empty.json"
guard let stringsVariableGroup = getVariableGroups(stringsResourceName).first else { throw failure("Couldn't find the variable group") }
guard let jsonVariableGroup = getVariableGroups(jsonResourceName).first else { throw failure("Couldn't find the variable group") }
let stringsResource = "en.lproj/Localizable.strings"
let jsonResource = "en-CA.lproj/empty.json"
do {
let refs = getFileReferences(stringsResource)
try expect(refs.count) == 1
try expect(refs.first!.uuid.hasPrefix("TEMP")) == false
try expect(stringsVariableGroup.children.filter { $0 == refs.first }.count) == 1
}
do {
let refs = getFileReferences(jsonResource)
try expect(refs.count) == 1
try expect(refs.first!.uuid.hasPrefix("TEMP")) == false
try expect(jsonVariableGroup.children.filter { $0 == refs.first }.count) == 1
}
}
$0.it("handles duplicate names") {
let directories = """

View File

@ -775,13 +775,13 @@ class SpecLoadingTests: XCTestCase {
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedTargets: [Scheme.BuildTarget] = [
Scheme.BuildTarget(target: .init(name: "Target1"), buildTypes: BuildType.all),
Scheme.BuildTarget(target: .init(name: "Target2"), buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: .init(name: "Target3"), buildTypes: []),
Scheme.BuildTarget(target: .init(name: "Target4"), buildTypes: [.testing]),
Scheme.BuildTarget(target: .init(name: "Target5"), buildTypes: []),
Scheme.BuildTarget(target: .init(name: "Target6"), buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: .init(name: "Target7", location: .project("ExternalProject")), buildTypes: [.running]),
Scheme.BuildTarget(target: "Target1", buildTypes: BuildType.all),
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "Target3", buildTypes: []),
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
Scheme.BuildTarget(target: "Target5", buildTypes: []),
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "ExternalProject/Target7", buildTypes: [.running]),
]
try expect(scheme.name) == "Scheme"
try expect(scheme.build.targets) == expectedTargets
@ -799,7 +799,7 @@ class SpecLoadingTests: XCTestCase {
targets: [
"Target1",
Scheme.Test.TestTarget(
targetReference: .init(name: "Target2", location: .project("ExternalProject")),
targetReference: "ExternalProject/Target2",
randomExecutionOrder: true,
parallelizable: true,
skippedTests: ["Test/testExample()"]
@ -855,6 +855,121 @@ class SpecLoadingTests: XCTestCase {
try expect(scheme.profile?.environmentVariables.isEmpty) == true
}
$0.it("parses scheme templates") {
let targetDictionary: [String: Any] = [
"deploymentTarget": "1.2.0",
"sources": ["targetSource"],
"templates": ["temp2", "temp"],
"templateAttributes": [
"source": "replacedSource",
],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"platform": "iOS",
"sources": [
"templateSource",
["path": "Sources/${target_name}"]
],
],
"temp2": [
"type": "framework",
"platform": "tvOS",
"deploymentTarget": "1.1.0",
"configFiles": [
"debug": "Configs/$target_name/debug.xcconfig",
"release": "Configs/${target_name}/release.xcconfig",
],
"sources": ["${source}"],
],
],
"schemeTemplates": [
"base_scheme": [
"build": [
"parallelizeBuild": false,
"buildImplicitDependencies": false,
"targets": [
"Target${name_1}": "all",
"Target2": "testing",
"Target${name_3}": "none",
"Target4": ["testing": true],
"Target5": ["testing": false],
"Target6": ["test", "analyze"],
],
"preActions": [
[
"script": "${pre-action-name}",
"name": "Before Build ${scheme_name}",
"settingsTarget": "Target${name_1}",
],
],
],
"test": [
"config": "debug",
"targets": [
"Target${name_1}",
[
"name": "Target2",
"parallelizable": true,
"randomExecutionOrder": true,
"skippedTests": ["Test/testExample()"],
],
],
"gatherCoverageData": true,
"disableMainThreadChecker": true,
],
],
],
"schemes": [
"temp2": [
"templates": ["base_scheme"],
"templateAttributes": [
"pre-action-name": "modified-name",
"name_1": "FirstTarget",
"name_3": "ThirdTarget",
],
],
],
])
let scheme = project.schemes.first!
let expectedTargets: [Scheme.BuildTarget] = [
Scheme.BuildTarget(target: "TargetFirstTarget", buildTypes: BuildType.all),
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "TargetThirdTarget", buildTypes: []),
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
Scheme.BuildTarget(target: "Target5", buildTypes: []),
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
]
try expect(scheme.name) == "temp2"
try expect(Set(scheme.build.targets)) == Set(expectedTargets)
try expect(scheme.build.preActions.first?.script) == "modified-name"
try expect(scheme.build.preActions.first?.name) == "Before Build temp2"
try expect(scheme.build.preActions.first?.settingsTarget) == "TargetFirstTarget"
try expect(scheme.build.parallelizeBuild) == false
try expect(scheme.build.buildImplicitDependencies) == false
let expectedTest = Scheme.Test(
config: "debug",
gatherCoverageData: true,
disableMainThreadChecker: true,
targets: [
"TargetFirstTarget",
Scheme.Test.TestTarget(
targetReference: "Target2",
randomExecutionOrder: true,
parallelizable: true,
skippedTests: ["Test/testExample()"]
),
]
)
try expect(scheme.test) == expectedTest
}
$0.it("parses settings") {
let project = try Project(path: fixturePath + "settings_test.yml")
let buildSettings: BuildSettings = ["SETTING": "value"]
@ -962,6 +1077,39 @@ class SpecLoadingTests: XCTestCase {
let parsedSpec = try getProjectSpec(dictionary)
try expect(parsedSpec) == expected
}
$0.it("parses packages") {
let project = Project(name: "spm", packages: [
"package1": SwiftPackage(url: "package.git", versionRequirement: .exact("1.2.2")),
"package2": SwiftPackage(url: "package.git", versionRequirement: .upToNextMajorVersion("1.2.2")),
"package3": SwiftPackage(url: "package.git", versionRequirement: .upToNextMinorVersion("1.2.2")),
"package4": SwiftPackage(url: "package.git", versionRequirement: .branch("master")),
"package5": SwiftPackage(url: "package.git", versionRequirement: .revision("x")),
"package6": SwiftPackage(url: "package.git", versionRequirement: .range(from: "1.2.0", to: "1.2.5")),
"package7": SwiftPackage(url: "package.git", versionRequirement: .exact("1.2.2")),
],
localPackages: ["../../Package"],
options: .init(localPackagesGroup: "MyPackages"))
let dictionary: [String: Any] = [
"name": "spm",
"options": [
"localPackagesGroup": "MyPackages"
],
"packages": [
"package1": ["url": "package.git", "exactVersion": "1.2.2"],
"package2": ["url": "package.git", "majorVersion": "1.2.2"],
"package3": ["url": "package.git", "minorVersion": "1.2.2"],
"package4": ["url": "package.git", "branch": "master"],
"package5": ["url": "package.git", "revision": "x"],
"package6": ["url": "package.git", "minVersion": "1.2.0", "maxVersion": "1.2.5"],
"package7": ["url": "package.git", "version": "1.2.2"],
],
"localPackages": ["../../Package"]
]
let parsedSpec = try getProjectSpec(dictionary)
try expect(parsedSpec) == project
}
}
}

View File

@ -3,3 +3,4 @@ set -e
swift run xcodegen --spec Tests/Fixtures/TestProject/project.yml
swift run xcodegen --spec Tests/Fixtures/CarthageProject/project.yml
swift run xcodegen --spec Tests/Fixtures/SPM/project.yml