Commit 5b6ec485 authored by Eric Firestone's avatar Eric Firestone

Fix issues with variant group, versioned, and asset catalog resources

parent 93e888b2
......@@ -103,6 +103,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
##### Bug Fixes
* Fix compiling of localized resources.
[Eric Firestone](https://github.com/efirestone)
[#1653](https://github.com/CocoaPods/CocoaPods/issues/1653)
* Fix compiling of asset catalog files inside resource bundles.
[Muhammed Yavuz Nuzumlalı](https://github.com/manuyavuz)
[#4501](https://github.com/CocoaPods/CocoaPods/issues/4501)
......
......@@ -177,6 +177,7 @@ module Pod
pod_name = file_accessor.spec.name
local = sandbox.local?(pod_name)
paths = file_accessor.send(file_accessor_key)
paths = allowable_project_paths(paths)
paths.each do |path|
group = pods_project.group_for_spec(file_accessor.spec.name, group_key)
pods_project.add_file_reference(path, group, local && reflect_file_system_structure_for_development)
......@@ -184,6 +185,54 @@ module Pod
end
end
# Filters a list of paths down to those paths which can be added to
# the Xcode project. Some paths are intermediates and only their children
# should be added, while some paths are treated as bundles and their
# children should not be added directly.
#
# @param [Array<Pathname>] paths
# The paths to files or directories on disk.
#
# @return [Array<Pathname>] The paths which can be added to the Xcode project
#
def allowable_project_paths(paths)
lproj_paths = Set.new
lproj_paths_with_files = Set.new
allowable_paths = paths.select do |path|
path_str = path.to_s.downcase
# We add the directory for a Core Data model, but not the items in it.
next if path_str =~ /.*\.xcdatamodeld\/.+/
# We add the directory for an asset catalog, but not the items in it.
next if path_str =~ /.*\.xcassets\/.+/
if path_str =~ /\.lproj(\/|$)/
# If the element is an .lproj directory then save it and potentially
# add it later if we don't find any contained items.
if path_str.end_with?('.lproj') && path.directory?
lproj_paths << path_str
next
end
# Collect the paths for the .lproj directories that contain files.
lproj_path = /(^.*\.lproj)\/.*/.match(path_str)[1]
lproj_paths_with_files << lproj_path
# Directories nested within an .lproj directory are added as file
# system references so their contained items are not added directly.
next if path.dirname.dirname.to_s.downcase == lproj_path
end
true
end
# Only add the path for the .lproj directories that do not have anything
# within them added as well. This generally happens if the glob within the
# resources directory was not a recursive glob.
allowable_paths + lproj_paths.subtract(lproj_paths_with_files).to_a
end
# Computes the destination sub-directory in the sandbox
#
# @param [Pathname] headers_sandbox
......
......@@ -64,6 +64,11 @@ module Pod
# @note The Frameworks are used only for presentation purposes as the
# xcconfig is the authoritative source about their information.
#
# @note Core Data model directories (.xcdatamodeld) defined in the `resources`
# property are currently added to the `Copy Resources` build phase like
# all other resources. The Xcode UI adds these to the `Compile Sources`
# build phase, but they will compile correctly either way.
#
# @return [void]
#
def add_files_to_build_phases
......@@ -99,6 +104,10 @@ module Pod
project.reference_for_path(res)
end
# Some nested files are not directly present in the Xcode project, such as the contents
# of an .xcdatamodeld directory. These files will return nil file references.
resource_refs.compact!
native_target.add_resources(resource_refs)
end
end
......@@ -108,12 +117,31 @@ module Pod
# @note The source files are grouped by Pod and in turn by subspec
# (recursively) in the resources group.
#
# @note Core Data model directories (.xcdatamodeld) are currently added to the
# `Copy Resources` build phase like all other resources. The Xcode UI adds
# these to the `Compile Sources` build phase, but they will compile
# correctly either way.
#
# @return [void]
#
def add_resources_bundle_targets
target.file_accessors.each do |file_accessor|
file_accessor.resource_bundles.each do |bundle_name, paths|
file_references = paths.map { |sf| project.reference_for_path(sf) }
file_references = paths.map do |path|
ref = project.reference_for_path(path)
# Some nested files are not directly present in the Xcode project, such as the contents
# of an .xcdatamodeld directory. These files are implicitly included by including their
# parent directory.
next if ref.nil?
# For variant groups, the variant group itself is added, not its members.
next ref.parent if ref.parent.is_a?(Xcodeproj::Project::Object::PBXVariantGroup)
ref
end
file_references = file_references.uniq.compact
label = target.resources_bundle_target_label(bundle_name)
bundle_target = project.new_resources_bundle(label, file_accessor.spec_consumer.platform_name)
bundle_target.deployment_target = deployment_target
......
......@@ -271,8 +271,9 @@ module Pod
# Creates subgroups to reflect the file system structure if
# reflect_file_system_structure is set to true.
# Makes a variant group if the path points to a localized file inside a
# *.lproj folder. The same variant group is returned for files with the
# same name, even if their file extensions differ.
# *.lproj directory. To support Apple Base Internationalization, the same
# variant group is returned for interface files and strings files with
# the same name.
#
# @param [Pathname] absolute_pathname
# The pathname of the file to get the group for.
......@@ -297,7 +298,7 @@ module Pod
relative_dir = relative_pathname.dirname
lproj_regex = /\.lproj/i
# Add subgroups for folders, but treat .lproj as a file
# Add subgroups for directories, but treat .lproj as a file
if reflect_file_system_structure
relative_dir.each_filename do|name|
break if name.to_s =~ lproj_regex
......@@ -306,18 +307,49 @@ module Pod
end
end
# Turn .lproj into a variant group
# Turn files inside .lproj directories into a variant group
if relative_dir.basename.to_s =~ lproj_regex
filename = absolute_pathname.basename.sub_ext('').to_s
group_name = variant_group_name(absolute_pathname)
lproj_parent_dir = absolute_pathname.dirname.dirname
group = @variant_groups_by_path_and_name[[lproj_parent_dir, filename]] ||
group.new_variant_group(filename, lproj_parent_dir)
@variant_groups_by_path_and_name[[lproj_parent_dir, filename]] ||= group
group = @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||
group.new_variant_group(group_name, lproj_parent_dir)
@variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||= group
end
group
end
# Returns the name to be used for a the variant group for a file at a given path.
# The path must be localized (within an *.lproj directory).
#
# @param [Pathname] The localized path to get a variant group name for.
#
# @return [String] The variant group name.
#
def variant_group_name(path)
unless path.to_s.downcase.include?('.lproj/')
raise ArgumentError, 'Only localized resources can be added to variant groups.'
end
# When using Base Internationalization for XIBs and Storyboards a strings
# file is generated with the same name as the XIB/Storyboard in each .lproj
# directory:
# Base.lproj/MyViewController.xib
# fr.lproj/MyViewController.strings
#
# In this scenario we want the variant group to be the same as the XIB or Storyboard.
#
# Base Internationalization: https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html
if path.extname.downcase == '.strings'
%w(.xib .storyboard).each do |extension|
possible_interface_file = path.dirname.dirname + 'Base.lproj' + path.basename.sub_ext(extension)
return possible_interface_file.basename.to_s if possible_interface_file.exist?
end
end
path.basename.to_s
end
#-------------------------------------------------------------------------#
end
end
......@@ -40,13 +40,29 @@ module Pod
file_ref.path.should == 'libBananalib.a'
end
it 'adds the files references of the resources the Pods project' do
it 'adds files references for the resources of the Pods project' do
@installer.install!
file_ref = @installer.pods_project['Pods/BananaLib/Resources/logo-sidebar.png']
file_ref.should.be.not.nil
file_ref.path.should == 'Resources/logo-sidebar.png'
end
it "add file references for localization directories if glob doesn't include contained files" do
@installer.install!
file_ref = @installer.pods_project['Pods/BananaLib/Resources/en.lproj']
file_ref.should.be.not.nil
end
it 'adds file references for files within CoreData directories' do
@installer.install!
model_ref = @installer.pods_project['Pods/BananaLib/Resources/Sample.xcdatamodeld']
model_ref.should.be.not.nil
# Files within the .xcdatamodeld directory are added automatically by adding the .xcdatamodeld directory.
file_ref = @installer.pods_project['Pods/BananaLib/Resources/Sample.xcdatamodeld/Sample.xcdatamodel']
file_ref.should.be.not.nil
end
it 'links the headers required for building the pod target' do
@installer.install!
headers_root = @pod_target.build_headers.root
......@@ -90,6 +106,49 @@ module Pod
#-------------------------------------------------------------------------#
describe 'Installation With Recursive Resources Glob' do
before do
spec = fixture_spec('banana-lib/BananaLib.podspec')
spec.resources = 'Resources/**/*'
@pod_target = fixture_pod_target(spec)
@file_accessor = @pod_target.file_accessors.first
@project = Project.new(config.sandbox.project_path)
@project.add_pod_group('BananaLib', fixture('banana-lib'))
@installer = Installer::FileReferencesInstaller.new(config.sandbox, [@pod_target], @project)
end
it "doesn't add file references for localization directories themselves" \
'if glob includes contained files' do
@installer.install!
file_ref = @installer.pods_project['Pods/BananaLib/Resources/en.lproj']
file_ref.should.be.nil
end
it 'creates file system reference variant groups for nested localized resource directories' do
@installer.install!
ref = @installer.pods_project['Pods/BananaLib/Resources/nested']
ref.should.be.not.nil
ref.is_a?(Xcodeproj::Project::Object::PBXVariantGroup).should.be.true
end
it "doesn't add file references for nested localized resources" do
@installer.install!
file_ref = @installer.pods_project['Pods/BananaLib/Resources/en.lproj/nested/logo-nested.png']
file_ref.should.be.nil
end
it "doesn't add file references for files within Asset Catalogs" do
@installer.install!
catalog_ref = @installer.pods_project['Pods/BananaLib/Resources/Images.xcassets']
catalog_ref.should.be.not.nil
# The asset catalog should be a "PBXFileReference" and therefore doesn't have children.
catalog_ref.is_a?(Xcodeproj::Project::Object::PBXFileReference).should.be.true
end
end
#-------------------------------------------------------------------------#
describe 'Private Helpers' do
describe '#file_accessors' do
it 'returns the file accessors' do
......
......@@ -398,6 +398,61 @@ module Pod
end
end
end
describe 'concerning resource bundles' do
before do
config.sandbox.prepare
@project = Project.new(config.sandbox.project_path)
config.sandbox.project = @project
@spec = fixture_spec('banana-lib/BananaLib.podspec')
@spec.resources = nil
@spec.resource_bundle = { 'banana_bundle' => ['Resources/**/*'] }
@project.add_pod_group('BananaLib', fixture('banana-lib'))
@pod_target = fixture_pod_target(@spec)
@pod_target.user_build_configurations = { 'Debug' => :debug, 'Release' => :release }
target_installer = Installer::PodTargetInstaller.new(config.sandbox, @pod_target)
# Use a file references installer to add the files so that the correct ones are added.
file_ref_installer = Installer::FileReferencesInstaller.new(config.sandbox, [@pod_target], @project)
file_ref_installer.install!
target_installer.install!
@bundle_target = @project.targets.find { |t| t.name == 'BananaLib-banana_bundle' }
@bundle_target.should.be.not.nil
end
it 'adds variant groups directly to resources' do
# The variant group item should be present.
group_build_file = @bundle_target.resources_build_phase.files.find do |bf|
bf.file_ref.path == 'Resources' && bf.file_ref.name == 'Main.storyboard'
end
group_build_file.should.be.not.nil
group_build_file.file_ref.is_a?(Xcodeproj::Project::Object::PBXVariantGroup).should.be.true
# An item within the variant group should not be present.
strings_build_file = @bundle_target.resources_build_phase.files.find do |bf|
bf.file_ref.path == 'Resources/en.lproj/Main.strings'
end
strings_build_file.should.be.nil
end
it 'adds Core Data models directly to resources' do
# The model directory item should be present.
dir_build_file = @bundle_target.resources_build_phase.files.find { |bf| bf.file_ref.path == 'Resources/Sample.xcdatamodeld' }
dir_build_file.should.be.not.nil
# An item within the model directory should not be present.
version_build_file = @bundle_target.resources_build_phase.files.find do |bf|
bf.file_ref.path =~ %r{Resources/Sample.xcdatamodeld/Sample.xcdatamodel}i
end
version_build_file.should.be.nil
end
end
end
end
end
......@@ -208,14 +208,14 @@ module Pod
it 'creates variant group for localized file' do
Pathname.any_instance.stubs(:realpath).returns(@localized_file)
ref = @project.add_file_reference(@localized_file, @group)
ref.hierarchy_path.should == '/Pods/BananaLib/Foo/Foo.strings'
ref.hierarchy_path.should == '/Pods/BananaLib/Foo.strings/Foo.strings'
ref.parent.class.should == Xcodeproj::Project::Object::PBXVariantGroup
end
it 'creates variant group for localized file in subgroup' do
Pathname.any_instance.stubs(:realpath).returns(@localized_file)
ref = @project.add_file_reference(@localized_file, @group, true)
ref.hierarchy_path.should == '/Pods/BananaLib/Dir/SubDir/Foo/Foo.strings'
ref.hierarchy_path.should == '/Pods/BananaLib/Dir/SubDir/Foo.strings/Foo.strings'
ref.parent.class.should == Xcodeproj::Project::Object::PBXVariantGroup
end
......@@ -250,6 +250,7 @@ module Pod
@nested_file2 = subdir + 'nested_file.m'
@localized_base_foo = subdir + 'Base.lproj/Foo.storyboard'
@localized_de_foo = subdir + 'de.lproj/Foo.strings'
@localized_de_foo_jpg = subdir + 'de.lproj/Foo.jpg'
@localized_de_bar = subdir + 'de.lproj/Bar.strings'
@localized_different_foo = poddir + 'Base.lproj/Foo.jpg'
@group = @project.group_for_spec('BananaLib')
......@@ -278,13 +279,13 @@ module Pod
it 'creates variant group for localized file' do
group = @project.group_for_path_in_group(@localized_base_foo, @group, false)
group.hierarchy_path.should == '/Pods/BananaLib/Foo'
group.hierarchy_path.should == '/Pods/BananaLib/Foo.storyboard'
group.class.should == Xcodeproj::Project::Object::PBXVariantGroup
end
it 'creates variant group for localized file when adding subgroups' do
group = @project.group_for_path_in_group(@localized_base_foo, @group, true)
group.hierarchy_path.should == '/Pods/BananaLib/Dir/SubDir/Foo'
group.hierarchy_path.should == '/Pods/BananaLib/Dir/SubDir/Foo.storyboard'
group.class.should == Xcodeproj::Project::Object::PBXVariantGroup
end
......@@ -293,13 +294,26 @@ module Pod
group.real_path.should == config.sandbox.pod_dir('BananaLib') + 'Dir/SubDir'
end
it "doesn't duplicate variant groups for same name and directory" do
it "doesn't duplicate variant groups for interface and strings files with " \
'same name and directory' do
Pathname.any_instance.stubs(:exist?).returns(false).then.returns(true)
group_1 = @project.group_for_path_in_group(@localized_base_foo, @group, false)
group_2 = @project.group_for_path_in_group(@localized_de_foo, @group, false)
group_1.uuid.should == group_2.uuid
@group.children.count.should == 1
end
it 'creates own variant groups for localized non-interface files with same name' do
# An image and a strings file should not be combined.
group_1 = @project.group_for_path_in_group(@localized_de_foo, @group, false)
group_2 = @project.group_for_path_in_group(@localized_de_foo_jpg, @group, false)
group_1.uuid.should != group_2.uuid
@group.children.count.should == 2
end
it 'makes separate variant groups for different names' do
group_1 = @project.group_for_path_in_group(@localized_base_foo, @group, false)
group_2 = @project.group_for_path_in_group(@localized_de_bar, @group, false)
......
......@@ -108,7 +108,11 @@ module Pod
it 'returns the resources' do
@accessor.resources.sort.should == [
@root + 'Resources/Base.lproj',
@root + 'Resources/Images.xcassets',
@root + 'Resources/Sample.xcdatamodeld',
@root + 'Resources/de.lproj',
@root + 'Resources/en.lproj',
@root + 'Resources/logo-sidebar.png',
@root + 'Resources/sub_dir',
]
......@@ -153,7 +157,11 @@ module Pod
@spec_consumer.stubs(:resource_bundles).returns('BananaLib' => 'Resources/*')
resource_paths = [
@root + 'Resources/logo-sidebar.png',
@root + 'Resources/Base.lproj',
@root + 'Resources/de.lproj',
@root + 'Resources/en.lproj',
@root + 'Resources/Images.xcassets',
@root + 'Resources/Sample.xcdatamodeld',
@root + 'Resources/sub_dir',
]
@accessor.resource_bundles.should == { 'BananaLib' => resource_paths }
......@@ -163,7 +171,11 @@ module Pod
@spec_consumer.stubs(:resource_bundles).returns('BananaLib' => 'Resources/*')
resource_paths = [
@root + 'Resources/logo-sidebar.png',
@root + 'Resources/Base.lproj',
@root + 'Resources/de.lproj',
@root + 'Resources/en.lproj',
@root + 'Resources/Images.xcassets',
@root + 'Resources/Sample.xcdatamodeld',
@root + 'Resources/sub_dir',
]
@accessor.resource_bundle_files.should == resource_paths
......@@ -173,7 +185,11 @@ module Pod
@spec_consumer.stubs(:exclude_files).returns(['**/*.png'])
@spec_consumer.stubs(:resource_bundles).returns('BananaLib' => 'Resources/*')
resource_paths = [
@root + 'Resources/Base.lproj',
@root + 'Resources/de.lproj',
@root + 'Resources/en.lproj',
@root + 'Resources/Images.xcassets',
@root + 'Resources/Sample.xcdatamodeld',
@root + 'Resources/sub_dir',
]
@accessor.resource_bundles.should == { 'BananaLib' => resource_paths }
......
......@@ -23,8 +23,16 @@ module Pod
Classes/BananaPrivate.h
Classes/BananaTrace.d
README
Resources/Base.lproj/Main.storyboard
Resources/Images.xcassets/Logo.imageset/Contents.json
Resources/Images.xcassets/Logo.imageset/logo.png
Resources/Sample.xcdatamodeld/.xccurrentversion
Resources/Sample.xcdatamodeld/Sample\ 2.xcdatamodel/contents
Resources/Sample.xcdatamodeld/Sample.xcdatamodel/contents
Resources/de.lproj/logo-localized.png
Resources/en.lproj/Main.strings
Resources/en.lproj/logo-localized.png
Resources/en.lproj/nested/logo-nested.png
Resources/logo-sidebar.png
Resources/sub_dir/logo-sidebar.png
framework/Source/MoreBanana.h
......@@ -51,8 +59,15 @@ module Pod
Bananalib.framework/Versions/Current
Classes
Resources
Resources/Base.lproj
Resources/Images.xcassets
Resources/Images.xcassets/Logo.imageset
Resources/Sample.xcdatamodeld
Resources/Sample.xcdatamodeld/Sample\ 2.xcdatamodel
Resources/Sample.xcdatamodeld/Sample.xcdatamodel
Resources/de.lproj
Resources/en.lproj
Resources/en.lproj/nested
Resources/sub_dir
framework
framework/Source
......@@ -137,7 +152,11 @@ module Pod
it 'can optionally include the directories in the results' do
paths = @path_list.relative_glob('Resources/*', :include_dirs => true).map(&:to_s)
paths.sort.should == %w(
Resources/Base.lproj
Resources/Images.xcassets
Resources/Sample.xcdatamodeld
Resources/de.lproj
Resources/en.lproj
Resources/logo-sidebar.png
Resources/sub_dir
)
......
......@@ -18,38 +18,41 @@ module Pod
# them from the comparison.
paths_without_git = relative_paths.reject { |p| p.include? '.git/' }
paths_without_git.sort.should == [
'.git',
'.gitmodules',
'BananaLib.podspec',
'libPusher',
'sub-dir',
'sub-dir/sub-dir-2',
'sub-dir/sub-dir-2/somefile.txt',
]
paths_without_git.sort.should == %w(
.git
.gitmodules
BananaLib.podspec
libPusher
sub-dir
sub-dir/sub-dir-2
sub-dir/sub-dir-2/somefile.txt
)
end
it 'returns the used files' do
paths = @cleaner.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{@root}/", '') }
relative_paths.sort.should == [
'Banana.modulemap',
'Bananalib.framework',
'Classes/Banana.h',
'Classes/Banana.m',
'Classes/BananaLib.pch',
'Classes/BananaPrivate.h',
'Classes/BananaTrace.d',
'LICENSE',
'README',
'Resources/Images.xcassets',
'Resources/logo-sidebar.png',
'Resources/sub_dir',
'framework/Source/MoreBanana.h',
'libBananalib.a',
'preserve_me.txt',
]
relative_paths.sort.should == %w(
Banana.modulemap
Bananalib.framework
Classes/Banana.h
Classes/Banana.m
Classes/BananaLib.pch
Classes/BananaPrivate.h
Classes/BananaTrace.d
LICENSE
README
Resources/Base.lproj
Resources/Images.xcassets
Resources/Sample.xcdatamodeld
Resources/de.lproj
Resources/en.lproj
Resources/logo-sidebar.png
Resources/sub_dir
framework/Source/MoreBanana.h
libBananalib.a
preserve_me.txt
)
end
it 'handles Pods with multiple file accessors' do
......@@ -64,21 +67,25 @@ module Pod
@cleaner = Sandbox::PodDirCleaner.new(@root, specs_by_platform)
paths = @cleaner.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{@root}/", '') }
relative_paths.sort.should == [
'Banana.modulemap',
'Bananalib.framework',
'Classes/Banana.h',
'Classes/Banana.m',
'Classes/BananaLib.pch',
'Classes/BananaPrivate.h',
'LICENSE',
'README',
'Resources/Images.xcassets',
'Resources/logo-sidebar.png',
'Resources/sub_dir',
'libBananalib.a',
'preserve_me.txt',
]
relative_paths.sort.should == %w(
Banana.modulemap
Bananalib.framework
Classes/Banana.h
Classes/Banana.m
Classes/BananaLib.pch
Classes/BananaPrivate.h
LICENSE
README
Resources/Base.lproj
Resources/Images.xcassets
Resources/Sample.xcdatamodeld
Resources/de.lproj
Resources/en.lproj
Resources/logo-sidebar.png
Resources/sub_dir
libBananalib.a
preserve_me.txt
)
end
it 'compacts the used files as nil would be converted to the empty string' do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment