mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2024-10-27 14:35:28 +03:00
Merge pull request #353 from yonaskolb/objcLinking
Add `requiresObjCLinking` to `Target`
This commit is contained in:
commit
6041e73ac3
@ -4,6 +4,7 @@
|
||||
|
||||
#### Added
|
||||
- Added `showEnvVars` to build scripts to disable printing the environment [351](https://github.com/yonaskolb/XcodeGen/pull/351) @keith
|
||||
- Added `requiresObjCLinking` to `target` [354](https://github.com/yonaskolb/XcodeGen/pull/353) @brentleyjones
|
||||
|
||||
#### Fixed
|
||||
- Sort files using localizedStandardCompare [341](https://github.com/yonaskolb/XcodeGen/pull/341) @rohitpal440
|
||||
|
@ -164,9 +164,11 @@ Settings are merged in the following order: groups, base, configs.
|
||||
- [ ] **settings**: **[Settings](#settings)** - Target specific build settings. Default platform and product type settings will be applied first before any custom settings defined here. Other context dependant settings will be set automatically as well:
|
||||
- `INFOPLIST_FILE`: If it doesn't exist your sources will be searched for `Info.plist` files and the first one found will be used for this setting
|
||||
- `FRAMEWORK_SEARCH_PATHS`: If carthage dependencies are used, the platform build path will be added to this setting
|
||||
- `OTHER_LDFLAGS`: See `requiresObjCLinking` below
|
||||
- [ ] **dependencies**: **[[Dependency](#dependency)]** - Dependencies for the target
|
||||
- [ ] **templates**: **[String]** - A list of target templates that will be merged in order
|
||||
- [ ] **transitivelyLinkDependencies**: **Bool** - If this is not specified the value from the project set in [Options](#options)`.transitivelyLinkDependencies` will be used.
|
||||
- [ ] **requiresObjCLinking**: **Bool** - If this is `true` any targets that link to this target will have `-ObjC` added to their `OTHER_LDFLAGS`. This is required if a static library has any catagories or extensions on Objective-C code. See [this guide](https://pewpewthespells.com/blog/objc_linker_flags.html#objc) for more details. Defaults to `true` if `type` is `library.static`. If you are 100% sure you don't have catagories or extensions on Objective-C code (pure Swift with no use of Foundation/UIKit) you can set this to `false`, otherwise it's best to leave it alone.
|
||||
- [ ] **prebuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *before* any other build phases
|
||||
- [ ] **postbuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *after* any other build phases
|
||||
- [ ] **buildRules**: **[[Build Rule](#build-rule)]** - Custom build rules
|
||||
|
@ -29,6 +29,7 @@ public struct Target: ProjectTarget {
|
||||
public var sources: [TargetSource]
|
||||
public var dependencies: [Dependency]
|
||||
public var transitivelyLinkDependencies: Bool?
|
||||
public var requiresObjCLinking: Bool?
|
||||
public var prebuildScripts: [BuildScript]
|
||||
public var postbuildScripts: [BuildScript]
|
||||
public var buildRules: [BuildRule]
|
||||
@ -61,6 +62,7 @@ public struct Target: ProjectTarget {
|
||||
sources: [TargetSource] = [],
|
||||
dependencies: [Dependency] = [],
|
||||
transitivelyLinkDependencies: Bool? = nil,
|
||||
requiresObjCLinking: Bool? = nil,
|
||||
prebuildScripts: [BuildScript] = [],
|
||||
postbuildScripts: [BuildScript] = [],
|
||||
buildRules: [BuildRule] = [],
|
||||
@ -77,6 +79,7 @@ public struct Target: ProjectTarget {
|
||||
self.sources = sources
|
||||
self.dependencies = dependencies
|
||||
self.transitivelyLinkDependencies = transitivelyLinkDependencies
|
||||
self.requiresObjCLinking = requiresObjCLinking
|
||||
self.prebuildScripts = prebuildScripts
|
||||
self.postbuildScripts = postbuildScripts
|
||||
self.buildRules = buildRules
|
||||
@ -196,6 +199,7 @@ extension Target: Equatable {
|
||||
lhs.platform == rhs.platform &&
|
||||
lhs.deploymentTarget == rhs.deploymentTarget &&
|
||||
lhs.transitivelyLinkDependencies == rhs.transitivelyLinkDependencies &&
|
||||
lhs.requiresObjCLinking == rhs.requiresObjCLinking &&
|
||||
lhs.settings == rhs.settings &&
|
||||
lhs.configFiles == rhs.configFiles &&
|
||||
lhs.sources == rhs.sources &&
|
||||
@ -268,6 +272,7 @@ extension Target: NamedJSONDictionaryConvertible {
|
||||
dependencies = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
|
||||
}
|
||||
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies")
|
||||
requiresObjCLinking = jsonDictionary.json(atKeyPath: "requiresObjCLinking")
|
||||
|
||||
prebuildScripts = jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
|
||||
postbuildScripts = jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
|
||||
|
@ -418,76 +418,7 @@ public class PBXProjGenerator {
|
||||
|
||||
var plistPath: Path?
|
||||
var searchForPlist = true
|
||||
|
||||
let configs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
|
||||
var buildSettings = project.getTargetBuildSettings(target: target, config: config)
|
||||
|
||||
// automatically set INFOPLIST_FILE path
|
||||
if !project.targetHasBuildSetting("INFOPLIST_FILE", basePath: project.basePath, target: target, config: config) {
|
||||
if searchForPlist {
|
||||
plistPath = getInfoPlist(target.sources)
|
||||
searchForPlist = false
|
||||
}
|
||||
if let plistPath = plistPath {
|
||||
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: project.basePath)
|
||||
}
|
||||
}
|
||||
|
||||
// automatically calculate bundle id
|
||||
if let bundleIdPrefix = project.options.bundleIdPrefix,
|
||||
!project.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: project.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
|
||||
}
|
||||
|
||||
// automatically set test target name
|
||||
if target.type == .uiTestBundle,
|
||||
!project.targetHasBuildSetting("TEST_TARGET_NAME", basePath: project.basePath, target: target, config: config) {
|
||||
for dependency in target.dependencies {
|
||||
if dependency.type == .target,
|
||||
let dependencyTarget = project.getTarget(dependency.reference),
|
||||
dependencyTarget.type == .application {
|
||||
buildSettings["TEST_TARGET_NAME"] = dependencyTarget.name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set Carthage search paths
|
||||
if !carthageDependencies.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]
|
||||
}
|
||||
}
|
||||
|
||||
var baseConfigurationReference: String?
|
||||
if let configPath = target.configFiles[config.name] {
|
||||
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: project.basePath + configPath)
|
||||
}
|
||||
let buildConfig = XCBuildConfiguration(
|
||||
name: config.name,
|
||||
baseConfigurationReference: baseConfigurationReference,
|
||||
buildSettings: buildSettings
|
||||
)
|
||||
return createObject(id: config.name + target.name, buildConfig)
|
||||
}
|
||||
|
||||
let buildConfigList = createObject(id: target.name, XCConfigurationList(
|
||||
buildConfigurations: configs.map { $0.reference },
|
||||
defaultConfigurationName: ""
|
||||
))
|
||||
var anyDependencyRequiresObjCLinking = false
|
||||
|
||||
var dependencies: [String] = []
|
||||
var targetFrameworkBuildFiles: [String] = []
|
||||
@ -534,6 +465,11 @@ public class PBXProjGenerator {
|
||||
PBXBuildFile(fileRef: dependencyBuildFile.object.fileRef!)
|
||||
)
|
||||
targetFrameworkBuildFiles.append(buildFile.reference)
|
||||
|
||||
if !anyDependencyRequiresObjCLinking
|
||||
&& dependencyTarget.requiresObjCLinking ?? (dependencyTarget.type == .staticLibrary) {
|
||||
anyDependencyRequiresObjCLinking = true
|
||||
}
|
||||
}
|
||||
|
||||
let embed = dependency.embed ?? (!dependencyTarget.type.isLibrary && (target.type.isApp
|
||||
@ -753,6 +689,89 @@ public class PBXProjGenerator {
|
||||
}
|
||||
|
||||
buildPhases += try target.postbuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
|
||||
|
||||
let configs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
|
||||
var buildSettings = project.getTargetBuildSettings(target: target, config: config)
|
||||
|
||||
// automatically set INFOPLIST_FILE path
|
||||
if !project.targetHasBuildSetting("INFOPLIST_FILE", basePath: project.basePath, target: target, config: config) {
|
||||
if searchForPlist {
|
||||
plistPath = getInfoPlist(target.sources)
|
||||
searchForPlist = false
|
||||
}
|
||||
if let plistPath = plistPath {
|
||||
buildSettings["INFOPLIST_FILE"] = plistPath.byRemovingBase(path: project.basePath)
|
||||
}
|
||||
}
|
||||
|
||||
// automatically calculate bundle id
|
||||
if let bundleIdPrefix = project.options.bundleIdPrefix,
|
||||
!project.targetHasBuildSetting("PRODUCT_BUNDLE_IDENTIFIER", basePath: project.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
|
||||
}
|
||||
|
||||
// automatically set test target name
|
||||
if target.type == .uiTestBundle,
|
||||
!project.targetHasBuildSetting("TEST_TARGET_NAME", basePath: project.basePath, target: target, config: config) {
|
||||
for dependency in target.dependencies {
|
||||
if dependency.type == .target,
|
||||
let dependencyTarget = project.getTarget(dependency.reference),
|
||||
dependencyTarget.type == .application {
|
||||
buildSettings["TEST_TARGET_NAME"] = dependencyTarget.name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// objc linkage
|
||||
if anyDependencyRequiresObjCLinking {
|
||||
let otherLinkingFlags = "OTHER_LDFLAGS"
|
||||
let objCLinking = "-ObjC"
|
||||
if var array = buildSettings[otherLinkingFlags] as? [String] {
|
||||
array.append(objCLinking)
|
||||
buildSettings[otherLinkingFlags] = array
|
||||
} else if let string = buildSettings[otherLinkingFlags] as? String {
|
||||
buildSettings[otherLinkingFlags] = [string, objCLinking]
|
||||
} else {
|
||||
buildSettings[otherLinkingFlags] = ["$(inherited)", objCLinking]
|
||||
}
|
||||
}
|
||||
|
||||
// set Carthage search paths
|
||||
if !carthageDependencies.isEmpty {
|
||||
let frameworkSearchPaths = "FRAMEWORK_SEARCH_PATHS"
|
||||
let carthagePlatformBuildPath = "$(PROJECT_DIR)/" + getCarthageBuildPath(platform: target.platform)
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
var baseConfigurationReference: String?
|
||||
if let configPath = target.configFiles[config.name] {
|
||||
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: project.basePath + configPath)
|
||||
}
|
||||
let buildConfig = XCBuildConfiguration(
|
||||
name: config.name,
|
||||
baseConfigurationReference: baseConfigurationReference,
|
||||
buildSettings: buildSettings
|
||||
)
|
||||
return createObject(id: config.name + target.name, buildConfig)
|
||||
}
|
||||
|
||||
let buildConfigList = createObject(id: target.name, XCConfigurationList(
|
||||
buildConfigurations: configs.map { $0.reference },
|
||||
defaultConfigurationName: ""
|
||||
))
|
||||
|
||||
let targetObject = targetObjects[target.name]!.object
|
||||
|
||||
|
@ -1442,6 +1442,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -1652,6 +1656,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -2083,6 +2091,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -2232,6 +2244,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -2795,6 +2811,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -2953,6 +2973,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = App_iOS/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.project.app;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
@ -51,6 +51,7 @@ targets:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: com.project.app
|
||||
dependencies:
|
||||
- target: Framework_iOS
|
||||
- target: StaticLibrary_ObjC
|
||||
- carthage: Alamofire
|
||||
- target: App_watchOS
|
||||
- target: iMessageApp
|
||||
|
@ -562,6 +562,89 @@ class ProjectGeneratorTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.it("sets -ObjC for targets that depend on requiresObjCLinking targets") {
|
||||
let requiresObjCLinking = Target(
|
||||
name: "requiresObjCLinking",
|
||||
type: .staticLibrary,
|
||||
platform: .iOS,
|
||||
dependencies: [],
|
||||
requiresObjCLinking: true
|
||||
)
|
||||
let doesntRequireObjCLinking = Target(
|
||||
name: "doesntRequireObjCLinking",
|
||||
type: .staticLibrary,
|
||||
platform: .iOS,
|
||||
dependencies: [],
|
||||
requiresObjCLinking: false
|
||||
)
|
||||
let implicitlyRequiresObjCLinking = Target(
|
||||
name: "implicitlyRequiresObjCLinking",
|
||||
type: .staticLibrary,
|
||||
platform: .iOS,
|
||||
sources: [TargetSource(path: "StaticLibrary_ObjC/StaticLibrary_ObjC.m")],
|
||||
dependencies: []
|
||||
)
|
||||
|
||||
let framework = Target(
|
||||
name: "framework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
dependencies: [Dependency(type: .target, reference: requiresObjCLinking.name, link: false)]
|
||||
)
|
||||
|
||||
let app1 = Target(
|
||||
name: "app1",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
dependencies: [Dependency(type: .target, reference: requiresObjCLinking.name)]
|
||||
)
|
||||
let app2 = Target(
|
||||
name: "app2",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
dependencies: [Dependency(type: .target, reference: doesntRequireObjCLinking.name)]
|
||||
)
|
||||
let app3 = Target(
|
||||
name: "app3",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
dependencies: [Dependency(type: .target, reference: implicitlyRequiresObjCLinking.name)]
|
||||
)
|
||||
|
||||
let targets = [requiresObjCLinking, doesntRequireObjCLinking, implicitlyRequiresObjCLinking, framework, app1, app2, app3]
|
||||
|
||||
let project = Project(
|
||||
basePath: fixturePath + "TestProject",
|
||||
name: "test",
|
||||
targets: targets,
|
||||
options: SpecOptions()
|
||||
)
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
|
||||
func buildSettings(for target: Target) throws -> BuildSettings {
|
||||
guard let nativeTarget = pbxProj.objects.targets(named: target.name).first?.object,
|
||||
let buildConfigList = nativeTarget.buildConfigurationList,
|
||||
let buildConfigs = pbxProj.objects.configurationLists.getReference(buildConfigList),
|
||||
let buildConfigReference = buildConfigs.buildConfigurations.first,
|
||||
let buildConfig = pbxProj.objects.buildConfigurations.getReference(buildConfigReference) else {
|
||||
throw failure("XCBuildConfiguration not found for Target \(target.name.quoted)")
|
||||
}
|
||||
|
||||
return buildConfig.buildSettings
|
||||
}
|
||||
|
||||
let frameworkOtherLinkerSettings = try buildSettings(for: framework)["OTHER_LDFLAGS"] as? [String] ?? []
|
||||
let app1OtherLinkerSettings = try buildSettings(for: app1)["OTHER_LDFLAGS"] as? [String] ?? []
|
||||
let app2OtherLinkerSettings = try buildSettings(for: app2)["OTHER_LDFLAGS"] as? [String] ?? []
|
||||
let app3OtherLinkerSettings = try buildSettings(for: app3)["OTHER_LDFLAGS"] as? [String] ?? []
|
||||
|
||||
try expect(frameworkOtherLinkerSettings.contains("-ObjC")) == false
|
||||
try expect(app1OtherLinkerSettings.contains("-ObjC")) == true
|
||||
try expect(app2OtherLinkerSettings.contains("-ObjC")) == false
|
||||
try expect(app3OtherLinkerSettings.contains("-ObjC")) == true
|
||||
}
|
||||
|
||||
$0.it("generates run scripts") {
|
||||
var scriptSpec = project
|
||||
|
Loading…
Reference in New Issue
Block a user