buildPresets

This commit is contained in:
Yonas Kolb
2017-07-26 23:18:45 +02:00
parent 4e35dc6b02
commit 9d5342d301
19 changed files with 560 additions and 382 deletions
@@ -272,26 +272,12 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
OBJECT_2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_SUSPICIOUS_MOVE = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_OPTIMIZATION_LEVEL = s;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
OBJECT_20 /* Release */ = {
OBJECT_2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -306,102 +292,69 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = TestProject/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.test;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
OBJECT_21 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = TestProject/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.test;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
OBJECT_3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
OBJECT_38 /* Release */ = {
OBJECT_20 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = TestProject/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.test;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
OBJECT_21 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = TestProject/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.test;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
OBJECT_3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -416,91 +369,72 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = s;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
OBJECT_38 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = MyFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
OBJECT_39 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = MyFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
OBJECT_39 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = MyFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
OBJECT_22 /* Build configuration list for PBXNativeTarget "TestProject" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJECT_21 /* Debug */,
OBJECT_20 /* Release */,
OBJECT_21 /* Release */,
OBJECT_20 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
@@ -508,17 +442,17 @@
OBJECT_4 /* Build configuration list for PBXProject */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJECT_3 /* Debug */,
OBJECT_2 /* Release */,
OBJECT_3 /* Release */,
OBJECT_2 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
OBJECT_40 /* Build configuration list for PBXNativeTarget "MyFramework" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJECT_38 /* Release */,
OBJECT_39 /* Debug */,
OBJECT_38 /* Debug */,
OBJECT_39 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Scheme LastUpgradeVersion="0830" version="1.3">
<AnalyzeAction buildConfiguration="Debug" />
<ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES" />
<TestAction selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv="YES" buildConfiguration="Debug">
<Testables />
<MacroExpansion>
<BuildableReference BlueprintIdentifier="OBJECT_5" ReferencedContainer="container:GeneratedProject.xcodeproj" BuildableName="TestProject.app" BlueprintName="MyScheme" BuildableIdentifier="primary" />
</MacroExpansion>
</TestAction>
<ProfileAction savedToolIdentifier="" useCustomWorkingDirectory="NO" shouldUseLaunchSchemeArgsEnv="YES" buildConfiguration="Release" debugDocumentVersioning="YES">
<BuildableProductRunnable runnableDebuggingMode="0">
<BuildableReference BlueprintIdentifier="OBJECT_5" ReferencedContainer="container:GeneratedProject.xcodeproj" BuildableName="TestProject.app" BlueprintName="MyScheme" BuildableIdentifier="primary" />
</BuildableProductRunnable>
</ProfileAction>
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES">
<BuildActionEntries>
<BuildActionEntry buildForArchiving="YES" buildForTesting="YES" buildForRunning="YES" buildForProfiling="NO" buildForAnalyzing="YES">
<BuildableReference BlueprintIdentifier="OBJECT_5" ReferencedContainer="container:GeneratedProject.xcodeproj" BuildableName="TestProject.app" BlueprintName="MyScheme" BuildableIdentifier="primary" />
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<LaunchAction selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle="0" buildConfiguration="Debug" debugServiceExtension="internal" ignoresPersistentStateOnLaunch="NO" useCustomWorkingDirectory="NO" allowLocationSimulation="YES" debugDocumentVersioning="YES">
<BuildableProductRunnable runnableDebuggingMode="0">
<BuildableReference BlueprintIdentifier="OBJECT_5" ReferencedContainer="container:GeneratedProject.xcodeproj" BuildableName="TestProject.app" BlueprintName="MyScheme" BuildableIdentifier="primary" />
</BuildableProductRunnable>
</LaunchAction>
</Scheme>
+2 -18
View File
@@ -1,21 +1,10 @@
name: GeneratedProject
settingGroups:
testGroup:
TEST_SETTING: test value
configs:
Debug:
type: debug
settingGroups:
- testGroup
buildSettings:
Release:
type: release
targets:
- name: TestProject
type: application
platform: iOS
sources: TestProject
buildSettings:
settings:
INFOPLIST_FILE: TestProject/Info.plist
PRODUCT_BUNDLE_IDENTIFIER: com.test
dependencies:
@@ -24,10 +13,5 @@ targets:
type: framework
platform: iOS
sources: MyFramework
buildSettings:
settings:
INFOPLIST_FILE: MyFramework/Info.plist
schemes:
MyScheme:
build:
targets:
TestProject: all
+50
View File
@@ -0,0 +1,50 @@
name: SettingsTest
settingPresets:
preset1:
settingPresets:
- preset2
settings:
SETTING 1: value 1
preset2:
SETTING 2: value 2
preset3:
settingPresets:
- preset2
settings:
default:
SETTING 9: value 9
configs:
config1:
SETTING 8: value 8
configs:
config1:
type: debug
settingPresets:
- preset1
settings:
SETTING 3: value 3
config2:
type: release
settingPresets:
- preset2
settings:
SETTING 4: value 4
targets:
- name: Target1
type: application
platform: iOS
settingPresets:
- preset1
settings:
SETTING 5: value 5
- name: Target2
type: application
platform: iOS
settingPresets:
- preset3
settings:
default:
SETTING 7: value 7
configs:
config1:
SETTING 6: value 6
+85 -12
View File
@@ -8,6 +8,7 @@ A command line tool that generates your Xcode project from a YAML project spec a
This allows for easy configuration which is git friendly, and means your project structure represents exacty what's on disk. The project can be re-generated on demand which means you can remove your xcode project from git and say goodbye to .xcodeproj merge conflicts!
Given a simple project spec file:
```yaml
name: My Project
configs:
@@ -20,7 +21,7 @@ targets:
type: application
platform: iOS
sources: MyApp
buildSettings:
settings:
INFOPLIST_FILE: MyApp/Info.plist
PRODUCT_BUNDLE_IDENTIFIER: com.myapp
dependencies:
@@ -60,23 +61,49 @@ Use `XcodeGen -help` to see the list of options:
- **project**: (optional): This is an optional path the generated xcode project file. If it is left out, the file will be written to the same directory as the spec, and with the same name as the spec file
# XcodeGen project spec
Each spec must contain a name which is used for the generated project name
## configs
Configs specify the configurations in your project.
Each config can specify a `type` of either `debug` or `release` which will then apply the default build settings for those types. A config can also specify its own list of `buildSettings`
Each config can specify a `type` of either `debug` or `release` which will then apply the default build settings for those types. A config can also specify its own list of `settings `
```yaml
configs:
Debug:
type: debug
buildSettings:
settings:
MY_COOL_SETTING: value
Release:
type: release
```
If no configs are specified, default `Debug` and `Release` configs will be created for you
## settingPresets
Setting presets can be used to group build settings together and reuse them elsewhere. Each preset specifies a list of settings. It can also list other setting presets that will be merged in.
```yaml
settingPresets:
preset:
BUILD_SETTING: value
preset2:
settings:
BUILD_SETTING: value
settingPresets:
- preset
preset3:
settings:
base:
BUILD_SETTING: value
configs:
debug:
BUILD_SETTING: value
settingPresets:
- preset
```
## targets
This is list of targets
```yaml
targets:
- name: MyTarget
@@ -111,6 +138,7 @@ Specifies the platform for the target. This will provide default build settings
#### sources
Specifies the source directories for the target. This can either be a single path or a list of paths. Applicable source files, resources, headers, and lproj files will be parsed appropriately
```yaml
targets:
- name: MyTarget
@@ -121,8 +149,9 @@ targets:
- MyOtherTargetSource2
```
#### buildSettings
Species the build settings for the target. This can either be a simple map of build settings, or they can be broken down into specific configurations. If supplying configuration specific settings, a `$base` configuration may be used to provide default build settings that apply accross all configurations
#### settings
Species the build settings for the target. This can either be a simple list of build settings, or you can specify a map of configs to settings. If supplying such config specific settings, then a `default` settings list can be provided for build settings that apply across all configurations
```yaml
configs:
test:
@@ -133,17 +162,27 @@ configs:
type: release
targets:
- name: MyTarget
buildSettings:
settings:
INFO_PLIST: Info.plist
- name: MyOtherTarget
buildSettings:
$base:
settings:
default:
MY_SETTING: default
MY_OTHER_SETTING: value
test:
MY_SETTING: test value
staging:
MY_SETTING: staging value
configs:
test:
MY_SETTING: test value
```
#### settingPresets
Specifies the target specific settingPresets. This is a simple list that maps to the top level setting presets. These settings will be merged in before any custom settings
```yaml
targets:
- name: MyTarget
settingPresets:
- mypreset1
- mypreset2
```
#### dependencies
@@ -164,6 +203,7 @@ targets:
#### configs
Specifies `.xcconfig` files for each configuration for the target.
```yaml
targets:
- name: MyTarget
@@ -172,6 +212,39 @@ targets:
Release: config_files/release.xcconfig
```
#### generateSchemes
This is a conveniance used to automatically generate schemes for a target based on large amount of configs. A list of names is provided, then for each of these names a scheme is created, using configs that contain the name with debug and release variants. This is useful for having different environment schemes.
For example, the following spec would create 3 schemes called:
- MyApp Test
- MyApp Staging
- MyApp Production
Each scheme would use different build configuration for the different build types, specifically debug configs for `run`, `test`, and `anaylze`, and release configs for `profile` and `archive`
```
configs:
Test Debug:
type: debug
Staging Debug:
type: debug
Production Debug:
type: debug
Test Release:
type: release
Staging Release:
type: release
Production Release:
type: release
targets
- name: MyApp
generateSchemes:
- Test
- Staging
- Production
```
---
## Attributions
@@ -1,50 +0,0 @@
//
// BuildSettingGroupType.swift
// XcodeGen
//
// Created by Yonas Kolb on 23/7/17.
//
//
import Foundation
import xcodeproj
import PathKit
import Yams
import JSONUtilities
enum BuildSettingsPreset {
case config(ConfigType)
case platform(Platform)
case product(PBXProductType)
case base
var path: String {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .product(product): return "Products/\(product.rawValue)"
case .base: return "base"
}
}
private static var buildSettings: [String: BuildSettings] = [:]
func getBuildSettings() throws -> BuildSettings? {
if let group = BuildSettingsPreset.buildSettings[path] {
return group
}
let settingsPath = Path(#file).parent().parent().parent() + "SettingPresets/\(path).yml"
guard settingsPath.exists,
let buildSettings = try? BuildSettings(path: settingsPath) else { return nil }
BuildSettingsPreset.buildSettings[path] = buildSettings
return buildSettings
}
}
extension BuildSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
self.init(dictionary: jsonDictionary)
}
}
+5 -24
View File
@@ -48,12 +48,8 @@ public class PBXProjGenerator {
}
public func generate() throws -> PBXProj {
let buildConfigs: [XCBuildConfiguration] = try spec.configs.map { config in
var buildSettings = config.buildSettings
if let type = config.type, let typeBuildSettings = try BuildSettingsPreset.config(type).getBuildSettings() {
buildSettings = typeBuildSettings.merged(buildSettings)
}
let buildConfigs: [XCBuildConfiguration] = spec.configs.map { config in
let buildSettings = spec.getProjectBuildSettings(config: config)
return XCBuildConfiguration(reference: id(), name: config.name, baseConfigurationReference: nil, buildSettings: buildSettings)
}
@@ -135,9 +131,10 @@ public class PBXProjGenerator {
sourceFilePaths += sourceGroups.filePaths
}
let configs: [XCBuildConfiguration] = try spec.configs.map { config in
let buildSettings = try getTargetBuildSettings(config: config.name, target: target)
let configs: [XCBuildConfiguration] = spec.configs.map { config in
let buildSettings = spec.getTargetBuildSettings(target: target, config: config)
var baseConfigurationReference: String?
if let configPath = target.configs[config.name] {
let path = basePath + configPath
baseConfigurationReference = fileReferencesByPath[path]
@@ -287,22 +284,6 @@ public class PBXProjGenerator {
return nil
}
func getTargetBuildSettings(config: String, target: Target) throws -> BuildSettings {
var buildSettings = BuildSettings()
func getBuildSettingPreset(_ type: BuildSettingsPreset) throws -> BuildSettings? {
return try type.getBuildSettings()
}
buildSettings += try getBuildSettingPreset(.base)
buildSettings += try getBuildSettingPreset(.platform(target.platform))
buildSettings += try getBuildSettingPreset(.product(target.type))
buildSettings += target.buildSettings?.buildSettings
buildSettings += target.buildSettings?.configSettings[config]
return buildSettings
}
func getFileReference(path: Path, inPath: Path) -> String {
if let fileReference = fileReferencesByPath[path] {
return fileReference
+10 -6
View File
@@ -21,28 +21,32 @@ extension Array where Element: ProjectElement {
}
}
extension BuildSettings {
extension BuildSettings: CustomStringConvertible {
init() {
public init() {
dictionary = [:]
}
static var empty = BuildSettings()
public static let empty = BuildSettings()
func merged(_ buildSettings: BuildSettings) -> BuildSettings {
public func merged(_ buildSettings: BuildSettings) -> BuildSettings {
var mergedSettings = self
mergedSettings.merge(buildSettings)
return mergedSettings
}
mutating func merge(_ buildSettings: BuildSettings) {
public mutating func merge(_ buildSettings: BuildSettings) {
for (key, value) in buildSettings.dictionary {
dictionary[key] = value
}
}
public var description: String {
return dictionary.map { "\($0) = \($1)" }.joined(separator: "\n")
}
}
func +=( lhs: inout BuildSettings, rhs: BuildSettings?) {
public func +=( lhs: inout BuildSettings, rhs: BuildSettings?) {
guard let rhs = rhs else { return }
lhs.merge(rhs)
}
+30 -6
View File
@@ -40,19 +40,39 @@ public class ProjectGenerator {
var errors: [SpecValidationError.Error] = []
func validateSettingPresets(_ settingsPresets: [String]) -> [SpecValidationError.Error] {
var errors: [SpecValidationError.Error] = []
for preset in settingsPresets {
if let settingsPreset = spec.settingPresets[preset] {
errors += validateSettingPresets(settingsPreset.settingPresets)
} else {
errors.append(.invalidSettingsPreset(preset))
}
}
return errors
}
for settingPreset in spec.settingPresets.values {
errors += validateSettingPresets(settingPreset.settingPresets)
}
for config in spec.configs {
errors += validateSettingPresets(config.settingPresets)
}
for target in spec.targets {
for dependency in target.dependencies {
if case .target(let targetName) = dependency, spec.getTarget(targetName) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: targetName))
}
}
if let buildSettings = target.buildSettings {
for config in buildSettings.configSettings.keys {
if spec.getConfig(config) == nil {
errors.append(.invalidBuildSettingConfig(config: config))
}
for config in target.settings.configSettings.keys {
if spec.getConfig(config) == nil {
errors.append(.invalidBuildSettingConfig(config))
}
}
for source in target.sources {
let sourcePath = path + source
if !sourcePath.exists {
@@ -68,6 +88,8 @@ public class ProjectGenerator {
errors.append(.invalidTargetGeneratedSchema(target: target.name, scheme: generatedScheme, configType: .release))
}
}
errors += validateSettingPresets(target.settingPresets)
}
for scheme in spec.schemes {
@@ -185,7 +207,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidTargetDependency(target: String, dependency: String)
case invalidSchemeTarget(scheme: String, target: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidBuildSettingConfig(config: String)
case invalidBuildSettingConfig(String)
case invalidSettingsPreset(String)
case missingTargetSource(target: String, source: String)
case invalidTargetGeneratedSchema(target: String, scheme: String, configType: ConfigType)
@@ -196,6 +219,7 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case let .invalidSchemeConfig(scheme, config): return "Scheme \(scheme.quoted) has invalid build configuration \(config.quoted)"
case let .invalidBuildSettingConfig(config): return "Build setting has invalid build configuration \(config.quoted)"
case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)"
case let .invalidSettingsPreset(preset): return "Invalid settings preset \(preset.quoted)"
case let .invalidTargetGeneratedSchema(target, scheme, configType): return "Target \(target.quoted) has an invalid schema generation name which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(scheme.quoted)"
}
}
+85
View File
@@ -0,0 +1,85 @@
//
// SettingsBuilder.swift
// XcodeGen
//
// Created by Yonas Kolb on 26/7/17.
//
//
import Foundation
import xcodeproj
import PathKit
extension Spec {
public func getProjectBuildSettings(config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
buildSettings += SettingsPresetFile.base.getBuildSettings()
if let type = config.type {
buildSettings += SettingsPresetFile.config(type).getBuildSettings()
}
for preset in config.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
buildSettings += config.settings
return buildSettings
}
public func getTargetBuildSettings(target: Target, config: Config) -> BuildSettings {
var buildSettings = BuildSettings()
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
for preset in target.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
buildSettings += getBuildSettings(settings: target.settings, config: config)
return buildSettings
}
public func getBuildSettings(preset: String, config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
let settingPreset = settingPresets[preset]!
buildSettings += getBuildSettings(settings: settingPreset.settings, config: config)
for preset in settingPreset.settingPresets {
buildSettings += getBuildSettings(preset: preset, config: config)
}
return buildSettings
}
public func getBuildSettings(settings: Settings, config: Config) -> BuildSettings {
var buildSettings: BuildSettings = .empty
buildSettings += settings.buildSettings
buildSettings += settings.configSettings[config.name]
return buildSettings
}
}
private var buildSettingFiles: [String: BuildSettings] = [:]
extension SettingsPresetFile {
public func getBuildSettings() -> BuildSettings? {
if let group = buildSettingFiles[path] {
return group
}
let settingsPath = Path(#file).parent().parent().parent() + "SettingPresets/\(path).yml"
guard settingsPath.exists,
let buildSettings = try? BuildSettings(path: settingsPath) else { return nil }
buildSettingFiles[path] = buildSettings
return buildSettings
}
}
@@ -1,39 +0,0 @@
//
// BuildSetting.swift
// XcodeGen
//
// Created by Yonas Kolb on 21/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct TargetBuildSettings: JSONObjectConvertible {
public let buildSettings: BuildSettings
public let configSettings: [String: BuildSettings]
public init(buildSettings: BuildSettings, configSettings: [String: BuildSettings] = [:]) {
self.buildSettings = buildSettings
self.configSettings = configSettings
}
public init(jsonDictionary: JSONDictionary) throws {
if jsonDictionary["$base"] != nil {
buildSettings = BuildSettings(dictionary: try jsonDictionary.json(atKeyPath: "$base"))
var configSettings: [String: BuildSettings] = [:]
for (key, value) in jsonDictionary {
if key != "baseSettings", let buildSettings = value as? JSONDictionary {
configSettings["key"] = BuildSettings(dictionary: buildSettings)
}
}
self.configSettings = configSettings
} else {
buildSettings = BuildSettings(dictionary: jsonDictionary)
configSettings = [:]
}
}
}
@@ -1,29 +0,0 @@
//
// 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: BuildSettings
public init(name: String, buildSettings: BuildSettings) {
self.name = name
self.buildSettings = buildSettings
}
}
extension BuildSettingGroup: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
self.buildSettings = BuildSettings(dictionary: jsonDictionary)
}
}
+15 -8
View File
@@ -10,18 +10,25 @@ import Foundation
import xcodeproj
import JSONUtilities
public struct Config {
public struct Config: Equatable {
public var name: String
public var type: ConfigType?
public var buildSettingGroups: [BuildSettingGroup]
public var buildSettings: BuildSettings
public var settings: BuildSettings
public var settingPresets: [String]
public init(name: String, type: ConfigType? = nil, buildSettingGroups: [BuildSettingGroup] = [], buildSettings: BuildSettings = .init()) {
public init(name: String, type: ConfigType? = nil, settings: BuildSettings = .empty, settingPresets: [String] = []) {
self.name = name
self.buildSettingGroups = buildSettingGroups
self.buildSettings = buildSettings
self.settings = settings
self.settingPresets = settingPresets
self.type = type
}
public static func ==(lhs: Config, rhs: Config) -> Bool {
return lhs.name == rhs.name &&
lhs.type == rhs.type &&
lhs.settings == rhs.settings &&
lhs.settingPresets == rhs.settingPresets
}
}
@@ -35,7 +42,7 @@ extension Config: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = name
type = jsonDictionary.json(atKeyPath: "type")
buildSettingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
buildSettings = BuildSettings(dictionary: jsonDictionary.json(atKeyPath: "buildSettings") ?? [:])
settings = BuildSettings(dictionary: jsonDictionary.json(atKeyPath: "settings") ?? [:])
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? []
}
}
@@ -0,0 +1,58 @@
//
// SettingPreset.swift
// XcodeGen
//
// Created by Yonas Kolb on 26/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct SettingPreset: Equatable, CustomStringConvertible {
public var settings: Settings
public var settingPresets: [String]
public init(settings: Settings, settingPresets: [String] = []) {
self.settings = settings
self.settingPresets = settingPresets
}
public static func ==(lhs: SettingPreset, rhs: SettingPreset) -> Bool {
return lhs.settings == rhs.settings && lhs.settingPresets == rhs.settingPresets
}
public var description: String {
return "\(settings)\nPresets: \(settingPresets)"
}
}
public enum SettingsPresetFile {
case config(ConfigType)
case platform(Platform)
case product(PBXProductType)
case base
var path: String {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .product(product): return "Products/\(product.rawValue)"
case .base: return "base"
}
}
}
extension SettingPreset: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
if jsonDictionary["settings"] == nil {
settings = try Settings(jsonDictionary: jsonDictionary)
settingPresets = []
} else {
settings = try jsonDictionary.json(atKeyPath: "settings")
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? []
}
}
}
+73
View File
@@ -0,0 +1,73 @@
//
// BuildSetting.swift
// XcodeGen
//
// Created by Yonas Kolb on 21/7/17.
//
//
import Foundation
import JSONUtilities
import xcodeproj
public struct Settings: Equatable, JSONObjectConvertible, CustomStringConvertible {
public let buildSettings: BuildSettings
public let configSettings: [String: BuildSettings]
public init(buildSettings: BuildSettings, configSettings: [String: BuildSettings] = [:]) {
self.buildSettings = buildSettings
self.configSettings = configSettings
}
public init(dictionary: [String: Any]) {
self.buildSettings = BuildSettings(dictionary: dictionary)
self.configSettings = [:]
}
static let empty: Settings = Settings(buildSettings: BuildSettings())
public init(jsonDictionary: JSONDictionary) throws {
if let configSettings: [String: BuildSettings] = jsonDictionary.json(atKeyPath: "configs") {
buildSettings = jsonDictionary.json(atKeyPath: "default") ?? [:]
self.configSettings = configSettings
} else {
buildSettings = BuildSettings(dictionary: jsonDictionary)
configSettings = [:]
}
}
public static func ==(lhs: Settings, rhs: Settings) -> Bool {
return lhs.buildSettings == rhs.buildSettings && lhs.configSettings == rhs.configSettings
}
public var description: String {
var string: String = ""
if !buildSettings.dictionary.isEmpty {
string += buildSettings.description
}
for (config, buildSettings) in configSettings {
if !buildSettings.dictionary.isEmpty {
string += "\n\(config)\n\t" + buildSettings.description.replacingOccurrences(of: "\n", with: "\n\t")
}
}
return string
}
}
extension Settings: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, Any)...) {
var dictionary: [String: Any] = [:]
elements.forEach { dictionary[$0.0] = $0.1 }
self.init(dictionary: dictionary)
}
}
extension BuildSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
self.init(dictionary: jsonDictionary)
}
}
+9 -6
View File
@@ -16,23 +16,25 @@ public struct Spec {
public var name: String
public var targets: [Target]
public var settingGroups: [BuildSettingGroup]
public var settings: Settings
public var settingPresets: [String: SettingPreset]
public var configs: [Config]
public var schemes: [Scheme]
public init(name: String, configs: [Config] = [], targets: [Target] = [], settingGroups: [BuildSettingGroup] = [], schemes: [Scheme] = []) {
public init(name: String, configs: [Config] = [], targets: [Target] = [], settings: Settings = .empty, settingPresets: [String: SettingPreset] = [:], schemes: [Scheme] = []) {
self.name = name
self.targets = targets
self.configs = configs
self.settingGroups = settingGroups
self.settings = settings
self.settingPresets = settingPresets
self.schemes = schemes
}
func getTarget(_ targetName: String) -> Target? {
public func getTarget(_ targetName: String) -> Target? {
return targets.first { $0.name == targetName }
}
func getConfig(_ configName: String) -> Config? {
public func getConfig(_ configName: String) -> Config? {
return configs.first { $0.name == configName }
}
}
@@ -53,7 +55,8 @@ extension Spec {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
settingGroups = try jsonDictionary.json(atKeyPath: "settingGroups")
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? [:]
configs = try jsonDictionary.json(atKeyPath: "configs")
if jsonDictionary["targets"] == nil {
targets = []
+7 -4
View File
@@ -14,7 +14,8 @@ public struct Target {
public var name: String
public var type: PBXProductType
public var platform: Platform
public var buildSettings: TargetBuildSettings?
public var settings: Settings
public var settingPresets: [String]
public var sources: [String]
public var sourceExludes: [String]
public var dependencies: [Dependency]
@@ -31,11 +32,12 @@ public struct Target {
return name
}
public init(name: String, type: PBXProductType, platform: Platform, buildSettings: TargetBuildSettings?, configs: [String: String] = [:], sources: [String] = [], sourceExludes: [String] = [], dependencies: [Dependency] = [], prebuildScripts: [String] = [], postbuildScripts: [String] = [], generateSchemes: [String] = []) {
public init(name: String, type: PBXProductType, platform: Platform, settings: Settings = .empty, settingPresets: [String] = [], configs: [String: String] = [:], sources: [String] = [], sourceExludes: [String] = [], dependencies: [Dependency] = [], prebuildScripts: [String] = [], postbuildScripts: [String] = [], generateSchemes: [String] = []) {
self.name = name
self.type = type
self.platform = platform
self.buildSettings = buildSettings
self.settings = settings
self.settingPresets = settingPresets
self.configs = configs
self.sources = sources
self.sourceExludes = sourceExludes
@@ -62,7 +64,8 @@ extension Target: JSONObjectConvertible {
} else {
throw SpecError.unknownTargetPlatform(platformString)
}
buildSettings = jsonDictionary.json(atKeyPath: "buildSettings")
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
settingPresets = jsonDictionary.json(atKeyPath: "settingsPresets") ?? []
configs = jsonDictionary.json(atKeyPath: "configs") ?? [:]
if let source: String = jsonDictionary.json(atKeyPath: "sources") {
sources = [source]
@@ -17,11 +17,11 @@ func projectGeneratorTests() {
describe("Project Generator") {
let application = Target(name: "MyApp", type: .application, platform: .iOS,
buildSettings: TargetBuildSettings(buildSettings: BuildSettings(dictionary: ["SETTING_1": "VALUE"])),
settings: Settings(buildSettings: BuildSettings(dictionary: ["SETTING_1": "VALUE"])),
dependencies: [.target("MyFramework")])
let framework = Target(name: "MyFramework", type: .framework, platform: .iOS,
buildSettings: TargetBuildSettings(buildSettings: BuildSettings(dictionary: ["SETTING_2": "VALUE"])))
settings: Settings(buildSettings: BuildSettings(dictionary: ["SETTING_2": "VALUE"])))
$0.describe("Config") {
@@ -42,6 +42,34 @@ func projectGeneratorTests() {
try expect(configs).contains(name: "config1")
try expect(configs).contains(name: "config2")
}
$0.it("merges settings") {
let spec = try Spec(path: fixturePath + "settings_test.yml")
let project = try getProject(spec)
let configs = project.pbxproj.objects.buildConfigurations
try expect(configs.count) == 6
guard let config = spec.getConfig("config1") else { throw failure("Couldn't find config1") }
let debugProjectSettings = spec.getProjectBuildSettings(config: config)
guard let target = spec.getTarget("Target2") else { throw failure("Couldn't find Target2") }
let targetDebugSettings = spec.getTargetBuildSettings(target: target, config: config)
var buildSettings = BuildSettings()
buildSettings += SettingsPresetFile.base.getBuildSettings()
buildSettings += SettingsPresetFile.config(.debug).getBuildSettings()
buildSettings += ["SETTING 1": "value 1",
"SETTING 2": "value 2",
"SETTING 3": "value 3"]
try expect(debugProjectSettings) == buildSettings
var expectedTargetDebugSettings = BuildSettings()
expectedTargetDebugSettings += SettingsPresetFile.product(.application).getBuildSettings()
expectedTargetDebugSettings += SettingsPresetFile.platform(.iOS).getBuildSettings()
expectedTargetDebugSettings += ["SETTING 7": "value 7", "SETTING 6": "value 6"]
try expect(targetDebugSettings) == expectedTargetDebugSettings
}
}
$0.describe("Targets") {
@@ -73,6 +73,23 @@ func specLoadingTests() {
try expect(target.target) == "Target"
try expect(target.buildTypes) == [.running, .testing, .profiling, .analyzing, .archiving]
}
$0.it("parses settings") {
let spec = try Spec(path: fixturePath + "settings_test.yml")
try expect(spec.settingPresets.count) == 3
let preset1 = SettingPreset(settings: ["SETTING 1": "value 1"], settingPresets: ["preset2"])
let preset2 = SettingPreset(settings: ["SETTING 2": "value 2"])
let preset3 = SettingPreset(settings: Settings(buildSettings: ["SETTING 9": "value 9"], configSettings: ["config1": ["SETTING 8": "value 8"]]), settingPresets: ["preset2"])
let config1 = Config(name: "config1", type: .debug, settings: BuildSettings(dictionary: ["SETTING 3": "value 3"]), settingPresets: ["preset1"])
let config2 = Config(name: "config2", type: .release, settings: BuildSettings(dictionary: ["SETTING 4": "value 4"]), settingPresets: ["preset2"])
try expect(spec.settingPresets["preset1"]) == preset1
try expect(spec.settingPresets["preset2"]) == preset2
try expect(spec.settingPresets["preset3"]) == preset3
try expect(spec.getConfig("config1")) == config1
try expect(spec.getConfig("config2")) == config2
}
}
}