refactored

This commit is contained in:
Yonas Kolb
2017-07-20 23:24:53 +02:00
parent 3c8062ac74
commit 1373ec19ac
14 changed files with 594 additions and 172 deletions
@@ -0,0 +1,21 @@
//
// ProjectExtensions.swift
// XcodeGen
//
// Created by Yonas Kolb on 19/7/17.
//
//
import Foundation
import xcodeproj
extension Array where Element: ProjectElement {
var referenceList: [String] {
return map { $0.reference }
}
var referenceSet: Set<String> {
return Set(referenceList)
}
}
@@ -11,15 +11,24 @@ import PathKit
import xcodeproj
import xcodeprojprotocols
public struct Generator {
public class ProjectGenerator {
public static func generate(spec: Spec, path: Path) throws {
let spec: Spec
let path: Path
public init(spec: Spec, path: Path) {
self.spec = spec
self.path = path
}
public func generate() throws -> XcodeProj {
let workspaceReferences: [XCWorkspace.Data.FileRef] = [XCWorkspace.Data.FileRef.project(path: path)]
let workspaceData = XCWorkspace.Data(path: path + "project.xcworkspace/contents.xcworkspacedata", references: workspaceReferences)
let workspace = XCWorkspace(path: path + "project.xcworkspace", data: workspaceData)
var objects: [PBXObject] = []
var groupsByPath: [String: String] = [:]
var ids = 0
func id() -> String {
@@ -28,47 +37,77 @@ public struct Generator {
// return "OBJECT_\(ids)"
}
let mainGroup = PBXGroup(reference: id(), children: [], sourceTree: .group)
let buildConfigs = spec.configs.map { config in
XCBuildConfiguration(reference: id(), name: config.name, baseConfigurationReference: nil, buildSettings: BuildSettings(dictionary: config.settings))
}
let buildConfigList = XCConfigurationList(reference: id(), buildConfigurations: Set(buildConfigs.map { $0.reference }), defaultConfigurationName: buildConfigs.first?.name ?? "", defaultConfigurationIsVisible: 0)
let buildConfigList = XCConfigurationList(reference: id(), buildConfigurations: buildConfigs.referenceSet, defaultConfigurationName: buildConfigs.first?.name ?? "", defaultConfigurationIsVisible: 0)
objects += buildConfigs.map { .xcBuildConfiguration($0) }
objects.append(.xcConfigurationList(buildConfigList))
var groups: [PBXGroup] = []
var fileReferences: [PBXFileReference] = []
var topLevelGroups: [PBXGroup] = []
func getGroup(path: Path) throws -> PBXGroup {
let directories = try path.children().filter { $0.isDirectory }
let files = try path.children().filter { $0.isFile }
var children: [String] = []
for path in files {
let fileReference = PBXFileReference(reference: id(), sourceTree: .group, path: path.lastComponent)
fileReferences.append(fileReference)
children.append(fileReference.reference)
}
for path in directories {
let group = try getGroup(path: path)
children.append(group.reference)
}
let group = PBXGroup(reference: id(), children: Set(children), sourceTree: .group, name: path.lastComponent, path: path.lastComponent)
groups.append(group)
return group
}
var targets: [String] = []
for target in spec.targets {
let sourcePaths: [Path] = try target.sources.reduce([]) { paths, source in
// $0 + spec.path.parent().glob($1)
let sourcePaths = spec.path.parent() + source
let sourceFiles = try sourcePaths.recursiveChildren().filter { $0.isFile }
return paths + sourceFiles
}
let fileReferences = sourcePaths.map { PBXFileReference(reference: id(), sourceTree: .group, path: $0.lastComponent) }
let buildFiles = fileReferences.map { PBXBuildFile(reference: id(), fileRef: $0.reference) }
let buildPhase = PBXSourcesBuildPhase(reference: id(), files: Set(buildFiles.map { $0.reference }))
let buildPhases = [buildPhase]
let source = spec.path.parent() + target.sources.first!
let productReference = PBXFileReference(reference: id(), sourceTree: .buildProductsDir, path: target.name, includeInIndex: 0)
let buildConfigList = XCConfigurationList(reference: id(), buildConfigurations: [], defaultConfigurationName: "")
let nativeTarget = PBXNativeTarget(reference: id(), buildConfigurationList: buildConfigList.reference, buildPhases: buildPhases.map{ $0.reference }, buildRules: [], dependencies: [], name: target.name, productReference: productReference.reference, productType: target.type)
objects += buildFiles.map { .pbxBuildFile($0) }
groups = []
fileReferences = []
let sourceGroup = try getGroup(path: source)
topLevelGroups.append(sourceGroup)
objects += groups.map { .pbxGroup($0) }
objects += fileReferences.map { .pbxFileReference($0) }
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 buildConfigList = XCConfigurationList(reference: id(), buildConfigurations: [], defaultConfigurationName: "")
objects.append(.xcConfigurationList(buildConfigList))
objects.append(.pbxNativeTarget(nativeTarget))
let productReference = PBXFileReference(reference: id(), sourceTree: .buildProductsDir, path: target.name, includeInIndex: 0)
objects.append(.pbxFileReference(productReference))
let nativeTarget = PBXNativeTarget(reference: id(), buildConfigurationList: buildConfigList.reference, buildPhases: buildPhases.referenceList, buildRules: [], dependencies: [], name: target.name, productReference: productReference.reference, productType: target.type)
objects.append(.pbxNativeTarget(nativeTarget))
targets.append(nativeTarget.reference)
}
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)
objects.append(.pbxProject(pbxProjectRoot))
@@ -87,7 +126,7 @@ public struct Generator {
let sharedData = XCSharedData(path: path + "xcshareddata", schemes: schemes)
let project = XcodeProj(path: path, workspace: workspace, pbxproj: pbxProject, sharedData: sharedData)
try project.write(override: true)
return project
}
}
-137
View File
@@ -1,137 +0,0 @@
//
// Spec.swift
// XcodeGen
//
// Created by Yonas Kolb on 19/5/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
import PathKit
import Yams
extension Spec {
public init(path: Path) throws {
var url = URL(string: path.string)!
if url.scheme == nil {
url = URL(fileURLWithPath: path.string)
}
let data = try Data(contentsOf: url)
let string = String(data: data, encoding: .utf8)!
try self.init(path: path, string: string)
}
public init(path: Path, string: String) throws {
let yaml = try Yams.load(yaml: string)
let json = yaml as! JSONDictionary
try self.init(path: path, jsonDictionary: json)
}
public init(path: Path, jsonDictionary: JSONDictionary) throws {
self.path = path
settingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
configs = try jsonDictionary.json(atKeyPath: "configs")
targets = try jsonDictionary.json(atKeyPath: "targets")
schemes = try jsonDictionary.json(atKeyPath: "schemes")
}
}
public struct Spec {
public var settingGroups: [BuildSettingGroup]
public var configs: [Config]
public var targets: [TargetSpec]
public var schemes: [SchemeSpec]
public var path: Path
}
public struct TargetSpec {
public var name: String
public var type: PBXProductType
public var localizedSource: String?
public var sources: [String]
public var sourceExludes: [String]
public var dependancies: [Dependancy]
public var prebuildScripts: [String]
public var postbuildScripts: [String]
}
extension TargetSpec: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
let typeString: String = try jsonDictionary.json(atKeyPath: "type")
if let type = PBXProductType(rawValue: typeString) {
self.type = type
} else {
switch typeString {
case "application": type = .application
case "framework": type = .framework
default: throw SpecError.unknownTargetType(typeString)
}
}
sources = jsonDictionary.json(atKeyPath: "sources") ?? []
sourceExludes = jsonDictionary.json(atKeyPath: "sourceExludes") ?? []
dependancies = jsonDictionary.json(atKeyPath: "dependancies") ?? []
prebuildScripts = jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
postbuildScripts = jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
}
}
public struct Dependancy {
public var path: String
public var type: DependancyType
public enum DependancyType: String {
case target
case system
}
}
extension Dependancy: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
path = try jsonDictionary.json(atKeyPath: "path")
type = try jsonDictionary.json(atKeyPath: "type")
}
}
public struct Config {
public var name: String
public var buildSettingGroups: [BuildSettingGroup]
public var settings: [String: String]
}
extension Config: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
buildSettingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
settings = jsonDictionary.json(atKeyPath: "settings") ?? [:]
}
}
public struct BuildSettingGroup {
public var name: String
public var buildSettings: [String: String]
}
extension BuildSettingGroup: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
buildSettings = [:]
for (key, value) in jsonDictionary {
buildSettings[key] = String(describing: value)
}
}
}
@@ -0,0 +1,27 @@
//
// BuildSettings.swift
// XcodeGen
//
// Created by Yonas Kolb on 20/7/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
public struct BuildSettingGroup {
public var name: String
public var buildSettings: [String: String]
}
extension BuildSettingGroup: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
buildSettings = [:]
for (key, value) in jsonDictionary {
buildSettings[key] = String(describing: value)
}
}
}
+26
View File
@@ -0,0 +1,26 @@
//
// Config.swift
// XcodeGen
//
// Created by Yonas Kolb on 20/7/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
public struct Config {
public var name: String
public var buildSettingGroups: [BuildSettingGroup]
public var settings: [String: String]
}
extension Config: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
buildSettingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
settings = jsonDictionary.json(atKeyPath: "settings") ?? [:]
}
}
@@ -10,7 +10,7 @@ import Foundation
import xcodeproj
import JSONUtilities
public struct SchemeSpec {
public struct Scheme {
public var name: String
public var build: Build
@@ -25,7 +25,7 @@ public struct SchemeSpec {
}
}
extension SchemeSpec: NamedJSONObjectConvertible {
extension Scheme: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
@@ -33,14 +33,14 @@ extension SchemeSpec: NamedJSONObjectConvertible {
}
}
extension SchemeSpec.Build: JSONObjectConvertible {
extension Scheme.Build: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
entries = try jsonDictionary.json(atKeyPath: "targets")
}
}
extension SchemeSpec.BuildEntry: NamedJSONObjectConvertible {
extension Scheme.BuildEntry: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
target = name
+77
View File
@@ -0,0 +1,77 @@
//
// Spec.swift
// XcodeGen
//
// Created by Yonas Kolb on 19/5/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
import PathKit
import Yams
extension Spec {
public init(path: Path) throws {
var url = URL(string: path.string)!
if url.scheme == nil {
url = URL(fileURLWithPath: path.string)
}
let data = try Data(contentsOf: url)
let string = String(data: data, encoding: .utf8)!
try self.init(path: path, string: string)
}
public init(path: Path, string: String) throws {
let yaml = try Yams.load(yaml: string)
let json = yaml as! JSONDictionary
try self.init(path: path, jsonDictionary: json)
}
public init(path: Path, jsonDictionary: JSONDictionary) throws {
self.path = path
settingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
configs = try jsonDictionary.json(atKeyPath: "configs")
targets = try jsonDictionary.json(atKeyPath: "targets")
schemes = try jsonDictionary.json(atKeyPath: "schemes")
}
}
public struct Spec {
public var settingGroups: [BuildSettingGroup]
public var configs: [Config]
public var targets: [Target]
public var schemes: [Scheme]
public var path: Path
}
public struct Dependancy {
public var path: String
public var type: DependancyType
public enum DependancyType: String {
case target
case system
}
}
extension Dependancy: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
path = try jsonDictionary.json(atKeyPath: "path")
type = try jsonDictionary.json(atKeyPath: "type")
}
}
+44
View File
@@ -0,0 +1,44 @@
//
// Target.swift
// XcodeGen
//
// Created by Yonas Kolb on 20/7/17.
//
//
import Foundation
import xcodeproj
import JSONUtilities
public struct Target {
public var name: String
public var type: PBXProductType
public var localizedSource: String?
public var sources: [String]
public var sourceExludes: [String]
public var dependancies: [Dependancy]
public var prebuildScripts: [String]
public var postbuildScripts: [String]
}
extension Target: NamedJSONObjectConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
let typeString: String = try jsonDictionary.json(atKeyPath: "type")
if let type = PBXProductType(rawValue: typeString) {
self.type = type
} else {
switch typeString {
case "application": type = .application
case "framework": type = .framework
default: throw SpecError.unknownTargetType(typeString)
}
}
sources = jsonDictionary.json(atKeyPath: "sources") ?? []
sourceExludes = jsonDictionary.json(atKeyPath: "sourceExludes") ?? []
dependancies = jsonDictionary.json(atKeyPath: "dependancies") ?? []
prebuildScripts = jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
postbuildScripts = jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
}
}