From 7cbcc9f0c4b1ee5db3b4087a2df018cba99b008b Mon Sep 17 00:00:00 2001 From: Dimitris Koutsogiorgas Date: Tue, 21 Jan 2020 14:05:19 -0800 Subject: [PATCH] Apply Xcode 11 `XCTUnwrap` fix to library and framwork targets. --- CHANGELOG.md | 4 + lib/cocoapods/target/build_settings.rb | 73 +++++++++++++++++-- lib/cocoapods/target/pod_target.rb | 7 ++ spec/cocoapods-integration-specs | 2 +- spec/spec_helper.rb | 6 +- .../aggregate_target_settings_spec.rb | 57 +++++++++++++-- .../pod_target_settings_spec.rb | 20 +++++ 7 files changed, 155 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a62e3774ad..cddcba9a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/lib/cocoapods/target/build_settings.rb b/lib/cocoapods/target/build_settings.rb index 2a3b804963..78b4ac9169 100644 --- a/lib/cocoapods/target/build_settings.rb +++ b/lib/cocoapods/target/build_settings.rb @@ -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 @@ -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 @@ -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] + 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 @@ -737,16 +766,18 @@ def linker_names_from_libraries(libraries) # @return [Array] 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] @@ -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 @@ -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 @@ -1037,6 +1077,12 @@ def initialize(target, configuration_name, configuration: nil) [] end + # @return [Array] + 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 @@ -1048,6 +1094,7 @@ def initialize(target, configuration_name, configuration: nil) # @return [Array] 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 @@ -1113,6 +1160,7 @@ def other_swift_flags_without_swift? # @return [Array] 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 @@ -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 diff --git a/lib/cocoapods/target/pod_target.rb b/lib/cocoapods/target/pod_target.rb index bdc083e125..fe12e9cf93 100644 --- a/lib/cocoapods/target/pod_target.rb +++ b/lib/cocoapods/target/pod_target.rb @@ -287,6 +287,13 @@ def spec_consumers specs.map { |spec| spec.consumer(platform) } end + # @return [Array] the library specification consumers for + # the target. + # + def library_spec_consumers + library_specs.map { |library_spec| library_spec.consumer(platform) } + end + # @return [Array] the test specification consumers for # the target. # diff --git a/spec/cocoapods-integration-specs b/spec/cocoapods-integration-specs index 953ca9ec98..47b83e3e7c 160000 --- a/spec/cocoapods-integration-specs +++ b/spec/cocoapods-integration-specs @@ -1 +1 @@ -Subproject commit 953ca9ec98bd8ae07077c3f219f9c656b08638d6 +Subproject commit 47b83e3e7c6decd33f6748a6eff46a594f171c07 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7f655431f0..b30bd6c3fe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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 #-----------------------------------------------------------------------------# diff --git a/spec/unit/target/build_settings/aggregate_target_settings_spec.rb b/spec/unit/target/build_settings/aggregate_target_settings_spec.rb index 5ada00b1b0..500cc44dc1 100644 --- a/spec/unit/target/build_settings/aggregate_target_settings_spec.rb +++ b/spec/unit/target/build_settings/aggregate_target_settings_spec.rb @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/spec/unit/target/build_settings/pod_target_settings_spec.rb b/spec/unit/target/build_settings/pod_target_settings_spec.rb index f44fd661c9..4f2e7c9486 100644 --- a/spec/unit/target/build_settings/pod_target_settings_spec.rb +++ b/spec/unit/target/build_settings/pod_target_settings_spec.rb @@ -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)