Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate ODR categories into projects. #10855

Merged
merged 1 commit into from Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -8,7 +8,9 @@ To install release candidates run `[sudo] gem install cocoapods --pre`

##### Enhancements

* None.
* Integrate ODR categories into projects.
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#10855](https://github.com/CocoaPods/CocoaPods/pull/10855)

##### Bug Fixes

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Expand Up @@ -7,7 +7,7 @@ GIT

GIT
remote: https://github.com/CocoaPods/Core.git
revision: 609a964026350b86d4c5bf9a30c03c1c699be32c
revision: bdfd495cc11162f2e82da157116024ac51d05f4c
branch: 1-11-stable
specs:
cocoapods-core (1.11.0.beta.1)
Expand Down
Expand Up @@ -404,6 +404,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
ON_DEMAND_RESOURCES_PREFETCH_ORDER = t2;
PRODUCT_BUNDLE_IDENTIFIER = com.junyixie..OnDemandResourcesDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
Expand All @@ -421,6 +422,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
ON_DEMAND_RESOURCES_PREFETCH_ORDER = t2;
PRODUCT_BUNDLE_IDENTIFIER = com.junyixie..OnDemandResourcesDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions examples/OnDemandResources Example/Podfile
@@ -1,5 +1,5 @@
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
workspace 'Examples.xcworkspace'
project 'OnDemandResourcesDemo.xcodeproj'

target 'OnDemandResourcesDemo' do
platform :ios, '13.2'
Expand Down
Expand Up @@ -36,7 +36,7 @@ TODO: Add long description of the pod here.

s.on_demand_resources = {
't1' => ['on_demand_bundle1/*'],
't2' => ['on_demand_bundle2/*']
't2' => { :paths => ['on_demand_bundle2/*'], :category => :prefetched },
}

s.pod_target_xcconfig = {
Expand All @@ -47,15 +47,15 @@ TODO: Add long description of the pod here.
app_spec.source_files = 'App1/Classes/**/*'

app_spec.on_demand_resources = {
'a1' => ['App1/app1_on_demand_bundle1/*']
'a1' => { :paths => ['App1/app1_on_demand_bundle1/*'], :category => :initial_install }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these do not show up above in project diff because they are app_specs which only live in Pods.xcodeproj which for examples are not tracked on git.

}
end

s.app_spec 'App2' do |app_spec|
app_spec.source_files = 'App2/Classes/**/*'

app_spec.on_demand_resources = {
'a2' => ['App2/app2_on_demand_bundle1/*']
'a2' => { :paths => ['App2/app2_on_demand_bundle1/*'], :category => :initial_install }
}
end

Expand Down
7 changes: 5 additions & 2 deletions lib/cocoapods/installer/project_cache/target_cache_key.rb
Expand Up @@ -162,8 +162,11 @@ def self.from_aggregate_target(sandbox, aggregate_target)
'BUILD_SETTINGS_CHECKSUM' => build_settings,
}
if aggregate_target.includes_resources? || aggregate_target.includes_on_demand_resources?
relative_file_paths = aggregate_target.resource_paths_by_config.values.flatten.uniq + aggregate_target.on_demand_resources.map(&:to_s)
contents['FILES'] = relative_file_paths.sort_by(&:downcase)
relative_resource_file_paths = aggregate_target.resource_paths_by_config.values.flatten.uniq
relative_on_demand_resource_file_paths = aggregate_target.on_demand_resources.map do |res|
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fixes an issue in which the target cache key for incremental installation was storing absolute paths for on demand resources. This would break incremental installation across developers but it is now fixed.

res.relative_path_from(sandbox.project_path.dirname).to_s
end
contents['FILES'] = (relative_resource_file_paths + relative_on_demand_resource_file_paths).sort_by(&:downcase)
end
TargetCacheKey.new(sandbox, :aggregate, contents)
end
Expand Down
Expand Up @@ -32,7 +32,8 @@ class TargetIntegrator
# For messages extensions, this only applies if it's embedded in a messages
# application.
#
EMBED_FRAMEWORK_TARGET_TYPES = [:application, :application_on_demand_install_capable, :unit_test_bundle, :ui_test_bundle, :watch2_extension, :messages_application].freeze
EMBED_FRAMEWORK_TARGET_TYPES = [:application, :application_on_demand_install_capable, :unit_test_bundle,
:ui_test_bundle, :watch2_extension, :messages_application].freeze

# @return [String] the name of the embed frameworks phase
#
Expand Down Expand Up @@ -457,9 +458,9 @@ def embed_frameworks_output_paths(framework_paths, xcframeworks)
#
# @return [void]
#
def add_on_demand_resources(sandbox, project, native_targets, file_accessors, parent_odr_group,
target_odr_group_name)
asset_tags_added = Set.new
def update_on_demand_resources(sandbox, project, native_targets, file_accessors, parent_odr_group,
target_odr_group_name)
category_to_tags = {}
file_accessors = Array(file_accessors)
native_targets = Array(native_targets)

Expand All @@ -469,8 +470,9 @@ def add_on_demand_resources(sandbox, project, native_targets, file_accessors, pa
old_odr_file_refs = old_target_odr_group&.recursive_children_groups&.each_with_object({}) do |group, hash|
hash[group.name] = group.files
end || {}
native_targets.each do |user_target|
user_target.remove_on_demand_resources(old_odr_file_refs)
native_targets.each do |native_target|
native_target.remove_on_demand_resources(old_odr_file_refs)
update_on_demand_resources_build_settings(native_target, nil => old_odr_file_refs.keys)
end
old_target_odr_group&.remove_from_project
return
Expand All @@ -480,17 +482,18 @@ def add_on_demand_resources(sandbox, project, native_targets, file_accessors, pa
current_file_refs = target_odr_group.recursive_children_groups.flat_map(&:files)

added_file_refs = file_accessors.flat_map do |file_accessor|
target_odr_files_refs = Hash[file_accessor.on_demand_resources.map do |tag, resources|
target_odr_files_refs = Hash[file_accessor.on_demand_resources.map do |tag, value|
tag_group = target_odr_group[tag] || target_odr_group.new_group(tag)
asset_tags_added << tag
resources_file_refs = resources.map do |resource|
category_to_tags[value[:category]] ||= []
category_to_tags[value[:category]] << tag
resources_file_refs = value[:paths].map do |resource|
odr_resource_file_ref = Pathname.new(resource).relative_path_from(sandbox.root)
tag_group.find_file_by_path(odr_resource_file_ref.to_s) || tag_group.new_file(odr_resource_file_ref)
end
[tag, resources_file_refs]
end]
native_targets.each do |user_target|
user_target.add_on_demand_resources(target_odr_files_refs)
native_targets.each do |native_target|
native_target.add_on_demand_resources(target_odr_files_refs)
end
target_odr_files_refs.values.flatten
end
Expand All @@ -506,10 +509,42 @@ def add_on_demand_resources(sandbox, project, native_targets, file_accessors, pa
end
target_odr_group.recursive_children_groups.each { |g| g.remove_from_project if g.empty? }

unless asset_tags_added.empty?
attributes = project.root_object.attributes
attributes['KnownAssetTags'] = (attributes['KnownAssetTags'] ||= []) | asset_tags_added.to_a
project.root_object.attributes = attributes
attributes = project.root_object.attributes
attributes['KnownAssetTags'] = (attributes['KnownAssetTags'] ||= []) | category_to_tags.values.flatten
project.root_object.attributes = attributes

native_targets.each do |native_target|
update_on_demand_resources_build_settings(native_target, category_to_tags)
end
end

def update_on_demand_resources_build_settings(native_target, category_to_tags)
%w[ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGS ON_DEMAND_RESOURCES_PREFETCH_ORDER].each do |category_key|
native_target.build_configurations.each do |c|
key = case category_key
when 'ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGS'
:initial_install
when 'ON_DEMAND_RESOURCES_PREFETCH_ORDER'
:prefetched
else
:download_on_demand
end
tags_for_category = (c.build_settings[category_key] || '').split
category_to_tags_dup = category_to_tags.dup
tags_to_add = category_to_tags_dup.delete(key) || []
tags_to_delete = category_to_tags_dup.values.flatten
tags_for_category = (tags_for_category + tags_to_add - tags_to_delete).flatten.compact.uniq
if tags_for_category.empty?
val = c.build_settings.delete(category_key)
native_target.project.mark_dirty! unless val.nil?
else
tags = tags_for_category.join(' ')
unless c.build_settings[category_key] == tags
c.build_settings[category_key] = tags
native_target.project.mark_dirty!
end
end
end
end
end
end
Expand Down Expand Up @@ -596,18 +631,22 @@ def add_copy_resources_script_phase
output_paths_by_config = {}
if use_input_output_paths
target.resource_paths_by_config.each do |config, resource_paths|
input_paths_key = XCFileListConfigKey.new(target.copy_resources_script_input_files_path(config), target.copy_resources_script_input_files_relative_path)
input_paths_key = XCFileListConfigKey.new(target.copy_resources_script_input_files_path(config),
target.copy_resources_script_input_files_relative_path)
input_paths_by_config[input_paths_key] = [script_path] + resource_paths

output_paths_key = XCFileListConfigKey.new(target.copy_resources_script_output_files_path(config), target.copy_resources_script_output_files_relative_path)
output_paths_key = XCFileListConfigKey.new(target.copy_resources_script_output_files_path(config),
target.copy_resources_script_output_files_relative_path)
output_paths_by_config[output_paths_key] = TargetIntegrator.resource_output_paths(resource_paths)
end
end

native_targets.each do |native_target|
# Static library targets cannot include resources. Skip this phase from being added instead.
next if native_target.symbol_type == :static_library
TargetIntegrator.create_or_update_copy_resources_script_phase_to_target(native_target, script_path, input_paths_by_config, output_paths_by_config)
TargetIntegrator.create_or_update_copy_resources_script_phase_to_target(native_target, script_path,
input_paths_by_config,
output_paths_by_config)
end
end

Expand Down Expand Up @@ -720,8 +759,8 @@ def add_on_demand_resources
# The 'Pods' group would always be there for production code however for tests its sometimes not added.
# This ensures its always present and makes it easier for existing and new tests.
parent_odr_group = target.user_project.main_group['Pods'] || target.user_project.new_group('Pods')
TargetIntegrator.add_on_demand_resources(target.sandbox, target.user_project, target.user_targets,
library_file_accessors, parent_odr_group, target_odr_group_name)
TargetIntegrator.update_on_demand_resources(target.sandbox, target.user_project, target.user_targets,
library_file_accessors, parent_odr_group, target_odr_group_name)
end
end

Expand Down
Expand Up @@ -271,17 +271,17 @@ def add_on_demand_resources(native_target, app_spec)
fa.spec.test_specification? && pod_target.test_app_hosts_by_spec[fa.spec]&.first == app_spec
end
target_odr_group_name = "#{pod_target.label}-OnDemandResources"
UserProjectIntegrator::TargetIntegrator.add_on_demand_resources(target.sandbox, native_target.project,
native_target, file_accessors,
parent_odr_group, target_odr_group_name)
UserProjectIntegrator::TargetIntegrator.update_on_demand_resources(target.sandbox, native_target.project,
native_target, file_accessors,
parent_odr_group, target_odr_group_name)
end

# Now add the ODRs of our own app spec declaration.
file_accessor = target.file_accessors.find { |fa| fa.spec == app_spec }
target_odr_group_name = "#{target.subspec_label(app_spec)}-OnDemandResources"
UserProjectIntegrator::TargetIntegrator.add_on_demand_resources(target.sandbox, native_target.project,
native_target, file_accessor,
parent_odr_group, target_odr_group_name)
UserProjectIntegrator::TargetIntegrator.update_on_demand_resources(target.sandbox, native_target.project,
native_target, file_accessor,
parent_odr_group, target_odr_group_name)
end

# @return [String] the message that should be displayed for the target
Expand Down
16 changes: 11 additions & 5 deletions lib/cocoapods/sandbox/file_accessor.rb
Expand Up @@ -219,7 +219,7 @@ def self.all_files(file_accessors)
file_accessors.map(&:preserve_paths),
file_accessors.map(&:readme),
file_accessors.map(&:resources),
file_accessors.flat_map { |f| f.on_demand_resources.values.flatten },
file_accessors.map(&:on_demand_resources_files),
file_accessors.map(&:source_files),
file_accessors.map(&:module_map),
]
Expand Down Expand Up @@ -334,20 +334,26 @@ def resource_bundle_files
resource_bundles.values.flatten
end

# @return [Hash{String => Array<Pathname>}] The paths of the on demand resources specified
# keyed by their tag.
# @return [Hash{String => Hash] The expanded paths of the on demand resources specified
# keyed by their tag including their category.
#
def on_demand_resources
result = {}
spec_consumer.on_demand_resources.each do |tag_name, file_patterns|
paths = expanded_paths(file_patterns,
paths = expanded_paths(file_patterns[:paths],
:exclude_patterns => spec_consumer.exclude_files,
:include_dirs => true)
result[tag_name] = paths
result[tag_name] = { :paths => paths, :category => file_patterns[:category] }
end
result
end

# @return [Array<Pathname>] The expanded paths of the on demand resources.
#
def on_demand_resources_files
on_demand_resources.values.flat_map { |v| v[:paths] }
end

# @return [Pathname] The of the prefix header file of the specification.
#
def prefix_header
Expand Down
2 changes: 1 addition & 1 deletion lib/cocoapods/target/aggregate_target.rb
Expand Up @@ -288,7 +288,7 @@ def on_demand_resources
@on_demand_resources ||= begin
pod_targets.flat_map do |pod_target|
library_file_accessors = pod_target.file_accessors.select { |fa| fa.spec.library_specification? }
library_file_accessors.flat_map { |fa| fa.on_demand_resources.values.flatten }
library_file_accessors.flat_map(&:on_demand_resources_files)
end.uniq
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/cocoapods/target/pod_target.rb
Expand Up @@ -203,7 +203,7 @@ def label
end
end

# @return [Array<String>] The list of all files tracked.
# @return [Array<Pathname>] The list of all files tracked.
#
def all_files
Sandbox::FileAccessor.all_files(file_accessors)
Expand Down
14 changes: 10 additions & 4 deletions spec/unit/installer/project_cache/target_cache_key_spec.rb
Expand Up @@ -38,20 +38,26 @@ module ProjectCache
fixture_target_definition('MyApp'), config.sandbox.root.dirname, nil,
nil, 'Debug' => [@banana_pod_target])
@banana_pod_target.stubs(:resource_paths).returns({})
@banana_pod_target.file_accessors.first.stubs(:on_demand_resources).returns('tag1' => ['/path/to/resource'])
on_demand_resources = { 'tag1' => { :paths => [Pathname('/path/to/resource')], :category => :download_on_demand } }
@banana_pod_target.file_accessors.first.stubs(:on_demand_resources).returns(on_demand_resources)
aggregate_target_cache_key = TargetCacheKey.from_aggregate_target(config.sandbox, aggregate_target)
library_on_demand_resources = @banana_pod_target.file_accessors.first.on_demand_resources.values.flatten.sort_by(&:downcase)
library_on_demand_resources = @banana_pod_target.file_accessors.first.on_demand_resources_files.map do |p|
p.relative_path_from(config.sandbox.root).to_s.downcase
end
aggregate_target_cache_key.to_h['FILES'].should.equal(library_on_demand_resources)
end

it 'should output files for aggregate target if it has aggregate both aggregate resources and on demand resources' do
aggregate_target = AggregateTarget.new(config.sandbox, BuildType.static_library, { 'Debug' => :debug }, [], Platform.ios,
fixture_target_definition('MyApp'), config.sandbox.root.dirname, nil,
nil, 'Debug' => [@banana_pod_target])
@banana_pod_target.file_accessors.first.stubs(:on_demand_resources).returns('tag1' => ['/path/to/resource'])
on_demand_resources = { 'tag1' => { :paths => [Pathname('/path/to/resource')], :category => :download_on_demand } }
@banana_pod_target.file_accessors.first.stubs(:on_demand_resources).returns(on_demand_resources)
aggregate_target_cache_key = TargetCacheKey.from_aggregate_target(config.sandbox, aggregate_target)
library_resources = @banana_pod_target.resource_paths.values.flatten.sort_by(&:downcase)
library_on_demand_resources = @banana_pod_target.file_accessors.first.on_demand_resources.values.flatten.sort_by(&:downcase)
library_on_demand_resources = @banana_pod_target.file_accessors.first.on_demand_resources_files.map do |p|
p.relative_path_from(config.sandbox.root).to_s.downcase
end
aggregate_target_cache_key.to_h['FILES'].should.equal(library_resources + library_on_demand_resources)
end
end
Expand Down