XcodeGen/Sources/XcodeGenKit/PBXProjGenerator.swift

496 lines
22 KiB
Swift
Raw Normal View History

2017-07-23 21:58:03 +03:00
//
// PBXProjGenerator.swift
// XcodeGen
//
// Created by Yonas Kolb on 23/7/17.
//
//
import Foundation
import Foundation
import PathKit
import xcodeproj
import JSONUtilities
import Yams
import ProjectSpec
2017-07-23 21:58:03 +03:00
public class PBXProjGenerator {
let spec: ProjectSpec
2017-07-26 00:09:51 +03:00
let basePath: Path
let currentXcodeVersion: String
2017-07-23 21:58:03 +03:00
2017-07-24 00:12:14 +03:00
var fileReferencesByPath: [Path: String] = [:]
2017-07-25 00:58:29 +03:00
var groupsByPath: [Path: PBXGroup] = [:]
2017-07-23 21:58:03 +03:00
var targetNativeReferences: [String: String] = [:]
var targetBuildFileReferences: [String: String] = [:]
var targetFileReferences: [String: String] = [:]
var topLevelGroups: [PBXGroup] = []
2017-07-25 01:32:36 +03:00
var carthageFrameworksByPlatform: [String: [String]] = [:]
2017-07-25 02:02:54 +03:00
var frameworkFiles: [String] = []
2017-07-23 21:58:03 +03:00
2017-07-31 13:29:56 +03:00
var uuids: Set<String> = []
2017-08-25 14:48:07 +03:00
var project: PBXProj!
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"
}
public init(spec: ProjectSpec, path: Path, currentXcodeVersion: String) {
self.currentXcodeVersion = currentXcodeVersion
2017-07-23 21:58:03 +03:00
self.spec = spec
2017-07-31 13:29:56 +03:00
basePath = path
2017-07-23 21:58:03 +03:00
}
2017-08-25 14:48:07 +03:00
public func generateUUID<T: PBXObject>(_ element: T.Type, _ id: String) -> String {
2017-07-28 01:18:39 +03:00
var uuid: String = ""
var counter: UInt = 0
let className: String = String(describing: T.self).replacingOccurrences(of: "PBX", with: "")
let classAcronym = String(className.characters.filter { String($0).lowercased() != String($0) })
let stringID = String(abs(id.hashValue).description.characters.prefix(10 - classAcronym.characters.count))
repeat {
counter += 1
uuid = "\(classAcronym)\(stringID)\(String(format: "%02d", counter))"
2017-07-31 13:29:56 +03:00
} while (uuids.contains(uuid))
2017-08-02 14:29:45 +03:00
uuids.insert(uuid)
2017-07-28 01:18:39 +03:00
return uuid
2017-07-23 21:58:03 +03:00
}
2017-08-25 14:48:07 +03:00
func addObject(_ object: PBXObject) {
2017-09-14 22:25:12 +03:00
project.addObject(object)
2017-08-25 14:48:07 +03:00
}
2017-07-23 21:58:03 +03:00
public func generate() throws -> PBXProj {
2017-07-28 01:18:39 +03:00
uuids = []
2017-08-25 14:48:07 +03:00
project = PBXProj(archiveVersion: 1, objectVersion: 46, rootObject: generateUUID(PBXProject.self, spec.name))
for group in spec.fileGroups {
_ = try getGroups(path: basePath + group)
}
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] {
baseConfigurationReference = getFileReference(path: basePath + configPath, inPath: basePath)
}
return XCBuildConfiguration(reference: generateUUID(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-09-14 22:25:12 +03:00
let buildConfigList = XCConfigurationList(reference: generateUUID(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-07-28 01:18:39 +03:00
targetNativeReferences[target.name] = generateUUID(PBXNativeTarget.self, target.name)
2017-07-23 21:58:03 +03:00
2017-07-28 01:18:39 +03:00
let fileReference = PBXFileReference(reference: generateUUID(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-07-28 01:18:39 +03:00
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference.reference), fileRef: fileReference.reference)
2017-08-25 14:48:07 +03:00
addObject(buildFile)
2017-07-23 21:58:03 +03:00
targetBuildFileReferences[target.name] = buildFile.reference
}
2017-07-24 14:35:21 +03:00
let targets = try spec.targets.map(generateTarget)
2017-07-23 21:58:03 +03:00
2017-07-28 01:18:39 +03:00
let productGroup = PBXGroup(reference: generateUUID(PBXGroup.self, "Products"), children: Array(targetFileReferences.values), sourceTree: .group, name: "Products")
2017-08-25 14:48:07 +03:00
addObject(productGroup)
2017-07-23 21:58:03 +03:00
topLevelGroups.append(productGroup)
2017-07-25 01:32:36 +03:00
if !carthageFrameworksByPlatform.isEmpty {
var platforms: [PBXGroup] = []
for (platform, fileReferences) in carthageFrameworksByPlatform {
2017-07-28 01:18:39 +03:00
let platformGroup = PBXGroup(reference: generateUUID(PBXGroup.self, platform), children: fileReferences, 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-09-14 22:25:12 +03:00
let carthageGroup = PBXGroup(reference: generateUUID(PBXGroup.self, "Carthage"), children: platforms.references, 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-07-28 01:18:39 +03:00
let group = PBXGroup(reference: generateUUID(PBXGroup.self, "Frameworks"), children: frameworkFiles, sourceTree: .group, name: "Frameworks")
2017-08-25 14:48:07 +03:00
addObject(group)
2017-07-25 02:02:54 +03:00
topLevelGroups.append(group)
}
2017-09-14 22:25:12 +03:00
let mainGroup = PBXGroup(reference: generateUUID(PBXGroup.self, "Project"), children: topLevelGroups.references, sourceTree: .group)
2017-08-25 14:48:07 +03:00
addObject(mainGroup)
2017-07-23 21:58:03 +03:00
2017-07-23 23:39:18 +03:00
let knownRegions: [String] = ["en", "Base"]
let projectAttributes: [String: Any] = ["LastUpgradeCheck": currentXcodeVersion].merged(spec.attributes)
2017-08-30 18:06:41 +03:00
let root = PBXProject(reference: project.rootObject,
buildConfigurationList: buildConfigList.reference,
compatibilityVersion: "Xcode 3.2",
mainGroup: mainGroup.reference,
developmentRegion: "English",
knownRegions: knownRegions,
2017-09-14 22:25:12 +03:00
targets: targets.references,
2017-08-30 18:06:41 +03:00
attributes: projectAttributes)
2017-08-25 14:48:07 +03:00
project.projects.append(root)
2017-07-23 21:58:03 +03:00
2017-08-25 14:48:07 +03:00
return project
2017-07-23 21:58:03 +03:00
}
struct SourceFile {
let path: Path
2017-07-24 00:12:14 +03:00
let fileReference: String
2017-07-23 21:58:03 +03:00
let buildFile: PBXBuildFile
}
func generateSourceFile(path: Path) -> SourceFile {
let fileReference = fileReferencesByPath[path]!
var settings: [String: Any]?
2017-07-23 21:58:03 +03:00
if getBuildPhaseForPath(path) == .headers {
settings = ["ATTRIBUTES": ["Public"]]
2017-07-23 21:58:03 +03:00
}
2017-07-28 01:18:39 +03:00
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference), fileRef: fileReference, settings: settings)
2017-08-25 14:48:07 +03:00
addObject(buildFile)
2017-07-23 21:58:03 +03:00
return SourceFile(path: path, fileReference: fileReference, buildFile: buildFile)
}
2017-07-31 13:29:56 +03:00
func generateTarget(_ target: Target) throws -> PBXNativeTarget {
2017-07-23 21:58:03 +03:00
let carthageFrameworks = Set(getCarthageFrameworks(target: target))
let sourcePaths = target.sources.map { basePath + $0 }
2017-07-26 13:40:25 +03:00
var sourceFilePaths: [Path] = []
for source in sourcePaths {
2017-07-28 01:18:39 +03:00
let sourceGroups = try getGroups(path: source)
2017-07-26 13:40:25 +03:00
sourceFilePaths += sourceGroups.filePaths
}
2017-07-23 21:58:03 +03:00
2017-08-27 13:28:18 +03:00
// find all Info.plist
let infoPlists: [Path] = sourcePaths.reduce([]) {
$0 + ((try? $1.recursiveChildren()) ?? []).filter { $0.lastComponent == "Info.plist" }
}
2017-07-27 00:06:34 +03:00
let configs: [XCBuildConfiguration] = spec.configs.map { config in
var buildSettings = spec.getTargetBuildSettings(target: target, config: config)
2017-08-27 13:28:18 +03:00
// automatically set INFOPLIST_FILE path
if let plistPath = infoPlists.first,
!spec.targetHasBuildSetting("INFOPLIST_FILE", basePath: basePath, target: target, config: config) {
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: basePath)
2017-08-27 13:28:18 +03:00
}
2017-09-25 16:30:11 +03:00
if let bundleIdPrefix = spec.options.bundleIdPrefix, !spec.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: basePath, target: target, config: config) {
buildSettings["PRODUCT_BUNDLE_IDENTIFIER"] = bundleIdPrefix + "." + target.name
}
// set Carthage search paths
if !carthageFrameworks.isEmpty {
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 {
buildSettings[frameworkSearchPaths] = ["$(inherited)", carthagePlatformBuildPath]
}
}
2017-07-27 00:06:34 +03:00
var baseConfigurationReference: String?
2017-07-27 15:41:18 +03:00
if let configPath = target.configFiles[config.name] {
2017-09-23 23:38:03 +03:00
baseConfigurationReference = getFileReference(path: basePath + configPath, inPath: basePath)
2017-07-24 19:03:41 +03:00
}
return XCBuildConfiguration(reference: generateUUID(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-09-14 22:25:12 +03:00
let buildConfigList = XCConfigurationList(reference: generateUUID(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
var dependancies: [String] = []
2017-07-25 02:02:54 +03:00
var targetFrameworkBuildFiles: [String] = []
2017-07-23 21:58:03 +03:00
var copyFiles: [String] = []
2017-08-03 21:03:56 +03:00
var extensions: [String] = []
2017-07-23 21:58:03 +03:00
for dependancy in target.dependencies {
2017-08-24 18:41:55 +03:00
let embed = dependancy.embed ?? (target.type.isApp ? true : false)
switch dependancy.type {
case .target:
let dependencyTargetName = dependancy.reference
2017-08-03 21:03:56 +03:00
guard let dependencyTarget = spec.getTarget(dependencyTargetName) else { continue }
let dependencyFileReference = targetFileReferences[dependencyTargetName]!
2017-08-25 14:48:07 +03:00
let targetProxy = PBXContainerItemProxy(reference: generateUUID(PBXContainerItemProxy.self, target.name), containerPortal: project.rootObject, remoteGlobalIDString: targetNativeReferences[dependencyTargetName]!, proxyType: .nativeTarget, remoteInfo: dependencyTargetName)
2017-08-03 21:03:56 +03:00
let targetDependancy = PBXTargetDependency(reference: generateUUID(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)
addObject(targetDependancy)
2017-07-23 21:58:03 +03:00
dependancies.append(targetDependancy.reference)
// don't bother linking a target dependency
// let dependencyBuildFile = targetBuildFileReferences[dependencyTargetName]!
// targetFrameworkBuildFiles.append(dependencyBuildFile)
2017-07-24 00:35:28 +03:00
2017-08-24 18:41:55 +03:00
if embed {
let embedSettings = dependancy.buildSettings
let embedFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, dependencyFileReference + target.name), fileRef: dependencyFileReference, settings: embedSettings)
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)
} else {
copyFiles.append(embedFile.reference)
}
}
case .framework:
2017-08-24 18:41:55 +03:00
let fileReference = getFileReference(path: Path(dependancy.reference), inPath: basePath)
2017-08-24 18:41:55 +03:00
let buildFile = PBXBuildFile(reference: generateUUID(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 {
let embedFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference, settings: dependancy.buildSettings)
addObject(embedFile)
2017-08-28 15:19:00 +03:00
copyFiles.append(embedFile.reference)
2017-08-24 18:41:55 +03:00
}
case .carthage:
2017-07-25 01:32:36 +03:00
if carthageFrameworksByPlatform[target.platform.rawValue] == nil {
carthageFrameworksByPlatform[target.platform.rawValue] = []
}
var platformPath = Path(getCarthageBuildPath(platform: target.platform))
2017-08-24 18:41:55 +03:00
var frameworkPath = platformPath + dependancy.reference
2017-07-25 02:02:54 +03:00
if frameworkPath.extension == nil {
frameworkPath = Path(frameworkPath.string + ".framework")
}
let fileReference = getFileReference(path: frameworkPath, inPath: platformPath)
2017-07-25 01:32:36 +03:00
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference)
2017-08-25 14:48:07 +03:00
addObject(buildFile)
2017-07-25 01:32:36 +03:00
carthageFrameworksByPlatform[target.platform.rawValue]?.append(fileReference)
2017-07-25 02:02:54 +03:00
targetFrameworkBuildFiles.append(buildFile.reference)
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-07-26 13:40:25 +03:00
let files = sourceFilePaths.filter { getBuildPhaseForPath($0) == buildPhase }.map(generateSourceFile)
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):
shellScript = try (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
let shellScriptPhase = PBXShellScriptBuildPhase(
2017-08-26 16:00:39 +03:00
reference: generateUUID(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,
shellPath: buildScript.shell ?? "/bin/sh",
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-07-28 01:18:39 +03:00
let sourcesBuildPhase = PBXSourcesBuildPhase(reference: generateUUID(PBXSourcesBuildPhase.self, target.name), files: getBuildFilesForPhase(.sources))
2017-08-25 14:48:07 +03:00
addObject(sourcesBuildPhase)
2017-07-23 21:58:03 +03:00
buildPhases.append(sourcesBuildPhase.reference)
2017-07-28 01:18:39 +03:00
let resourcesBuildPhase = PBXResourcesBuildPhase(reference: generateUUID(PBXResourcesBuildPhase.self, target.name), files: getBuildFilesForPhase(.resources))
2017-08-25 14:48:07 +03:00
addObject(resourcesBuildPhase)
2017-07-23 21:58:03 +03:00
buildPhases.append(resourcesBuildPhase.reference)
2017-07-28 01:18:39 +03:00
let headersBuildPhase = PBXHeadersBuildPhase(reference: generateUUID(PBXHeadersBuildPhase.self, target.name), files: getBuildFilesForPhase(.headers))
2017-08-25 14:48:07 +03:00
addObject(headersBuildPhase)
2017-07-23 21:58:03 +03:00
buildPhases.append(headersBuildPhase.reference)
if !targetFrameworkBuildFiles.isEmpty {
let frameworkBuildPhase = PBXFrameworksBuildPhase(
reference: generateUUID(PBXFrameworksBuildPhase.self, target.name),
2017-09-14 22:25:12 +03:00
files: targetFrameworkBuildFiles,
runOnlyForDeploymentPostprocessing: 0)
2017-08-25 14:48:07 +03:00
addObject(frameworkBuildPhase)
buildPhases.append(frameworkBuildPhase.reference)
}
if !extensions.isEmpty {
let copyFilesPhase = PBXCopyFilesBuildPhase(
reference: generateUUID(PBXCopyFilesBuildPhase.self, "embed app extensions" + target.name),
dstPath: "",
dstSubfolderSpec: .plugins,
2017-09-14 22:25:12 +03:00
files: extensions)
2017-08-25 14:48:07 +03:00
addObject(copyFilesPhase)
buildPhases.append(copyFilesPhase.reference)
}
2017-07-23 21:58:03 +03:00
if !copyFiles.isEmpty {
let copyFilesPhase = PBXCopyFilesBuildPhase(
reference: generateUUID(PBXCopyFilesBuildPhase.self, "embed frameworks" + target.name),
dstPath: "",
dstSubfolderSpec: .frameworks,
2017-09-14 22:25:12 +03:00
files: copyFiles)
2017-08-25 14:48:07 +03:00
addObject(copyFilesPhase)
buildPhases.append(copyFilesPhase.reference)
}
2017-07-23 21:58:03 +03:00
if !carthageFrameworks.isEmpty {
2017-07-25 02:24:14 +03:00
if target.type.isApp {
2017-08-25 17:22:40 +03:00
let inputPaths = carthageFrameworks.map { "$(SRCROOT)/\(carthageBuildPath)/\(target.platform)/\($0)\($0.contains(".") ? "" : ".framework")" }
2017-09-14 22:25:12 +03:00
let carthageScript = PBXShellScriptBuildPhase(reference: generateUUID(PBXShellScriptBuildPhase.self, "Carthage" + target.name), files: [], name: "Carthage", inputPaths: inputPaths, 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]!,
buildConfigurationList: buildConfigList.reference,
buildPhases: buildPhases,
buildRules: [],
dependencies: dependancies,
name: target.name,
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
}
func getCarthageBuildPath(platform: Platform) -> String {
let carthagePath = Path(carthageBuildPath)
var platformName = platform.rawValue
if platform == .macOS {
platformName = "Mac"
}
return "\(carthagePath)/\(platformName)"
}
func getCarthageFrameworks(target: Target) -> [String] {
var frameworks: [String] = []
for dependency in target.dependencies {
2017-08-24 18:41:55 +03:00
switch dependency.type {
case .carthage: frameworks.append(dependency.reference)
case .target:
if let target = spec.getTarget(dependency.reference) {
frameworks += getCarthageFrameworks(target: target)
}
default: break
}
}
return frameworks
}
2017-07-23 21:58:03 +03:00
func getBuildPhaseForPath(_ path: Path) -> BuildPhase? {
if path.lastComponent == "Info.plist" {
return nil
}
2017-07-23 21:58:03 +03:00
if let fileExtension = path.extension {
switch fileExtension {
case "swift", "m", "cpp": return .sources
2017-07-23 21:58:03 +03:00
case "h", "hh", "hpp", "ipp", "tpp", "hxx", "def": return .headers
case "xcconfig": return nil
2017-07-23 21:58:03 +03:00
default: return .resources
}
}
return nil
}
2017-07-25 02:02:54 +03:00
func getFileReference(path: Path, inPath: Path) -> String {
2017-07-25 01:32:36 +03:00
if let fileReference = fileReferencesByPath[path] {
return fileReference
} else {
2017-07-28 01:18:39 +03:00
let fileReference = PBXFileReference(reference: generateUUID(PBXFileReference.self, path.lastComponent), sourceTree: .group, path: path.byRemovingBase(path: inPath).string)
2017-08-25 14:48:07 +03:00
addObject(fileReference)
2017-07-25 01:32:36 +03:00
fileReferencesByPath[path] = fileReference.reference
return fileReference.reference
}
}
2017-07-28 01:18:39 +03:00
func getGroups(path: Path, depth: Int = 0) throws -> (filePaths: [Path], groups: [PBXGroup]) {
2017-07-23 21:58:03 +03:00
2017-07-28 01:18:39 +03:00
let excludedFiles: [String] = [".DS_Store"]
2017-07-24 00:12:14 +03:00
let directories = try path.children().filter { $0.isDirectory && $0.extension == nil && $0.extension != "lproj" }
let filePaths = try path.children().filter { $0.isFile || $0.extension != nil && $0.extension != "lproj" }.filter { !excludedFiles.contains($0.lastComponent) }
2017-07-24 00:12:14 +03:00
let localisedDirectories = try path.children().filter { $0.extension == "lproj" }
2017-07-23 21:58:03 +03:00
var groupChildren: [String] = []
2017-07-24 14:50:59 +03:00
var allFilePaths: [Path] = filePaths
2017-07-25 00:26:45 +03:00
var groups: [PBXGroup] = []
2017-07-23 21:58:03 +03:00
2017-07-28 01:18:39 +03:00
for path in directories {
let subGroups = try getGroups(path: path, depth: depth + 1)
2017-07-25 00:26:45 +03:00
allFilePaths += subGroups.filePaths
groupChildren.append(subGroups.groups.first!.reference)
groups += subGroups.groups
2017-07-24 14:26:58 +03:00
}
2017-07-25 02:02:54 +03:00
for filePath in filePaths {
let fileReference = getFileReference(path: filePath, inPath: path)
2017-07-25 01:32:36 +03:00
groupChildren.append(fileReference)
2017-07-23 21:58:03 +03:00
}
2017-07-24 00:12:14 +03:00
for localisedDirectory in localisedDirectories {
for path in try localisedDirectory.children() {
let filePath = "\(localisedDirectory.lastComponent)/\(path.lastComponent)"
2017-07-28 01:18:39 +03:00
let fileReference = PBXFileReference(reference: generateUUID(PBXFileReference.self, localisedDirectory.lastComponent), sourceTree: .group, name: localisedDirectory.lastComponentWithoutExtension, path: filePath)
2017-08-25 14:48:07 +03:00
addObject(fileReference)
2017-07-24 00:12:14 +03:00
2017-07-28 01:18:39 +03:00
let variantGroup = PBXVariantGroup(reference: generateUUID(PBXVariantGroup.self, path.lastComponent), children: Set([fileReference.reference]), name: path.lastComponent, sourceTree: .group)
2017-08-25 14:48:07 +03:00
addObject(variantGroup)
2017-07-24 00:12:14 +03:00
fileReferencesByPath[path] = variantGroup.reference
groupChildren.append(variantGroup.reference)
allFilePaths.append(path)
2017-07-24 00:12:14 +03:00
}
}
2017-07-24 19:03:41 +03:00
let groupPath: String = depth == 0 ? path.byRemovingBase(path: basePath).string : path.lastComponent
2017-07-25 00:26:45 +03:00
let group: PBXGroup
2017-07-25 00:58:29 +03:00
if let cachedGroup = groupsByPath[path] {
2017-07-25 00:26:45 +03:00
group = cachedGroup
} else {
2017-07-28 01:18:39 +03:00
group = PBXGroup(reference: generateUUID(PBXGroup.self, path.lastComponent), children: groupChildren, sourceTree: .group, name: path.lastComponent, path: groupPath)
2017-08-25 14:48:07 +03:00
addObject(group)
2017-07-25 00:26:45 +03:00
if depth == 0 {
topLevelGroups.append(group)
}
2017-07-25 00:58:29 +03:00
groupsByPath[path] = group
2017-07-25 00:26:45 +03:00
}
groups.insert(group, at: 0)
return (allFilePaths, groups)
2017-07-23 21:58:03 +03:00
}
}