Skip to content

Commit

Permalink
Integrate ODR categories into projects.
Browse files Browse the repository at this point in the history
  • Loading branch information
dnkoutso committed Aug 10, 2021
1 parent 22a6218 commit afff0da
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 69 deletions.
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
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 }
}
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|
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

0 comments on commit afff0da

Please sign in to comment.