framework linking

This commit is contained in:
Yonas Kolb
2017-07-22 12:42:05 +02:00
parent 11761e2bf0
commit 36eabc1383
6 changed files with 270 additions and 110 deletions
+75 -11
View File
@@ -21,7 +21,13 @@ public class ProjectGenerator {
var groups: [PBXGroup] = []
var fileReferences: [PBXFileReference] = []
var groupsByPath: [String: String] = [:]
var targetNativeReferences: [String: String] = [:]
var targetBuildFileReferences: [String: String] = [:]
var targetFileReferences: [String: String] = [:]
var ids = 0
var projectReference: String
var buildSettingGroups: [String: BuildSettings] = [:]
@@ -57,6 +63,8 @@ public class ProjectGenerator {
public init(spec: Spec, path: Path) {
self.spec = spec
self.path = path
projectReference = ""
projectReference = id()
}
func id() -> String {
@@ -85,7 +93,18 @@ public class ProjectGenerator {
var topLevelGroups: [PBXGroup] = []
var targets: [String] = []
for target in spec.targets {
targetNativeReferences[target.name] = id()
let fileReference = PBXFileReference(reference: id(), sourceTree: .buildProductsDir, explicitFileType: target.type.fileExtension, path: target.filename, includeInIndex: 0)
objects.append(.pbxFileReference(fileReference))
targetFileReferences[target.name] = fileReference.reference
let buildFile = PBXBuildFile(reference: id(), fileRef: fileReference.reference)
objects.append(.pbxBuildFile(buildFile))
targetBuildFileReferences[target.name] = buildFile.reference
}
for target in spec.targets {
let source = spec.path.parent() + target.sources.first!
@@ -98,9 +117,6 @@ public class ProjectGenerator {
let buildFiles = fileReferences.map { PBXBuildFile(reference: id(), fileRef: $0.reference) }
objects += buildFiles.map { .pbxBuildFile($0) }
let buildPhase = PBXSourcesBuildPhase(reference: id(), files: buildFiles.referenceSet)
let buildPhases = [buildPhase]
objects += buildPhases.map { .pbxSourcesBuildPhase($0) }
let configs: [XCBuildConfiguration] = try spec.configs.map { config in
let buildSettings = try getTargetBuildSettings(config: config, target: target)
@@ -110,23 +126,71 @@ public class ProjectGenerator {
let buildConfigList = XCConfigurationList(reference: id(), buildConfigurations: configs.referenceSet, defaultConfigurationName: "")
objects.append(.xcConfigurationList(buildConfigList))
var dependancies: [String] = []
var frameworkFiles: [String] = []
var copyFiles: [String] = []
for dependancy in target.dependencies {
switch dependancy.type {
case .target:
let targetProxy = PBXContainerItemProxy(reference: id(), containerPortal: projectReference, remoteGlobalIDString: targetNativeReferences[dependancy.name]!, proxyType: .nativeTarget, remoteInfo: dependancy.name)
let targetDependancy = PBXTargetDependency(reference: id(), target: targetNativeReferences[dependancy.name]!, targetProxy: targetProxy.reference )
let productReference = PBXFileReference(reference: id(), sourceTree: .buildProductsDir, path: target.name, includeInIndex: 0)
objects.append(.pbxFileReference(productReference))
objects.append(.pbxContainerItemProxy(targetProxy))
objects.append(.pbxTargetDependency(targetDependancy))
dependancies.append(targetDependancy.reference)
let nativeTarget = PBXNativeTarget(reference: id(), buildConfigurationList: buildConfigList.reference, buildPhases: buildPhases.referenceList, buildRules: [], dependencies: [], name: target.name, productReference: productReference.reference, productType: target.type)
let dependencyBuildFile = targetBuildFileReferences[dependancy.name]!
frameworkFiles.append(dependencyBuildFile)
copyFiles.append(dependencyBuildFile)
case .system:
//TODO: handle system frameworks
break
}
}
let fileReference = targetFileReferences[target.name]!
if target.type == .framework {
let buildFile = PBXBuildFile(reference: targetBuildFileReferences[target.name]!, fileRef: fileReference)
objects.append(.pbxBuildFile(buildFile))
}
var buildPhases: [String] = []
let buildPhase = PBXSourcesBuildPhase(reference: id(), files: buildFiles.referenceSet)
objects.append(.pbxSourcesBuildPhase(buildPhase))
buildPhases.append(buildPhase.reference)
let frameworkPhase = PBXFrameworksBuildPhase(reference: id(), files: Set(frameworkFiles), runOnlyForDeploymentPostprocessing: 0)
objects.append(.pbxFrameworksBuildPhase(frameworkPhase))
buildPhases.append(frameworkPhase.reference)
let copyPhase = PBXCopyFilesBuildPhase(reference: id(), dstPath: "", dstSubfolderSpec: .frameworks, files: Set(copyFiles))
objects.append(.pbxCopyFilesBuildPhase(copyPhase))
buildPhases.append(copyPhase.reference)
let nativeTarget = PBXNativeTarget(
reference: targetNativeReferences[target.name]!,
buildConfigurationList: buildConfigList.reference,
buildPhases: buildPhases,
buildRules: [],
dependencies: dependancies,
name: target.name,
productReference: fileReference,
productType: target.type)
objects.append(.pbxNativeTarget(nativeTarget))
targets.append(nativeTarget.reference)
}
let productGroup = PBXGroup(reference: id(), children: Set(targetFileReferences.values), sourceTree: .group, name: "Products")
objects.append(.pbxGroup(productGroup))
topLevelGroups.append(productGroup)
let mainGroup = PBXGroup(reference: id(), children: topLevelGroups.referenceSet, sourceTree: .group)
objects.append(.pbxGroup(mainGroup))
let pbxProjectRoot = PBXProject(reference: id(), buildConfigurationList: buildConfigList.reference, compatibilityVersion: "Xcode 3.2", mainGroup: mainGroup.reference, targets: targets)
let pbxProjectRoot = PBXProject(reference: projectReference, buildConfigurationList: buildConfigList.reference, compatibilityVersion: "Xcode 3.2", mainGroup: mainGroup.reference, targets: Array(targetNativeReferences.values))
objects.append(.pbxProject(pbxProjectRoot))
return PBXProj(path: path + "project.pbxproj", name: path.lastComponentWithoutExtension, archiveVersion: 1, objectVersion: 46, rootObject: pbxProjectRoot.reference, objects: objects)
return PBXProj(path: path + "project.pbxproj", name: path.lastComponentWithoutExtension, archiveVersion: 1, objectVersion: 46, rootObject: projectReference, objects: objects)
}
func getTargetBuildSettings(config: Config, target: Target) throws -> BuildSettings {
+12 -4
View File
@@ -17,10 +17,18 @@ public struct Target {
public var localizedSource: String?
public var sources: [String]
public var sourceExludes: [String]
public var dependancies: [Dependancy]
public var dependencies: [Dependency]
public var prebuildScripts: [String]
public var postbuildScripts: [String]
public var buildSettings: TargetBuildSettings?
public var filename: String {
var name = self.name
if let fileExtension = type.fileExtension {
name += ".\(fileExtension)"
}
return name
}
}
extension Target: NamedJSONDictionaryConvertible {
@@ -41,13 +49,13 @@ extension Target: NamedJSONDictionaryConvertible {
buildSettings = jsonDictionary.json(atKeyPath: "buildSettings")
sources = jsonDictionary.json(atKeyPath: "sources") ?? []
sourceExludes = jsonDictionary.json(atKeyPath: "sourceExludes") ?? []
dependancies = jsonDictionary.json(atKeyPath: "dependancies") ?? []
dependencies = jsonDictionary.json(atKeyPath: "dependencies") ?? []
prebuildScripts = jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
postbuildScripts = jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
}
}
public struct Dependancy {
public struct Dependency {
public var name: String
public var type: DependancyType
@@ -58,7 +66,7 @@ public struct Dependancy {
}
}
extension Dependancy: JSONObjectConvertible {
extension Dependency: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
+10 -2
View File
@@ -20,16 +20,24 @@ public struct SpecLinter {
fixits.append(.createdConfigs)
}
for target in spec.targets {
for dependency in target.dependencies {
if dependency.type == .target && !spec.targets.contains(where: { $0.name == dependency.name}) {
errors.append(.invalidTargetDependency(dependency.name))
}
}
}
return (spec, fixits, errors)
}
}
public enum SpecLinterError: CustomStringConvertible {
case invalidDependancy(String)
case invalidTargetDependency(String)
public var description: String {
switch self {
case let .invalidDependancy(dependancy): return "Invalid dependancy \(dependancy)"
case let .invalidTargetDependency(dependency): return "Invalid target dependency \(dependency)"
}
}
}