mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
Merge remote-tracking branch 'upstream/master' into ts-no-overwriting-xcconfig-value
This commit is contained in:
@@ -17,7 +17,6 @@ import ProjectSpec
|
||||
public class PBXProjGenerator {
|
||||
|
||||
let spec: ProjectSpec
|
||||
let basePath: Path
|
||||
let currentXcodeVersion: String
|
||||
|
||||
var fileReferencesByPath: [Path: String] = [:]
|
||||
@@ -28,7 +27,7 @@ public class PBXProjGenerator {
|
||||
var targetBuildFiles: [String: PBXBuildFile] = [:]
|
||||
var targetFileReferences: [String: String] = [:]
|
||||
var topLevelGroups: [PBXGroup] = []
|
||||
var carthageFrameworksByPlatform: [String: [String]] = [:]
|
||||
var carthageFrameworksByPlatform: [String: Set<String>] = [:]
|
||||
var frameworkFiles: [String] = []
|
||||
|
||||
var uuids: Set<String> = []
|
||||
@@ -38,10 +37,9 @@ public class PBXProjGenerator {
|
||||
return spec.options.carthageBuildPath ?? "Carthage/Build"
|
||||
}
|
||||
|
||||
public init(spec: ProjectSpec, path: Path, currentXcodeVersion: String) {
|
||||
public init(spec: ProjectSpec, currentXcodeVersion: String) {
|
||||
self.currentXcodeVersion = currentXcodeVersion
|
||||
self.spec = spec
|
||||
basePath = path
|
||||
}
|
||||
|
||||
public func generateUUID<T: PBXObject>(_ element: T.Type, _ id: String) -> String {
|
||||
@@ -64,17 +62,17 @@ public class PBXProjGenerator {
|
||||
|
||||
public func generate() throws -> PBXProj {
|
||||
uuids = []
|
||||
project = PBXProj(archiveVersion: 1, objectVersion: 46, rootObject: generateUUID(PBXProject.self, spec.name))
|
||||
project = PBXProj(objectVersion: 46, rootObject: generateUUID(PBXProject.self, spec.name))
|
||||
|
||||
for group in spec.fileGroups {
|
||||
_ = try getGroups(path: basePath + group)
|
||||
_ = try getGroups(path: spec.basePath + group)
|
||||
}
|
||||
|
||||
let buildConfigs: [XCBuildConfiguration] = spec.configs.map { config in
|
||||
let buildSettings = spec.getProjectBuildSettings(basePath: basePath, config: config)
|
||||
var baseConfigurationReference: String?
|
||||
if let configPath = spec.configFiles[config.name] {
|
||||
baseConfigurationReference = getFileReference(path: basePath + configPath, inPath: basePath)
|
||||
baseConfigurationReference = getFileReference(path: spec.basePath + configPath, inPath: spec.basePath)
|
||||
}
|
||||
return XCBuildConfiguration(reference: generateUUID(XCBuildConfiguration.self, config.name), name: config.name, baseConfigurationReference: baseConfigurationReference, buildSettings: buildSettings)
|
||||
}
|
||||
@@ -105,11 +103,11 @@ public class PBXProjGenerator {
|
||||
if !carthageFrameworksByPlatform.isEmpty {
|
||||
var platforms: [PBXGroup] = []
|
||||
for (platform, fileReferences) in carthageFrameworksByPlatform {
|
||||
let platformGroup = PBXGroup(reference: generateUUID(PBXGroup.self, platform), children: fileReferences, sourceTree: .group, name: platform, path: platform)
|
||||
let platformGroup = PBXGroup(reference: generateUUID(PBXGroup.self, platform), children: fileReferences.sorted(), sourceTree: .group, name: platform, path: platform)
|
||||
addObject(platformGroup)
|
||||
platforms.append(platformGroup)
|
||||
}
|
||||
let carthageGroup = PBXGroup(reference: generateUUID(PBXGroup.self, "Carthage"), children: platforms.references, sourceTree: .group, name: "Carthage", path: carthageBuildPath)
|
||||
let carthageGroup = PBXGroup(reference: generateUUID(PBXGroup.self, "Carthage"), children: platforms.references.sorted(), sourceTree: .group, name: "Carthage", path: carthageBuildPath)
|
||||
addObject(carthageGroup)
|
||||
frameworkFiles.append(carthageGroup.reference)
|
||||
}
|
||||
@@ -159,7 +157,7 @@ public class PBXProjGenerator {
|
||||
|
||||
let carthageDependencies = getAllCarthageDependencies(target: target)
|
||||
|
||||
let sourcePaths = target.sources.map { basePath + $0 }
|
||||
let sourcePaths = target.sources.map { spec.basePath + $0 }
|
||||
var sourceFiles: [SourceFile] = []
|
||||
|
||||
for source in sourcePaths {
|
||||
@@ -177,13 +175,13 @@ public class PBXProjGenerator {
|
||||
|
||||
// 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)
|
||||
!spec.targetHasBuildSetting("INFOPLIST_FILE", basePath: spec.basePath, target: target, config: config) {
|
||||
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: spec.basePath)
|
||||
}
|
||||
|
||||
// automatically calculate bundle id
|
||||
if let bundleIdPrefix = spec.options.bundleIdPrefix,
|
||||
!spec.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: basePath, target: target, config: config) {
|
||||
!spec.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: spec.basePath, target: target, config: config) {
|
||||
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
|
||||
@@ -191,7 +189,7 @@ public class PBXProjGenerator {
|
||||
|
||||
// automatically set test target name
|
||||
if target.type == .uiTestBundle,
|
||||
!spec.targetHasBuildSetting("TEST_TARGET_NAME", basePath: basePath, target: target, config: config) {
|
||||
!spec.targetHasBuildSetting("TEST_TARGET_NAME", basePath: spec.basePath, target: target, config: config) {
|
||||
for dependency in target.dependencies {
|
||||
if dependency.type == .target,
|
||||
let dependencyTarget = spec.getTarget(dependency.reference),
|
||||
@@ -219,7 +217,7 @@ public class PBXProjGenerator {
|
||||
|
||||
var baseConfigurationReference: String?
|
||||
if let configPath = target.configFiles[config.name] {
|
||||
baseConfigurationReference = getFileReference(path: basePath + configPath, inPath: basePath)
|
||||
baseConfigurationReference = getFileReference(path: spec.basePath + configPath, inPath: spec.basePath)
|
||||
}
|
||||
return XCBuildConfiguration(reference: generateUUID(XCBuildConfiguration.self, config.name + target.name), name: config.name, baseConfigurationReference: baseConfigurationReference, buildSettings: buildSettings)
|
||||
}
|
||||
@@ -250,9 +248,9 @@ public class PBXProjGenerator {
|
||||
addObject(targetDependency)
|
||||
dependencies.append(targetDependency.reference)
|
||||
|
||||
if dependencyTarget.type.isLibrary || dependencyTarget.type.isFramework {
|
||||
if (dependencyTarget.type.isLibrary || dependencyTarget.type.isFramework) && dependency.link {
|
||||
let dependencyBuildFile = targetBuildFiles[dependencyTargetName]!
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, dependencyBuildFile.reference + target.name), fileRef: dependencyBuildFile.fileRef)
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, dependencyBuildFile.reference + target.name), fileRef: dependencyBuildFile.fileRef!)
|
||||
addObject(buildFile)
|
||||
targetFrameworkBuildFiles.append(buildFile.reference)
|
||||
}
|
||||
@@ -277,7 +275,7 @@ public class PBXProjGenerator {
|
||||
|
||||
case .framework:
|
||||
|
||||
let fileReference = getFileReference(path: Path(dependency.reference), inPath: basePath)
|
||||
let fileReference = getFileReference(path: Path(dependency.reference), inPath: spec.basePath)
|
||||
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference)
|
||||
addObject(buildFile)
|
||||
@@ -293,9 +291,6 @@ public class PBXProjGenerator {
|
||||
copyFrameworksReferences.append(embedFile.reference)
|
||||
}
|
||||
case .carthage:
|
||||
if carthageFrameworksByPlatform[target.platform.carthageDirectoryName] == nil {
|
||||
carthageFrameworksByPlatform[target.platform.carthageDirectoryName] = []
|
||||
}
|
||||
var platformPath = Path(getCarthageBuildPath(platform: target.platform))
|
||||
var frameworkPath = platformPath + dependency.reference
|
||||
if frameworkPath.extension == nil {
|
||||
@@ -305,7 +300,7 @@ public class PBXProjGenerator {
|
||||
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference)
|
||||
addObject(buildFile)
|
||||
carthageFrameworksByPlatform[target.platform.carthageDirectoryName]?.append(fileReference)
|
||||
carthageFrameworksByPlatform[target.platform.carthageDirectoryName, default: []].insert(fileReference)
|
||||
|
||||
targetFrameworkBuildFiles.append(buildFile.reference)
|
||||
if target.platform == .macOS && target.type.isApp {
|
||||
@@ -330,7 +325,7 @@ public class PBXProjGenerator {
|
||||
var shellScript: String
|
||||
switch buildScript.script {
|
||||
case let .path(path):
|
||||
shellScript = try (basePath + path).read()
|
||||
shellScript = try (spec.basePath + path).read()
|
||||
case let .script(script):
|
||||
shellScript = script
|
||||
}
|
||||
@@ -430,11 +425,11 @@ public class PBXProjGenerator {
|
||||
|
||||
let nativeTarget = PBXNativeTarget(
|
||||
reference: targetNativeReferences[target.name]!,
|
||||
name: target.name,
|
||||
buildConfigurationList: buildConfigList.reference,
|
||||
buildPhases: buildPhases,
|
||||
buildRules: [],
|
||||
dependencies: dependencies,
|
||||
name: target.name,
|
||||
productReference: fileReference,
|
||||
productType: target.type)
|
||||
addObject(nativeTarget)
|
||||
@@ -549,13 +544,11 @@ public class PBXProjGenerator {
|
||||
for localisedDirectory in localisedDirectories {
|
||||
let localisationName = localisedDirectory.lastComponentWithoutExtension
|
||||
for path in try localisedDirectory.children().sorted { $0.lastComponent < $1.lastComponent } {
|
||||
|
||||
|
||||
let filePath = "\(localisedDirectory.lastComponent)/\(path.lastComponent)"
|
||||
|
||||
// find base localisation variant group
|
||||
let name = path.lastComponentWithoutExtension
|
||||
let variantGroup = baseLocalisationVariantGroups.first { Path($0.name).lastComponentWithoutExtension == name }
|
||||
let variantGroup = baseLocalisationVariantGroups.first { Path($0.name!).lastComponentWithoutExtension == name }
|
||||
|
||||
let fileReference: String
|
||||
if let cachedFileReference = fileReferencesByPath[path] {
|
||||
@@ -571,7 +564,9 @@ public class PBXProjGenerator {
|
||||
}
|
||||
|
||||
if let variantGroup = variantGroup {
|
||||
variantGroup.children.append(fileReference)
|
||||
if !variantGroup.children.contains(fileReference) {
|
||||
variantGroup.children.append(fileReference)
|
||||
}
|
||||
} else {
|
||||
// add SourceFile to group if there is no Base.lproj directory
|
||||
let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference),
|
||||
@@ -583,7 +578,7 @@ public class PBXProjGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
let groupPath: String = depth == 0 ? path.byRemovingBase(path: basePath).string : path.lastComponent
|
||||
let groupPath: String = depth == 0 ? path.byRemovingBase(path: spec.basePath).string : path.lastComponent
|
||||
let group: PBXGroup
|
||||
if let cachedGroup = groupsByPath[path] {
|
||||
group = cachedGroup
|
||||
|
||||
@@ -16,12 +16,10 @@ import ProjectSpec
|
||||
public class ProjectGenerator {
|
||||
|
||||
var spec: ProjectSpec
|
||||
var path: Path
|
||||
let currentXcodeVersion = "0900"
|
||||
|
||||
public init(spec: ProjectSpec, path: Path) {
|
||||
public init(spec: ProjectSpec) {
|
||||
self.spec = spec
|
||||
self.path = path
|
||||
}
|
||||
|
||||
var defaultDebugConfig: Config {
|
||||
@@ -33,8 +31,8 @@ public class ProjectGenerator {
|
||||
}
|
||||
|
||||
public func generateProject() throws -> XcodeProj {
|
||||
try spec.validate(path: path)
|
||||
let pbxProjGenerator = PBXProjGenerator(spec: spec, path: path, currentXcodeVersion: currentXcodeVersion)
|
||||
try spec.validate()
|
||||
let pbxProjGenerator = PBXProjGenerator(spec: spec, currentXcodeVersion: currentXcodeVersion)
|
||||
let pbxProject = try pbxProjGenerator.generate()
|
||||
let workspace = try generateWorkspace()
|
||||
let sharedData = try generateSharedData(pbxProject: pbxProject)
|
||||
|
||||
@@ -18,7 +18,7 @@ extension ProjectSpec {
|
||||
public func getProjectBuildSettings(basePath: Path, config: Config) -> BuildSettings {
|
||||
var buildSettings: BuildSettings = [:]
|
||||
|
||||
if let type = config.type {
|
||||
if let type = config.type, options.settingPresets.applyProject {
|
||||
buildSettings += SettingsPresetFile.base.getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
|
||||
}
|
||||
@@ -40,10 +40,12 @@ extension ProjectSpec {
|
||||
|
||||
public func getTargetBuildSettings(basePath: Path, target: Target, config: Config) -> BuildSettings {
|
||||
var buildSettings = BuildSettings()
|
||||
|
||||
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
|
||||
|
||||
if options.settingPresets.applyTarget {
|
||||
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
|
||||
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
|
||||
}
|
||||
buildSettings += getBuildSettings(settings: target.settings, config: config)
|
||||
// Do not overwrite target xcconfig's values.
|
||||
if let configPath = target.configFiles[config.name] {
|
||||
@@ -79,8 +81,10 @@ extension ProjectSpec {
|
||||
|
||||
buildSettings += settings.buildSettings
|
||||
|
||||
if let configSettings = settings.configSettings[config.name] {
|
||||
buildSettings += getBuildSettings(settings: configSettings, config: config)
|
||||
for (configVariant, settings) in settings.configSettings {
|
||||
if config.name.lowercased().contains(configVariant.lowercased()) {
|
||||
buildSettings += getBuildSettings(settings: settings, config: config)
|
||||
}
|
||||
}
|
||||
|
||||
return buildSettings
|
||||
|
||||
@@ -16,7 +16,7 @@ public struct SpecLoader {
|
||||
|
||||
public static func loadSpec(path: Path) throws -> ProjectSpec {
|
||||
let dictionary = try loadDictionary(path: path)
|
||||
return try ProjectSpec(jsonDictionary: dictionary)
|
||||
return try ProjectSpec(basePath: path.parent(), jsonDictionary: dictionary)
|
||||
}
|
||||
|
||||
private static func loadDictionary(path: Path) throws -> JSONDictionary {
|
||||
|
||||
@@ -11,7 +11,7 @@ import PathKit
|
||||
|
||||
extension ProjectSpec {
|
||||
|
||||
public mutating func validate(path: Path) throws {
|
||||
public mutating func validate() throws {
|
||||
|
||||
if configs.isEmpty {
|
||||
configs = [Config(name: "Debug", type: .debug), Config(name: "Release", type: .release)]
|
||||
@@ -28,17 +28,22 @@ extension ProjectSpec {
|
||||
errors.append(.invalidSettingsGroup(group))
|
||||
}
|
||||
}
|
||||
for config in settings.configSettings.keys {
|
||||
if !configs.contains(where: { $0.name.lowercased().contains(config.lowercased())}) {
|
||||
errors.append(.invalidConfigReference(config))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
for fileGroup in fileGroups {
|
||||
if !(path + fileGroup).exists {
|
||||
if !(basePath + fileGroup).exists {
|
||||
errors.append(.invalidFileGroup(fileGroup))
|
||||
}
|
||||
}
|
||||
|
||||
for (config, configFile) in configFiles {
|
||||
if !(path + configFile).exists {
|
||||
if !(basePath + configFile).exists {
|
||||
errors.append(.invalidConfigFile(configFile: configFile, config: config))
|
||||
}
|
||||
}
|
||||
@@ -55,7 +60,7 @@ extension ProjectSpec {
|
||||
}
|
||||
|
||||
for (config, configFile) in target.configFiles {
|
||||
if !(path + configFile).exists {
|
||||
if !(basePath + configFile).exists {
|
||||
errors.append(.invalidTargetConfigFile(configFile: configFile, config: config, target: target.name))
|
||||
}
|
||||
}
|
||||
@@ -67,7 +72,7 @@ extension ProjectSpec {
|
||||
}
|
||||
|
||||
for source in target.sources {
|
||||
let sourcePath = path + source
|
||||
let sourcePath = basePath + source
|
||||
if !sourcePath.exists {
|
||||
errors.append(.missingTargetSource(target: target.name, source: sourcePath.string))
|
||||
}
|
||||
@@ -84,6 +89,15 @@ extension ProjectSpec {
|
||||
}
|
||||
}
|
||||
|
||||
if scheme.configVariants.isEmpty {
|
||||
if !configs.contains(where: { $0.type == .debug }) {
|
||||
errors.append(.missingConfigTypeForGeneratedTargetScheme(target: target.name, configType: .debug))
|
||||
}
|
||||
if !configs.contains(where: { $0.type == .release }) {
|
||||
errors.append(.missingConfigTypeForGeneratedTargetScheme(target: target.name, configType: .release))
|
||||
}
|
||||
}
|
||||
|
||||
for testTarget in scheme.testTargets {
|
||||
if getTarget(testTarget) == nil {
|
||||
errors.append(.invalidTargetSchemeTest(target: target.name, testTarget: testTarget))
|
||||
@@ -94,7 +108,7 @@ extension ProjectSpec {
|
||||
let scripts = target.prebuildScripts + target.postbuildScripts
|
||||
for script in scripts {
|
||||
if case let .path(pathString) = script.script {
|
||||
let scriptPath = path + pathString
|
||||
let scriptPath = basePath + pathString
|
||||
if !scriptPath.exists {
|
||||
errors.append(.invalidBuildScriptPath(target: target.name, path: pathString))
|
||||
}
|
||||
@@ -150,6 +164,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
|
||||
case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType)
|
||||
case invalidTargetSchemeTest(target: String, testTarget: String)
|
||||
case invalidFileGroup(String)
|
||||
case invalidConfigReference(String)
|
||||
case missingConfigTypeForGeneratedTargetScheme(target: String, configType: ConfigType)
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
@@ -162,9 +178,11 @@ public struct SpecValidationError: Error, CustomStringConvertible {
|
||||
case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)"
|
||||
case let .invalidSettingsGroup(group): return "Invalid settings group \(group.quoted)"
|
||||
case let .invalidBuildScriptPath(target, path): return "Target \(target.quoted) has a script path that doesn't exist \(path.quoted)"
|
||||
case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has invalid scheme config varians which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
|
||||
case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has an invalid scheme config variant which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
|
||||
case let .invalidTargetSchemeTest(target, test): return "Target \(target.quoted) scheme has invalid test \(test.quoted)"
|
||||
case let .invalidFileGroup(group): return "Invalid file group \(group.quoted)"
|
||||
case let .invalidConfigReference(config): return "Invalid config reference \(config.quoted)"
|
||||
case let .missingConfigTypeForGeneratedTargetScheme(target, configType): return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user