Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Swift Packages #624

Merged
merged 16 commits into from
Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

#### 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)
- Added `language` and `region` options for `run` and `test` scheme [#654](https://github.com/yonaskolb/XcodeGen/pull/654)
- Added `debugEnabled` option for `run` and `test` scheme [#657](https://github.com/yonaskolb/XcodeGen/pull/657)
- 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 All @@ -16,6 +17,7 @@

#### 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

## 2.7.0

Expand Down
51 changes: 50 additions & 1 deletion Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- [Aggregate Target](#aggregate-target)
- [Target Template](#target-template)
- [Scheme](#scheme)
- [Swift Package](#swift-package)

## General

Expand All @@ -46,6 +47,8 @@ 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.

### Include

Expand Down Expand Up @@ -117,6 +120,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:
Expand Down Expand Up @@ -373,6 +377,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**:

Expand All @@ -396,7 +401,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
Expand Down Expand Up @@ -433,6 +438,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.
Expand Down Expand Up @@ -756,3 +780,28 @@ schemes:
customArchiveName: MyTarget
revealArchiveInOrganizer: false
```

## 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
```
40 changes: 38 additions & 2 deletions Docs/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
"repositoryURL": "https://github.com/tuist/xcodeproj.git",
"state": {
"branch": null,
"revision": "b951777f42e9acbfb8f19da623b43aaa604422f9",
"version": "7.0.0"
"revision": "0f563e2d7d604499e7b57a28c78ff23d5c545acd",
"version": "7.1.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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/tuist/xcodeproj.git", .exact("7.1.0")),
.package(url: "https://github.com/jakeheis/SwiftCLI.git", .exact("5.2.2")),
],
targets: [
Expand Down
7 changes: 7 additions & 0 deletions Sources/ProjectSpec/Dependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public struct Dependency: Equatable {
case framework
case carthage(findFrameworks: Bool?)
case sdk(root: String?)
case package(product: String?)
}
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -114,6 +119,8 @@ extension Dependency: JSONEncodable {
}
case .sdk:
dict["sdk"] = reference
case .package:
dict["package"] = reference
}

return dict
Expand Down
47 changes: 33 additions & 14 deletions Sources/ProjectSpec/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -41,6 +44,8 @@ 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] = [:],
Expand All @@ -56,6 +61,8 @@ 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
Expand Down Expand Up @@ -126,6 +133,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)
}
}
Expand Down Expand Up @@ -160,6 +169,12 @@ extension Project {
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 {
Expand Down Expand Up @@ -187,6 +202,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),
Expand Down Expand Up @@ -242,19 +258,22 @@ extension Project: JSONEncodable {
let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) }
let schemesPairs = schemes.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() },
]
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() }

return dictionary
}
}
7 changes: 6 additions & 1 deletion Sources/ProjectSpec/SpecOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -102,6 +104,7 @@ public struct SpecOptions: Equatable {
self.groupSortPosition = groupSortPosition
self.generateEmptyDirectories = generateEmptyDirectories
self.findCarthageFrameworks = findCarthageFrameworks
self.localPackagesGroup = localPackagesGroup
}
}

Expand Down Expand Up @@ -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")
}
}

Expand All @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProjectSpec/SpecParsingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 invalidVersion(String)

Expand All @@ -19,6 +20,8 @@ public enum SpecParsingError: Error, CustomStringConvertible {
return "Invalid Source Build Phase: \(error)"
case let .invalidVersion(version):
return "Invalid version: \(version)"
case let .unknownPackageRequirement(package):
return "Unknown package requirement: \(package)"
}
}
}
Loading