cocoapods 1.10.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +261 -5
  3. data/README.md +11 -11
  4. data/lib/cocoapods/command/outdated.rb +12 -1
  5. data/lib/cocoapods/command/repo/push.rb +17 -0
  6. data/lib/cocoapods/command/spec/cat.rb +3 -1
  7. data/lib/cocoapods/command/spec/lint.rb +1 -1
  8. data/lib/cocoapods/command/spec/which.rb +3 -1
  9. data/lib/cocoapods/command/spec.rb +18 -9
  10. data/lib/cocoapods/config.rb +1 -1
  11. data/lib/cocoapods/downloader/cache.rb +95 -6
  12. data/lib/cocoapods/downloader.rb +4 -2
  13. data/lib/cocoapods/external_sources/podspec_source.rb +1 -1
  14. data/lib/cocoapods/gem_version.rb +1 -1
  15. data/lib/cocoapods/generator/acknowledgements.rb +1 -1
  16. data/lib/cocoapods/generator/app_target_helper.rb +7 -3
  17. data/lib/cocoapods/generator/copy_dsyms_script.rb +4 -4
  18. data/lib/cocoapods/generator/copy_xcframework_script.rb +4 -48
  19. data/lib/cocoapods/generator/embed_frameworks_script.rb +2 -1
  20. data/lib/cocoapods/generator/script_phase_constants.rb +1 -0
  21. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +31 -4
  22. data/lib/cocoapods/installer/analyzer.rb +12 -8
  23. data/lib/cocoapods/installer/podfile_validator.rb +2 -2
  24. data/lib/cocoapods/installer/pre_integrate_hooks_context.rb +9 -0
  25. data/lib/cocoapods/installer/project_cache/project_cache_analyzer.rb +9 -2
  26. data/lib/cocoapods/installer/project_cache/project_installation_cache.rb +15 -2
  27. data/lib/cocoapods/installer/project_cache/target_cache_key.rb +7 -4
  28. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +149 -9
  29. data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +10 -3
  30. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +25 -6
  31. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb +6 -19
  32. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +70 -58
  33. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +48 -6
  34. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +2 -2
  35. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +2 -5
  36. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +1 -1
  37. data/lib/cocoapods/installer.rb +52 -4
  38. data/lib/cocoapods/resolver.rb +4 -4
  39. data/lib/cocoapods/sandbox/file_accessor.rb +57 -10
  40. data/lib/cocoapods/sandbox/headers_store.rb +3 -1
  41. data/lib/cocoapods/sandbox/path_list.rb +1 -1
  42. data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +1 -1
  43. data/lib/cocoapods/sources_manager.rb +14 -8
  44. data/lib/cocoapods/target/aggregate_target.rb +23 -1
  45. data/lib/cocoapods/target/build_settings.rb +45 -20
  46. data/lib/cocoapods/target/pod_target.rb +47 -22
  47. data/lib/cocoapods/target.rb +1 -1
  48. data/lib/cocoapods/user_interface.rb +4 -0
  49. data/lib/cocoapods/validator.rb +25 -5
  50. data/lib/cocoapods/version_metadata.rb +1 -1
  51. data/lib/cocoapods/xcode/xcframework/xcframework_slice.rb +10 -1
  52. data/lib/cocoapods/xcode/xcframework.rb +8 -3
  53. metadata +26 -19
@@ -16,12 +16,14 @@ module Pod
16
16
  [
17
17
  ['--regex', 'Interpret the `QUERY` as a regular expression'],
18
18
  ['--show-all', 'Print all versions of the given podspec'],
19
+ ['--version', 'Print a specific version of the given podspec'],
19
20
  ].concat(super)
20
21
  end
21
22
 
22
23
  def initialize(argv)
23
24
  @use_regex = argv.flag?('regex')
24
25
  @show_all = argv.flag?('show-all')
26
+ @version = argv.option('version')
25
27
  @query = argv.shift_argument
26
28
  @query = @query.gsub('.podspec', '') unless @query.nil?
27
29
  super
@@ -35,7 +37,7 @@ module Pod
35
37
 
36
38
  def run
37
39
  query = @use_regex ? @query : Regexp.escape(@query)
38
- UI.puts get_path_of_spec(query, @show_all)
40
+ UI.puts get_path_of_spec(query, @show_all || @version)
39
41
  end
40
42
  end
41
43
  end
@@ -33,13 +33,14 @@ module Pod
33
33
  # @param [String] spec
34
34
  # The name of the specification.
35
35
  #
36
- # @param [Bool] show_all
37
- # Whether the paths for all the versions should be returned or
38
- # only the one for the last version.
36
+ # @param [Bool,String] version_filter
37
+ # - If set to false, will return only the spec path for the latest version (the default).
38
+ # - If set to true, will return a list of all paths of all the versions of that spec.
39
+ # - If set to a String, will return only the spec path for the version specified by that string.
39
40
  #
40
41
  # @return [Pathname] the absolute path or paths of the given podspec
41
42
  #
42
- def get_path_of_spec(spec, show_all = false)
43
+ def get_path_of_spec(spec, version_filter = false)
43
44
  sets = config.sources_manager.search_by_name(spec)
44
45
 
45
46
  if sets.count == 1
@@ -51,12 +52,14 @@ module Pod
51
52
  raise Informative, "More than one spec found for '#{spec}':\n#{names}"
52
53
  end
53
54
 
54
- unless show_all
55
+ if version_filter.is_a? String
56
+ all_paths_from_set(set, version_filter).split(/\n/).first
57
+ elsif version_filter == true
58
+ all_paths_from_set(set)
59
+ else
55
60
  best_spec, spec_source = spec_and_source_from_set(set)
56
- return pathname_from_spec(best_spec, spec_source)
61
+ pathname_from_spec(best_spec, spec_source)
57
62
  end
58
-
59
- all_paths_from_set(set)
60
63
  end
61
64
 
62
65
  # @return [Pathname] the absolute path of the given spec and source
@@ -67,7 +70,7 @@ module Pod
67
70
 
68
71
  # @return [String] of spec paths one on each line
69
72
  #
70
- def all_paths_from_set(set)
73
+ def all_paths_from_set(set, specific_version = nil)
71
74
  paths = ''
72
75
 
73
76
  sources = set.sources
@@ -75,12 +78,18 @@ module Pod
75
78
  sources.each do |source|
76
79
  versions = source.versions(set.name)
77
80
 
81
+ if specific_version
82
+ versions = versions.select { |v| v.version == specific_version }
83
+ end
84
+
78
85
  versions.each do |version|
79
86
  spec = source.specification(set.name, version)
80
87
  paths += "#{pathname_from_spec(spec, source)}\n"
81
88
  end
82
89
  end
83
90
 
91
+ raise Informative, "Can't find spec for #{set.name}." if paths.empty?
92
+
84
93
  paths
85
94
  end
86
95
 
@@ -163,7 +163,7 @@ module Pod
163
163
  #
164
164
  def installation_root
165
165
  @installation_root ||= begin
166
- current_dir = Pathname.new(ActiveSupport::Multibyte::Unicode.normalize(Dir.pwd))
166
+ current_dir = Pathname.new(Dir.pwd.unicode_normalize(:nfkc))
167
167
  current_path = current_dir
168
168
  until current_path.root?
169
169
  if podfile_path_in_dir(current_path)
@@ -69,6 +69,91 @@ module Pod
69
69
  end
70
70
  end
71
71
 
72
+ # Convenience method for acquiring a shared lock to safely read from the
73
+ # cache. See `Cache.lock` for more details.
74
+ #
75
+ # @param [Pathname] location
76
+ # the path to require a lock for.
77
+ #
78
+ # @param [block] &block
79
+ # the block to execute inside the lock.
80
+ #
81
+ # @return [void]
82
+ #
83
+ def self.read_lock(location, &block)
84
+ Cache.lock(location, File::LOCK_SH, &block)
85
+ end
86
+
87
+ # Convenience method for acquiring an exclusive lock to safely write to
88
+ # the cache. See `Cache.lock` for more details.
89
+ #
90
+ # @param [Pathname] location
91
+ # the path to require a lock for.
92
+ #
93
+ # @param [block] &block
94
+ # the block to execute inside the lock.
95
+ #
96
+ # @return [void]
97
+ #
98
+ def self.write_lock(location, &block)
99
+ Cache.lock(location, File::LOCK_EX, &block)
100
+ end
101
+
102
+ # Creates a .lock file at `location`, aquires a lock of type
103
+ # `lock_type`, checks that it is valid, and executes passed block while
104
+ # holding on to that lock. Afterwards, the .lock file is deleted, which is
105
+ # why validation of the lock is necessary, as you might have a lock on a
106
+ # file that doesn't exist on the filesystem anymore.
107
+ #
108
+ # @param [Pathname] location
109
+ # the path to require a lock for.
110
+ #
111
+ # @param [locking_constant] lock_type
112
+ # the type of lock, either exclusive (File::LOCK_EX) or shared
113
+ # (File::LOCK_SH).
114
+ #
115
+ # @return [void]
116
+ #
117
+ def self.lock(location, lock_type)
118
+ raise ArgumentError, 'no block given' unless block_given?
119
+ lockfile = "#{location}.lock"
120
+ f = nil
121
+ loop do
122
+ f.close if f
123
+ f = File.open(lockfile, File::CREAT, 0o644)
124
+ f.flock(lock_type)
125
+ break if Cache.valid_lock?(f, lockfile)
126
+ end
127
+ begin
128
+ yield location
129
+ ensure
130
+ if lock_type == File::LOCK_SH
131
+ f.flock(File::LOCK_EX)
132
+ File.delete(lockfile) if Cache.valid_lock?(f, lockfile)
133
+ else
134
+ File.delete(lockfile)
135
+ end
136
+ f.close
137
+ end
138
+ end
139
+
140
+ # Checks that the lock is on a file that still exists on the filesystem.
141
+ #
142
+ # @param [File] file
143
+ # the actual file that we have a lock for.
144
+ #
145
+ # @param [String] filename
146
+ # the filename of the file that we have a lock for.
147
+ #
148
+ # @return [Boolean]
149
+ # true if `filename` still exists and is the same file as `file`
150
+ #
151
+ def self.valid_lock?(file, filename)
152
+ file.stat.ino == File.stat(filename).ino
153
+ rescue Errno::ENOENT
154
+ false
155
+ end
156
+
72
157
  private
73
158
 
74
159
  # Ensures the cache on disk was created with the same CocoaPods version as
@@ -112,7 +197,7 @@ module Pod
112
197
  #
113
198
  def path_for_spec(request, slug_opts = {})
114
199
  path = root + 'Specs' + request.slug(**slug_opts)
115
- path.sub_ext('.podspec.json')
200
+ Pathname.new(path.to_path + '.podspec.json')
116
201
  end
117
202
 
118
203
  # @param [Request] request
@@ -197,10 +282,12 @@ module Pod
197
282
  def copy_and_clean(source, destination, spec)
198
283
  specs_by_platform = group_subspecs_by_platform(spec)
199
284
  destination.parent.mkpath
200
- FileUtils.rm_rf(destination)
201
- FileUtils.cp_r(source, destination)
202
- Pod::Installer::PodSourcePreparer.new(spec, destination).prepare!
203
- Sandbox::PodDirCleaner.new(destination, specs_by_platform).clean!
285
+ Cache.write_lock(destination) do
286
+ FileUtils.rm_rf(destination)
287
+ FileUtils.cp_r(source, destination)
288
+ Pod::Installer::PodSourcePreparer.new(spec, destination).prepare!
289
+ Sandbox::PodDirCleaner.new(destination, specs_by_platform).clean!
290
+ end
204
291
  end
205
292
 
206
293
  def group_subspecs_by_platform(spec)
@@ -226,7 +313,9 @@ module Pod
226
313
  #
227
314
  def write_spec(spec, path)
228
315
  path.dirname.mkpath
229
- path.open('w') { |f| f.write spec.to_pretty_json }
316
+ Cache.write_lock(path) do
317
+ path.open('w') { |f| f.write spec.to_pretty_json }
318
+ end
230
319
  end
231
320
  end
232
321
  end
@@ -50,8 +50,10 @@ module Pod
50
50
 
51
51
  if target && result.location && target != result.location
52
52
  UI.message "Copying #{request.name} from `#{result.location}` to #{UI.path target}", '> ' do
53
- FileUtils.rm_rf target
54
- FileUtils.cp_r(result.location, target)
53
+ Cache.read_lock(result.location) do
54
+ FileUtils.rm_rf target
55
+ FileUtils.cp_r(result.location, target)
56
+ end
55
57
  end
56
58
  end
57
59
  result
@@ -16,7 +16,7 @@ module Pod
16
16
  else
17
17
  require 'cocoapods/open-uri'
18
18
  begin
19
- open(podspec_uri) { |io| store_podspec(sandbox, io.read, is_json) }
19
+ OpenURI.open_uri(podspec_uri) { |io| store_podspec(sandbox, io.read, is_json) }
20
20
  rescue OpenURI::HTTPError => e
21
21
  status = e.io.status.join(' ')
22
22
  raise Informative, "Failed to fetch podspec for `#{name}` at `#{podspec_uri}`.\n Error: #{status}"
@@ -1,5 +1,5 @@
1
1
  module Pod
2
2
  # The version of the CocoaPods command line tool.
3
3
  #
4
- VERSION = '1.10.0'.freeze unless defined? Pod::VERSION
4
+ VERSION = '1.11.0'.freeze unless defined? Pod::VERSION
5
5
  end
@@ -67,7 +67,7 @@ module Pod
67
67
  # the specification for which license is needed.
68
68
  #
69
69
  # @return [String] The text of the license.
70
- # @return [Nil] If not license text could be found.
70
+ # @return [Nil] If no license text could be found.
71
71
  #
72
72
  def license_text(spec)
73
73
  return nil unless spec.license
@@ -9,7 +9,7 @@ module Pod
9
9
  # @param [Project] project
10
10
  # the Xcodeproj to generate the target into.
11
11
  #
12
- # @param [Symbol] platform
12
+ # @param [Symbol] platform_name
13
13
  # the platform of the target. Can be `:ios` or `:osx`, etc.
14
14
  #
15
15
  # @param [String] deployment_target
@@ -18,10 +18,14 @@ module Pod
18
18
  # @param [String] name
19
19
  # The name to use for the target, defaults to 'App'.
20
20
  #
21
+ # @param [String] product_basename
22
+ # The product basename to use for the target, defaults to `name`.
23
+ #
21
24
  # @return [PBXNativeTarget] the new target that was created.
22
25
  #
23
- def self.add_app_target(project, platform, deployment_target, name = 'App')
24
- project.new_target(:application, name, platform, deployment_target)
26
+ def self.add_app_target(project, platform_name, deployment_target, name = 'App', product_basename = nil)
27
+ project.new_target(:application, name, platform_name, deployment_target, nil,
28
+ nil, product_basename)
25
29
  end
26
30
 
27
31
  # Creates and links an import file for the given pod target and into the given native target.
@@ -1,18 +1,18 @@
1
1
  module Pod
2
2
  module Generator
3
3
  class CopydSYMsScript
4
- # @return [Array<Pathname>] dsym_paths the dSYM paths to include in the script contents.
4
+ # @return [Array<Pathname, String>] dsym_paths the dSYM paths to include in the script contents.
5
5
  #
6
6
  attr_reader :dsym_paths
7
7
 
8
- # @return [Array<Pathname>] bcsymbolmap_paths the bcsymbolmap paths to include in the script contents.
8
+ # @return [Array<Pathname, String>] bcsymbolmap_paths the bcsymbolmap paths to include in the script contents.
9
9
  #
10
10
  attr_reader :bcsymbolmap_paths
11
11
 
12
12
  # Initialize a new instance
13
13
  #
14
- # @param [Array<Pathname>] dsym_paths @see dsym_paths
15
- # @param [Array<Pathname>] bcsymbolmap_paths @see bcsymbolmap_paths
14
+ # @param [Array<Pathname, String>] dsym_paths @see dsym_paths
15
+ # @param [Array<Pathname, String>] bcsymbolmap_paths @see bcsymbolmap_paths
16
16
  #
17
17
  def initialize(dsym_paths, bcsymbolmap_paths)
18
18
  @dsym_paths = Array(dsym_paths)
@@ -70,8 +70,8 @@ copy_dir()
70
70
  local destination="$2"
71
71
 
72
72
  # Use filter instead of exclude so missing patterns don't throw errors.
73
- echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \\"- CVS/\\" --filter \\"- .svn/\\" --filter \\"- .git/\\" --filter \\"- .hg/\\" \\"${source}\\" \\"${destination}\\""
74
- rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" "${source}" "${destination}"
73
+ echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \\"- CVS/\\" --filter \\"- .svn/\\" --filter \\"- .git/\\" --filter \\"- .hg/\\" \\"${source}*\\" \\"${destination}\\""
74
+ rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" "${source}"/* "${destination}"
75
75
  }
76
76
 
77
77
  SELECT_SLICE_RETVAL=""
@@ -130,54 +130,11 @@ select_slice() {
130
130
  done
131
131
  }
132
132
 
133
- install_library() {
134
- local source="$1"
135
- local name="$2"
136
- local destination="#{Target::BuildSettings::XCFRAMEWORKS_BUILD_DIR_VARIABLE}/${name}"
137
-
138
- # Libraries can contain headers, module maps, and a binary, so we'll copy everything in the folder over
139
-
140
- local source="$binary"
141
- echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \\"- CVS/\\" --filter \\"- .svn/\\" --filter \\"- .git/\\" --filter \\"- .hg/\\" \\"${source}/*\\" \\"${destination}\\""
142
- rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" "${source}/*" "${destination}"
143
- }
144
-
145
- # Copies a framework to derived data for use in later build phases
146
- install_framework()
147
- {
148
- local source="$1"
149
- local name="$2"
150
- local destination="#{Pod::Target::BuildSettings::XCFRAMEWORKS_BUILD_DIR_VARIABLE}/${name}"
151
-
152
- if [ ! -d "$destination" ]; then
153
- mkdir -p "$destination"
154
- fi
155
-
156
- copy_dir "$source" "$destination"
157
- echo "Copied $source to $destination"
158
- }
159
-
160
- install_xcframework_library() {
161
- local basepath="$1"
162
- local name="$2"
163
- local paths=("$@")
164
-
165
- # Locate the correct slice of the .xcframework for the current architectures
166
- select_slice "${paths[@]}"
167
- local target_path="$SELECT_SLICE_RETVAL"
168
- if [[ -z "$target_path" ]]; then
169
- echo "warning: [CP] Unable to find matching .xcframework slice in '${paths[@]}' for the current build architectures ($ARCHS)."
170
- return
171
- fi
172
-
173
- install_framework "$basepath/$target_path" "$name"
174
- }
175
-
176
133
  install_xcframework() {
177
134
  local basepath="$1"
178
135
  local name="$2"
179
136
  local package_type="$3"
180
- local paths=("$@")
137
+ local paths=("${@:4}")
181
138
 
182
139
  # Locate the correct slice of the .xcframework for the current architectures
183
140
  select_slice "${paths[@]}"
@@ -195,7 +152,6 @@ install_xcframework() {
195
152
  fi
196
153
 
197
154
  copy_dir "$source/" "$destination"
198
-
199
155
  echo "Copied $source to $destination"
200
156
  }
201
157
 
@@ -218,7 +174,7 @@ install_xcframework() {
218
174
  def install_xcframework_args(xcframework, slices)
219
175
  root = xcframework.path
220
176
  args = [shell_escape("${PODS_ROOT}/#{root.relative_path_from(sandbox_root)}")]
221
- args << shell_escape(xcframework.name)
177
+ args << shell_escape(xcframework.target_name)
222
178
  is_framework = xcframework.build_type.framework?
223
179
  args << shell_escape(is_framework ? 'framework' : 'library')
224
180
  slices.each do |slice|
@@ -159,8 +159,9 @@ code_sign_if_enabled() {
159
159
  end
160
160
  xcframeworks_by_config.each do |config, xcframeworks|
161
161
  xcframeworks.select { |xcf| xcf.build_type.dynamic_framework? }.each do |xcframework|
162
+ target_name = xcframework.target_name
162
163
  name = xcframework.name
163
- contents_by_config[config] << %( install_framework "#{Target::BuildSettings::XCFRAMEWORKS_BUILD_DIR_VARIABLE}/#{name}/#{name}.framework"\n)
164
+ contents_by_config[config] << %( install_framework "#{Target::BuildSettings::XCFRAMEWORKS_BUILD_DIR_VARIABLE}/#{target_name}/#{name}.framework"\n)
164
165
  end
165
166
  end
166
167
  script << "\n" unless contents_by_config.empty?
@@ -79,6 +79,7 @@ install_dsym() {
79
79
  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
80
80
  else
81
81
  # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
82
+ mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
82
83
  touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
83
84
  fi
84
85
  fi
@@ -31,6 +31,10 @@ module Pod
31
31
  #
32
32
  attr_reader :sandbox
33
33
 
34
+ # @return [Podfile] The Podfile to analyze dependencies.
35
+ #
36
+ attr_reader :podfile
37
+
34
38
  # @return [Array<Specifications>] The specifications returned by the
35
39
  # resolver.
36
40
  #
@@ -45,11 +49,13 @@ module Pod
45
49
  # Init a new SandboxAnalyzer
46
50
  #
47
51
  # @param [Sandbox] sandbox @see sandbox
52
+ # @param [Podfile] podfile @see podfile
48
53
  # @param [Array<Specifications>] specs @see specs
49
54
  # @param [Bool] update_mode @see update_mode
50
55
  #
51
- def initialize(sandbox, specs, update_mode)
56
+ def initialize(sandbox, podfile, specs, update_mode)
52
57
  @sandbox = sandbox
58
+ @podfile = podfile
53
59
  @specs = specs
54
60
  @update_mode = update_mode
55
61
  end
@@ -103,7 +109,7 @@ module Pod
103
109
  #
104
110
  def pod_added?(pod)
105
111
  return true if resolved_pods.include?(pod) && !sandbox_pods.include?(pod)
106
- return true if !folder_exist?(pod) && !sandbox.local?(pod)
112
+ return true if !sandbox.local?(pod) && !folder_exist?(pod)
107
113
  false
108
114
  end
109
115
 
@@ -139,6 +145,7 @@ module Pod
139
145
  return true if spec.version != sandbox_version(pod)
140
146
  return true if spec.checksum != sandbox_checksum(pod)
141
147
  return true if resolved_spec_names(pod) != sandbox_spec_names(pod)
148
+ return true if podfile_dependency(pod) != sandbox_dependency(pod)
142
149
  return true if sandbox.predownloaded?(pod)
143
150
  return true if folder_empty?(pod)
144
151
  false
@@ -161,14 +168,14 @@ module Pod
161
168
  # @return [Array<String>] The name of the resolved Pods.
162
169
  #
163
170
  def resolved_pods
164
- specs.map { |spec| spec.root.name }.uniq
171
+ @resolved_pods ||= specs.map { |spec| spec.root.name }.uniq
165
172
  end
166
173
 
167
174
  # @return [Array<String>] The name of the Pods stored in the sandbox
168
175
  # manifest.
169
176
  #
170
177
  def sandbox_pods
171
- sandbox_manifest.pod_names.map { |name| Specification.root_name(name) }.uniq
178
+ @sandbox_pods ||= sandbox_manifest.pod_names.map { |name| Specification.root_name(name) }.uniq
172
179
  end
173
180
 
174
181
  # @return [Array<String>] The name of the resolved specifications
@@ -223,6 +230,26 @@ module Pod
223
230
  sandbox_manifest.checksum(pod)
224
231
  end
225
232
 
233
+ # @return [Dependency, nil] The dependency with the given name stored in the sandbox.
234
+ #
235
+ # @param [String] pod
236
+ # the name of the Pod.
237
+ #
238
+ def sandbox_dependency(pod)
239
+ sandbox_manifest.dependencies.find { |d| d.name == pod }
240
+ end
241
+
242
+ #--------------------------------------#
243
+
244
+ # @return [Dependency, nil] The dependency with the given name from the podfile.
245
+ #
246
+ # @param [String] pod
247
+ # the name of the Pod.
248
+ #
249
+ def podfile_dependency(pod)
250
+ podfile.dependencies.find { |d| d.name == pod }
251
+ end
252
+
226
253
  #--------------------------------------#
227
254
 
228
255
  def folder_exist?(pod)