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 External Target Reference #655

Merged
merged 43 commits into from
Oct 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
213deb8
Use pbxTarget methods to get buildableName instead of using project.yml
kateinoigakukun Sep 14, 2019
c4d63e5
Extract projectName and pbxProj dependency from getBuildEntry
kateinoigakukun Sep 14, 2019
67b3256
Support external project file for build target
kateinoigakukun Sep 14, 2019
599a2c8
Add test case for scheme generation
kateinoigakukun Sep 14, 2019
66bdcdb
Parse externalProject in build scheme
kateinoigakukun Sep 14, 2019
4e087b6
Add test case for parsing externalProject in project spec
kateinoigakukun Sep 14, 2019
0efb1be
Update ProjectSpec.md
kateinoigakukun Sep 14, 2019
8e378ae
CHANGELOG.md
kateinoigakukun Sep 14, 2019
6397368
Merge branch 'master' into external-target-ref
kateinoigakukun Sep 14, 2019
e4844e9
Fix default value JSON encoding
kateinoigakukun Sep 15, 2019
30fc642
Introduce ProjectName/Target syntax to reference target
kateinoigakukun Sep 22, 2019
5f02e68
Fix test cases for external project
kateinoigakukun Sep 22, 2019
cd1d370
Merge branch 'master' into external-target-ref
kateinoigakukun Sep 22, 2019
b96d077
Merge branch 'master' into external-target-ref
kateinoigakukun Sep 30, 2019
1bf6d76
Revert unnecesasry changes
kateinoigakukun Sep 30, 2019
7d18052
Revert "Update ProjectSpec.md"
kateinoigakukun Sep 30, 2019
566d538
Update docs
kateinoigakukun Sep 30, 2019
2ec09a7
Update fixtures
kateinoigakukun Sep 30, 2019
0a43435
Merge branch 'master' into external-target-ref
kateinoigakukun Oct 10, 2019
236c7c4
Add Hashable conformance for TargetReference
kateinoigakukun Oct 10, 2019
d1bed71
Fix unit test build error for TargetReference
kateinoigakukun Oct 10, 2019
d978cf4
Rename ExternalProject -> ProjectReference
kateinoigakukun Oct 15, 2019
5561378
Move TargetReference to it's own file
kateinoigakukun Oct 15, 2019
e0dfc72
Conform TargetReference to CustomStringConvertible
kateinoigakukun Oct 15, 2019
1c64092
Improve TargetReference initializer interface
kateinoigakukun Oct 15, 2019
c2ab781
Fix buildableName generation logic
kateinoigakukun Oct 15, 2019
f49a172
Update Fixture
kateinoigakukun Oct 15, 2019
552af50
Replace External Project with Project Reference in ProjectSpec.md
kateinoigakukun Oct 15, 2019
be9c6a2
Merge branch 'master' into external-target-ref
kateinoigakukun Oct 15, 2019
d7864ff
Update CHANGELOG.md
kateinoigakukun Oct 15, 2019
4d8ffe7
Remove file header
kateinoigakukun Oct 21, 2019
7075777
Make 'let' properties as 'var'
kateinoigakukun Oct 21, 2019
70cb2df
Minimize protocol conformance declarations
kateinoigakukun Oct 21, 2019
c432337
Throw error instead of fatalError
kateinoigakukun Oct 21, 2019
0866203
Merge branch 'master' into external-target-ref
kateinoigakukun Oct 21, 2019
3166751
Revert 'Throw error instead of fatalError'
kateinoigakukun Oct 22, 2019
89e5c56
Merge branch 'external-target-ref' of github.com:kateinoigakukun/Xcod…
kateinoigakukun Oct 22, 2019
848dfe2
Re-revert 'Throw error instead of fatalError' partially
kateinoigakukun Oct 23, 2019
c4305dd
Add validation for project reference
kateinoigakukun Oct 27, 2019
1285a3e
Rename TargetReference.init label
kateinoigakukun Oct 27, 2019
fbc7e94
Use propery initializer for TestTarget
kateinoigakukun Oct 27, 2019
d3560e7
Use convenience initializer for TargetReference.local
kateinoigakukun Oct 27, 2019
bbed01d
Cache pbxproj by reference name
kateinoigakukun Oct 27, 2019
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## Next Version

#### Added
- Support Target Reference to another project. [#655](https://github.com/yonaskolb/XcodeGen/pull/655) @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
Expand Down
19 changes: 19 additions & 0 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ You can also use environment variables in your configuration file, by using `${S
- [ ] **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

Expand Down Expand Up @@ -839,3 +840,21 @@ targets:
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"]
```
19 changes: 18 additions & 1 deletion Sources/ProjectSpec/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ public struct Project: BuildSettingsContainer {
public var fileGroups: [String]
public var configFiles: [String: String]
public var include: [String] = []
public var projectReferences: [ProjectReference] = [] {
didSet {
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}
}

private var targetsMap: [String: Target]
private var aggregateTargetsMap: [String: AggregateTarget]
private var projectReferencesMap: [String: ProjectReference]

public init(
basePath: Path = "",
Expand All @@ -49,7 +55,8 @@ public struct Project: BuildSettingsContainer {
options: SpecOptions = SpecOptions(),
fileGroups: [String] = [],
configFiles: [String: String] = [:],
attributes: [String: Any] = [:]
attributes: [String: Any] = [:],
projectReferences: [ProjectReference] = []
) {
self.basePath = basePath
self.name = name
Expand All @@ -67,6 +74,12 @@ public struct Project: BuildSettingsContainer {
self.fileGroups = fileGroups
self.configFiles = configFiles
self.attributes = attributes
self.projectReferences = projectReferences
projectReferencesMap = Dictionary(uniqueKeysWithValues: self.projectReferences.map { ($0.name, $0) })
}

public func getProjectReference(_ projectName: String) -> ProjectReference? {
return projectReferencesMap[projectName]
}

public func getTarget(_ targetName: String) -> Target? {
Expand Down Expand Up @@ -164,6 +177,7 @@ 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 }
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") ?? [:]
Expand All @@ -182,6 +196,7 @@ extension Project {
}
targetsMap = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
aggregateTargetsMap = Dictionary(uniqueKeysWithValues: aggregateTargets.map { ($0.name, $0) })
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}

static func resolveProject(jsonDictionary: JSONDictionary) -> JSONDictionary {
Expand Down Expand Up @@ -258,6 +273,7 @@ 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 projectReferencesPairs = projectReferences.map { ($0.name, $0.toJSONValue()) }

var dictionary: JSONDictionary = [:]
dictionary["name"] = name
Expand All @@ -274,6 +290,7 @@ extension Project: JSONEncodable {
dictionary["aggregateTargets"] = Dictionary(uniqueKeysWithValues: aggregateTargetsPairs)
dictionary["schemes"] = Dictionary(uniqueKeysWithValues: schemesPairs)
dictionary["settingGroups"] = settingGroups.mapValues { $0.toJSONValue() }
dictionary["projectReferences"] = projectReferencesPairs

return dictionary
}
Expand Down
27 changes: 27 additions & 0 deletions Sources/ProjectSpec/ProjectReference.swift
Original file line number Diff line number Diff line change
@@ -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,
]
}
}
41 changes: 24 additions & 17 deletions Sources/ProjectSpec/Scheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,33 @@ public struct Scheme: Equatable {
public static let randomExecutionOrderDefault = false
public static let parallelizableDefault = false

public let name: String
public var name: String { return targetReference.name }
public let targetReference: TargetReference
public var randomExecutionOrder: Bool
public var parallelizable: Bool
public var skippedTests: [String]

public init(
name: String,
targetReference: TargetReference,
randomExecutionOrder: Bool = randomExecutionOrderDefault,
parallelizable: Bool = parallelizableDefault,
skippedTests: [String] = []
) {
self.name = name
self.targetReference = targetReference
self.randomExecutionOrder = randomExecutionOrder
self.parallelizable = parallelizable
self.skippedTests = skippedTests
}

public init(stringLiteral value: String) {
name = value
randomExecutionOrder = false
parallelizable = false
skippedTests = []
do {
targetReference = try TargetReference(value)
randomExecutionOrder = false
parallelizable = false
skippedTests = []
} catch {
fatalError(SpecParsingError.invalidTargetReference(value).description)
}
}
}

Expand Down Expand Up @@ -239,10 +244,10 @@ public struct Scheme: Equatable {
}

public struct BuildTarget: Equatable, Hashable {
public var target: String
public var target: TargetReference
public var buildTypes: [BuildType]

public init(target: String, buildTypes: [BuildType] = BuildType.all) {
public init(target: TargetReference, buildTypes: [BuildType] = BuildType.all) {
self.target = target
self.buildTypes = buildTypes
}
Expand Down Expand Up @@ -320,7 +325,7 @@ extension Scheme.Test: JSONObjectConvertible {
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
if let string = target as? String {
return TestTarget(name: string)
return try TestTarget(targetReference: TargetReference(string))
} else if let dictionary = target as? JSONDictionary {
return try TestTarget(jsonDictionary: dictionary)
} else {
Expand Down Expand Up @@ -371,7 +376,7 @@ extension Scheme.Test: JSONEncodable {
extension Scheme.Test.TestTarget: JSONObjectConvertible {

public init(jsonDictionary: JSONDictionary) throws {
name = try 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") ?? []
Expand All @@ -380,12 +385,13 @@ extension Scheme.Test.TestTarget: JSONObjectConvertible {

extension Scheme.Test.TestTarget: JSONEncodable {
public func toJSONValue() -> Any {
if !randomExecutionOrder && !parallelizable {
return name
if randomExecutionOrder == Scheme.Test.TestTarget.randomExecutionOrderDefault,
parallelizable == Scheme.Test.TestTarget.parallelizableDefault {
return targetReference.reference
}

var dict: JSONDictionary = [
"name": name,
"name": targetReference.reference,
]

if randomExecutionOrder != Scheme.Test.TestTarget.randomExecutionOrderDefault {
Expand Down Expand Up @@ -496,7 +502,7 @@ extension Scheme.Build: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
let targetDictionary: JSONDictionary = try jsonDictionary.json(atKeyPath: "targets")
var targets: [Scheme.BuildTarget] = []
for (target, possibleBuildTypes) in targetDictionary {
for (targetRepr, possibleBuildTypes) in targetDictionary {
let buildTypes: [BuildType]
if let string = possibleBuildTypes as? String {
switch string {
Expand All @@ -513,9 +519,10 @@ extension Scheme.Build: JSONObjectConvertible {
} else {
buildTypes = BuildType.all
}
let target = try TargetReference(targetRepr)
targets.append(Scheme.BuildTarget(target: target, buildTypes: buildTypes))
}
self.targets = targets.sorted { $0.target < $1.target }
self.targets = targets.sorted { $0.target.name < $1.target.name }
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? Scheme.Build.parallelizeBuildDefault
Expand All @@ -525,7 +532,7 @@ extension Scheme.Build: JSONObjectConvertible {

extension Scheme.Build: JSONEncodable {
public func toJSONValue() -> Any {
let targetPairs = targets.map { ($0.target, $0.buildTypes.map { $0.toJSONValue() }) }
let targetPairs = targets.map { ($0.target.reference, $0.buildTypes.map { $0.toJSONValue() }) }

var dict: JSONDictionary = [
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProjectSpec/SpecParsingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public enum SpecParsingError: Error, CustomStringConvertible {
case invalidDependency([String: Any])
case unknownPackageRequirement([String: Any])
case invalidSourceBuildPhase(String)
case invalidTargetReference(String)
case invalidVersion(String)

public var description: String {
Expand All @@ -18,6 +19,8 @@ public enum SpecParsingError: Error, CustomStringConvertible {
return "Unknown Target dependency: \(dependency)"
case let .invalidSourceBuildPhase(error):
return "Invalid Source Build Phase: \(error)"
case let .invalidTargetReference(targetReference):
return "Invalid Target Reference Syntax: \(targetReference)"
case let .invalidVersion(version):
return "Invalid version: \(version)"
case let .unknownPackageRequirement(package):
Expand Down
11 changes: 9 additions & 2 deletions Sources/ProjectSpec/SpecValidation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,15 @@ extension Project {

for scheme in schemes {
for buildTarget in scheme.build.targets {
if getProjectTarget(buildTarget.target) == nil {
errors.append(.invalidSchemeTarget(scheme: scheme.name, target: buildTarget.target))
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 {
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProjectSpec/SpecValidationError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,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 {
Expand Down Expand Up @@ -73,6 +74,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
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."
}
Expand Down
57 changes: 57 additions & 0 deletions Sources/ProjectSpec/TargetReference.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 1 addition & 1 deletion Sources/ProjectSpec/TargetScheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(name: string)
return .init(targetReference: try TargetReference(string))
} else if let dictionary = target as? JSONDictionary {
return try .init(jsonDictionary: dictionary)
} else {
Expand Down
Loading