2017-07-23 21:58:03 +03:00
|
|
|
import Foundation
|
|
|
|
import PathKit
|
2017-10-01 12:42:07 +03:00
|
|
|
import xcproj
|
2017-07-23 21:58:03 +03:00
|
|
|
import JSONUtilities
|
|
|
|
import Yams
|
2017-07-28 17:31:53 +03:00
|
|
|
import ProjectSpec
|
2017-07-23 21:58:03 +03:00
|
|
|
|
|
|
|
public class PBXProjGenerator {
|
|
|
|
|
2017-07-28 17:31:53 +03:00
|
|
|
let spec: ProjectSpec
|
2017-09-19 14:14:38 +03:00
|
|
|
let currentXcodeVersion: String
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let proj: PBXProj
|
|
|
|
let sourceGenerator: SourceGenerator
|
|
|
|
let referenceGenerator = ReferenceGenerator()
|
2017-07-23 21:58:03 +03:00
|
|
|
|
|
|
|
var targetNativeReferences: [String: String] = [:]
|
2017-10-17 04:18:28 +03:00
|
|
|
var targetBuildFiles: [String: PBXBuildFile] = [:]
|
2017-07-23 21:58:03 +03:00
|
|
|
var targetFileReferences: [String: String] = [:]
|
2017-11-15 03:19:53 +03:00
|
|
|
var topLevelGroups: Set<String> = []
|
2017-10-25 22:05:53 +03:00
|
|
|
var carthageFrameworksByPlatform: [String: Set<String>] = [:]
|
2017-07-25 02:02:54 +03:00
|
|
|
var frameworkFiles: [String] = []
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
var generated = false
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-08-25 17:22:40 +03:00
|
|
|
var carthageBuildPath: String {
|
|
|
|
return spec.options.carthageBuildPath ?? "Carthage/Build"
|
|
|
|
}
|
|
|
|
|
2017-10-24 22:30:06 +03:00
|
|
|
public init(spec: ProjectSpec, currentXcodeVersion: String) {
|
2017-09-19 14:14:38 +03:00
|
|
|
self.currentXcodeVersion = currentXcodeVersion
|
2017-07-23 21:58:03 +03:00
|
|
|
self.spec = spec
|
2017-11-13 23:33:48 +03:00
|
|
|
proj = PBXProj(objectVersion: 46, rootObject: referenceGenerator.generate(PBXProject.self, spec.name))
|
2017-11-22 14:48:44 +03:00
|
|
|
sourceGenerator = SourceGenerator(spec: spec, referenceGenerator: referenceGenerator) { _ in }
|
2017-11-13 02:15:07 +03:00
|
|
|
sourceGenerator.addObject = { [weak self] object in
|
2017-11-12 20:52:11 +03:00
|
|
|
self?.addObject(object)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
func addObject(_ object: PBXObject) {
|
2017-11-16 03:54:07 +03:00
|
|
|
proj.objects.addObject(object)
|
2017-08-25 14:48:07 +03:00
|
|
|
}
|
|
|
|
|
2017-07-23 21:58:03 +03:00
|
|
|
public func generate() throws -> PBXProj {
|
2017-11-12 20:52:11 +03:00
|
|
|
if generated {
|
|
|
|
fatalError("Cannot use PBXProjGenerator to generate more than once")
|
|
|
|
}
|
|
|
|
generated = true
|
2017-09-24 20:35:26 +03:00
|
|
|
for group in spec.fileGroups {
|
2017-11-12 20:52:11 +03:00
|
|
|
try sourceGenerator.getFileGroups(path: group)
|
2017-09-23 23:38:50 +03:00
|
|
|
}
|
|
|
|
|
2017-07-27 00:06:34 +03:00
|
|
|
let buildConfigs: [XCBuildConfiguration] = spec.configs.map { config in
|
|
|
|
let buildSettings = spec.getProjectBuildSettings(config: config)
|
2017-09-23 23:48:50 +03:00
|
|
|
var baseConfigurationReference: String?
|
|
|
|
if let configPath = spec.configFiles[config.name] {
|
2017-11-15 18:59:29 +03:00
|
|
|
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: spec.basePath + configPath)
|
2017-09-23 23:48:50 +03:00
|
|
|
}
|
2017-11-12 20:52:11 +03:00
|
|
|
return XCBuildConfiguration(reference: referenceGenerator.generate(XCBuildConfiguration.self, config.name), name: config.name, baseConfigurationReference: baseConfigurationReference, buildSettings: buildSettings)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2017-07-26 13:51:55 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildConfigList = XCConfigurationList(reference: referenceGenerator.generate(XCConfigurationList.self, spec.name), buildConfigurations: buildConfigs.references, defaultConfigurationName: buildConfigs.first?.name ?? "", defaultConfigurationIsVisible: 0)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
buildConfigs.forEach(addObject)
|
|
|
|
addObject(buildConfigList)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
|
|
|
for target in spec.targets {
|
2017-11-12 20:52:11 +03:00
|
|
|
targetNativeReferences[target.name] = referenceGenerator.generate(PBXNativeTarget.self, target.name)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let fileReference = PBXFileReference(reference: referenceGenerator.generate(PBXFileReference.self, target.name), sourceTree: .buildProductsDir, explicitFileType: target.type.fileExtension, path: target.filename, includeInIndex: 0)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(fileReference)
|
2017-07-23 21:58:03 +03:00
|
|
|
targetFileReferences[target.name] = fileReference.reference
|
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, fileReference.reference), fileRef: fileReference.reference)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(buildFile)
|
2017-10-17 04:18:28 +03:00
|
|
|
targetBuildFiles[target.name] = buildFile
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-07-24 14:35:21 +03:00
|
|
|
let targets = try spec.targets.map(generateTarget)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let productGroup = PBXGroup(reference: referenceGenerator.generate(PBXGroup.self, "Products"), children: Array(targetFileReferences.values), sourceTree: .group, name: "Products")
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(productGroup)
|
2017-11-15 03:19:53 +03:00
|
|
|
topLevelGroups.insert(productGroup.reference)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-07-25 01:32:36 +03:00
|
|
|
if !carthageFrameworksByPlatform.isEmpty {
|
|
|
|
var platforms: [PBXGroup] = []
|
|
|
|
for (platform, fileReferences) in carthageFrameworksByPlatform {
|
2017-11-15 11:46:41 +03:00
|
|
|
let platformGroup = PBXGroup(reference: referenceGenerator.generate(PBXGroup.self, "Carthage" + platform), children: fileReferences.sorted(), sourceTree: .group, name: platform, path: platform)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(platformGroup)
|
2017-07-25 01:32:36 +03:00
|
|
|
platforms.append(platformGroup)
|
|
|
|
}
|
2017-11-12 20:52:11 +03:00
|
|
|
let carthageGroup = PBXGroup(reference: referenceGenerator.generate(PBXGroup.self, "Carthage"), children: platforms.references.sorted(), sourceTree: .group, name: "Carthage", path: carthageBuildPath)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(carthageGroup)
|
2017-08-26 23:15:41 +03:00
|
|
|
frameworkFiles.append(carthageGroup.reference)
|
2017-07-25 01:32:36 +03:00
|
|
|
}
|
|
|
|
|
2017-07-25 02:02:54 +03:00
|
|
|
if !frameworkFiles.isEmpty {
|
2017-11-12 20:52:11 +03:00
|
|
|
let group = PBXGroup(reference: referenceGenerator.generate(PBXGroup.self, "Frameworks"), children: frameworkFiles, sourceTree: .group, name: "Frameworks")
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(group)
|
2017-11-15 03:19:53 +03:00
|
|
|
topLevelGroups.insert(group.reference)
|
2017-07-25 02:02:54 +03:00
|
|
|
}
|
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
for rootGroup in sourceGenerator.rootGroups {
|
2017-11-15 03:19:53 +03:00
|
|
|
topLevelGroups.insert(rootGroup)
|
2017-11-12 20:52:11 +03:00
|
|
|
}
|
|
|
|
|
2017-11-15 03:19:53 +03:00
|
|
|
let mainGroup = PBXGroup(reference: referenceGenerator.generate(PBXGroup.self, "Project"), children: Array(topLevelGroups), sourceTree: .group)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(mainGroup)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
sortGroups(group: mainGroup)
|
|
|
|
|
2017-09-19 14:14:38 +03:00
|
|
|
let projectAttributes: [String: Any] = ["LastUpgradeCheck": currentXcodeVersion].merged(spec.attributes)
|
2017-10-10 21:39:18 +03:00
|
|
|
let root = PBXProject(name: spec.name,
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: proj.rootObject,
|
2017-08-30 18:06:41 +03:00
|
|
|
buildConfigurationList: buildConfigList.reference,
|
|
|
|
compatibilityVersion: "Xcode 3.2",
|
|
|
|
mainGroup: mainGroup.reference,
|
2017-11-15 14:13:32 +03:00
|
|
|
developmentRegion: spec.options.developmentLanguage ?? "en",
|
2017-11-16 00:17:45 +03:00
|
|
|
knownRegions: sourceGenerator.knownRegions.sorted(),
|
2017-09-14 22:25:12 +03:00
|
|
|
targets: targets.references,
|
2017-08-30 18:06:41 +03:00
|
|
|
attributes: projectAttributes)
|
2017-11-16 03:54:07 +03:00
|
|
|
proj.objects.projects.append(root)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
return proj
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
func sortGroups(group: PBXGroup) {
|
|
|
|
// sort children
|
2017-12-05 22:57:31 +03:00
|
|
|
let children = group.children
|
|
|
|
.flatMap { proj.objects.getFileElement(reference: $0) }
|
2017-11-12 20:52:11 +03:00
|
|
|
.sorted { child1, child2 in
|
|
|
|
if child1.sortOrder == child2.sortOrder {
|
2017-12-05 22:57:31 +03:00
|
|
|
return child1.nameOrPath < child2.nameOrPath
|
2017-11-12 20:52:11 +03:00
|
|
|
} else {
|
|
|
|
return child1.sortOrder < child2.sortOrder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
group.children = children.map { $0.reference }.filter { $0 != group.reference }
|
|
|
|
|
|
|
|
// sort sub groups
|
2017-11-16 03:54:07 +03:00
|
|
|
let childGroups = group.children.flatMap { proj.objects.groups[$0] }
|
2017-11-12 20:52:11 +03:00
|
|
|
childGroups.forEach(sortGroups)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-07-31 13:29:56 +03:00
|
|
|
func generateTarget(_ target: Target) throws -> PBXNativeTarget {
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-15 11:46:41 +03:00
|
|
|
sourceGenerator.targetName = target.name
|
2017-10-02 02:29:32 +03:00
|
|
|
let carthageDependencies = getAllCarthageDependencies(target: target)
|
2017-08-27 00:40:42 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let sourceFiles = try sourceGenerator.getAllSourceFiles(sources: target.sources)
|
2017-07-26 13:40:25 +03:00
|
|
|
|
2017-10-31 17:46:47 +03:00
|
|
|
// find all Info.plist files
|
|
|
|
let infoPlists: [Path] = target.sources.map { spec.basePath + $0.path }.flatMap { (path) -> [Path] in
|
|
|
|
if path.isFile {
|
|
|
|
if path.lastComponent == "Info.plist" {
|
|
|
|
return [path]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if let children = try? path.recursiveChildren() {
|
|
|
|
return children.filter { $0.lastComponent == "Info.plist" }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return []
|
2017-08-27 13:28:18 +03:00
|
|
|
}
|
|
|
|
|
2017-07-27 00:06:34 +03:00
|
|
|
let configs: [XCBuildConfiguration] = spec.configs.map { config in
|
2017-08-27 00:40:42 +03:00
|
|
|
var buildSettings = spec.getTargetBuildSettings(target: target, config: config)
|
|
|
|
|
2017-08-27 13:28:18 +03:00
|
|
|
// automatically set INFOPLIST_FILE path
|
2017-09-23 22:29:12 +03:00
|
|
|
if let plistPath = infoPlists.first,
|
2017-10-24 22:30:06 +03:00
|
|
|
!spec.targetHasBuildSetting("INFOPLIST_FILE", basePath: spec.basePath, target: target, config: config) {
|
|
|
|
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: spec.basePath)
|
2017-08-27 13:28:18 +03:00
|
|
|
}
|
|
|
|
|
2017-09-28 15:31:23 +03:00
|
|
|
// automatically calculate bundle id
|
|
|
|
if let bundleIdPrefix = spec.options.bundleIdPrefix,
|
2017-10-24 22:30:06 +03:00
|
|
|
!spec.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: spec.basePath, target: target, config: config) {
|
2017-09-25 22:08:15 +03:00
|
|
|
let characterSet = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-.")).inverted
|
|
|
|
let escapedTargetName = target.name.replacingOccurrences(of: "_", with: "-").components(separatedBy: characterSet).joined(separator: "")
|
|
|
|
buildSettings["PRODUCT_BUNDLE_IDENTIFIER"] = bundleIdPrefix + "." + escapedTargetName
|
2017-09-24 22:21:57 +03:00
|
|
|
}
|
|
|
|
|
2017-09-28 15:31:23 +03:00
|
|
|
// automatically set test target name
|
|
|
|
if target.type == .uiTestBundle,
|
2017-10-24 22:30:06 +03:00
|
|
|
!spec.targetHasBuildSetting("TEST_TARGET_NAME", basePath: spec.basePath, target: target, config: config) {
|
2017-09-28 15:31:23 +03:00
|
|
|
for dependency in target.dependencies {
|
|
|
|
if dependency.type == .target,
|
|
|
|
let dependencyTarget = spec.getTarget(dependency.reference),
|
|
|
|
dependencyTarget.type == .application {
|
|
|
|
buildSettings["TEST_TARGET_NAME"] = dependencyTarget.name
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-27 00:40:42 +03:00
|
|
|
// set Carthage search paths
|
2017-10-02 02:29:32 +03:00
|
|
|
if !carthageDependencies.isEmpty {
|
2017-08-27 00:40:42 +03:00
|
|
|
let frameworkSearchPaths = "FRAMEWORK_SEARCH_PATHS"
|
|
|
|
let carthagePlatformBuildPath = "$(PROJECT_DIR)/" + getCarthageBuildPath(platform: target.platform)
|
|
|
|
var newSettings: [String] = []
|
|
|
|
if var array = buildSettings[frameworkSearchPaths] as? [String] {
|
|
|
|
array.append(carthagePlatformBuildPath)
|
|
|
|
buildSettings[frameworkSearchPaths] = array
|
|
|
|
} else if let string = buildSettings[frameworkSearchPaths] as? String {
|
|
|
|
buildSettings[frameworkSearchPaths] = [string, carthagePlatformBuildPath]
|
|
|
|
} else {
|
2017-08-27 13:31:45 +03:00
|
|
|
buildSettings[frameworkSearchPaths] = ["$(inherited)", carthagePlatformBuildPath]
|
2017-08-27 00:40:42 +03:00
|
|
|
}
|
|
|
|
}
|
2017-07-27 00:06:34 +03:00
|
|
|
|
2017-08-27 00:40:42 +03:00
|
|
|
var baseConfigurationReference: String?
|
2017-07-27 15:41:18 +03:00
|
|
|
if let configPath = target.configFiles[config.name] {
|
2017-11-16 13:49:30 +03:00
|
|
|
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: spec.basePath + configPath)
|
2017-07-24 19:03:41 +03:00
|
|
|
}
|
2017-11-12 20:52:11 +03:00
|
|
|
return XCBuildConfiguration(reference: referenceGenerator.generate(XCBuildConfiguration.self, config.name + target.name), name: config.name, baseConfigurationReference: baseConfigurationReference, buildSettings: buildSettings)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2017-08-25 14:48:07 +03:00
|
|
|
configs.forEach(addObject)
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildConfigList = XCConfigurationList(reference: referenceGenerator.generate(XCConfigurationList.self, target.name), buildConfigurations: configs.references, defaultConfigurationName: "")
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(buildConfigList)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-10-02 18:08:39 +03:00
|
|
|
var dependencies: [String] = []
|
2017-07-25 02:02:54 +03:00
|
|
|
var targetFrameworkBuildFiles: [String] = []
|
2017-10-17 06:59:44 +03:00
|
|
|
var copyFrameworksReferences: [String] = []
|
|
|
|
var copyResourcesReferences: [String] = []
|
2017-10-17 07:28:08 +03:00
|
|
|
var copyWatchReferences: [String] = []
|
2017-08-03 21:03:56 +03:00
|
|
|
var extensions: [String] = []
|
|
|
|
|
2017-09-29 18:34:01 +03:00
|
|
|
for dependency in target.dependencies {
|
2017-08-24 18:41:55 +03:00
|
|
|
|
2017-09-29 18:34:01 +03:00
|
|
|
let embed = dependency.embed ?? (target.type.isApp ? true : false)
|
|
|
|
switch dependency.type {
|
2017-08-27 12:53:16 +03:00
|
|
|
case .target:
|
2017-09-29 18:34:01 +03:00
|
|
|
let dependencyTargetName = dependency.reference
|
2017-08-03 21:03:56 +03:00
|
|
|
guard let dependencyTarget = spec.getTarget(dependencyTargetName) else { continue }
|
|
|
|
let dependencyFileReference = targetFileReferences[dependencyTargetName]!
|
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let targetProxy = PBXContainerItemProxy(reference: referenceGenerator.generate(PBXContainerItemProxy.self, target.name), containerPortal: proj.rootObject, remoteGlobalIDString: targetNativeReferences[dependencyTargetName]!, proxyType: .nativeTarget, remoteInfo: dependencyTargetName)
|
|
|
|
let targetDependency = PBXTargetDependency(reference: referenceGenerator.generate(PBXTargetDependency.self, dependencyTargetName + target.name), target: targetNativeReferences[dependencyTargetName]!, targetProxy: targetProxy.reference)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(targetProxy)
|
2017-09-29 18:34:01 +03:00
|
|
|
addObject(targetDependency)
|
2017-10-02 18:08:39 +03:00
|
|
|
dependencies.append(targetDependency.reference)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-10-26 01:55:49 +03:00
|
|
|
if (dependencyTarget.type.isLibrary || dependencyTarget.type.isFramework) && dependency.link {
|
2017-10-17 04:18:28 +03:00
|
|
|
let dependencyBuildFile = targetBuildFiles[dependencyTargetName]!
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, dependencyBuildFile.reference + target.name), fileRef: dependencyBuildFile.fileRef!)
|
2017-10-17 04:18:28 +03:00
|
|
|
addObject(buildFile)
|
|
|
|
targetFrameworkBuildFiles.append(buildFile.reference)
|
|
|
|
}
|
2017-07-24 00:35:28 +03:00
|
|
|
|
2017-10-17 04:18:46 +03:00
|
|
|
if embed && !dependencyTarget.type.isLibrary {
|
|
|
|
|
2017-09-29 18:34:01 +03:00
|
|
|
let embedSettings = dependency.buildSettings
|
2017-11-12 20:52:11 +03:00
|
|
|
let embedFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, dependencyFileReference + target.name), fileRef: dependencyFileReference, settings: embedSettings)
|
2017-08-24 18:41:55 +03:00
|
|
|
addObject(embedFile)
|
|
|
|
|
2017-08-03 21:03:56 +03:00
|
|
|
if dependencyTarget.type.isExtension {
|
2017-08-24 18:41:55 +03:00
|
|
|
// embed app extension
|
2017-08-03 21:03:56 +03:00
|
|
|
extensions.append(embedFile.reference)
|
2017-10-17 06:59:44 +03:00
|
|
|
} else if dependencyTarget.type.isFramework {
|
|
|
|
copyFrameworksReferences.append(embedFile.reference)
|
2017-10-17 07:28:08 +03:00
|
|
|
} else if dependencyTarget.type.isApp && dependencyTarget.platform == .watchOS {
|
|
|
|
copyWatchReferences.append(embedFile.reference)
|
2017-08-03 21:03:56 +03:00
|
|
|
} else {
|
2017-10-17 06:59:44 +03:00
|
|
|
copyResourcesReferences.append(embedFile.reference)
|
2017-08-03 21:03:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-27 12:53:16 +03:00
|
|
|
case .framework:
|
2017-11-21 09:47:57 +03:00
|
|
|
let fileReference: String
|
2017-11-21 15:51:04 +03:00
|
|
|
if dependency.implicit {
|
2017-11-21 09:47:57 +03:00
|
|
|
fileReference = sourceGenerator.getFileReference(path: Path(dependency.reference), inPath: spec.basePath, sourceTree: .buildProductsDir)
|
|
|
|
} else {
|
|
|
|
fileReference = sourceGenerator.getFileReference(path: Path(dependency.reference), inPath: spec.basePath)
|
|
|
|
}
|
2017-08-24 18:41:55 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(buildFile)
|
2017-08-24 18:41:55 +03:00
|
|
|
|
2017-07-25 02:02:54 +03:00
|
|
|
targetFrameworkBuildFiles.append(buildFile.reference)
|
|
|
|
if !frameworkFiles.contains(fileReference) {
|
|
|
|
frameworkFiles.append(fileReference)
|
|
|
|
}
|
2017-08-24 18:41:55 +03:00
|
|
|
|
|
|
|
if embed {
|
2017-11-12 20:52:11 +03:00
|
|
|
let embedFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference, settings: dependency.buildSettings)
|
2017-08-24 18:41:55 +03:00
|
|
|
addObject(embedFile)
|
2017-10-17 06:59:44 +03:00
|
|
|
copyFrameworksReferences.append(embedFile.reference)
|
2017-08-24 18:41:55 +03:00
|
|
|
}
|
2017-08-27 12:53:16 +03:00
|
|
|
case .carthage:
|
2017-08-27 00:40:42 +03:00
|
|
|
var platformPath = Path(getCarthageBuildPath(platform: target.platform))
|
2017-09-29 18:34:01 +03:00
|
|
|
var frameworkPath = platformPath + dependency.reference
|
2017-07-25 02:02:54 +03:00
|
|
|
if frameworkPath.extension == nil {
|
|
|
|
frameworkPath = Path(frameworkPath.string + ".framework")
|
|
|
|
}
|
2017-11-12 20:52:11 +03:00
|
|
|
let fileReference = sourceGenerator.getFileReference(path: frameworkPath, inPath: platformPath)
|
2017-07-25 01:32:36 +03:00
|
|
|
|
2017-11-12 20:52:11 +03:00
|
|
|
let buildFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(buildFile)
|
2017-10-25 22:05:53 +03:00
|
|
|
carthageFrameworksByPlatform[target.platform.carthageDirectoryName, default: []].insert(fileReference)
|
2017-07-25 01:32:36 +03:00
|
|
|
|
2017-07-25 02:02:54 +03:00
|
|
|
targetFrameworkBuildFiles.append(buildFile.reference)
|
2017-10-01 13:52:57 +03:00
|
|
|
if target.platform == .macOS && target.type.isApp {
|
2017-11-12 20:52:11 +03:00
|
|
|
let embedFile = PBXBuildFile(reference: referenceGenerator.generate(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference, settings: dependency.buildSettings)
|
2017-10-01 03:23:45 +03:00
|
|
|
addObject(embedFile)
|
2017-10-17 06:59:44 +03:00
|
|
|
copyFrameworksReferences.append(embedFile.reference)
|
2017-10-01 03:23:45 +03:00
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let fileReference = targetFileReferences[target.name]!
|
|
|
|
var buildPhases: [String] = []
|
|
|
|
|
2017-09-14 22:25:12 +03:00
|
|
|
func getBuildFilesForPhase(_ buildPhase: BuildPhase) -> [String] {
|
2017-11-03 17:01:53 +03:00
|
|
|
let files = sourceFiles
|
2017-11-15 03:19:53 +03:00
|
|
|
.filter { $0.buildPhase == buildPhase }
|
2017-11-03 17:01:53 +03:00
|
|
|
.sorted { $0.path.lastComponent < $1.path.lastComponent }
|
2017-09-25 16:24:32 +03:00
|
|
|
files.forEach { addObject($0.buildFile) }
|
2017-09-14 22:25:12 +03:00
|
|
|
return files.map { $0.buildFile.reference }
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-08-26 16:00:39 +03:00
|
|
|
func getBuildScript(buildScript: BuildScript) throws -> PBXShellScriptBuildPhase {
|
2017-08-01 19:40:40 +03:00
|
|
|
|
2017-08-28 18:05:50 +03:00
|
|
|
var shellScript: String
|
2017-08-26 16:00:39 +03:00
|
|
|
switch buildScript.script {
|
2017-08-01 19:40:40 +03:00
|
|
|
case let .path(path):
|
2017-10-24 22:30:06 +03:00
|
|
|
shellScript = try (spec.basePath + path).read()
|
2017-08-01 19:40:40 +03:00
|
|
|
case let .script(script):
|
|
|
|
shellScript = script
|
|
|
|
}
|
2017-08-28 18:05:50 +03:00
|
|
|
shellScript = shellScript.replacingOccurrences(of: "\"", with: "\\\"") // TODO: remove when xcodeproj escaped values
|
2017-08-25 12:37:14 +03:00
|
|
|
let shellScriptPhase = PBXShellScriptBuildPhase(
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: referenceGenerator.generate(PBXShellScriptBuildPhase.self, String(describing: buildScript.name) + shellScript + target.name),
|
2017-08-01 19:40:40 +03:00
|
|
|
files: [],
|
2017-08-26 16:00:39 +03:00
|
|
|
name: buildScript.name ?? "Run Script",
|
2017-09-14 22:25:12 +03:00
|
|
|
inputPaths: buildScript.inputFiles,
|
|
|
|
outputPaths: buildScript.outputFiles,
|
2017-09-19 13:34:22 +03:00
|
|
|
shellPath: buildScript.shell ?? "/bin/sh",
|
2017-08-25 12:37:14 +03:00
|
|
|
shellScript: shellScript)
|
2017-08-26 16:00:39 +03:00
|
|
|
shellScriptPhase.runOnlyForDeploymentPostprocessing = buildScript.runOnlyWhenInstalling ? 1 : 0
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(shellScriptPhase)
|
2017-08-01 19:40:40 +03:00
|
|
|
buildPhases.append(shellScriptPhase.reference)
|
|
|
|
return shellScriptPhase
|
|
|
|
}
|
|
|
|
|
2017-08-26 16:00:39 +03:00
|
|
|
_ = try target.prebuildScripts.map(getBuildScript)
|
2017-08-01 19:40:40 +03:00
|
|
|
|
2017-11-13 00:17:08 +03:00
|
|
|
let sourcesBuildPhaseFiles = getBuildFilesForPhase(.sources)
|
|
|
|
if !sourcesBuildPhaseFiles.isEmpty {
|
|
|
|
let sourcesBuildPhase = PBXSourcesBuildPhase(reference: referenceGenerator.generate(PBXSourcesBuildPhase.self, target.name), files: sourcesBuildPhaseFiles)
|
|
|
|
addObject(sourcesBuildPhase)
|
|
|
|
buildPhases.append(sourcesBuildPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-13 00:17:08 +03:00
|
|
|
let resourcesBuildPhaseFiles = getBuildFilesForPhase(.resources) + copyResourcesReferences
|
|
|
|
if !resourcesBuildPhaseFiles.isEmpty {
|
|
|
|
let resourcesBuildPhase = PBXResourcesBuildPhase(reference: referenceGenerator.generate(PBXResourcesBuildPhase.self, target.name), files: resourcesBuildPhaseFiles)
|
|
|
|
addObject(resourcesBuildPhase)
|
|
|
|
buildPhases.append(resourcesBuildPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-11-13 00:17:08 +03:00
|
|
|
let headersBuildPhaseFiles = getBuildFilesForPhase(.headers)
|
|
|
|
if !headersBuildPhaseFiles.isEmpty && (target.type == .framework || target.type == .dynamicLibrary) {
|
|
|
|
let headersBuildPhase = PBXHeadersBuildPhase(reference: referenceGenerator.generate(PBXHeadersBuildPhase.self, target.name), files: headersBuildPhaseFiles)
|
2017-10-31 18:55:04 +03:00
|
|
|
addObject(headersBuildPhase)
|
|
|
|
buildPhases.append(headersBuildPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-08-03 21:06:21 +03:00
|
|
|
if !targetFrameworkBuildFiles.isEmpty {
|
|
|
|
|
|
|
|
let frameworkBuildPhase = PBXFrameworksBuildPhase(
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: referenceGenerator.generate(PBXFrameworksBuildPhase.self, target.name),
|
2017-09-14 22:25:12 +03:00
|
|
|
files: targetFrameworkBuildFiles,
|
2017-08-03 21:06:21 +03:00
|
|
|
runOnlyForDeploymentPostprocessing: 0)
|
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(frameworkBuildPhase)
|
2017-08-03 21:06:21 +03:00
|
|
|
buildPhases.append(frameworkBuildPhase.reference)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !extensions.isEmpty {
|
|
|
|
|
|
|
|
let copyFilesPhase = PBXCopyFilesBuildPhase(
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: referenceGenerator.generate(PBXCopyFilesBuildPhase.self, "embed app extensions" + target.name),
|
2017-08-03 21:06:21 +03:00
|
|
|
dstPath: "",
|
|
|
|
dstSubfolderSpec: .plugins,
|
2017-09-14 22:25:12 +03:00
|
|
|
files: extensions)
|
2017-08-03 21:06:21 +03:00
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(copyFilesPhase)
|
2017-08-03 21:06:21 +03:00
|
|
|
buildPhases.append(copyFilesPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-10-17 06:59:44 +03:00
|
|
|
if !copyFrameworksReferences.isEmpty {
|
2017-08-03 21:06:21 +03:00
|
|
|
|
|
|
|
let copyFilesPhase = PBXCopyFilesBuildPhase(
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: referenceGenerator.generate(PBXCopyFilesBuildPhase.self, "embed frameworks" + target.name),
|
2017-08-03 21:06:21 +03:00
|
|
|
dstPath: "",
|
|
|
|
dstSubfolderSpec: .frameworks,
|
2017-10-17 06:59:44 +03:00
|
|
|
files: copyFrameworksReferences)
|
2017-08-03 21:06:21 +03:00
|
|
|
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(copyFilesPhase)
|
2017-08-03 21:06:21 +03:00
|
|
|
buildPhases.append(copyFilesPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-10-17 07:28:08 +03:00
|
|
|
if !copyWatchReferences.isEmpty {
|
|
|
|
|
|
|
|
let copyFilesPhase = PBXCopyFilesBuildPhase(
|
2017-11-12 20:52:11 +03:00
|
|
|
reference: referenceGenerator.generate(PBXCopyFilesBuildPhase.self, "embed watch content" + target.name),
|
2017-10-17 07:28:08 +03:00
|
|
|
dstPath: "$(CONTENTS_FOLDER_PATH)/Watch",
|
|
|
|
dstSubfolderSpec: .productsDirectory,
|
|
|
|
files: copyWatchReferences)
|
|
|
|
|
|
|
|
addObject(copyFilesPhase)
|
|
|
|
buildPhases.append(copyFilesPhase.reference)
|
|
|
|
}
|
|
|
|
|
2017-10-05 04:53:08 +03:00
|
|
|
let carthageFrameworksToEmbed = Array(Set(carthageDependencies
|
2017-10-31 17:46:47 +03:00
|
|
|
.filter { $0.embed ?? true }
|
|
|
|
.map { $0.reference }))
|
2017-10-05 04:53:08 +03:00
|
|
|
.sorted()
|
|
|
|
|
2017-10-02 02:29:32 +03:00
|
|
|
if !carthageFrameworksToEmbed.isEmpty {
|
2017-07-25 02:24:14 +03:00
|
|
|
|
2017-09-29 18:51:08 +03:00
|
|
|
if target.type.isApp && target.platform != .macOS {
|
2017-10-05 04:53:08 +03:00
|
|
|
let inputPaths = carthageFrameworksToEmbed.map { "$(SRCROOT)/\(carthageBuildPath)/\(target.platform)/\($0)\($0.contains(".") ? "" : ".framework")" }
|
|
|
|
let outputPaths = carthageFrameworksToEmbed.map { "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/\($0)\($0.contains(".") ? "" : ".framework")" }
|
2017-11-12 20:52:11 +03:00
|
|
|
let carthageScript = PBXShellScriptBuildPhase(reference: referenceGenerator.generate(PBXShellScriptBuildPhase.self, "Carthage" + target.name), files: [], name: "Carthage", inputPaths: inputPaths, outputPaths: outputPaths, shellPath: "/bin/sh", shellScript: "/usr/local/bin/carthage copy-frameworks\n")
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(carthageScript)
|
2017-07-25 02:24:14 +03:00
|
|
|
buildPhases.append(carthageScript.reference)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 16:00:39 +03:00
|
|
|
_ = try target.postbuildScripts.map(getBuildScript)
|
2017-08-01 19:40:40 +03:00
|
|
|
|
2017-07-23 21:58:03 +03:00
|
|
|
let nativeTarget = PBXNativeTarget(
|
|
|
|
reference: targetNativeReferences[target.name]!,
|
2017-10-24 21:30:44 +03:00
|
|
|
name: target.name,
|
2017-07-23 21:58:03 +03:00
|
|
|
buildConfigurationList: buildConfigList.reference,
|
|
|
|
buildPhases: buildPhases,
|
|
|
|
buildRules: [],
|
2017-10-02 18:08:39 +03:00
|
|
|
dependencies: dependencies,
|
2017-07-23 21:58:03 +03:00
|
|
|
productReference: fileReference,
|
|
|
|
productType: target.type)
|
2017-08-25 14:48:07 +03:00
|
|
|
addObject(nativeTarget)
|
2017-07-24 14:35:21 +03:00
|
|
|
return nativeTarget
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-08-27 00:40:42 +03:00
|
|
|
func getCarthageBuildPath(platform: Platform) -> String {
|
|
|
|
|
|
|
|
let carthagePath = Path(carthageBuildPath)
|
2017-09-29 18:23:01 +03:00
|
|
|
let platformName = platform.carthageDirectoryName
|
2017-08-27 00:40:42 +03:00
|
|
|
return "\(carthagePath)/\(platformName)"
|
|
|
|
}
|
|
|
|
|
2017-11-12 22:39:35 +03:00
|
|
|
func getAllCarthageDependencies(target: Target, visitedTargets: [String: Bool] = [:]) -> [Dependency] {
|
|
|
|
|
|
|
|
// this is used to resolve cyclical target dependencies
|
|
|
|
var visitedTargets = visitedTargets
|
|
|
|
visitedTargets[target.name] = true
|
|
|
|
|
2017-10-02 02:29:32 +03:00
|
|
|
var frameworks: [Dependency] = []
|
2017-11-12 22:39:35 +03:00
|
|
|
|
2017-08-27 00:40:42 +03:00
|
|
|
for dependency in target.dependencies {
|
2017-08-24 18:41:55 +03:00
|
|
|
switch dependency.type {
|
2017-10-02 02:29:32 +03:00
|
|
|
case .carthage:
|
|
|
|
frameworks.append(dependency)
|
2017-08-24 18:41:55 +03:00
|
|
|
case .target:
|
2017-11-12 22:39:35 +03:00
|
|
|
let targetName = dependency.reference
|
|
|
|
if visitedTargets[targetName] == true {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
if let target = spec.getTarget(targetName) {
|
|
|
|
frameworks += getAllCarthageDependencies(target: target, visitedTargets: visitedTargets)
|
2017-08-27 00:40:42 +03:00
|
|
|
}
|
|
|
|
default: break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return frameworks
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|