Skip to content

Commit

Permalink
Apply Xcode 11 XCTUnwrap fix to library and framwork targets.
Browse files Browse the repository at this point in the history
  • Loading branch information
dnkoutso committed Jan 23, 2020
1 parent e62532c commit 7cbcc9f
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`

##### Bug Fixes

* Apply Xcode 11 `XCTUnwrap` fix for libraries and frameworks.
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#9500](https://github.com/CocoaPods/CocoaPods/pull/9500)

* Fix resources script when building a project from a symlink.
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#9423](https://github.com/CocoaPods/CocoaPods/issues/9423)
Expand Down
73 changes: 67 additions & 6 deletions lib/cocoapods/target/build_settings.rb
Expand Up @@ -351,6 +351,23 @@ def _ld_runpath_search_paths(requires_host_target: false, test_bundle: false)

#-------------------------------------------------------------------------#

# @!group Protected Methods

protected

# Xcode 11 causes an issue with frameworks or libraries before 12.2 deployment target that link or are part of
# test bundles that use XCTUnwrap. Apple has provided an official work around for this.
#
# @see https://developer.apple.com/documentation/xcode_release_notes/xcode_11_release_notes
#
# @return [Boolean] Whether to apply the fix or not.
#
define_build_settings_method :should_apply_xctunwrap_fix?, :memoized => true do
false
end

#-------------------------------------------------------------------------#

# @!group Private Methods

private
Expand Down Expand Up @@ -492,9 +509,15 @@ def self.build_settings_names
attr_reader :test_xcconfig
alias test_xcconfig? test_xcconfig

# @return [Boolean]
# whether settings are being generated for an application bundle
#
attr_reader :app_xcconfig
alias app_xcconfig? app_xcconfig

# @return [Boolean]
# whether settings are being generated for an library bundle
#
attr_reader :library_xcconfig
alias library_xcconfig? library_xcconfig

Expand Down Expand Up @@ -683,6 +706,12 @@ def initialize(target, non_library_spec = nil, configuration: nil)
file_accessors.flat_map(&:vendored_xcframeworks).map { |path| Xcode::XCFramework.new(path) }
end

# @return [Array<String>]
define_build_settings_method :system_framework_search_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true do
return ['$(PLATFORM_DIR)/Developer/usr/lib'] if should_apply_xctunwrap_fix?
[]
end

#-------------------------------------------------------------------------#

# @!group Libraries
Expand Down Expand Up @@ -737,16 +766,18 @@ def linker_names_from_libraries(libraries)

# @return [Array<String>]
define_build_settings_method :library_search_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true do
return [] if library_xcconfig? && target.build_as_static?
library_search_paths = should_apply_xctunwrap_fix? ? ['$(PLATFORM_DIR)/Developer/usr/lib'] : []
return library_search_paths if library_xcconfig? && target.build_as_static?

vendored = library_search_paths_to_import.dup
vendored.concat dependent_targets.flat_map { |pt| pt.build_settings[@configuration].vendored_dynamic_library_search_paths }
library_search_paths.concat library_search_paths_to_import.dup
library_search_paths.concat dependent_targets.flat_map { |pt| pt.build_settings[@configuration].vendored_dynamic_library_search_paths }
if library_xcconfig?
vendored.delete(target.configuration_build_dir(CONFIGURATION_BUILD_DIR_VARIABLE))
library_search_paths.delete(target.configuration_build_dir(CONFIGURATION_BUILD_DIR_VARIABLE))
else
vendored.concat(dependent_targets.flat_map { |pt| pt.build_settings[@configuration].library_search_paths_to_import })
library_search_paths.concat(dependent_targets.flat_map { |pt| pt.build_settings[@configuration].library_search_paths_to_import })
end
vendored

library_search_paths
end

# @return [Array<String>]
Expand Down Expand Up @@ -837,6 +868,7 @@ def other_swift_flags_without_swift?
define_build_settings_method :swift_include_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true do
paths = dependent_targets.flat_map { |pt| pt.build_settings[@configuration].swift_include_paths_to_import }
paths.concat swift_include_paths_to_import if non_library_xcconfig?
paths.concat ['$(PLATFORM_DIR)/Developer/usr/lib'] if should_apply_xctunwrap_fix?
paths
end

Expand Down Expand Up @@ -966,6 +998,14 @@ def pod_target_xcconfig_values_by_consumer_by_key
end
end

# @return [Boolean]
define_build_settings_method :should_apply_xctunwrap_fix?, :memoized => true do
library_xcconfig? &&
target.platform.name == :ios &&
Version.new(target.platform.deployment_target) < Version.new('12.2') &&
target.library_spec_consumers.flat_map(&:frameworks).include?('XCTest')
end

#-------------------------------------------------------------------------#
end

Expand Down Expand Up @@ -1037,6 +1077,12 @@ def initialize(target, configuration_name, configuration: nil)
[]
end

# @return [Array<String>]
define_build_settings_method :system_framework_search_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true do
return ['$(PLATFORM_DIR)/Developer/usr/lib'] if should_apply_xctunwrap_fix?
[]
end

#-------------------------------------------------------------------------#

# @!group Libraries
Expand All @@ -1048,6 +1094,7 @@ def initialize(target, configuration_name, configuration: nil)

# @return [Array<String>]
define_build_settings_method :library_search_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true, :from_pod_targets_to_link => true, :from_search_paths_aggregate_targets => :vendored_dynamic_library_search_paths do
return ['$(PLATFORM_DIR)/Developer/usr/lib'] if should_apply_xctunwrap_fix?
[]
end

Expand Down Expand Up @@ -1113,6 +1160,7 @@ def other_swift_flags_without_swift?

# @return [Array<String>]
define_build_settings_method :swift_include_paths, :build_setting => true, :memoized => true, :sorted => true, :uniqued => true, :from_pod_targets_to_link => true, :from_search_paths_aggregate_targets => :swift_include_paths_to_import do
return ['$(PLATFORM_DIR)/Developer/usr/lib'] if should_apply_xctunwrap_fix?
[]
end

Expand Down Expand Up @@ -1246,6 +1294,19 @@ def user_target_xcconfig_values_by_consumer_by_key
merged_xcconfigs(user_target_xcconfig_values_by_consumer_by_key, :user_target_xcconfig)
end

# @return [Boolean]
define_build_settings_method :should_apply_xctunwrap_fix?, :memoized => true do
target.library? &&
target.platform.name == :ios &&
Version.new(target.target_definition.platform.deployment_target) < Version.new('12.2') &&
target.user_targets.any? do |ut|
flags = (ut.resolved_build_setting('OTHER_LDFLAGS', :resolve_against_xcconfig => true) || {}).values.flatten.uniq
parsed_flags = Xcodeproj::Config::OtherLinkerFlagsParser.parse(flags)
all_frameworks = (parsed_flags[:frameworks] + parsed_flags[:weak_frameworks]).uniq
all_frameworks.include?('XCTest') || all_frameworks.include?('"XCTest"')
end
end

#-------------------------------------------------------------------------#
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/cocoapods/target/pod_target.rb
Expand Up @@ -287,6 +287,13 @@ def spec_consumers
specs.map { |spec| spec.consumer(platform) }
end

# @return [Array<Specification::Consumer>] the library specification consumers for
# the target.
#
def library_spec_consumers
library_specs.map { |library_spec| library_spec.consumer(platform) }
end

# @return [Array<Specification::Consumer>] the test specification consumers for
# the target.
#
Expand Down
6 changes: 4 additions & 2 deletions spec/spec_helper.rb
Expand Up @@ -138,10 +138,12 @@ def fixture_pod_target_with_specs(specs, build_type = Pod::BuildType.static_libr

def fixture_aggregate_target(pod_targets = [], build_type = Pod::BuildType.static_library,
user_build_configurations = Pod::Target::DEFAULT_BUILD_CONFIGURATIONS, archs = [],
platform = Pod::Platform.new(:ios, '6.0'), target_definition = nil)
platform = Pod::Platform.new(:ios, '6.0'), target_definition = nil, user_project = nil,
user_target_uuids = [])
target_definition ||= pod_targets.flat_map(&:target_definitions).first || fixture_target_definition
Pod::AggregateTarget.new(config.sandbox, build_type, user_build_configurations, archs, platform,
target_definition, config.sandbox.root.dirname, nil, nil, 'Release' => pod_targets)
target_definition, config.sandbox.root.dirname, user_project, user_target_uuids,
'Release' => pod_targets)
end

#-----------------------------------------------------------------------------#
Expand Down
57 changes: 52 additions & 5 deletions spec/unit/target/build_settings/aggregate_target_settings_spec.rb
Expand Up @@ -13,13 +13,18 @@ def pod_target(spec, target_definition)
end

before do
@target_definition = fixture_target_definition
project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@project = Xcodeproj::Project.open(project_path)
@project.save
@native_target = @project.targets.find { |t| t.name == 'SampleProject' }
@target_definition = fixture_target_definition('SampleProject')
@specs = specs
@specs.first.user_target_xcconfig = { 'OTHER_LDFLAGS' => '-no_compact_unwind', 'USE_HEADERMAP' => 'NO' } unless @specs.empty?
@specs.first.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11' } unless @specs.empty?
@pod_targets = @specs.map { |spec| pod_target(spec, @target_definition) }
@target = fixture_aggregate_target(@pod_targets, BuildType.static_library, { 'Release' => :release }, [],
Platform.new(:ios, '6.0'), @target_definition)
Platform.new(:ios, '6.0'), @target_definition, @project,
[@native_target.uuid])
unless @specs.empty?
@target.target_definition.whitelist_pod_for_configuration(@specs.first.name, 'Release')
end
Expand Down Expand Up @@ -190,7 +195,7 @@ def pod_target(spec, target_definition)
end

it 'links the pod targets with the aggregate target' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"BananaLib-Pods"'
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"BananaLib-Pods-SampleProject"'
end
end

Expand Down Expand Up @@ -316,6 +321,46 @@ def specs
@target.stubs(:build_type => BuildType.static_library)
@generator.generate.to_hash['LD_RUNPATH_SEARCH_PATHS'].should == "$(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'"
end

it 'includes the xctunwrap fix for library targets with iOS deployment target < 12.2 and XCTest is linked' do
SpecHelper.create_sample_app_copy_from_fixture('SampleProject/Sample Lib')
project_path = File.join(temporary_directory, 'SampleProject', 'Sample Lib', 'Sample Lib.xcodeproj')
project = Xcodeproj::Project.open(project_path)
native_target = project.targets.find { |t| t.name == 'SampleFramework' }
native_target.build_configurations.each do |configuration|
configuration.build_settings['OTHER_LDFLAGS'] = '-framework "XCTest"'
end
project.save
target_definition = fixture_target_definition('SampleFramework')
pod_targets = specs.map { |spec| pod_target(spec, target_definition) }
target = fixture_aggregate_target(pod_targets, BuildType.static_library, { 'Release' => :release }, [],
Platform.new(:ios, '6.0'), target_definition, project,
[native_target.uuid])
generator = AggregateTargetSettings.new(target, 'Release', :configuration => :release)
hash = generator.generate.to_hash
hash['SYSTEM_FRAMEWORK_SEARCH_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['LIBRARY_SEARCH_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['SWIFT_INCLUDE_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
end

it 'does not include xctunwrap fix for a library target with higher than 12.1 deployment target' do
SpecHelper.create_sample_app_copy_from_fixture('SampleProject/Sample Lib')
project_path = File.join(temporary_directory, 'SampleProject', 'Sample Lib', 'Sample Lib.xcodeproj')
project = Xcodeproj::Project.open(project_path)
project.save
native_target = project.targets.find { |t| t.name == 'SampleFramework' }
native_target.stubs(:deployment_target).returns('12.2')
target_definition = fixture_target_definition('SampleFramework')
pod_targets = specs.map { |spec| pod_target(spec, target_definition) }
target = fixture_aggregate_target(pod_targets, BuildType.static_library, { 'Release' => :release }, [],
Platform.new(:ios, '12.2'), target_definition, project,
[native_target.uuid])
generator = AggregateTargetSettings.new(target, 'Release', :configuration => :release)
hash = generator.generate.to_hash
hash['SYSTEM_FRAMEWORK_SEARCH_PATHS'].should.be.nil
hash['LIBRARY_SEARCH_PATHS'].should.not.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['SWIFT_INCLUDE_PATHS'].should.be.nil
end
end

describe 'with a scoped pod target' do
Expand Down Expand Up @@ -387,14 +432,16 @@ def pod_target(spec, target_definition)

it 'includes correct default runpath search path list for OSX unit test bundle user target' do
@target.stubs(:platform).returns(Platform.new(:osx, '10.10'))
mock_user_target = mock('usertarget', :symbol_type => :unit_test_bundle)
mock_user_target = mock('usertarget')
mock_user_target.stubs(:symbol_type).returns(:unit_test_bundle)
@target.stubs(:user_targets).returns([mock_user_target])
@generator.generate.to_hash['LD_RUNPATH_SEARCH_PATHS'].should == "$(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks'"
end

it 'includes correct default runpath search path list for OSX application user target' do
@target.stubs(:platform).returns(Platform.new(:osx, '10.10'))
mock_user_target = mock('usertarget', :symbol_type => :application)
mock_user_target = mock('usertarget')
mock_user_target.stubs(:symbol_type).returns(:application)
@target.stubs(:user_targets).returns([mock_user_target])
@generator.generate.to_hash['LD_RUNPATH_SEARCH_PATHS'].should == "$(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'"
end
Expand Down
20 changes: 20 additions & 0 deletions spec/unit/target/build_settings/pod_target_settings_spec.rb
Expand Up @@ -184,6 +184,26 @@ class BuildSettings
@generator.module_map_file_to_import.should.be.nil
end

it 'includes xctunwrap fix for a pod target with deployment target < 12.2 and links XCTest' do
@spec.frameworks = ['XCTest']
@pod_target.stubs(:platform).returns(Platform.new(:ios, '12.1'))
generator = PodTargetSettings.new(@pod_target, nil, :configuration => :debug)
hash = generator.generate.to_hash
hash['SYSTEM_FRAMEWORK_SEARCH_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['LIBRARY_SEARCH_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['SWIFT_INCLUDE_PATHS'].should.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
end

it 'does not include xctunwrap fix for a pod target with higher than 12.1 deployment target' do
@spec.frameworks = ['XCTest']
@pod_target.stubs(:platform).returns(Platform.new(:ios, '12.2'))
generator = PodTargetSettings.new(@pod_target, nil, :configuration => :debug)
hash = generator.generate.to_hash
hash['SYSTEM_FRAMEWORK_SEARCH_PATHS'].should.be.nil
hash['LIBRARY_SEARCH_PATHS'].should.not.include '"$(PLATFORM_DIR)/Developer/usr/lib"'
hash['SWIFT_INCLUDE_PATHS'].should.be.nil
end

it 'saves the xcconfig' do
path = temporary_directory + 'sample.xcconfig'
@generator.save_as(path)
Expand Down

0 comments on commit 7cbcc9f

Please sign in to comment.