xcocoapods 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6303 -0
  3. data/LICENSE +28 -0
  4. data/README.md +80 -0
  5. data/bin/pod +56 -0
  6. data/bin/sandbox-pod +168 -0
  7. data/lib/cocoapods.rb +73 -0
  8. data/lib/cocoapods/command.rb +175 -0
  9. data/lib/cocoapods/command/cache.rb +28 -0
  10. data/lib/cocoapods/command/cache/clean.rb +90 -0
  11. data/lib/cocoapods/command/cache/list.rb +69 -0
  12. data/lib/cocoapods/command/env.rb +66 -0
  13. data/lib/cocoapods/command/init.rb +128 -0
  14. data/lib/cocoapods/command/install.rb +45 -0
  15. data/lib/cocoapods/command/ipc.rb +19 -0
  16. data/lib/cocoapods/command/ipc/list.rb +40 -0
  17. data/lib/cocoapods/command/ipc/podfile.rb +31 -0
  18. data/lib/cocoapods/command/ipc/podfile_json.rb +30 -0
  19. data/lib/cocoapods/command/ipc/repl.rb +51 -0
  20. data/lib/cocoapods/command/ipc/spec.rb +29 -0
  21. data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
  22. data/lib/cocoapods/command/lib.rb +11 -0
  23. data/lib/cocoapods/command/lib/create.rb +105 -0
  24. data/lib/cocoapods/command/lib/lint.rb +121 -0
  25. data/lib/cocoapods/command/list.rb +39 -0
  26. data/lib/cocoapods/command/options/project_directory.rb +36 -0
  27. data/lib/cocoapods/command/options/repo_update.rb +34 -0
  28. data/lib/cocoapods/command/outdated.rb +140 -0
  29. data/lib/cocoapods/command/repo.rb +29 -0
  30. data/lib/cocoapods/command/repo/add.rb +103 -0
  31. data/lib/cocoapods/command/repo/lint.rb +82 -0
  32. data/lib/cocoapods/command/repo/list.rb +93 -0
  33. data/lib/cocoapods/command/repo/push.rb +281 -0
  34. data/lib/cocoapods/command/repo/remove.rb +36 -0
  35. data/lib/cocoapods/command/repo/update.rb +28 -0
  36. data/lib/cocoapods/command/setup.rb +103 -0
  37. data/lib/cocoapods/command/spec.rb +112 -0
  38. data/lib/cocoapods/command/spec/cat.rb +51 -0
  39. data/lib/cocoapods/command/spec/create.rb +283 -0
  40. data/lib/cocoapods/command/spec/edit.rb +87 -0
  41. data/lib/cocoapods/command/spec/env_spec.rb +53 -0
  42. data/lib/cocoapods/command/spec/lint.rb +137 -0
  43. data/lib/cocoapods/command/spec/which.rb +43 -0
  44. data/lib/cocoapods/command/update.rb +101 -0
  45. data/lib/cocoapods/config.rb +347 -0
  46. data/lib/cocoapods/core_overrides.rb +1 -0
  47. data/lib/cocoapods/downloader.rb +190 -0
  48. data/lib/cocoapods/downloader/cache.rb +233 -0
  49. data/lib/cocoapods/downloader/request.rb +86 -0
  50. data/lib/cocoapods/downloader/response.rb +16 -0
  51. data/lib/cocoapods/executable.rb +222 -0
  52. data/lib/cocoapods/external_sources.rb +57 -0
  53. data/lib/cocoapods/external_sources/abstract_external_source.rb +205 -0
  54. data/lib/cocoapods/external_sources/downloader_source.rb +30 -0
  55. data/lib/cocoapods/external_sources/path_source.rb +55 -0
  56. data/lib/cocoapods/external_sources/podspec_source.rb +54 -0
  57. data/lib/cocoapods/gem_version.rb +5 -0
  58. data/lib/cocoapods/generator/acknowledgements.rb +107 -0
  59. data/lib/cocoapods/generator/acknowledgements/markdown.rb +44 -0
  60. data/lib/cocoapods/generator/acknowledgements/plist.rb +94 -0
  61. data/lib/cocoapods/generator/app_target_helper.rb +244 -0
  62. data/lib/cocoapods/generator/bridge_support.rb +22 -0
  63. data/lib/cocoapods/generator/constant.rb +19 -0
  64. data/lib/cocoapods/generator/copy_resources_script.rb +230 -0
  65. data/lib/cocoapods/generator/dummy_source.rb +31 -0
  66. data/lib/cocoapods/generator/embed_frameworks_script.rb +215 -0
  67. data/lib/cocoapods/generator/header.rb +103 -0
  68. data/lib/cocoapods/generator/info_plist_file.rb +116 -0
  69. data/lib/cocoapods/generator/module_map.rb +99 -0
  70. data/lib/cocoapods/generator/prefix_header.rb +60 -0
  71. data/lib/cocoapods/generator/umbrella_header.rb +46 -0
  72. data/lib/cocoapods/hooks_manager.rb +132 -0
  73. data/lib/cocoapods/installer.rb +703 -0
  74. data/lib/cocoapods/installer/analyzer.rb +972 -0
  75. data/lib/cocoapods/installer/analyzer/analysis_result.rb +87 -0
  76. data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +98 -0
  77. data/lib/cocoapods/installer/analyzer/pod_variant.rb +67 -0
  78. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +157 -0
  79. data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
  80. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +240 -0
  81. data/lib/cocoapods/installer/analyzer/specs_state.rb +84 -0
  82. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +53 -0
  83. data/lib/cocoapods/installer/analyzer/target_inspector.rb +260 -0
  84. data/lib/cocoapods/installer/installation_options.rb +158 -0
  85. data/lib/cocoapods/installer/pod_source_installer.rb +202 -0
  86. data/lib/cocoapods/installer/pod_source_preparer.rb +77 -0
  87. data/lib/cocoapods/installer/podfile_validator.rb +139 -0
  88. data/lib/cocoapods/installer/post_install_hooks_context.rb +132 -0
  89. data/lib/cocoapods/installer/pre_install_hooks_context.rb +51 -0
  90. data/lib/cocoapods/installer/source_provider_hooks_context.rb +34 -0
  91. data/lib/cocoapods/installer/user_project_integrator.rb +250 -0
  92. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +463 -0
  93. data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
  94. data/lib/cocoapods/installer/xcode.rb +8 -0
  95. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +416 -0
  96. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +181 -0
  97. data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +84 -0
  98. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +334 -0
  99. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +777 -0
  100. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +116 -0
  101. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +86 -0
  102. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +256 -0
  103. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer_helper.rb +68 -0
  104. data/lib/cocoapods/installer/xcode/target_validator.rb +147 -0
  105. data/lib/cocoapods/open-uri.rb +33 -0
  106. data/lib/cocoapods/project.rb +414 -0
  107. data/lib/cocoapods/resolver.rb +585 -0
  108. data/lib/cocoapods/resolver/lazy_specification.rb +79 -0
  109. data/lib/cocoapods/sandbox.rb +404 -0
  110. data/lib/cocoapods/sandbox/file_accessor.rb +444 -0
  111. data/lib/cocoapods/sandbox/headers_store.rb +146 -0
  112. data/lib/cocoapods/sandbox/path_list.rb +220 -0
  113. data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +85 -0
  114. data/lib/cocoapods/sandbox/podspec_finder.rb +23 -0
  115. data/lib/cocoapods/sources_manager.rb +157 -0
  116. data/lib/cocoapods/target.rb +261 -0
  117. data/lib/cocoapods/target/aggregate_target.rb +338 -0
  118. data/lib/cocoapods/target/build_settings.rb +1075 -0
  119. data/lib/cocoapods/target/pod_target.rb +559 -0
  120. data/lib/cocoapods/user_interface.rb +459 -0
  121. data/lib/cocoapods/user_interface/error_report.rb +187 -0
  122. data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
  123. data/lib/cocoapods/validator.rb +981 -0
  124. metadata +533 -0
@@ -0,0 +1,68 @@
1
+ module Pod
2
+ class Installer
3
+ class Xcode
4
+ class PodsProjectGenerator
5
+ module TargetInstallerHelper
6
+ # @param [Generator] generator
7
+ # the generator to use for generating the content.
8
+ #
9
+ # @param [Pathname] path
10
+ # the pathname to save the content into.
11
+ #
12
+ # Saves the content the provided path unless the path exists and the contents are exactly the same.
13
+ #
14
+ def update_changed_file(generator, path)
15
+ if path.exist?
16
+ contents = generator.generate.to_s
17
+ content_stream = StringIO.new(contents)
18
+ identical = File.open(path, 'rb') { |f| FileUtils.compare_stream(f, content_stream) }
19
+ return if identical
20
+
21
+ File.open(path, 'w') { |f| f.write(contents) }
22
+ else
23
+ path.dirname.mkpath
24
+ generator.save_as(path)
25
+ end
26
+ end
27
+
28
+ # Creates the Info.plist file which sets public framework attributes
29
+ #
30
+ # @param [Sandbox] sandbox @see #sandbox
31
+ # The sandbox where the generated Info.plist file should be saved.
32
+ #
33
+ # @param [Pathname] path
34
+ # the path to save the generated Info.plist file.
35
+ #
36
+ # @param [PBXNativeTarget] native_target
37
+ # the native target to link the generated Info.plist file into.
38
+ #
39
+ # @param [Version] version
40
+ # the version to use for when generating this Info.plist file.
41
+ #
42
+ # @param [Platform] platform
43
+ # the platform to use for when generating this Info.plist file.
44
+ #
45
+ # @param [Symbol] bundle_package_type
46
+ # the CFBundlePackageType of the target this Info.plist file is for.
47
+ #
48
+ # @return [void]
49
+ #
50
+ def create_info_plist_file_with_sandbox(sandbox, path, native_target, version, platform, bundle_package_type = :fmwk)
51
+ UI.message "- Generating Info.plist file at #{UI.path(path)}" do
52
+ generator = Generator::InfoPlistFile.new(version, platform, bundle_package_type)
53
+ update_changed_file(generator, path)
54
+
55
+ relative_path_string = path.relative_path_from(sandbox.root).to_s
56
+ native_target.build_configurations.each do |c|
57
+ c.build_settings['INFOPLIST_FILE'] = relative_path_string
58
+ end
59
+ end
60
+ end
61
+
62
+ module_function :update_changed_file
63
+ module_function :create_info_plist_file_with_sandbox
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,147 @@
1
+ module Pod
2
+ class Installer
3
+ class Xcode
4
+ # The {Xcode::TargetValidator} ensures that the pod and aggregate target
5
+ # configuration is valid for installation.
6
+ #
7
+ class TargetValidator
8
+ # @return [Array<AggregateTarget>] The aggregate targets that should be
9
+ # validated.
10
+ #
11
+ attr_reader :aggregate_targets
12
+
13
+ # @return [Array<PodTarget>] The pod targets that should be validated.
14
+ #
15
+ attr_reader :pod_targets
16
+
17
+ # Create a new TargetValidator with aggregate and pod targets to
18
+ # validate.
19
+ #
20
+ # @param [Array<AggregateTarget>] aggregate_targets
21
+ # The aggregate targets to validate.
22
+ #
23
+ # @param [Array<PodTarget>] pod_targets
24
+ # The pod targets to validate.
25
+ #
26
+ def initialize(aggregate_targets, pod_targets)
27
+ @aggregate_targets = aggregate_targets
28
+ @pod_targets = pod_targets
29
+ end
30
+
31
+ # Perform the validation steps for the provided aggregate and pod
32
+ # targets.
33
+ #
34
+ def validate!
35
+ verify_no_duplicate_framework_and_library_names
36
+ verify_no_static_framework_transitive_dependencies
37
+ verify_swift_pods_swift_version
38
+ verify_swift_pods_have_module_dependencies
39
+ end
40
+
41
+ private
42
+
43
+ def verify_no_duplicate_framework_and_library_names
44
+ aggregate_targets.each do |aggregate_target|
45
+ aggregate_target.user_build_configurations.keys.each do |config|
46
+ pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
47
+ file_accessors = pod_targets.flat_map(&:file_accessors)
48
+
49
+ frameworks = file_accessors.flat_map(&:vendored_frameworks).uniq.map(&:basename)
50
+ frameworks += pod_targets.select { |pt| pt.should_build? && pt.requires_frameworks? }.map(&:product_module_name).uniq
51
+ verify_no_duplicate_names(frameworks, aggregate_target.label, 'frameworks')
52
+
53
+ libraries = file_accessors.flat_map(&:vendored_libraries).uniq.map(&:basename)
54
+ libraries += pod_targets.select { |pt| pt.should_build? && !pt.requires_frameworks? }.map(&:product_name)
55
+ verify_no_duplicate_names(libraries, aggregate_target.label, 'libraries')
56
+ end
57
+ end
58
+ end
59
+
60
+ def verify_no_duplicate_names(names, label, type)
61
+ duplicates = names.map { |n| n.to_s.downcase }.group_by { |f| f }.select { |_, v| v.size > 1 }.keys
62
+
63
+ unless duplicates.empty?
64
+ raise Informative, "The '#{label}' target has " \
65
+ "#{type} with conflicting names: #{duplicates.to_sentence}."
66
+ end
67
+ end
68
+
69
+ def verify_no_static_framework_transitive_dependencies
70
+ aggregate_targets.each do |aggregate_target|
71
+ next unless aggregate_target.requires_frameworks?
72
+
73
+ aggregate_target.user_build_configurations.keys.each do |config|
74
+ dynamic_pod_targets = aggregate_target.pod_targets_for_build_configuration(config).reject(&:static_framework?)
75
+
76
+ dependencies = dynamic_pod_targets.select(&:should_build?).flat_map(&:dependencies)
77
+ depended_upon_targets = dynamic_pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
78
+
79
+ static_libs = depended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
80
+ unless static_libs.empty?
81
+ raise Informative, "The '#{aggregate_target.label}' target has " \
82
+ "transitive dependencies that include static binaries: (#{static_libs.to_sentence})"
83
+ end
84
+
85
+ static_framework_deps = dynamic_pod_targets.select(&:should_build?).flat_map(&:recursive_dependent_targets).select(&:static_framework?)
86
+ unless static_framework_deps.empty?
87
+ raise Informative, "The '#{aggregate_target.label}' target has " \
88
+ "transitive dependencies that include static frameworks: (#{static_framework_deps.flat_map(&:name).to_sentence})"
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def verify_swift_pods_swift_version
95
+ error_message_for_target_definition = lambda do |target_definition|
96
+ "`#{target_definition.name}` (Swift #{target_definition.swift_version})"
97
+ end
98
+ swift_pod_targets = pod_targets.select(&:uses_swift?)
99
+ error_messages = swift_pod_targets.map do |pod_target|
100
+ next unless pod_target.spec_swift_version.nil?
101
+ swift_target_definitions = pod_target.target_definitions.reject { |target| target.swift_version.blank? }
102
+ next if swift_target_definitions.uniq(&:swift_version).count == 1
103
+ if swift_target_definitions.empty?
104
+ "- `#{pod_target.name}` does not specify a Swift version and none of the targets " \
105
+ "(#{pod_target.target_definitions.map { |td| "`#{td.name}`" }.to_sentence}) integrating it have the " \
106
+ '`SWIFT_VERSION` attribute set. Please contact the author or set the `SWIFT_VERSION` attribute in at ' \
107
+ 'least one of the targets that integrate this pod.'
108
+ else
109
+ target_errors = swift_target_definitions.map(&error_message_for_target_definition).to_sentence
110
+ "- `#{pod_target.name}` is integrated by multiple targets that use a different Swift version: #{target_errors}."
111
+ end
112
+ end.compact
113
+
114
+ unless error_messages.empty?
115
+ raise Informative, "Unable to determine Swift version for the following pods:\n\n #{error_messages.join('\n')}"
116
+ end
117
+ end
118
+
119
+ def verify_swift_pods_have_module_dependencies
120
+ error_messages = []
121
+ pod_targets.each do |pod_target|
122
+ next unless pod_target.uses_swift?
123
+
124
+ non_module_dependencies = []
125
+ pod_target.dependent_targets.each do |dependent_target|
126
+ next if !dependent_target.should_build? || dependent_target.defines_module?
127
+ non_module_dependencies << dependent_target.name
128
+ end
129
+
130
+ next if non_module_dependencies.empty?
131
+
132
+ error_messages << "The Swift pod `#{pod_target.name}` depends upon #{non_module_dependencies.map { |d| "`#{d}`" }.to_sentence}, " \
133
+ 'which do not define modules. ' \
134
+ 'To opt into those targets generating module maps '\
135
+ '(which is necessary to import them from Swift when building as static libraries), ' \
136
+ 'you may set `use_modular_headers!` globally in your Podfile, '\
137
+ 'or specify `:modular_headers => true` for particular dependencies.'
138
+ end
139
+ return if error_messages.empty?
140
+
141
+ raise Informative, 'The following Swift pods cannot yet be integrated '\
142
+ "as static libraries:\n\n#{error_messages.join("\n\n")}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,33 @@
1
+ # rubocop:disable Style/FileName
2
+
3
+ require 'open-uri'
4
+
5
+ # Allow OpenURI to follow http to https redirects.
6
+ #
7
+ module OpenURI
8
+ # Whether {#open} should follow a redirect.
9
+ #
10
+ # Inspiration from: https://gist.github.com/1271420
11
+ # Relevant issue: http://redmine.ruby-lang.org/issues/3719
12
+ # Source here: https://github.com/ruby/ruby/blob/trunk/lib/open-uri.rb
13
+ #
14
+ # This test is intended to forbid a redirection from http://... to
15
+ # file:///etc/passwd, file:///dev/zero, etc. CVE-2011-1521
16
+ # https to http redirect is also forbidden intentionally.
17
+ # It avoids sending secure cookie or referrer by non-secure HTTP protocol.
18
+ # (RFC 2109 4.3.1, RFC 2965 3.3, RFC 2616 15.1.3)
19
+ # However this is ad hoc. It should be extensible/configurable.
20
+ #
21
+ # @param [URI::Generic] uri1
22
+ # the origin uri from where the redirect origins
23
+ #
24
+ # @param [URI::Generic] uri2
25
+ # the target uri where to where the redirect points to
26
+ #
27
+ # @return [Bool]
28
+ #
29
+ def self.redirectable?(uri1, uri2)
30
+ uri1.scheme.downcase == uri2.scheme.downcase ||
31
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:https?|ftp)\z/i =~ uri2.scheme)
32
+ end
33
+ end
@@ -0,0 +1,414 @@
1
+ require 'xcodeproj'
2
+ require 'active_support/core_ext/string/inflections'
3
+
4
+ module Pod
5
+ # The Pods project.
6
+ #
7
+ # Model class which provides helpers for working with the Pods project
8
+ # through the installation process.
9
+ #
10
+ class Project < Xcodeproj::Project
11
+ # @return [PBXGroup] The group for the support files of the aggregate
12
+ # targets.
13
+ #
14
+ attr_reader :support_files_group
15
+
16
+ # @return [PBXGroup] The group for the Pods.
17
+ #
18
+ attr_reader :pods
19
+
20
+ # @return [PBXGroup] The group for Development Pods.
21
+ #
22
+ attr_reader :development_pods
23
+
24
+ # Initialize a new instance
25
+ #
26
+ # @param [Pathname, String] path @see #path
27
+ # @param [Bool] skip_initialization
28
+ # Whether the project should be initialized from scratch.
29
+ # @param [Int] object_version
30
+ # Object version to use for serialization, defaults to Xcode 3.2 compatible.
31
+ #
32
+ def initialize(path, skip_initialization = false,
33
+ object_version = Xcodeproj::Constants::DEFAULT_OBJECT_VERSION)
34
+ super(path, skip_initialization, object_version)
35
+ @support_files_group = new_group('Targets Support Files')
36
+ @refs_by_absolute_path = {}
37
+ @variant_groups_by_path_and_name = {}
38
+ @pods = new_group('Pods')
39
+ @development_pods = new_group('Development Pods')
40
+ self.symroot = LEGACY_BUILD_ROOT
41
+ end
42
+
43
+ # Generates a list of new UUIDs that created objects can be assigned.
44
+ #
45
+ # @note Overridden to generate UUIDs in a much faster way, since we don't need to check for collisions
46
+ # (as the Pods project is regenerated each time, and thus all UUIDs will have come from this method)
47
+ #
48
+ # @param [Integer] count
49
+ # The number of UUIDs to generate
50
+ #
51
+ # @return [Void]
52
+ #
53
+ def generate_available_uuid_list(count = 100)
54
+ start = @generated_uuids.size
55
+ uniques = Array.new(count) { |i| format('%011X0', start + i) }
56
+ @generated_uuids += uniques
57
+ @available_uuids += uniques
58
+ end
59
+
60
+ public
61
+
62
+ # @!group Legacy Xcode build root
63
+ #-------------------------------------------------------------------------#
64
+
65
+ LEGACY_BUILD_ROOT = '${SRCROOT}/../build'
66
+
67
+ # @param [String] symroot
68
+ # The build root that is used when Xcode is configured to not use the
69
+ # workspace’s build root. Defaults to `${SRCROOT}/../build`.
70
+ #
71
+ # @return [void]
72
+ #
73
+ def symroot=(symroot)
74
+ root_object.build_configuration_list.build_configurations.each do |config|
75
+ config.build_settings['SYMROOT'] = symroot
76
+ end
77
+ end
78
+
79
+ public
80
+
81
+ # @!group Pod Groups
82
+ #-------------------------------------------------------------------------#
83
+
84
+ # Creates a new group for the Pod with the given name and configures its
85
+ # path.
86
+ #
87
+ # @param [String] pod_name
88
+ # The name of the Pod.
89
+ #
90
+ # @param [#to_s] path
91
+ # The path to the root of the Pod.
92
+ #
93
+ # @param [Bool] development
94
+ # Wether the group should be added to the Development Pods group.
95
+ #
96
+ # @param [Bool] absolute
97
+ # Wether the path of the group should be set as absolute.
98
+ #
99
+ # @return [PBXGroup] The new group.
100
+ #
101
+ def add_pod_group(pod_name, path, development = false, absolute = false)
102
+ raise '[BUG]' if pod_group(pod_name)
103
+
104
+ parent_group = development ? development_pods : pods
105
+ source_tree = absolute ? :absolute : :group
106
+
107
+ group = parent_group.new_group(pod_name, path, source_tree)
108
+ group
109
+ end
110
+
111
+ # @return [Array<PBXGroup>] Returns all the group of the Pods.
112
+ #
113
+ def pod_groups
114
+ pods.children.objects + development_pods.children.objects
115
+ end
116
+
117
+ # Returns the group for the Pod with the given name.
118
+ #
119
+ # @param [String] pod_name
120
+ # The name of the Pod.
121
+ #
122
+ # @return [PBXGroup] The group.
123
+ #
124
+ def pod_group(pod_name)
125
+ pod_groups.find { |group| group.name == pod_name }
126
+ end
127
+
128
+ # @return [Hash] The names of the specification subgroups by key.
129
+ #
130
+ SPEC_SUBGROUPS = {
131
+ :resources => 'Resources',
132
+ :frameworks => 'Frameworks',
133
+ :developer => 'Pod',
134
+ }
135
+
136
+ # Returns the group for the specification with the give name creating it if
137
+ # needed.
138
+ #
139
+ # @param [String] spec_name
140
+ # The full name of the specification.
141
+ #
142
+ # @return [PBXGroup] The group.
143
+ #
144
+ def group_for_spec(spec_name, subgroup_key = nil)
145
+ pod_name = Specification.root_name(spec_name)
146
+ group = pod_group(pod_name)
147
+ raise "[Bug] Unable to locate group for Pod named `#{pod_name}`" unless group
148
+ if spec_name != pod_name
149
+ subspecs_names = spec_name.gsub(pod_name + '/', '').split('/')
150
+ subspecs_names.each do |name|
151
+ group = group[name] || group.new_group(name)
152
+ end
153
+ end
154
+
155
+ if subgroup_key
156
+ subgroup_name = SPEC_SUBGROUPS[subgroup_key]
157
+ raise ArgumentError, "Unrecognized subgroup key `#{subgroup_key}`" unless subgroup_name
158
+ group = group[subgroup_name] || group.new_group(subgroup_name)
159
+ end
160
+
161
+ group
162
+ end
163
+
164
+ # Returns the support files group for the Pod with the given name.
165
+ #
166
+ # @param [String] pod_name
167
+ # The name of the Pod.
168
+ #
169
+ # @return [PBXGroup] The group.
170
+ #
171
+ def pod_support_files_group(pod_name, dir)
172
+ group = pod_group(pod_name)
173
+ support_files_group = group['Support Files']
174
+ unless support_files_group
175
+ support_files_group = group.new_group('Support Files', dir)
176
+ end
177
+ support_files_group
178
+ end
179
+
180
+ public
181
+
182
+ # @!group File references
183
+ #-------------------------------------------------------------------------#
184
+
185
+ # Adds a file reference to given path as a child of the given group.
186
+ #
187
+ # @param [Array<Pathname,String>] absolute_path
188
+ # The path of the file.
189
+ #
190
+ # @param [PBXGroup] group
191
+ # The group for the new file reference.
192
+ #
193
+ # @param [Bool] reflect_file_system_structure
194
+ # Whether group structure should reflect the file system structure.
195
+ # If yes, where needed, intermediate groups are created, similar to
196
+ # how mkdir -p operates.
197
+ #
198
+ # @param [Pathname] base_path
199
+ # The base path for newly created groups when reflect_file_system_structure is true.
200
+ # If nil, the provided group's real_path is used.
201
+ #
202
+ # @return [PBXFileReference] The new file reference.
203
+ #
204
+ def add_file_reference(absolute_path, group, reflect_file_system_structure = false, base_path = nil)
205
+ file_path_name = absolute_path.is_a?(Pathname) ? absolute_path : Pathname(absolute_path)
206
+ if ref = reference_for_path(file_path_name)
207
+ return ref
208
+ end
209
+
210
+ group = group_for_path_in_group(file_path_name, group, reflect_file_system_structure, base_path)
211
+ ref = group.new_file(file_path_name.realpath)
212
+ @refs_by_absolute_path[file_path_name.to_s] = ref
213
+ end
214
+
215
+ # Returns the file reference for the given absolute path.
216
+ #
217
+ # @param [#to_s] absolute_path
218
+ # The absolute path of the file whose reference is needed.
219
+ #
220
+ # @return [PBXFileReference] The file reference.
221
+ # @return [Nil] If no file reference could be found.
222
+ #
223
+ def reference_for_path(absolute_path)
224
+ absolute_path = absolute_path.is_a?(Pathname) ? absolute_path : Pathname(absolute_path)
225
+ unless absolute_path.absolute?
226
+ raise ArgumentError, "Paths must be absolute #{absolute_path}"
227
+ end
228
+
229
+ refs_by_absolute_path[absolute_path.to_s] ||= refs_by_absolute_path[absolute_path.realpath.to_s]
230
+ end
231
+
232
+ # Adds a file reference to the Podfile.
233
+ #
234
+ # @param [#to_s] podfile_path
235
+ # The path of the Podfile.
236
+ #
237
+ # @return [PBXFileReference] The new file reference.
238
+ #
239
+ def add_podfile(podfile_path)
240
+ new_file(podfile_path, :project).tap do |podfile_ref|
241
+ mark_ruby_file_ref(podfile_ref)
242
+ end
243
+ end
244
+
245
+ # Sets the syntax of the provided file reference to be Ruby, in the case that
246
+ # the file does not already have a ".rb" file extension (ex. the Podfile)
247
+ #
248
+ # @param [PBXFileReference] file_ref
249
+ # The file reference to change
250
+ #
251
+ def mark_ruby_file_ref(file_ref)
252
+ file_ref.xc_language_specification_identifier = 'xcode.lang.ruby'
253
+ file_ref.explicit_file_type = 'text.script.ruby'
254
+ file_ref.last_known_file_type = 'text'
255
+ end
256
+
257
+ # Adds a new build configuration to the project and populates it with
258
+ # default settings according to the provided type.
259
+ #
260
+ # @note This method extends the original Xcodeproj implementation to
261
+ # include a preprocessor definition named after the build
262
+ # setting. This is done to support the TargetEnvironmentHeader
263
+ # specification of Pods available only on certain build
264
+ # configurations.
265
+ #
266
+ # @param [String] name
267
+ # The name of the build configuration.
268
+ #
269
+ # @param [Symbol] type
270
+ # The type of the build configuration used to populate the build
271
+ # settings, must be :debug or :release.
272
+ #
273
+ # @return [XCBuildConfiguration] The new build configuration.
274
+ #
275
+ def add_build_configuration(name, type)
276
+ build_configuration = super
277
+ settings = build_configuration.build_settings
278
+ definitions = settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)']
279
+ defines = [defininition_for_build_configuration(name)]
280
+ defines << 'DEBUG' if type == :debug
281
+ defines.each do |define|
282
+ value = "#{define}=1"
283
+ unless definitions.include?(value)
284
+ definitions.unshift(value)
285
+ end
286
+ end
287
+ settings['GCC_PREPROCESSOR_DEFINITIONS'] = definitions
288
+
289
+ if type == :debug
290
+ settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = 'DEBUG'
291
+ end
292
+
293
+ build_configuration
294
+ end
295
+
296
+ # @param [String] name
297
+ # The name of the build configuration.
298
+ #
299
+ # @return [String] The preprocessor definition to set for the configuration.
300
+ #
301
+ def defininition_for_build_configuration(name)
302
+ "POD_CONFIGURATION_#{name.underscore}".gsub(/[^a-zA-Z0-9_]/, '_').upcase
303
+ end
304
+
305
+ private
306
+
307
+ # @!group Private helpers
308
+ #-------------------------------------------------------------------------#
309
+
310
+ # @return [Hash{String => PBXFileReference}] The file references grouped
311
+ # by absolute path.
312
+ #
313
+ attr_reader :refs_by_absolute_path
314
+
315
+ # @return [Hash{[Pathname, String] => PBXVariantGroup}] The variant groups
316
+ # grouped by absolute path of parent dir and name.
317
+ #
318
+ attr_reader :variant_groups_by_path_and_name
319
+
320
+ # Returns the group for an absolute file path in another group.
321
+ # Creates subgroups to reflect the file system structure if
322
+ # reflect_file_system_structure is set to true.
323
+ # Makes a variant group if the path points to a localized file inside a
324
+ # *.lproj directory. To support Apple Base Internationalization, the same
325
+ # variant group is returned for interface files and strings files with
326
+ # the same name.
327
+ #
328
+ # @param [Pathname] absolute_pathname
329
+ # The pathname of the file to get the group for.
330
+ #
331
+ # @param [PBXGroup] group
332
+ # The parent group used as the base of the relative path.
333
+ #
334
+ # @param [Bool] reflect_file_system_structure
335
+ # Whether group structure should reflect the file system structure.
336
+ # If yes, where needed, intermediate groups are created, similar to
337
+ # how mkdir -p operates.
338
+ #
339
+ # @param [Pathname] base_path
340
+ # The base path for the newly created group. If nil, the provided group's real_path is used.
341
+ #
342
+ # @return [PBXGroup] The appropriate group for the filepath.
343
+ # Can be PBXVariantGroup, if the file is localized.
344
+ #
345
+ def group_for_path_in_group(absolute_pathname, group, reflect_file_system_structure, base_path = nil)
346
+ unless absolute_pathname.absolute?
347
+ raise ArgumentError, "Paths must be absolute #{absolute_pathname}"
348
+ end
349
+ unless base_path.nil? || base_path.absolute?
350
+ raise ArgumentError, "Paths must be absolute #{base_path}"
351
+ end
352
+
353
+ relative_base = base_path.nil? ? group.real_path : base_path.realdirpath
354
+ relative_pathname = absolute_pathname.relative_path_from(relative_base)
355
+ relative_dir = relative_pathname.dirname
356
+
357
+ # Add subgroups for directories, but treat .lproj as a file
358
+ if reflect_file_system_structure
359
+ path = relative_base
360
+ relative_dir.each_filename do |name|
361
+ break if name.to_s.downcase.include? '.lproj'
362
+ next if name == '.'
363
+ # Make sure groups have the correct absolute path set, as intermittent
364
+ # directories may not be included in the group structure
365
+ path += name
366
+ group = group.children.find { |c| c.display_name == name } || group.new_group(name, path)
367
+ end
368
+ end
369
+
370
+ # Turn files inside .lproj directories into a variant group
371
+ if relative_dir.basename.to_s.downcase.include? '.lproj'
372
+ group_name = variant_group_name(absolute_pathname)
373
+ lproj_parent_dir = absolute_pathname.dirname.dirname
374
+ group = @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||=
375
+ group.new_variant_group(group_name, lproj_parent_dir)
376
+ end
377
+
378
+ group
379
+ end
380
+
381
+ # Returns the name to be used for a the variant group for a file at a given path.
382
+ # The path must be localized (within an *.lproj directory).
383
+ #
384
+ # @param [Pathname] The localized path to get a variant group name for.
385
+ #
386
+ # @return [String] The variant group name.
387
+ #
388
+ def variant_group_name(path)
389
+ unless path.to_s.downcase.include?('.lproj/')
390
+ raise ArgumentError, 'Only localized resources can be added to variant groups.'
391
+ end
392
+
393
+ # When using Base Internationalization for XIBs and Storyboards a strings
394
+ # file is generated with the same name as the XIB/Storyboard in each .lproj
395
+ # directory:
396
+ # Base.lproj/MyViewController.xib
397
+ # fr.lproj/MyViewController.strings
398
+ #
399
+ # In this scenario we want the variant group to be the same as the XIB or Storyboard.
400
+ #
401
+ # Base Internationalization: https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html
402
+ if path.extname.downcase == '.strings'
403
+ %w(.xib .storyboard).each do |extension|
404
+ possible_interface_file = path.dirname.dirname + 'Base.lproj' + path.basename.sub_ext(extension)
405
+ return possible_interface_file.basename.to_s if possible_interface_file.exist?
406
+ end
407
+ end
408
+
409
+ path.basename.to_s
410
+ end
411
+
412
+ #-------------------------------------------------------------------------#
413
+ end
414
+ end