cocoapods 0.37.2 → 0.38.0.beta.1

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +136 -1
  3. data/lib/cocoapods.rb +0 -5
  4. data/lib/cocoapods/command.rb +3 -0
  5. data/lib/cocoapods/command/cache.rb +28 -0
  6. data/lib/cocoapods/command/cache/clean.rb +90 -0
  7. data/lib/cocoapods/command/cache/list.rb +69 -0
  8. data/lib/cocoapods/command/lib.rb +11 -4
  9. data/lib/cocoapods/command/list.rb +4 -4
  10. data/lib/cocoapods/command/outdated.rb +1 -10
  11. data/lib/cocoapods/command/project.rb +3 -2
  12. data/lib/cocoapods/command/spec.rb +0 -17
  13. data/lib/cocoapods/command/spec/cat.rb +1 -1
  14. data/lib/cocoapods/command/spec/create.rb +1 -0
  15. data/lib/cocoapods/command/spec/edit.rb +1 -1
  16. data/lib/cocoapods/command/spec/lint.rb +10 -4
  17. data/lib/cocoapods/config.rb +6 -0
  18. data/lib/cocoapods/downloader/cache.rb +48 -1
  19. data/lib/cocoapods/executable.rb +27 -6
  20. data/lib/cocoapods/gem_version.rb +1 -1
  21. data/lib/cocoapods/generator/copy_resources_script.rb +1 -0
  22. data/lib/cocoapods/generator/embed_frameworks_script.rb +23 -28
  23. data/lib/cocoapods/generator/header.rb +5 -1
  24. data/lib/cocoapods/generator/umbrella_header.rb +1 -1
  25. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +139 -33
  26. data/lib/cocoapods/generator/xcconfig/private_pod_xcconfig.rb +2 -2
  27. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +3 -3
  28. data/lib/cocoapods/installer.rb +64 -109
  29. data/lib/cocoapods/installer/analyzer.rb +167 -336
  30. data/lib/cocoapods/installer/analyzer/analysis_result.rb +46 -0
  31. data/lib/cocoapods/installer/analyzer/specs_state.rb +76 -0
  32. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +41 -0
  33. data/lib/cocoapods/installer/analyzer/target_inspector.rb +203 -0
  34. data/lib/cocoapods/installer/file_references_installer.rb +48 -13
  35. data/lib/cocoapods/installer/podfile_validator.rb +86 -0
  36. data/lib/cocoapods/installer/{hooks_context.rb → post_install_hooks_context.rb} +3 -3
  37. data/lib/cocoapods/installer/pre_install_hooks_context.rb +41 -0
  38. data/lib/cocoapods/installer/target_installer.rb +1 -7
  39. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +15 -17
  40. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +4 -4
  41. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +16 -16
  42. data/lib/cocoapods/sandbox/file_accessor.rb +20 -2
  43. data/lib/cocoapods/sandbox/path_list.rb +15 -13
  44. data/lib/cocoapods/sandbox/podspec_finder.rb +1 -0
  45. data/lib/cocoapods/sources_manager.rb +2 -0
  46. data/lib/cocoapods/target.rb +7 -37
  47. data/lib/cocoapods/target/aggregate_target.rb +25 -1
  48. data/lib/cocoapods/target/pod_target.rb +106 -10
  49. data/lib/cocoapods/user_interface.rb +26 -0
  50. data/lib/cocoapods/user_interface/error_report.rb +6 -0
  51. data/lib/cocoapods/validator.rb +22 -0
  52. metadata +21 -16
  53. data/lib/cocoapods/generator/target_environment_header.rb +0 -171
  54. data/lib/cocoapods/hooks/installer_representation.rb +0 -133
  55. data/lib/cocoapods/hooks/library_representation.rb +0 -93
  56. data/lib/cocoapods/hooks/pod_representation.rb +0 -70
@@ -0,0 +1,86 @@
1
+ module Pod
2
+ class Installer
3
+ # Validate the podfile before installing to catch errors and
4
+ # problems
5
+ #
6
+ class PodfileValidator
7
+ # @return [Podfile] The podfile being validated
8
+ #
9
+ attr_reader :podfile
10
+
11
+ # @return [Array<String>] any errors that have occured during the validation
12
+ #
13
+ attr_reader :errors
14
+
15
+ # Initialize a new instance
16
+ # @param [Podfile] podfile
17
+ # The podfile to validate
18
+ #
19
+ def initialize(podfile)
20
+ @podfile = podfile
21
+ @errors = []
22
+ @validated = false
23
+ end
24
+
25
+ # Validate the podfile
26
+ # Errors are added to the errors array
27
+ #
28
+ def validate
29
+ validate_pod_directives
30
+
31
+ @validated = true
32
+ end
33
+
34
+ # Wether the podfile is valid is not
35
+ # NOTE: Will execute `validate` if the podfile
36
+ # has not yet been validated
37
+ #
38
+ def valid?
39
+ validate unless @validated
40
+
41
+ @validated && errors.size == 0
42
+ end
43
+
44
+ # A message describing any errors in the
45
+ # validation
46
+ #
47
+ def message
48
+ errors.join("\n")
49
+ end
50
+
51
+ private
52
+
53
+ def add_error(error)
54
+ errors << error
55
+ end
56
+
57
+ def validate_pod_directives
58
+ dependencies = podfile.target_definitions.flat_map do |_, target|
59
+ target.dependencies
60
+ end.uniq
61
+
62
+ dependencies.each do |dependency|
63
+ validate_conflicting_external_sources!(dependency)
64
+ end
65
+ end
66
+
67
+ def validate_conflicting_external_sources!(dependency)
68
+ external_source = dependency.external_source
69
+ return false if external_source.nil?
70
+
71
+ available_downloaders = Downloader.downloader_class_by_key.keys
72
+ specified_downloaders = external_source.select { |key| available_downloaders.include?(key) }
73
+ if specified_downloaders.size > 1
74
+ add_error "The dependency `#{dependency.name}` specifies more than one download strategy(#{specified_downloaders.keys.join(',')})." \
75
+ 'Only one is allowed'
76
+ end
77
+
78
+ pod_spec_or_path = external_source[:podspec].present? || external_source[:path].present?
79
+ if pod_spec_or_path && specified_downloaders.size > 0
80
+ add_error "The dependency `#{dependency.name}` specifies `podspec` or `path` in combination with other" \
81
+ ' download strategies. This is not allowed'
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -3,7 +3,7 @@ module Pod
3
3
  # Context object designed to be used with the HooksManager which describes
4
4
  # the context of the installer.
5
5
  #
6
- class HooksContext
6
+ class PostInstallHooksContext
7
7
  # @return [String] The path to the sandbox root (`Pods` directory).
8
8
  #
9
9
  attr_accessor :sandbox_root
@@ -13,7 +13,7 @@ module Pod
13
13
  #
14
14
  attr_accessor :umbrella_targets
15
15
 
16
- # Generate a {HooksContext}.
16
+ # @return [PostInstallHooksContext] Convenience class generator method
17
17
  #
18
18
  # @param [Sandbox] sandbox
19
19
  # The sandbox
@@ -66,7 +66,7 @@ module Pod
66
66
  #
67
67
  attr_accessor :specs
68
68
 
69
- # @return [Symbol] The platform (either `:ios` or `:osx`).
69
+ # @return [Symbol] The platform (either `:ios`, `:watchos` or `:osx`).
70
70
  #
71
71
  attr_accessor :platform_name
72
72
 
@@ -0,0 +1,41 @@
1
+ module Pod
2
+ class Installer
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer before analysis has been completed.
5
+ #
6
+ class PreInstallHooksContext
7
+ # @return [String] The path to the sandbox root (`Pods` directory).
8
+ #
9
+ attr_accessor :sandbox_root
10
+
11
+ # @return [Podfile] The Podfile for the project.
12
+ #
13
+ attr_accessor :podfile
14
+
15
+ # @return [Sandbox] The Sandbox for the project.
16
+ #
17
+ attr_accessor :sandbox
18
+
19
+ # @return [Lockfile] The Lockfile for the project.
20
+ #
21
+ attr_accessor :lockfile
22
+
23
+ # @param [Sandbox] sandbox see {#sandbox}
24
+ #
25
+ # @param [Podfile] podfile see {#podfile}
26
+ #
27
+ # @param [Lockfile] lockfile see {#lockfile}
28
+ #
29
+ # @return [PreInstallHooksContext] Convenience class method to generate the
30
+ # static context.
31
+ #
32
+ def self.generate(sandbox, podfile, lockfile)
33
+ result = new
34
+ result.podfile = podfile
35
+ result.sandbox = sandbox
36
+ result.lockfile = lockfile
37
+ result
38
+ end
39
+ end
40
+ end
41
+ end
@@ -67,7 +67,7 @@ module Pod
67
67
  def custom_build_settings
68
68
  settings = {}
69
69
 
70
- if target.archs
70
+ unless target.archs.empty?
71
71
  settings['ARCHS'] = target.archs
72
72
  end
73
73
 
@@ -184,12 +184,6 @@ module Pod
184
184
  sandbox.project
185
185
  end
186
186
 
187
- # @return [TargetDefinition] the target definition of the library.
188
- #
189
- def target_definition
190
- target.target_definition
191
- end
192
-
193
187
  # @return [PBXGroup] the group where the file references to the support
194
188
  # files should be stored.
195
189
  #
@@ -20,7 +20,6 @@ module Pod
20
20
  create_umbrella_header
21
21
  create_embed_frameworks_script
22
22
  end
23
- create_target_environment_header
24
23
  create_bridge_support_file
25
24
  create_copy_resources_script
26
25
  create_acknowledgements
@@ -32,6 +31,12 @@ module Pod
32
31
 
33
32
  private
34
33
 
34
+ # @return [TargetDefinition] the target definition of the library.
35
+ #
36
+ def target_definition
37
+ target.target_definition
38
+ end
39
+
35
40
  # Ensure that vendored static frameworks and libraries are not linked
36
41
  # twice to the aggregate target, which shares the xcconfig of the user
37
42
  # target.
@@ -73,16 +78,6 @@ module Pod
73
78
  end
74
79
  end
75
80
 
76
- # Generates a header which allows to inspect at compile time the installed
77
- # pods and the installed specifications of a pod.
78
- #
79
- def create_target_environment_header
80
- path = target.target_environment_header_path
81
- generator = Generator::TargetEnvironmentHeader.new(target.specs_by_build_configuration)
82
- generator.save_as(path)
83
- add_file_to_support_group(path)
84
- end
85
-
86
81
  # Generates the bridge support metadata if requested by the {Podfile}.
87
82
  #
88
83
  # @note The bridge support metadata is added to the resources of the
@@ -92,7 +87,7 @@ module Pod
92
87
  # @return [void]
93
88
  #
94
89
  def create_bridge_support_file
95
- if target_definition.podfile.generate_bridge_support?
90
+ if target.podfile.generate_bridge_support?
96
91
  path = target.bridge_support_path
97
92
  headers = native_target.headers_build_phase.files.map { |bf| sandbox.root + bf.file_ref.path }
98
93
  generator = Generator::BridgeSupport.new(headers)
@@ -112,7 +107,7 @@ module Pod
112
107
  end
113
108
  resources_by_config = {}
114
109
  target.user_build_configurations.keys.each do |config|
115
- file_accessors = library_targets.select { |t| t.include_in_build_config?(config) }.flat_map(&:file_accessors)
110
+ file_accessors = library_targets.select { |t| t.include_in_build_config?(target_definition, config) }.flat_map(&:file_accessors)
116
111
  resource_paths = file_accessors.flat_map { |accessor| accessor.resources.flat_map { |res| res.relative_path_from(project.path.dirname) } }
117
112
  resource_bundles = file_accessors.flat_map { |accessor| accessor.resource_bundles.keys.map { |name| "${BUILT_PRODUCTS_DIR}/#{name.shellescape}.bundle" } }
118
113
  resources_by_config[config] = (resource_paths + resource_bundles).uniq
@@ -149,11 +144,14 @@ module Pod
149
144
  path = target.embed_frameworks_script_path
150
145
  frameworks_by_config = {}
151
146
  target.user_build_configurations.keys.each do |config|
152
- frameworks_by_config[config] = target.pod_targets.select do |pod_target|
153
- pod_target.include_in_build_config?(config) && pod_target.should_build?
154
- end.map(&:product_name)
147
+ relevant_pod_targets = target.pod_targets.select do |pod_target|
148
+ pod_target.include_in_build_config?(target_definition, config) && pod_target.should_build?
149
+ end
150
+ frameworks_by_config[config] = relevant_pod_targets.map do |pod_target|
151
+ "#{target_definition.label}/#{pod_target.product_name}"
152
+ end
155
153
  end
156
- generator = Generator::EmbedFrameworksScript.new(target_definition, frameworks_by_config)
154
+ generator = Generator::EmbedFrameworksScript.new(frameworks_by_config)
157
155
  generator.save_as(path)
158
156
  add_file_to_support_group(path)
159
157
  end
@@ -130,7 +130,7 @@ module Pod
130
130
 
131
131
  bundle_target.build_configurations.each do |c|
132
132
  c.build_settings['PRODUCT_NAME'] = bundle_name
133
- if target.requires_frameworks?
133
+ if target.requires_frameworks? && target.scoped?
134
134
  c.build_settings['CONFIGURATION_BUILD_DIR'] = target.configuration_build_dir
135
135
  end
136
136
  end
@@ -174,7 +174,6 @@ module Pod
174
174
  def create_prefix_header
175
175
  path = target.prefix_header_path
176
176
  generator = Generator::PrefixHeader.new(target.file_accessors, target.platform)
177
- generator.imports << target.target_environment_header_path.basename
178
177
  generator.save_as(path)
179
178
  add_file_to_support_group(path)
180
179
 
@@ -187,6 +186,7 @@ module Pod
187
186
  ENABLE_OBJECT_USE_OBJC_FROM = {
188
187
  :ios => Version.new('6'),
189
188
  :osx => Version.new('10.8'),
189
+ :watchos => Version.new('2.0'),
190
190
  }
191
191
 
192
192
  # Returns the compiler flags for the source files of the given specification.
@@ -235,8 +235,8 @@ module Pod
235
235
  flags << '-DOS_OBJECT_USE_OBJC=0'
236
236
  end
237
237
  end
238
- if target_definition.inhibits_warnings_for_pod?(consumer.spec.root.name)
239
- flags << '-w -Xanalyzer -analyzer-disable-checker -Xanalyzer deadcode'
238
+ if target.inhibit_warnings?
239
+ flags << '-w -Xanalyzer -analyzer-disable-all-checks'
240
240
  end
241
241
  flags * ' '
242
242
  end
@@ -142,19 +142,13 @@ module Pod
142
142
  # @return [void]
143
143
  #
144
144
  def add_embed_frameworks_script_phase
145
- phase_name = 'Embed Pods Frameworks'
146
145
  targets_to_embed_in = native_targets_to_integrate.select do |target|
147
146
  EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
148
147
  end
149
148
  targets_to_embed_in.each do |native_target|
150
- embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == phase_name }
151
- unless embed_build_phase.present?
152
- UI.message("Adding Build Phase '#{phase_name}' to project.")
153
- embed_build_phase = native_target.new_shell_script_build_phase(phase_name)
154
- end
149
+ phase = create_or_update_build_phase(native_target, 'Embed Pods Frameworks')
155
150
  script_path = target.embed_frameworks_script_relative_path
156
- embed_build_phase.shell_script = %("#{script_path}"\n)
157
- embed_build_phase.show_env_vars_in_log = '0'
151
+ phase.shell_script = %("#{script_path}"\n)
158
152
  end
159
153
  end
160
154
 
@@ -189,11 +183,9 @@ module Pod
189
183
  def add_copy_resources_script_phase
190
184
  phase_name = 'Copy Pods Resources'
191
185
  native_targets_to_integrate.each do |native_target|
192
- phase = native_target.shell_script_build_phases.find { |bp| bp.name == phase_name }
193
- phase ||= native_target.new_shell_script_build_phase(phase_name)
186
+ phase = create_or_update_build_phase(native_target, phase_name)
194
187
  script_path = target.copy_resources_script_relative_path
195
188
  phase.shell_script = %("#{script_path}"\n)
196
- phase.show_env_vars_in_log = '0'
197
189
  end
198
190
  end
199
191
 
@@ -209,10 +201,8 @@ module Pod
209
201
  def add_check_manifest_lock_script_phase
210
202
  phase_name = 'Check Pods Manifest.lock'
211
203
  native_targets_to_integrate.each do |native_target|
212
- next if native_target.shell_script_build_phases.any? { |phase| phase.name == phase_name }
213
- phase = native_target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
214
- native_target.build_phases.unshift(phase)
215
- phase.name = phase_name
204
+ phase = create_or_update_build_phase(native_target, phase_name)
205
+ native_target.build_phases.unshift(phase).uniq!
216
206
  phase.shell_script = <<-EOS.strip_heredoc
217
207
  diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
218
208
  if [[ $? != 0 ]] ; then
@@ -222,7 +212,6 @@ module Pod
222
212
  exit 1
223
213
  fi
224
214
  EOS
225
- phase.show_env_vars_in_log = '0'
226
215
  end
227
216
  end
228
217
 
@@ -278,6 +267,17 @@ module Pod
278
267
  "Integrating target `#{target.name}` " \
279
268
  "(#{UI.path target.user_project_path} project)"
280
269
  end
270
+
271
+ def create_or_update_build_phase(target, phase_name, phase_class = Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
272
+ target.build_phases.grep(phase_class).find { |phase| phase.name == phase_name } ||
273
+ target.project.new(phase_class).tap do |phase|
274
+ UI.message("Adding Build Phase '#{phase_name}' to project.") do
275
+ phase.name = phase_name
276
+ phase.show_env_vars_in_log = '0'
277
+ target.build_phases << phase
278
+ end
279
+ end
280
+ end
281
281
  end
282
282
  end
283
283
  end
@@ -152,13 +152,31 @@ module Pod
152
152
  paths_for_attribute(:vendored_frameworks, true)
153
153
  end
154
154
 
155
+ # @param [Pathname] framework
156
+ # The vendored framework to search into.
157
+ # @return [Pathname] The path of the header directory of the
158
+ # vendored framework.
159
+ #
160
+ def self.vendored_frameworks_headers_dir(framework)
161
+ (framework + 'Headers').realpath
162
+ end
163
+
164
+ # @param [Pathname] framework
165
+ # The vendored framework to search into.
166
+ # @return [Array<Pathname>] The paths of the headers included in the
167
+ # vendored framework.
168
+ #
169
+ def self.vendored_frameworks_headers(framework)
170
+ headers_dir = vendored_frameworks_headers_dir(framework)
171
+ Pathname.glob(headers_dir + '**/' + GLOB_PATTERNS[:public_header_files])
172
+ end
173
+
155
174
  # @return [Array<Pathname>] The paths of the framework headers that come
156
175
  # shipped with the Pod.
157
176
  #
158
177
  def vendored_frameworks_headers
159
178
  vendored_frameworks.map do |framework|
160
- headers_dir = (framework + 'Headers').realpath
161
- Pathname.glob(headers_dir + GLOB_PATTERNS[:public_header_files])
179
+ self.class.vendored_frameworks_headers(framework)
162
180
  end.flatten.uniq
163
181
  end
164
182
 
@@ -21,6 +21,7 @@ module Pod
21
21
  #
22
22
  def initialize(root)
23
23
  @root = root
24
+ @glob_cache = {}
24
25
  end
25
26
 
26
27
  # @return [Array<String>] The list of absolute the path of all the files
@@ -55,6 +56,7 @@ module Pod
55
56
  relative_paths = absolute_paths.map { |p| p[root_length..-1] }
56
57
  @files = relative_paths - relative_dirs
57
58
  @dirs = relative_dirs.map { |d| d.gsub(/\/\.\.?$/, '') }.reject { |d| d == '.' || d == '..' } .uniq
59
+ @glob_cache = {}
58
60
  end
59
61
 
60
62
  #-----------------------------------------------------------------------#
@@ -101,6 +103,10 @@ module Pod
101
103
  def relative_glob(patterns, options = {})
102
104
  return [] if patterns.empty?
103
105
 
106
+ cache_key = options.merge(:patterns => patterns)
107
+ cached_value = @glob_cache[cache_key]
108
+ return cached_value if cached_value
109
+
104
110
  dir_pattern = options[:dir_pattern]
105
111
  exclude_patterns = options[:exclude_patterns]
106
112
  include_dirs = options[:include_dirs]
@@ -112,19 +118,15 @@ module Pod
112
118
  end
113
119
 
114
120
  list = Array(patterns).map do |pattern|
115
- if pattern.is_a?(String)
116
- if directory?(pattern) && dir_pattern
117
- pattern += '/' unless pattern.end_with?('/')
118
- pattern += dir_pattern
119
- end
120
- expanded_patterns = dir_glob_equivalent_patterns(pattern)
121
- full_list.select do |path|
122
- expanded_patterns.any? do |p|
123
- File.fnmatch(p, path, File::FNM_CASEFOLD | File::FNM_PATHNAME)
124
- end
121
+ if directory?(pattern) && dir_pattern
122
+ pattern += '/' unless pattern.end_with?('/')
123
+ pattern += dir_pattern
124
+ end
125
+ expanded_patterns = dir_glob_equivalent_patterns(pattern)
126
+ full_list.select do |path|
127
+ expanded_patterns.any? do |p|
128
+ File.fnmatch(p, path, File::FNM_CASEFOLD | File::FNM_PATHNAME)
125
129
  end
126
- else
127
- full_list.select { |path| path.match(pattern) }
128
130
  end
129
131
  end.flatten
130
132
 
@@ -133,7 +135,7 @@ module Pod
133
135
  exclude_options = { :dir_pattern => '**/*', :include_dirs => include_dirs }
134
136
  list -= relative_glob(exclude_patterns, exclude_options)
135
137
  end
136
- list
138
+ @glob_cache[cache_key] = list
137
139
  end
138
140
 
139
141
  #-----------------------------------------------------------------------#