Support for Swift Packages (#624)

Support for Swift Packages
This commit is contained in:
Yonas Kolb 2019-09-28 00:31:21 +10:00 committed by GitHub
commit c780ed3c2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1319 additions and 36 deletions

View File

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

View File

@ -23,6 +23,7 @@
- [Aggregate Target](#aggregate-target)
- [Target Template](#target-template)
- [Scheme](#scheme)
- [Swift Package](#swift-package)
## General
@ -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
@ -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:
@ -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**:
@ -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
@ -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.
@ -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
```

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

@ -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"
}
},
{

View File

@ -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: [

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]
@ -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] = [:],
@ -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
@ -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)
}
}
@ -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 {
@ -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),
@ -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
}
}

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 invalidVersion(String)
@ -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)"
}
}
}

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

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)
@ -61,6 +63,10 @@ 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):

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

@ -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,6 +222,7 @@ 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] = targetObjects.valueArray + targetAggregateObjects.valueArray
pbxProject.targets = allTargets
@ -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)
}
}
@ -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

@ -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) }
}

View File

@ -0,0 +1,457 @@
// !$*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,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

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

@ -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"))
@ -458,6 +464,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 +508,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

@ -960,6 +960,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