diff --git a/CHANGELOG.md b/CHANGELOG.md index 4263cfba59..6d201324c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,11 @@ To install release candidates run `[sudo] gem install cocoapods --pre` ##### Bug Fixes -* None. - +* Make `APPLICATION_EXTENSION_API_ONLY` build setting not break when performing a cached incremental install. + [Igor Makarov](https://github.com/igor-makarov) + [#8967](https://github.com/CocoaPods/CocoaPods/issues/8967) + [#9141](https://github.com/CocoaPods/CocoaPods/issues/9141) + [#9142](https://github.com/CocoaPods/CocoaPods/pull/9142) ## 1.8.0.beta.2 (2019-08-27) diff --git a/lib/cocoapods/installer/analyzer.rb b/lib/cocoapods/installer/analyzer.rb index bf73f24477..040700ff77 100644 --- a/lib/cocoapods/installer/analyzer.rb +++ b/lib/cocoapods/installer/analyzer.rb @@ -438,6 +438,18 @@ def generate_targets(resolver_specs_by_target, target_inspections) end target.search_paths_aggregate_targets.concat(search_paths_aggregate_targets).freeze end + + aggregate_targets.each do |aggregate_target| + is_app_extension = !(aggregate_target.user_targets.map(&:symbol_type) & + [:app_extension, :watch_extension, :watch2_extension, :tv_extension, :messages_extension]).empty? + is_app_extension ||= aggregate_target.user_targets.any? { |ut| ut.common_resolved_build_setting('APPLICATION_EXTENSION_API_ONLY') == 'YES' } + + next unless is_app_extension + + aggregate_target.mark_application_extension_api_only + aggregate_target.pod_targets.each(&:mark_application_extension_api_only) + end + if installation_options.integrate_targets? # Copy embedded target pods that cannot have their pods embedded as frameworks to # their host targets, and ensure we properly link library pods to their host targets diff --git a/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb b/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb index 600e403974..7e26e9ee6b 100644 --- a/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb +++ b/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb @@ -41,10 +41,6 @@ def install! aggregate_target = aggregate_target_installation_result.target aggregate_native_target = aggregate_target_installation_result.native_target project = aggregate_native_target.project - is_app_extension = !(aggregate_target.user_targets.map(&:symbol_type) & - [:app_extension, :watch_extension, :watch2_extension, :tv_extension, :messages_extension]).empty? - is_app_extension ||= aggregate_target.user_targets.any? { |ut| ut.common_resolved_build_setting('APPLICATION_EXTENSION_API_ONLY') == 'YES' } - configure_app_extension_api_only_to_native_target(aggregate_native_target) if is_app_extension # Wire up dependencies that are part of inherit search paths for this aggregate target. aggregate_target.search_paths_aggregate_targets.each do |search_paths_target| aggregate_native_target.add_dependency(aggregate_target_installation_results[search_paths_target.name].native_target) @@ -54,7 +50,6 @@ def install! if pod_target_installation_result = pod_target_installation_results[pod_target.name] pod_target_native_target = pod_target_installation_result.native_target aggregate_native_target.add_dependency(pod_target_native_target) - configure_app_extension_api_only_to_native_target(pod_target_native_target) if is_app_extension else # Hit the cache is_local = sandbox.local?(pod_target.pod_name) @@ -65,17 +60,6 @@ def install! end end end - - private - - # Sets the APPLICATION_EXTENSION_API_ONLY build setting to YES for all - # configurations of the given native target. - # - def configure_app_extension_api_only_to_native_target(native_target) - native_target.build_configurations.each do |config| - config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES' - end - end end end end diff --git a/lib/cocoapods/target.rb b/lib/cocoapods/target.rb index 9273c70b59..cad7eeecbd 100644 --- a/lib/cocoapods/target.rb +++ b/lib/cocoapods/target.rb @@ -46,6 +46,10 @@ class Target attr_reader :build_type private :build_type + # @return [Boolean] whether the target can be linked to app extensions only. + # + attr_reader :application_extension_api_only + # Initialize a new target # # @param [Sandbox] sandbox @see #sandbox @@ -63,6 +67,7 @@ def initialize(sandbox, host_requires_frameworks, user_build_configurations, arc @platform = platform @build_type = build_type + @application_extension_api_only = false @build_settings = create_build_settings end @@ -302,6 +307,13 @@ def dummy_source_path support_files_dir + "#{label}-dummy.m" end + # mark the target as extension-only, + # translates to APPLICATION_EXTENSION_API_ONLY = YES in the build settings + # + def mark_application_extension_api_only + @application_extension_api_only = true + end + #-------------------------------------------------------------------------# private diff --git a/lib/cocoapods/target/aggregate_target.rb b/lib/cocoapods/target/aggregate_target.rb index 072ac542dd..0e785063c2 100644 --- a/lib/cocoapods/target/aggregate_target.rb +++ b/lib/cocoapods/target/aggregate_target.rb @@ -98,6 +98,7 @@ def merge_embedded_pod_targets(embedded_pod_targets_for_build_configuration) AggregateTarget.new(sandbox, host_requires_frameworks, user_build_configurations, archs, platform, target_definition, client_root, user_project, user_target_uuids, merged, :build_type => build_type).tap do |aggregate_target| aggregate_target.search_paths_aggregate_targets.concat(search_paths_aggregate_targets).freeze + aggregate_target.mark_application_extension_api_only if application_extension_api_only end end diff --git a/lib/cocoapods/target/build_settings.rb b/lib/cocoapods/target/build_settings.rb index 8c151bd2d5..8f42439ed2 100644 --- a/lib/cocoapods/target/build_settings.rb +++ b/lib/cocoapods/target/build_settings.rb @@ -856,6 +856,11 @@ def requires_objc_linker_flag? target.configuration_build_dir(CONFIGURATION_BUILD_DIR_VARIABLE) end + # @return [String] + define_build_settings_method :application_extension_api_only, :build_setting => true, :memoized => true do + target.application_extension_api_only ? 'YES' : nil + end + #-------------------------------------------------------------------------# # @!group Target Properties diff --git a/spec/unit/installer/analyzer_spec.rb b/spec/unit/installer/analyzer_spec.rb index 38ed0ce521..2c02ceb836 100644 --- a/spec/unit/installer/analyzer_spec.rb +++ b/spec/unit/installer/analyzer_spec.rb @@ -1,7 +1,7 @@ require File.expand_path('../../../spec_helper', __FILE__) module Pod - describe Installer::Analyzer do + describe Installer::Analyzer do # rubocop:disable Metrics/BlockLength describe 'Analysis' do before do repos = [Source.new(fixture('spec-repos/test_repo')), TrunkSource.new(fixture('spec-repos/trunk'))] @@ -2029,6 +2029,149 @@ module Pod analyzer.analyze end.message.should.match /Sample Extensions Project \(false\) and Today Extension \(true\) do not both set use_frameworks!\./ end + + describe 'APPLICATION_EXTENSION_API_ONLY' do + it 'configures APPLICATION_EXTENSION_API_ONLY for app extension targets' do + @podfile.use_frameworks! + analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile) + result = analyzer.analyze + + result.targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['Pods-Sample Extensions Project', false], ['Pods-Today Extension', true]] + result.pod_targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['matryoshka-Bar', true], ['matryoshka-Bar-Foo', false], ['JSONKit', false], ['monkey', true]] + end + + it 'configures APPLICATION_EXTENSION_API_ONLY for watch app extension targets' do + @user_project = Xcodeproj::Project.open(SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')) + targets = @user_project.targets + targets.delete(targets.find('Sample Extensions Project').first) + extension_target = targets.find('Today Extension').first + extension_target.product_type = 'com.apple.product-type.watchkit2-extension' + extension_target.name = 'watchOS Extension Target' + extension_target.symbol_type.should == :watch2_extension + @user_project.save + project_path = @user_project.path + @podfile = Pod::Podfile.new do + source SpecHelper.test_repo_url + platform :watchos, '2.0' + project project_path + + target 'watchOS Extension Target' do + use_frameworks! + pod 'monkey' + end + end + + @podfile.use_frameworks! + analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile) + result = analyzer.analyze + + result.targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['Pods-watchOS Extension Target', true]] + result.pod_targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['monkey', true]] + end + + it 'configures APPLICATION_EXTENSION_API_ONLY for TV app extension targets' do + @user_project = Xcodeproj::Project.open(SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')) + targets = @user_project.targets + targets.delete(targets.find('Sample Extensions Project').first) + extension_target = targets.find('Today Extension').first + extension_target.product_type = 'com.apple.product-type.tv-app-extension' + extension_target.name = 'tvOS Extension Target' + extension_target.symbol_type.should == :tv_extension + @user_project.save + project_path = @user_project.path + @podfile = Pod::Podfile.new do + source SpecHelper.test_repo_url + platform :tvos, '9.0' + project project_path + + target 'tvOS Extension Target' do + use_frameworks! + pod 'monkey' + end + end + + @podfile.use_frameworks! + analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile) + result = analyzer.analyze + + result.targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['Pods-tvOS Extension Target', true]] + result.pod_targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['monkey', true]] + end + + it 'configures APPLICATION_EXTENSION_API_ONLY for messages extension targets' do + @user_project = Xcodeproj::Project.open(SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')) + targets = @user_project.targets + app_target = targets.find { |t| t.name == 'Sample Extensions Project' } + app_target.product_type = 'com.apple.product-type.application.messages' + extension_target = targets.find { |t| t.name == 'Today Extension' } + extension_target.product_type = 'com.apple.product-type.app-extension.messages' + extension_target.name = 'Messages Extension Target' + extension_target.symbol_type.should == :messages_extension + @user_project.save + project_path = @user_project.path + @podfile = Pod::Podfile.new do + source SpecHelper.test_repo_url + platform :ios, '8.0' + project project_path + + target 'Sample Extensions Project' do + pod 'JSONKit', '1.4' + end + + target 'Messages Extension Target' do + use_frameworks! + pod 'monkey' + end + end + + @podfile.use_frameworks! + analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile) + result = analyzer.analyze + + result.targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['Pods-Sample Extensions Project', false], ['Pods-Messages Extension Target', true]] + result.pod_targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['JSONKit', false], ['monkey', true]] + end + + it 'configures APPLICATION_EXTENSION_API_ONLY when build setting is set in user target' do + @user_project = Xcodeproj::Project.open(SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')) + targets = @user_project.targets + app_target = targets.find { |t| t.name == 'Sample Extensions Project' } + app_target.build_configurations.each { |c| c.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES' } + @user_project.save + project_path = @user_project.path + @podfile = Pod::Podfile.new do + source SpecHelper.test_repo_url + platform :ios, '8.0' + project project_path + + target 'Sample Extensions Project' do + pod 'JSONKit', '1.4' + end + + target 'Today Extension' do + use_frameworks! + pod 'monkey' + end + end + + @podfile.use_frameworks! + analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile) + result = analyzer.analyze + + result.targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['Pods-Sample Extensions Project', true], ['Pods-Today Extension', true]] + result.pod_targets.map { |t| [t.name, t.application_extension_api_only] }. + should == [['JSONKit', true], ['monkey', true]] + end + end end #-------------------------------------------------------------------------# diff --git a/spec/unit/installer/xcode/multi_pods_project_generator_spec.rb b/spec/unit/installer/xcode/multi_pods_project_generator_spec.rb index 7ab0d39468..4ab81200ba 100644 --- a/spec/unit/installer/xcode/multi_pods_project_generator_spec.rb +++ b/spec/unit/installer/xcode/multi_pods_project_generator_spec.rb @@ -537,60 +537,6 @@ class Xcode } end - it 'configures APPLICATION_EXTENSION_API_ONLY for pod targets of an aggregate target' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :app_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - projects_by_pod_targets = pod_generator_result.projects_by_pod_targets - pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.dependencies.each do |dependency| - project = projects_by_pod_targets.find { |_, pod_targets| pod_targets.find { |t| t.label == dependency.name } }.first - build_settings = project.targets.find { |t| t.name == dependency.name }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for app extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :app_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for watch2 extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :watch2_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for tvOS extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :tv_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for Messages extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :messages_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - it "uses the user project's object version for the all projects" do tmp_directory = Pathname(Dir.tmpdir) + 'CocoaPods' FileUtils.mkdir_p(tmp_directory) @@ -598,7 +544,6 @@ class Xcode proj.save user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :application) - user_target.expects(:common_resolved_build_setting).with('APPLICATION_EXTENSION_API_ONLY').returns('NO') target = AggregateTarget.new(config.sandbox, false, { 'App Store' => :release, 'Debug' => :debug, 'Release' => :release, 'Test' => :debug }, diff --git a/spec/unit/installer/xcode/single_pods_project_generator_spec.rb b/spec/unit/installer/xcode/single_pods_project_generator_spec.rb index 7a51cc84c7..eab13eb890 100644 --- a/spec/unit/installer/xcode/single_pods_project_generator_spec.rb +++ b/spec/unit/installer/xcode/single_pods_project_generator_spec.rb @@ -368,58 +368,6 @@ class Xcode } end - it 'configures APPLICATION_EXTENSION_API_ONLY for pod targets of an aggregate target' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :app_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.dependencies.each do |dependency| - build_settings = pod_generator_result.project.targets.find { |t| t.name == dependency.name }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for app extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :app_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for watch2 extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :watch2_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for tvOS extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :tv_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - - it 'configures APPLICATION_EXTENSION_API_ONLY for Messages extension targets' do - user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :messages_extension) - @ios_target.stubs(:user_targets).returns([user_target]) - pod_generator_result = @generator.generate! - build_settings = pod_generator_result.project.targets.find { |t| t.name == 'Pods-SampleApp-iOS' }.build_configurations.map(&:build_settings) - build_settings.each do |build_setting| - build_setting['APPLICATION_EXTENSION_API_ONLY'].should == 'YES' - end - end - it "uses the user project's object version for the pods project" do tmp_directory = Pathname(Dir.tmpdir) + 'CocoaPods' FileUtils.mkdir_p(tmp_directory) @@ -427,7 +375,6 @@ class Xcode proj.save user_target = stub('SampleApp-iOS-User-Target', :symbol_type => :application) - user_target.expects(:common_resolved_build_setting).with('APPLICATION_EXTENSION_API_ONLY').returns('NO') target = AggregateTarget.new(config.sandbox, false, { 'App Store' => :release, 'Debug' => :debug, 'Release' => :release, 'Test' => :debug }, diff --git a/spec/unit/target/build_settings_spec.rb b/spec/unit/target/build_settings_spec.rb index aef0bc2a54..73064ba445 100644 --- a/spec/unit/target/build_settings_spec.rb +++ b/spec/unit/target/build_settings_spec.rb @@ -69,6 +69,33 @@ def aggregate(aggregate_target, configuration_name = 'Release') #---------------------------------------------------------------------# + describe '::application_extension_api_only' do + it 'does not set APPLICATION_EXTENSION_API_ONLY missing in the target' do + target = fixture_pod_target('integration/Reachability/Reachability.podspec') + build_settings = pod(target) + other_swift_flags = build_settings.xcconfig.to_hash['APPLICATION_EXTENSION_API_ONLY'] + other_swift_flags.should.be.nil + end + + it 'does not set APPLICATION_EXTENSION_API_ONLY when false in the target' do + target = fixture_pod_target('integration/Reachability/Reachability.podspec') + target.instance_variable_set(:@application_extension_api_only, false) + build_settings = pod(target) + other_swift_flags = build_settings.xcconfig.to_hash['APPLICATION_EXTENSION_API_ONLY'] + other_swift_flags.should.be.nil + end + + it 'sets APPLICATION_EXTENSION_API_ONLY to YES when true in the target' do + target = fixture_pod_target('integration/Reachability/Reachability.podspec') + target.instance_variable_set(:@application_extension_api_only, true) + build_settings = pod(target) + other_swift_flags = build_settings.xcconfig.to_hash['APPLICATION_EXTENSION_API_ONLY'] + other_swift_flags.should.== 'YES' + end + end + + #---------------------------------------------------------------------# + describe '::add_language_specific_settings' do it 'does not add OTHER_SWIFT_FLAGS to the xcconfig if the target does not use swift' do target = fixture_pod_target('integration/Reachability/Reachability.podspec')