2017-07-23 21:58:03 +03:00
|
|
|
import Foundation
|
2018-01-24 10:22:46 +03:00
|
|
|
import JSONUtilities
|
2017-07-23 21:58:03 +03:00
|
|
|
import PathKit
|
2018-01-24 10:22:46 +03:00
|
|
|
import ProjectSpec
|
2017-10-01 12:42:07 +03:00
|
|
|
import xcproj
|
2017-07-23 21:58:03 +03:00
|
|
|
import Yams
|
|
|
|
|
|
|
|
public class PBXProjGenerator {
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let project: Project
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let pbxProj: PBXProj
|
2018-01-24 10:22:46 +03:00
|
|
|
var sourceGenerator: SourceGenerator!
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
var targetObjects: [String: ObjectReference<PBXTarget>] = [:]
|
|
|
|
var targetBuildFiles: [String: ObjectReference<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 {
|
2018-04-12 16:13:40 +03:00
|
|
|
return project.options.carthageBuildPath ?? "Carthage/Build"
|
2017-08-25 17:22:40 +03:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
public init(project: Project) {
|
|
|
|
self.project = project
|
|
|
|
pbxProj = PBXProj(rootObject: "", objectVersion: 46)
|
|
|
|
sourceGenerator = SourceGenerator(project: project) { [unowned self] id, object in
|
2018-01-24 10:22:46 +03:00
|
|
|
self.addObject(id: id, object)
|
2017-11-12 20:52:11 +03:00
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
func addObject(id: String, _ object: PBXObject) -> String {
|
2018-04-12 16:13:40 +03:00
|
|
|
let reference = pbxProj.objects.generateReference(object, id)
|
|
|
|
pbxProj.objects.addObject(object, reference: reference)
|
2018-01-24 10:22:46 +03:00
|
|
|
return reference
|
|
|
|
}
|
|
|
|
|
|
|
|
func createObject<T>(id: String, _ object: T) -> ObjectReference<T> {
|
|
|
|
let reference = addObject(id: id, object)
|
|
|
|
return ObjectReference(reference: reference, object: 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
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
for group in project.fileGroups {
|
2017-11-12 20:52:11 +03:00
|
|
|
try sourceGenerator.getFileGroups(path: group)
|
2017-09-23 23:38:50 +03:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let buildConfigs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
|
|
|
|
let buildSettings = project.getProjectBuildSettings(config: config)
|
2017-09-23 23:48:50 +03:00
|
|
|
var baseConfigurationReference: String?
|
2018-04-12 16:13:40 +03:00
|
|
|
if let configPath = project.configFiles[config.name] {
|
|
|
|
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: project.basePath + configPath)
|
2017-09-23 23:48:50 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
return createObject(
|
|
|
|
id: config.name,
|
|
|
|
XCBuildConfiguration(
|
|
|
|
name: config.name,
|
|
|
|
baseConfigurationReference: baseConfigurationReference,
|
|
|
|
buildSettings: buildSettings
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2017-07-26 13:51:55 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let configName = project.options.defaultConfig ?? buildConfigs.first?.object.name ?? ""
|
2018-01-24 10:22:46 +03:00
|
|
|
let buildConfigList = createObject(
|
2018-04-12 16:13:40 +03:00
|
|
|
id: project.name,
|
2018-01-24 10:22:46 +03:00
|
|
|
XCConfigurationList(
|
|
|
|
buildConfigurations: buildConfigs.map { $0.reference },
|
2018-03-19 23:13:31 +03:00
|
|
|
defaultConfigurationName: configName
|
2018-01-24 10:22:46 +03:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let mainGroup = createObject(
|
|
|
|
id: "Project",
|
|
|
|
PBXGroup(
|
|
|
|
children: [],
|
|
|
|
sourceTree: .group,
|
2018-04-12 16:13:40 +03:00
|
|
|
usesTabs: project.options.usesTabs,
|
|
|
|
indentWidth: project.options.indentWidth,
|
|
|
|
tabWidth: project.options.tabWidth
|
2018-01-24 10:22:46 +03:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let pbxProject = createObject(
|
|
|
|
id: project.name,
|
2018-01-24 10:22:46 +03:00
|
|
|
PBXProject(
|
2018-04-12 16:13:40 +03:00
|
|
|
name: project.name,
|
2018-01-24 10:22:46 +03:00
|
|
|
buildConfigurationList: buildConfigList.reference,
|
|
|
|
compatibilityVersion: "Xcode 3.2",
|
|
|
|
mainGroup: mainGroup.reference,
|
2018-04-12 16:13:40 +03:00
|
|
|
developmentRegion: project.options.developmentLanguage ?? "en"
|
2018-01-24 10:22:46 +03:00
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
pbxProj.rootObject = pbxProject.reference
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
for target in project.targets {
|
2018-01-24 10:22:46 +03:00
|
|
|
let targetObject: PBXTarget
|
|
|
|
|
|
|
|
if target.isLegacy {
|
|
|
|
targetObject = PBXLegacyTarget(
|
|
|
|
name: target.name,
|
|
|
|
buildToolPath: target.legacy?.toolPath,
|
|
|
|
buildArgumentsString: target.legacy?.arguments,
|
|
|
|
passBuildSettingsInEnvironment: target.legacy?.passSettings ?? false,
|
|
|
|
buildWorkingDirectory: target.legacy?.workingDirectory
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
targetObject = PBXNativeTarget(name: target.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
targetObjects[target.name] = createObject(id: target.name, targetObject)
|
2017-12-26 13:47:18 +03:00
|
|
|
|
2018-01-21 15:16:14 +03:00
|
|
|
var explicitFileType: String?
|
|
|
|
var lastKnownFileType: String?
|
|
|
|
let fileType = PBXFileReference.fileType(path: Path(target.filename))
|
2018-01-30 11:13:28 +03:00
|
|
|
if target.platform == .macOS || target.platform == .watchOS || target.type == .framework {
|
2018-01-21 15:16:14 +03:00
|
|
|
explicitFileType = fileType
|
|
|
|
} else {
|
|
|
|
lastKnownFileType = fileType
|
|
|
|
}
|
|
|
|
|
2018-04-13 19:06:05 +03:00
|
|
|
if !target.isLegacy {
|
|
|
|
let fileReference = createObject(
|
|
|
|
id: target.name,
|
|
|
|
PBXFileReference(
|
|
|
|
sourceTree: .buildProductsDir,
|
|
|
|
explicitFileType: explicitFileType,
|
|
|
|
lastKnownFileType: lastKnownFileType,
|
|
|
|
path: target.filename,
|
|
|
|
includeInIndex: false
|
|
|
|
)
|
2018-01-24 10:22:46 +03:00
|
|
|
)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-04-13 19:06:05 +03:00
|
|
|
targetFileReferences[target.name] = fileReference.reference
|
|
|
|
targetBuildFiles[target.name] = createObject(
|
|
|
|
id: fileReference.reference,
|
2018-06-08 08:21:21 +03:00
|
|
|
PBXBuildFile(fileRef: fileReference.reference)
|
2018-04-13 19:06:05 +03:00
|
|
|
)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
try project.targets.forEach(generateTarget)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let productGroup = createObject(
|
|
|
|
id: "Products",
|
|
|
|
PBXGroup(
|
|
|
|
children: Array(targetFileReferences.values),
|
|
|
|
sourceTree: .group,
|
|
|
|
name: "Products"
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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] = []
|
2018-01-24 10:22:46 +03:00
|
|
|
var platformReferences: [String] = []
|
2017-07-25 01:32:36 +03:00
|
|
|
for (platform, fileReferences) in carthageFrameworksByPlatform {
|
2018-01-24 10:22:46 +03:00
|
|
|
let platformGroup: ObjectReference<PBXGroup> = createObject(
|
|
|
|
id: "Carthage" + platform,
|
|
|
|
PBXGroup(
|
|
|
|
children: fileReferences.sorted(),
|
|
|
|
sourceTree: .group,
|
|
|
|
path: platform
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2018-01-24 10:22:46 +03:00
|
|
|
platformReferences.append(platformGroup.reference)
|
|
|
|
platforms.append(platformGroup.object)
|
2017-07-25 01:32:36 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
let carthageGroup = createObject(
|
|
|
|
id: "Carthage",
|
|
|
|
PBXGroup(
|
|
|
|
children: platformReferences.sorted(),
|
|
|
|
sourceTree: .group,
|
|
|
|
name: "Carthage",
|
|
|
|
path: carthageBuildPath
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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 {
|
2018-01-24 10:22:46 +03:00
|
|
|
let group = createObject(
|
|
|
|
id: "Frameworks",
|
|
|
|
PBXGroup(
|
|
|
|
children: frameworkFiles,
|
|
|
|
sourceTree: .group,
|
|
|
|
name: "Frameworks"
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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
|
|
|
}
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
mainGroup.object.children = Array(topLevelGroups)
|
2017-11-12 20:52:11 +03:00
|
|
|
sortGroups(group: mainGroup)
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let projectAttributes: [String: Any] = ["LastUpgradeCheck": project.xcodeVersion]
|
|
|
|
.merged(project.attributes)
|
2018-01-24 10:22:46 +03:00
|
|
|
.merged(generateTargetAttributes() ?? [:])
|
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
pbxProject.object.knownRegions = sourceGenerator.knownRegions.sorted()
|
|
|
|
pbxProject.object.targets = targetObjects.values.sorted { $0.object.name < $1.object.name }.map { $0.reference }
|
|
|
|
pbxProject.object.attributes = projectAttributes
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
return pbxProj
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-01-05 22:54:01 +03:00
|
|
|
func generateTargetAttributes() -> [String: Any]? {
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
var targetAttributes: [String: [String: Any]] = [:]
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let uiTestTargets = pbxProj.objects.nativeTargets.objectReferences.filter { $0.object.productType == .uiTestBundle }
|
2018-01-05 22:54:01 +03:00
|
|
|
for uiTestTarget in uiTestTargets {
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
// look up TEST_TARGET_NAME build setting
|
|
|
|
func testTargetName(_ target: PBXTarget) -> String? {
|
|
|
|
guard let configurationList = target.buildConfigurationList else { return nil }
|
2018-04-12 16:13:40 +03:00
|
|
|
guard let buildConfigurationReferences = self.pbxProj.objects.configurationLists[configurationList]?.buildConfigurations else { return nil }
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
let configs = buildConfigurationReferences
|
2018-05-12 08:10:18 +03:00
|
|
|
.compactMap { ref in self.pbxProj.objects.buildConfigurations[ref] }
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
return configs
|
2018-05-12 08:10:18 +03:00
|
|
|
.compactMap { $0.buildSettings["TEST_TARGET_NAME"] as? String }
|
2018-03-27 06:21:15 +03:00
|
|
|
.first
|
|
|
|
}
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
guard let name = testTargetName(uiTestTarget.object) else { continue }
|
2018-04-12 16:13:40 +03:00
|
|
|
guard let target = self.pbxProj.objects.targets(named: name).first else { continue }
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
targetAttributes[uiTestTarget.reference, default: [:]].merge(["TestTargetID": target.reference])
|
|
|
|
}
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
for target in project.targets {
|
2018-03-27 07:47:23 +03:00
|
|
|
guard let targetReference = targetObjects[target.name]?.reference else {
|
|
|
|
continue
|
2018-03-27 06:21:15 +03:00
|
|
|
}
|
2018-03-27 07:47:23 +03:00
|
|
|
if !target.attributes.isEmpty {
|
|
|
|
targetAttributes[targetReference, default: [:]].merge(target.attributes)
|
|
|
|
}
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 07:47:23 +03:00
|
|
|
func getSingleBuildSetting(_ setting: String) -> String? {
|
2018-05-12 08:10:18 +03:00
|
|
|
let settings = project.configs.compactMap {
|
2018-04-12 16:13:40 +03:00
|
|
|
project.getCombinedBuildSettings(basePath: project.basePath, target: target, config: $0)[setting] as? String
|
2018-03-27 07:47:23 +03:00
|
|
|
}
|
2018-04-12 16:13:40 +03:00
|
|
|
guard settings.count == project.configs.count,
|
2018-03-27 07:47:23 +03:00
|
|
|
let firstSetting = settings.first,
|
2018-03-27 09:18:29 +03:00
|
|
|
settings.filter({ $0 == firstSetting }).count == settings.count else {
|
|
|
|
return nil
|
2018-03-27 07:47:23 +03:00
|
|
|
}
|
|
|
|
return firstSetting
|
|
|
|
}
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 07:47:23 +03:00
|
|
|
func setTargetAttribute(attribute: String, buildSetting: String) {
|
|
|
|
if let setting = getSingleBuildSetting(buildSetting) {
|
|
|
|
targetAttributes[targetReference, default: [:]].merge([attribute: setting])
|
|
|
|
}
|
|
|
|
}
|
2018-03-27 09:18:29 +03:00
|
|
|
|
2018-03-27 07:47:23 +03:00
|
|
|
setTargetAttribute(attribute: "ProvisioningStyle", buildSetting: "CODE_SIGN_STYLE")
|
|
|
|
setTargetAttribute(attribute: "DevelopmentTeam", buildSetting: "DEVELOPMENT_TEAM")
|
2018-01-05 05:02:47 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
|
2018-03-27 06:21:15 +03:00
|
|
|
return targetAttributes.isEmpty ? nil : ["TargetAttributes": targetAttributes]
|
2018-01-05 05:02:47 +03:00
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
func sortGroups(group: ObjectReference<PBXGroup>) {
|
2017-11-12 20:52:11 +03:00
|
|
|
// sort children
|
2018-01-24 10:22:46 +03:00
|
|
|
let children = group.object.children
|
2018-05-12 08:10:18 +03:00
|
|
|
.compactMap { reference -> ObjectReference<PBXFileElement>? in
|
2018-04-12 16:13:40 +03:00
|
|
|
guard let fileElement = pbxProj.objects.getFileElement(reference: reference) else {
|
2018-01-24 10:22:46 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ObjectReference(reference: reference, object: fileElement)
|
|
|
|
}
|
2017-11-12 20:52:11 +03:00
|
|
|
.sorted { child1, child2 in
|
2018-01-24 10:22:46 +03:00
|
|
|
if child1.object.sortOrder == child2.object.sortOrder {
|
2018-07-17 15:25:37 +03:00
|
|
|
return child1.object.nameOrPath.localizedStandardCompare(child2.object.nameOrPath) == .orderedAscending
|
2017-11-12 20:52:11 +03:00
|
|
|
} else {
|
2018-01-24 10:22:46 +03:00
|
|
|
return child1.object.sortOrder < child2.object.sortOrder
|
2017-11-12 20:52:11 +03:00
|
|
|
}
|
2018-04-12 16:32:40 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
group.object.children = children.map { $0.reference }.filter { $0 != group.reference }
|
2017-11-12 20:52:11 +03:00
|
|
|
|
|
|
|
// sort sub groups
|
2018-05-12 08:10:18 +03:00
|
|
|
let childGroups = group.object.children.compactMap { reference -> ObjectReference<PBXGroup>? in
|
2018-04-12 16:13:40 +03:00
|
|
|
guard let group = pbxProj.objects.groups[reference] else {
|
2018-01-24 10:22:46 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ObjectReference(reference: reference, object: group) }
|
2017-11-12 20:52:11 +03:00
|
|
|
childGroups.forEach(sortGroups)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2017-12-26 12:56:02 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
func generateTarget(_ target: Target) throws {
|
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-12-26 12:56:02 +03:00
|
|
|
var plistPath: Path?
|
2017-12-20 04:10:13 +03:00
|
|
|
var searchForPlist = true
|
2017-08-27 13:28:18 +03:00
|
|
|
|
2018-04-12 16:13:40 +03:00
|
|
|
let configs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
|
|
|
|
var buildSettings = project.getTargetBuildSettings(target: target, config: config)
|
2017-08-27 00:40:42 +03:00
|
|
|
|
2017-08-27 13:28:18 +03:00
|
|
|
// automatically set INFOPLIST_FILE path
|
2018-04-12 16:13:40 +03:00
|
|
|
if !project.targetHasBuildSetting("INFOPLIST_FILE", basePath: project.basePath, target: target, config: config) {
|
2017-12-20 04:10:13 +03:00
|
|
|
if searchForPlist {
|
2017-12-20 20:04:23 +03:00
|
|
|
plistPath = getInfoPlist(target.sources)
|
2017-12-20 04:10:13 +03:00
|
|
|
searchForPlist = false
|
|
|
|
}
|
|
|
|
if let plistPath = plistPath {
|
2018-04-12 16:13:40 +03:00
|
|
|
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: project.basePath)
|
2017-12-20 04:10:13 +03:00
|
|
|
}
|
2017-08-27 13:28:18 +03:00
|
|
|
}
|
|
|
|
|
2017-09-28 15:31:23 +03:00
|
|
|
// automatically calculate bundle id
|
2018-04-12 16:13:40 +03:00
|
|
|
if let bundleIdPrefix = project.options.bundleIdPrefix,
|
|
|
|
!project.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: project.basePath, target: target, config: config) {
|
2017-09-25 22:08:15 +03:00
|
|
|
let characterSet = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-.")).inverted
|
2017-12-26 13:47:18 +03:00
|
|
|
let escapedTargetName = target.name
|
|
|
|
.replacingOccurrences(of: "_", with: "-")
|
|
|
|
.components(separatedBy: characterSet)
|
|
|
|
.joined(separator: "")
|
2017-09-25 22:08:15 +03:00
|
|
|
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,
|
2018-04-12 16:13:40 +03:00
|
|
|
!project.targetHasBuildSetting("TEST_TARGET_NAME", basePath: project.basePath, target: target, config: config) {
|
2017-09-28 15:31:23 +03:00
|
|
|
for dependency in target.dependencies {
|
|
|
|
if dependency.type == .target,
|
2018-04-12 16:13:40 +03:00
|
|
|
let dependencyTarget = project.getTarget(dependency.reference),
|
2017-09-28 15:31:23 +03:00
|
|
|
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] {
|
2018-04-12 16:13:40 +03:00
|
|
|
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: project.basePath + configPath)
|
2017-07-24 19:03:41 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
let buildConfig = XCBuildConfiguration(
|
2017-12-26 13:47:18 +03:00
|
|
|
name: config.name,
|
|
|
|
baseConfigurationReference: baseConfigurationReference,
|
|
|
|
buildSettings: buildSettings
|
|
|
|
)
|
2018-01-24 10:22:46 +03:00
|
|
|
return createObject(id: config.name + target.name, buildConfig)
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2018-01-24 10:22:46 +03:00
|
|
|
|
|
|
|
let buildConfigList = createObject(id: target.name, XCConfigurationList(
|
|
|
|
buildConfigurations: configs.map { $0.reference },
|
2017-12-26 13:47:18 +03:00
|
|
|
defaultConfigurationName: ""
|
2018-01-24 10:22:46 +03:00
|
|
|
))
|
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] = []
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-07-03 21:34:20 +03:00
|
|
|
let targetDependencies = (target.transitivelyLinkDependencies ?? project.options.transitivelyLinkDependencies) ?
|
|
|
|
getAllDependenciesPlusTransitiveNeedingEmbedding(target: target) : target.dependencies
|
2017-08-03 21:03:56 +03:00
|
|
|
|
2018-06-29 21:17:25 +03:00
|
|
|
for dependency in targetDependencies {
|
2017-08-24 18:41:55 +03:00
|
|
|
|
2018-03-01 13:52:25 +03:00
|
|
|
let embed = dependency.embed ?? target.shouldEmbedDependencies
|
2018-06-27 15:18:12 +03:00
|
|
|
|
2018-07-04 02:50:00 +03:00
|
|
|
func getEmbedSettings(codeSign: Bool) -> [String: Any] {
|
|
|
|
var embedAttributes: [String] = []
|
|
|
|
if codeSign {
|
|
|
|
embedAttributes.append("CodeSignOnCopy")
|
|
|
|
}
|
|
|
|
if dependency.removeHeaders {
|
|
|
|
embedAttributes.append("RemoveHeadersOnCopy")
|
|
|
|
}
|
|
|
|
return ["ATTRIBUTES": embedAttributes]
|
2018-06-27 15:18:12 +03:00
|
|
|
}
|
|
|
|
|
2017-09-29 18:34:01 +03:00
|
|
|
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
|
2018-04-12 16:13:40 +03:00
|
|
|
guard let dependencyTarget = project.getTarget(dependencyTargetName) else { continue }
|
2017-08-03 21:03:56 +03:00
|
|
|
let dependencyFileReference = targetFileReferences[dependencyTargetName]!
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let targetProxy = createObject(
|
2018-06-26 15:19:22 +03:00
|
|
|
id: "\(target.name)-\(dependency.reference)",
|
2018-01-24 10:22:46 +03:00
|
|
|
PBXContainerItemProxy(
|
2018-04-12 16:13:40 +03:00
|
|
|
containerPortal: pbxProj.rootObject,
|
2018-01-24 10:22:46 +03:00
|
|
|
remoteGlobalIDString: targetObjects[dependencyTargetName]!.reference,
|
|
|
|
proxyType: .nativeTarget,
|
|
|
|
remoteInfo: dependencyTargetName
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2018-01-24 10:22:46 +03:00
|
|
|
|
|
|
|
let targetDependency = createObject(
|
|
|
|
id: dependencyTargetName + target.name,
|
|
|
|
PBXTargetDependency(
|
|
|
|
target: targetObjects[dependencyTargetName]!.reference,
|
|
|
|
targetProxy: targetProxy.reference
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-07-23 21:58:03 +03:00
|
|
|
|
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]!
|
2018-01-24 10:22:46 +03:00
|
|
|
let buildFile = createObject(
|
|
|
|
id: dependencyBuildFile.reference + target.name,
|
|
|
|
PBXBuildFile(fileRef: dependencyBuildFile.object.fileRef!)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-10-17 04:18:28 +03:00
|
|
|
targetFrameworkBuildFiles.append(buildFile.reference)
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
let shouldEmbed = target.type.isApp
|
|
|
|
|| (target.type.isTest && (dependencyTarget.type.isFramework || dependencyTarget.type == .bundle))
|
|
|
|
if (dependency.embed ?? shouldEmbed) && !dependencyTarget.type.isLibrary {
|
2018-01-24 10:22:46 +03:00
|
|
|
let embedFile = createObject(
|
|
|
|
id: dependencyFileReference + target.name,
|
|
|
|
PBXBuildFile(
|
|
|
|
fileRef: dependencyFileReference,
|
2018-07-04 02:50:00 +03:00
|
|
|
settings: getEmbedSettings(codeSign: dependency.codeSign ?? !dependencyTarget.type.isExecutable)
|
2018-01-24 10:22:46 +03:00
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-08-24 18:41:55 +03:00
|
|
|
|
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-12-26 13:47:18 +03:00
|
|
|
fileReference = sourceGenerator.getFileReference(
|
|
|
|
path: Path(dependency.reference),
|
2018-04-12 16:13:40 +03:00
|
|
|
inPath: project.basePath,
|
2017-12-26 13:47:18 +03:00
|
|
|
sourceTree: .buildProductsDir
|
|
|
|
)
|
2017-11-21 09:47:57 +03:00
|
|
|
} else {
|
2017-12-26 13:47:18 +03:00
|
|
|
fileReference = sourceGenerator.getFileReference(
|
|
|
|
path: Path(dependency.reference),
|
2018-04-12 16:13:40 +03:00
|
|
|
inPath: project.basePath
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-11-21 09:47:57 +03:00
|
|
|
}
|
2017-08-24 18:41:55 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let buildFile = createObject(
|
|
|
|
id: fileReference + target.name,
|
|
|
|
PBXBuildFile(fileRef: fileReference)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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 {
|
2018-01-24 10:22:46 +03:00
|
|
|
let embedFile = createObject(
|
|
|
|
id: fileReference + target.name,
|
2018-07-04 02:50:00 +03:00
|
|
|
PBXBuildFile(fileRef: fileReference, settings: getEmbedSettings(codeSign: dependency.codeSign ?? true))
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-10-17 06:59:44 +03:00
|
|
|
copyFrameworksReferences.append(embedFile.reference)
|
2017-08-24 18:41:55 +03:00
|
|
|
}
|
2018-07-03 14:45:00 +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
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let buildFile = createObject(
|
|
|
|
id: fileReference + target.name,
|
|
|
|
PBXBuildFile(fileRef: fileReference)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2018-01-24 10:22:46 +03:00
|
|
|
|
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)
|
2018-03-01 13:52:42 +03:00
|
|
|
if target.platform == .macOS && embed {
|
2018-01-24 10:22:46 +03:00
|
|
|
let embedFile = createObject(
|
|
|
|
id: fileReference + target.name,
|
2018-07-04 02:50:00 +03:00
|
|
|
PBXBuildFile(fileRef: fileReference, settings: getEmbedSettings(codeSign: dependency.codeSign ?? true))
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 19:06:05 +03:00
|
|
|
let fileReference = targetFileReferences[target.name]
|
2017-07-23 21:58:03 +03:00
|
|
|
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 }
|
2018-01-24 10:22:46 +03:00
|
|
|
.reduce(into: [SourceFile]()) { output, sourceFile in
|
|
|
|
if !output.contains(where: { $0.fileReference == sourceFile.fileReference }) {
|
|
|
|
output.append(sourceFile)
|
|
|
|
}
|
2018-01-03 12:58:42 +03:00
|
|
|
}
|
2017-11-03 17:01:53 +03:00
|
|
|
.sorted { $0.path.lastComponent < $1.path.lastComponent }
|
2018-01-24 10:22:46 +03:00
|
|
|
return files.map { createObject(id: $0.fileReference + target.name, $0.buildFile) }
|
|
|
|
.map { $0.reference }
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
func generateBuildScript(buildScript: BuildScript) throws {
|
2017-08-01 19:40:40 +03:00
|
|
|
|
2017-12-07 17:48:04 +03:00
|
|
|
let 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):
|
2018-04-12 16:13:40 +03:00
|
|
|
shellScript = try (project.basePath + path).read()
|
2017-08-01 19:40:40 +03:00
|
|
|
case let .script(script):
|
|
|
|
shellScript = script
|
|
|
|
}
|
2017-12-07 17:48:04 +03:00
|
|
|
|
2017-08-25 12:37:14 +03:00
|
|
|
let shellScriptPhase = PBXShellScriptBuildPhase(
|
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-12-26 13:47:18 +03:00
|
|
|
shellScript: shellScript
|
|
|
|
)
|
2018-01-28 12:01:47 +03:00
|
|
|
shellScriptPhase.runOnlyForDeploymentPostprocessing = buildScript.runOnlyWhenInstalling
|
2018-07-20 00:10:22 +03:00
|
|
|
shellScriptPhase.showEnvVarsInLog = buildScript.showEnvVars
|
2018-01-24 10:22:46 +03:00
|
|
|
let shellScriptPhaseReference = createObject(id: String(describing: buildScript.name) + shellScript + target.name, shellScriptPhase)
|
|
|
|
buildPhases.append(shellScriptPhaseReference.reference)
|
2017-08-01 19:40:40 +03:00
|
|
|
}
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
try target.prebuildScripts.forEach(generateBuildScript)
|
2017-08-01 19:40:40 +03:00
|
|
|
|
2017-11-13 00:17:08 +03:00
|
|
|
let sourcesBuildPhaseFiles = getBuildFilesForPhase(.sources)
|
2018-05-14 03:38:43 +03:00
|
|
|
let sourcesBuildPhase = createObject(id: target.name, PBXSourcesBuildPhase(files: sourcesBuildPhaseFiles))
|
|
|
|
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 {
|
2018-01-24 10:22:46 +03:00
|
|
|
let resourcesBuildPhase = createObject(id: target.name, PBXResourcesBuildPhase(files: resourcesBuildPhaseFiles))
|
2017-11-13 00:17:08 +03:00
|
|
|
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) {
|
2018-01-24 10:22:46 +03:00
|
|
|
let headersBuildPhase = createObject(id: target.name, PBXHeadersBuildPhase(files: headersBuildPhaseFiles))
|
2017-10-31 18:55:04 +03:00
|
|
|
buildPhases.append(headersBuildPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2017-08-03 21:06:21 +03:00
|
|
|
if !targetFrameworkBuildFiles.isEmpty {
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let frameworkBuildPhase = createObject(
|
|
|
|
id: target.name,
|
2018-01-28 12:01:47 +03:00
|
|
|
PBXFrameworksBuildPhase(files: targetFrameworkBuildFiles)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-08-03 21:06:21 +03:00
|
|
|
buildPhases.append(frameworkBuildPhase.reference)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !extensions.isEmpty {
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let copyFilesPhase = createObject(
|
|
|
|
id: "embed app extensions" + target.name,
|
|
|
|
PBXCopyFilesBuildPhase(
|
|
|
|
dstPath: "",
|
|
|
|
dstSubfolderSpec: .plugins,
|
|
|
|
files: extensions
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-08-03 21:06:21 +03:00
|
|
|
|
|
|
|
buildPhases.append(copyFilesPhase.reference)
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
|
2018-05-17 03:18:46 +03:00
|
|
|
copyFrameworksReferences += getBuildFilesForPhase(.frameworks)
|
2017-10-17 06:59:44 +03:00
|
|
|
if !copyFrameworksReferences.isEmpty {
|
2017-08-03 21:06:21 +03:00
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let copyFilesPhase = createObject(
|
|
|
|
id: "embed frameworks" + target.name,
|
|
|
|
PBXCopyFilesBuildPhase(
|
|
|
|
dstPath: "",
|
|
|
|
dstSubfolderSpec: .frameworks,
|
|
|
|
files: copyFrameworksReferences
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
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 {
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
let copyFilesPhase = createObject(
|
|
|
|
id: "embed watch content" + target.name,
|
|
|
|
PBXCopyFilesBuildPhase(
|
|
|
|
dstPath: "$(CONTENTS_FOLDER_PATH)/Watch",
|
|
|
|
dstSubfolderSpec: .productsDirectory,
|
|
|
|
files: copyWatchReferences
|
|
|
|
)
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2017-10-17 07:28:08 +03:00
|
|
|
|
|
|
|
buildPhases.append(copyFilesPhase.reference)
|
|
|
|
}
|
|
|
|
|
2017-12-26 13:47:18 +03:00
|
|
|
let carthageFrameworksToEmbed = Array(Set(
|
|
|
|
carthageDependencies
|
2018-03-01 13:52:25 +03:00
|
|
|
.filter { $0.embed ?? target.shouldEmbedDependencies }
|
2017-12-26 13:47:18 +03:00
|
|
|
.map { $0.reference }
|
|
|
|
))
|
2017-10-05 04:53:08 +03:00
|
|
|
.sorted()
|
|
|
|
|
2018-03-01 13:52:25 +03:00
|
|
|
if !carthageFrameworksToEmbed.isEmpty && target.platform != .macOS {
|
|
|
|
|
|
|
|
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")" }
|
2018-04-12 16:13:40 +03:00
|
|
|
let carthageExecutable = project.options.carthageExecutablePath ?? "carthage"
|
2018-03-01 13:52:25 +03:00
|
|
|
let carthageScript = createObject(
|
|
|
|
id: "Carthage" + target.name,
|
|
|
|
PBXShellScriptBuildPhase(
|
|
|
|
files: [],
|
|
|
|
name: "Carthage",
|
|
|
|
inputPaths: inputPaths,
|
|
|
|
outputPaths: outputPaths,
|
|
|
|
shellPath: "/bin/sh",
|
|
|
|
shellScript: "\(carthageExecutable) copy-frameworks\n"
|
2017-12-26 13:47:18 +03:00
|
|
|
)
|
2018-03-01 13:52:25 +03:00
|
|
|
)
|
|
|
|
buildPhases.append(carthageScript.reference)
|
2017-07-25 02:24:14 +03:00
|
|
|
}
|
|
|
|
|
2018-05-13 10:13:48 +03:00
|
|
|
let buildRules = target.buildRules.map { buildRule in
|
2018-06-08 08:21:21 +03:00
|
|
|
createObject(
|
|
|
|
id: "\(target.name)-\(buildRule.action)-\(buildRule.fileType)",
|
2018-05-13 10:13:48 +03:00
|
|
|
PBXBuildRule(
|
|
|
|
compilerSpec: buildRule.action.compilerSpec,
|
|
|
|
fileType: buildRule.fileType.fileType,
|
|
|
|
isEditable: true,
|
|
|
|
filePatterns: buildRule.fileType.pattern,
|
|
|
|
name: buildRule.name ?? "Build Rule",
|
|
|
|
outputFiles: buildRule.outputFiles,
|
|
|
|
outputFilesCompilerFlags: buildRule.outputFilesCompilerFlags,
|
|
|
|
script: buildRule.action.script
|
2018-06-08 08:21:21 +03:00
|
|
|
)
|
|
|
|
).reference
|
2018-05-13 10:13:48 +03:00
|
|
|
}
|
|
|
|
|
2018-01-24 10:22:46 +03:00
|
|
|
try target.postbuildScripts.forEach(generateBuildScript)
|
|
|
|
|
|
|
|
let targetObject = targetObjects[target.name]!.object
|
|
|
|
|
|
|
|
targetObject.name = target.name
|
|
|
|
targetObject.buildConfigurationList = buildConfigList.reference
|
|
|
|
targetObject.buildPhases = buildPhases
|
|
|
|
targetObject.dependencies = dependencies
|
|
|
|
targetObject.productName = target.name
|
2018-05-13 10:13:48 +03:00
|
|
|
targetObject.buildRules = buildRules
|
2018-01-24 10:22:46 +03:00
|
|
|
targetObject.productReference = fileReference
|
|
|
|
if !target.isLegacy {
|
|
|
|
targetObject.productType = target.type
|
2017-12-19 00:26:49 +03:00
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
|
|
|
|
2017-12-20 20:04:23 +03:00
|
|
|
func getInfoPlist(_ sources: [TargetSource]) -> Path? {
|
2017-12-20 04:10:13 +03:00
|
|
|
return sources
|
|
|
|
.lazy
|
2018-04-12 16:13:40 +03:00
|
|
|
.map { self.project.basePath + $0.path }
|
2017-12-20 04:10:13 +03:00
|
|
|
.flatMap { (path) -> Path? in
|
|
|
|
if path.isFile {
|
|
|
|
return path.lastComponent == "Info.plist" ? path : nil
|
|
|
|
} else {
|
|
|
|
return path.first(where: { $0.lastComponent == "Info.plist" })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.first
|
|
|
|
}
|
|
|
|
|
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)"
|
|
|
|
}
|
|
|
|
|
2018-04-20 00:46:05 +03:00
|
|
|
func getAllCarthageDependencies(target: Target) -> [Dependency] {
|
2017-11-12 22:39:35 +03:00
|
|
|
// this is used to resolve cyclical target dependencies
|
2018-04-20 00:46:05 +03:00
|
|
|
var visitedTargets: Set<String> = []
|
2017-10-02 02:29:32 +03:00
|
|
|
var frameworks: [Dependency] = []
|
2017-11-12 22:39:35 +03:00
|
|
|
|
2018-04-20 00:46:05 +03:00
|
|
|
var queue: [Target] = [target]
|
|
|
|
while !queue.isEmpty {
|
|
|
|
let target = queue.removeFirst()
|
|
|
|
if visitedTargets.contains(target.name) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for dependency in target.dependencies {
|
|
|
|
switch dependency.type {
|
|
|
|
case .carthage:
|
|
|
|
frameworks.append(dependency)
|
|
|
|
case .target:
|
|
|
|
if let target = project.getTarget(dependency.reference) {
|
|
|
|
queue.append(target)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break
|
2017-08-27 00:40:42 +03:00
|
|
|
}
|
|
|
|
}
|
2018-04-20 00:46:05 +03:00
|
|
|
|
|
|
|
visitedTargets.update(with: target.name)
|
2017-08-27 00:40:42 +03:00
|
|
|
}
|
2018-04-20 00:46:05 +03:00
|
|
|
|
2017-08-27 00:40:42 +03:00
|
|
|
return frameworks
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-07-03 21:34:20 +03:00
|
|
|
func getAllDependenciesPlusTransitiveNeedingEmbedding(target topLevelTarget: Target) -> [Dependency] {
|
2018-06-29 06:32:54 +03:00
|
|
|
// this is used to resolve cyclical target dependencies
|
|
|
|
var visitedTargets: Set<String> = []
|
|
|
|
var dependencies: [String: Dependency] = [:]
|
|
|
|
var queue: [Target] = [topLevelTarget]
|
|
|
|
while !queue.isEmpty {
|
|
|
|
let target = queue.removeFirst()
|
|
|
|
if visitedTargets.contains(target.name) {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
let isTopLevel = target == topLevelTarget
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
for dependency in target.dependencies {
|
|
|
|
// don't overwrite dependencies, to allow top level ones to rule
|
|
|
|
if dependencies.contains(reference: dependency.reference) {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
// don't want a dependency if it's going to be embedded or statically linked in a non-top level target
|
|
|
|
// in .target check we filter out targets that will embed all of their dependencies
|
|
|
|
switch dependency.type {
|
|
|
|
case .framework, .carthage:
|
|
|
|
if isTopLevel || dependency.embed == nil {
|
|
|
|
dependencies[dependency.reference] = dependency
|
|
|
|
}
|
|
|
|
case .target:
|
|
|
|
if let dependencyTarget = project.getTarget(dependency.reference) {
|
|
|
|
if isTopLevel || (dependency.embed == nil && dependencyTarget.type != .staticLibrary) {
|
|
|
|
dependencies[dependency.reference] = dependency
|
|
|
|
if !dependencyTarget.shouldEmbedDependencies {
|
|
|
|
// traverse target's dependencies if it doesn't embed them itself
|
|
|
|
queue.append(dependencyTarget)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
visitedTargets.update(with: target.name)
|
|
|
|
}
|
2018-07-03 14:45:00 +03:00
|
|
|
|
2018-06-29 06:32:54 +03:00
|
|
|
return dependencies.sorted(by: { $0.key < $1.key }).map { $0.value }
|
|
|
|
}
|
2017-07-23 21:58:03 +03:00
|
|
|
}
|
2018-03-01 13:52:25 +03:00
|
|
|
|
|
|
|
extension Target {
|
|
|
|
|
|
|
|
var shouldEmbedDependencies: Bool {
|
|
|
|
return type.isApp || type.isTest
|
|
|
|
}
|
|
|
|
}
|