cocoapods 1.0.0 → 1.1.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +329 -0
  3. data/lib/cocoapods/command/init.rb +6 -6
  4. data/lib/cocoapods/command/ipc/list.rb +40 -0
  5. data/lib/cocoapods/command/ipc/podfile.rb +31 -0
  6. data/lib/cocoapods/command/ipc/repl.rb +51 -0
  7. data/lib/cocoapods/command/ipc/spec.rb +29 -0
  8. data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
  9. data/lib/cocoapods/command/ipc.rb +18 -0
  10. data/lib/cocoapods/command/lib/create.rb +105 -0
  11. data/lib/cocoapods/command/lib/lint.rb +111 -0
  12. data/lib/cocoapods/command/lib.rb +3 -207
  13. data/lib/cocoapods/command/repo/push.rb +44 -20
  14. data/lib/cocoapods/command/setup.rb +2 -1
  15. data/lib/cocoapods/command/spec/lint.rb +4 -0
  16. data/lib/cocoapods/command.rb +2 -1
  17. data/lib/cocoapods/config.rb +4 -1
  18. data/lib/cocoapods/downloader/cache.rb +1 -0
  19. data/lib/cocoapods/downloader.rb +20 -0
  20. data/lib/cocoapods/executable.rb +1 -1
  21. data/lib/cocoapods/gem_version.rb +1 -1
  22. data/lib/cocoapods/generator/acknowledgements/plist.rb +4 -1
  23. data/lib/cocoapods/generator/copy_resources_script.rb +4 -10
  24. data/lib/cocoapods/generator/header.rb +2 -1
  25. data/lib/cocoapods/generator/prefix_header.rb +0 -12
  26. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +38 -5
  27. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +5 -4
  28. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +5 -1
  29. data/lib/cocoapods/installer/analyzer/target_inspector.rb +24 -1
  30. data/lib/cocoapods/installer/analyzer.rb +161 -1
  31. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +29 -9
  32. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +204 -0
  33. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +314 -0
  34. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +401 -0
  35. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +214 -0
  36. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +265 -0
  37. data/lib/cocoapods/installer/xcode.rb +7 -0
  38. data/lib/cocoapods/installer.rb +50 -214
  39. data/lib/cocoapods/resolver.rb +15 -9
  40. data/lib/cocoapods/sandbox/headers_store.rb +4 -10
  41. data/lib/cocoapods/sandbox/path_list.rb +20 -9
  42. data/lib/cocoapods/sources_manager.rb +7 -10
  43. data/lib/cocoapods/target/aggregate_target.rb +20 -0
  44. data/lib/cocoapods/target/pod_target.rb +37 -7
  45. data/lib/cocoapods/user_interface/error_report.rb +7 -0
  46. data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
  47. data/lib/cocoapods/user_interface.rb +7 -5
  48. data/lib/cocoapods/validator.rb +59 -11
  49. metadata +112 -83
  50. data/lib/cocoapods/command/inter_process_communication.rb +0 -177
  51. data/lib/cocoapods/installer/file_references_installer.rb +0 -310
  52. data/lib/cocoapods/installer/migrator.rb +0 -86
  53. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +0 -191
  54. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +0 -368
  55. data/lib/cocoapods/installer/target_installer.rb +0 -210
@@ -33,11 +33,16 @@ module Pod
33
33
  cache_path: Config.instance.cache_root + 'Pods'
34
34
  )
35
35
  can_cache &&= !Config.instance.skip_download_cache
36
+
37
+ request = preprocess_request(request)
38
+
36
39
  if can_cache
37
40
  raise ArgumentError, 'Must provide a `cache_path` when caching.' unless cache_path
38
41
  cache = Cache.new(cache_path)
39
42
  result = cache.download_pod(request)
40
43
  else
44
+ raise ArgumentError, 'Must provide a `target` when caching is disabled.' unless target
45
+
41
46
  require 'cocoapods/installer/pod_source_preparer'
42
47
  result, = download_request(request, target)
43
48
  Installer::PodSourcePreparer.new(result.spec, result.location).prepare!
@@ -110,6 +115,21 @@ module Pod
110
115
  end
111
116
  end
112
117
 
118
+ # Return a new request after preprocessing by the downloader
119
+ #
120
+ # @param [Request] request
121
+ # the request that needs preprocessing
122
+ #
123
+ # @return [Request] the preprocessed request
124
+ #
125
+ def self.preprocess_request(request)
126
+ Request.new(
127
+ :spec => request.spec,
128
+ :released => request.released_pod?,
129
+ :name => request.name,
130
+ :params => Downloader.preprocess_options(request.params))
131
+ end
132
+
113
133
  public
114
134
 
115
135
  class DownloaderError; include CLAide::InformativeError; end
@@ -108,7 +108,7 @@ module Pod
108
108
  #
109
109
  def self.which!(program)
110
110
  which(program).tap do |bin|
111
- raise Informative, "Unable to locate the executable `#{executable}`" unless bin
111
+ raise Informative, "Unable to locate the executable `#{program}`" unless bin
112
112
  end
113
113
  end
114
114
 
@@ -1,5 +1,5 @@
1
1
  module Pod
2
2
  # The version of the CocoaPods command line tool.
3
3
  #
4
- VERSION = '1.0.0'.freeze unless defined? Pod::VERSION
4
+ VERSION = '1.1.0'.freeze unless defined? Pod::VERSION
5
5
  end
@@ -33,11 +33,14 @@ module Pod
33
33
 
34
34
  def hash_for_spec(spec)
35
35
  if (license = license_text(spec))
36
- {
36
+ hash = {
37
37
  :Type => 'PSGroupSpecifier',
38
38
  :Title => sanitize_encoding(spec.name),
39
39
  :FooterText => sanitize_encoding(license),
40
40
  }
41
+ hash[:License] = sanitize_encoding(spec.license[:type]) if spec.license[:type]
42
+
43
+ hash
41
44
  end
42
45
  end
43
46
 
@@ -119,12 +119,6 @@ case "${TARGETED_DEVICE_FAMILY}" in
119
119
  ;;
120
120
  esac
121
121
 
122
- realpath() {
123
- DIRECTORY="$(cd "${1%/*}" && pwd)"
124
- FILENAME="${1##*/}"
125
- echo "$DIRECTORY/$FILENAME"
126
- }
127
-
128
122
  install_resource()
129
123
  {
130
124
  if [[ "$1" = /* ]] ; then
@@ -144,8 +138,8 @@ EOM
144
138
  ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \\"$RESOURCE_PATH\\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
145
139
  ;;
146
140
  *\.xib)
147
- echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \\"$RESOURCE_PATH\\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT}"
148
- ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \\"$RESOURCE_PATH\\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}"
141
+ echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \\"$RESOURCE_PATH\\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
142
+ ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \\"$RESOURCE_PATH\\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
149
143
  ;;
150
144
  *.framework)
151
145
  echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
@@ -166,7 +160,7 @@ EOM
166
160
  xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
167
161
  ;;
168
162
  *.xcassets)
169
- ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
163
+ ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
170
164
  XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
171
165
  ;;
172
166
  *)
@@ -195,7 +189,7 @@ then
195
189
  # Find all other xcassets (this unfortunately includes those of path pods and other targets).
196
190
  OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
197
191
  while read line; do
198
- if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
192
+ if [[ $line != "${PODS_ROOT}*" ]]; then
199
193
  XCASSET_FILES+=("$line")
200
194
  fi
201
195
  done <<<"$OTHER_XCASSETS"
@@ -40,8 +40,9 @@ module Pod
40
40
  #
41
41
  def generate
42
42
  result = ''
43
+ result << "#ifdef __OBJC__\n"
43
44
  result << generate_platform_import_header
44
-
45
+ result << "#endif\n"
45
46
  result << "\n"
46
47
 
47
48
  imports.each do |import|
@@ -55,18 +55,6 @@ module Pod
55
55
 
56
56
  result
57
57
  end
58
-
59
- protected
60
-
61
- # Generates the contents of the header according to the platform.
62
- #
63
- # @return [String]
64
- #
65
- def generate_platform_import_header
66
- result = "#ifdef __OBJC__\n"
67
- result << super
68
- result << "#endif\n"
69
- end
70
58
  end
71
59
  end
72
60
  end
@@ -4,7 +4,7 @@ module Pod
4
4
  # Generates the xcconfigs for the aggregate targets.
5
5
  #
6
6
  class AggregateXCConfig
7
- # @return [Target] the target represented by this xcconfig.
7
+ # @return [AggregateTarget] the target represented by this xcconfig.
8
8
  #
9
9
  attr_reader :target
10
10
 
@@ -60,10 +60,8 @@ module Pod
60
60
  'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1',
61
61
  'FRAMEWORK_SEARCH_PATHS' => '$(inherited) ',
62
62
  'LIBRARY_SEARCH_PATHS' => '$(inherited) ',
63
- }
64
- if pod_targets.any?(&:uses_swift?)
65
- config['EMBEDDED_CONTENT_CONTAINS_SWIFT'] = 'YES'
66
- end
63
+ }.merge(embedded_content_settings)
64
+
67
65
  @xcconfig = Xcodeproj::Config.new(config)
68
66
 
69
67
  @xcconfig.merge!(merged_user_target_xcconfigs)
@@ -90,6 +88,41 @@ module Pod
90
88
 
91
89
  protected
92
90
 
91
+ def target_swift_version
92
+ settings = target.native_target.resolved_build_setting('SWIFT_VERSION') unless target.native_target.nil?
93
+ settings.values.compact.uniq.first unless settings.nil?
94
+ end
95
+
96
+ EMBED_STANDARD_LIBRARIES_MINIMUM_VERSION = Gem::Version.new('2.3')
97
+
98
+ # @return [Hash<String, String>] the build settings necessary for Swift
99
+ # targets to be correctly embedded in their host.
100
+ #
101
+ def embedded_content_settings
102
+ # For embedded targets, which live in a host target, CocoaPods
103
+ # copies all of the embedded target's pod_targets its host
104
+ # target. Therefore, this check will properly require the Swift
105
+ # libs in the host target, if the embedded target has any pod targets
106
+ # that use Swift. Setting this for the embedded target would
107
+ # cause an App Store rejection because frameworks cannot be embedded
108
+ # in embedded targets.
109
+
110
+ swift_version = Gem::Version.new(target_swift_version)
111
+ should_embed = !target.requires_host_target? && pod_targets.any?(&:uses_swift?)
112
+ embed_value = should_embed ? 'YES' : 'NO'
113
+
114
+ config = {
115
+ 'ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES' => embed_value,
116
+ 'EMBEDDED_CONTENT_CONTAINS_SWIFT' => embed_value,
117
+ }
118
+
119
+ if swift_version >= EMBED_STANDARD_LIBRARIES_MINIMUM_VERSION || !should_embed
120
+ config.delete('EMBEDDED_CONTENT_CONTAINS_SWIFT')
121
+ end
122
+
123
+ config
124
+ end
125
+
93
126
  # @return [Hash<String, String>] the build settings necessary to import
94
127
  # the pod targets.
95
128
  #
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/try'
2
+
1
3
  module Pod
2
4
  module Generator
3
5
  module XCConfig
@@ -283,9 +285,9 @@ module Pod
283
285
  #
284
286
  def self.add_language_specific_settings(target, xcconfig)
285
287
  if target.uses_swift?
286
- build_settings = {
287
- 'OTHER_SWIFT_FLAGS' => '$(inherited) ' + quote(%w(-D COCOAPODS)),
288
- }
288
+ other_swift_flags = ['$(inherited)', quote(%w(-D COCOAPODS))]
289
+ other_swift_flags << quote(%w(-suppress-warnings)) if target.try(:inhibit_warnings?)
290
+ build_settings = { 'OTHER_SWIFT_FLAGS' => other_swift_flags.join(' ') }
289
291
  xcconfig.merge!(build_settings)
290
292
  end
291
293
  end
@@ -314,7 +316,6 @@ module Pod
314
316
  search_paths << search_path
315
317
  end
316
318
  end
317
- search_paths
318
319
  end
319
320
  end
320
321
 
@@ -23,7 +23,11 @@ module Pod
23
23
  #
24
24
  def scope_suffixes
25
25
  return { variants.first => nil } if variants.count == 1
26
- scope_by_specs
26
+ Hash[scope_by_specs.map do |variant, scope|
27
+ require 'digest'
28
+ scope = Digest::MD5.hexdigest(scope)[0..7] if !scope.nil? && scope.length >= 50
29
+ [variant, scope]
30
+ end]
27
31
  end
28
32
 
29
33
  # Groups the collection by result of the block.
@@ -44,6 +44,7 @@ module Pod
44
44
  result.platform = compute_platform(targets)
45
45
  result.archs = compute_archs(targets)
46
46
  result.project = user_project
47
+ result.target_definition.swift_version = compute_swift_version_from_targets(targets)
47
48
  result
48
49
  end
49
50
 
@@ -72,7 +73,7 @@ module Pod
72
73
  else
73
74
  raise Informative, 'Could not automatically select an Xcode project. ' \
74
75
  "Specify one in your Podfile like so:\n\n" \
75
- " xcodeproj 'path/to/Project.xcodeproj'\n"
76
+ " project 'path/to/Project.xcodeproj'\n"
76
77
  end
77
78
  end
78
79
  path
@@ -205,6 +206,28 @@ module Pod
205
206
  end
206
207
  end
207
208
  end
209
+
210
+ # Compute the Swift version for the target build configurations. If more
211
+ # than one Swift version is defined for a given target, then it will raise.
212
+ #
213
+ # @param [Array<PBXNativeTarget>] targets
214
+ # the targets that are checked for Swift versions.
215
+ #
216
+ # @return [String] the targets Swift version or nil
217
+ #
218
+ def compute_swift_version_from_targets(targets)
219
+ versions = targets.flat_map do |target|
220
+ target.resolved_build_setting('SWIFT_VERSION').values
221
+ end.flatten.compact.uniq
222
+ case versions.count
223
+ when 0
224
+ nil
225
+ when 1
226
+ versions.first
227
+ else
228
+ raise Informative, 'There may only be up to 1 unique SWIFT_VERSION per target.'
229
+ end
230
+ end
208
231
  end
209
232
  end
210
233
  end
@@ -226,16 +226,142 @@ module Pod
226
226
 
227
227
  private
228
228
 
229
+ # Copies the pod_targets of any of the app embedded aggregate targets into
230
+ # their potential host aggregate target, if that potential host aggregate target's
231
+ # user_target hosts any of the app embedded aggregate targets' user_targets
232
+ #
233
+ # @param [AggregateTarget] aggregate_target the aggregate target whose user_target
234
+ # might host one or more of the embedded aggregate targets' user_targets
235
+ #
236
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
237
+ # representing the embedded targets to be integrated
238
+ #
239
+ def copy_embedded_target_pod_targets_to_host(aggregate_target, embedded_aggregate_targets)
240
+ return if aggregate_target.requires_host_target?
241
+ # Get the uuids of the aggregate_target's user_targets' embedded targets if any
242
+ embedded_uuids = Set.new(aggregate_target.user_targets.map do |target|
243
+ aggregate_target.user_project.embedded_targets_in_native_target(target).map(&:uuid)
244
+ end.flatten)
245
+ return if embedded_uuids.empty?
246
+ embedded_aggregate_targets.each do |embedded_target|
247
+ next unless embedded_target.user_targets.map(&:uuid).any? do |embedded_uuid|
248
+ embedded_uuids.include? embedded_uuid
249
+ end
250
+ pod_target_names = aggregate_target.pod_targets.map(&:name)
251
+ # This embedded target is hosted by the aggregate target's user_target; copy over the non-duplicate pod_targets
252
+ aggregate_target.pod_targets = aggregate_target.pod_targets + embedded_target.pod_targets.select do |pod_target|
253
+ !pod_target_names.include? pod_target.name
254
+ end
255
+ end
256
+ end
257
+
258
+ # Raises an error if there are embedded targets in the Podfile, but
259
+ # their host targets have not been declared in the Podfile. As it
260
+ # finds host targets, it collection information on host target types.
261
+ #
262
+ # @param [Array<AggregateTarget>] aggregate_targets the generated
263
+ # aggregate targets
264
+ #
265
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
266
+ # representing the embedded targets to be integrated
267
+ #
268
+ def analyze_host_targets_in_podfile(aggregate_targets, embedded_aggregate_targets)
269
+ target_definitions_by_uuid = {}
270
+ # Collect aggregate target definitions by uuid to later lookup host target
271
+ # definitions and verify their compatiblity with their embedded targets
272
+ aggregate_targets.each do |target|
273
+ target.user_targets.map(&:uuid).each do |uuid|
274
+ target_definitions_by_uuid[uuid] = target.target_definition
275
+ end
276
+ end
277
+ aggregate_target_user_projects = aggregate_targets.map(&:user_project)
278
+ embedded_targets_missing_hosts = []
279
+ host_uuid_to_embedded_target_definitions = {}
280
+ # Search all of the known user projects for each embedded target's hosts
281
+ embedded_aggregate_targets.each do |target|
282
+ host_uuids = []
283
+ aggregate_target_user_projects.product(target.user_targets).each do |user_project, user_target|
284
+ host_uuids += user_project.host_targets_for_embedded_target(user_target).map(&:uuid)
285
+ end
286
+ # For each host, keep track of its embedded target definitions
287
+ # to later verify each embedded target's compatiblity with its host,
288
+ # ignoring the hosts that aren't known to CocoaPods (no target
289
+ # definitions in the Podfile)
290
+ host_uuids.each do |uuid|
291
+ (host_uuid_to_embedded_target_definitions[uuid] ||= []) << target.target_definition if target_definitions_by_uuid.key? uuid
292
+ end
293
+ # If none of the hosts are known to CocoaPods (no target definitions
294
+ # in the Podfile), add it to the list of targets missing hosts
295
+ embedded_targets_missing_hosts << target unless host_uuids.any? do |uuid|
296
+ target_definitions_by_uuid.key? uuid
297
+ end
298
+ end
299
+
300
+ unless embedded_targets_missing_hosts.empty?
301
+ embedded_targets_missing_hosts_product_types = embedded_targets_missing_hosts.map(&:user_targets).flatten.map(&:symbol_type).uniq
302
+ # If the targets missing hosts are only frameworks, then this is likely
303
+ # a project for doing framework development. In that case, just warn that
304
+ # the frameworks that these targets depend on won't be integrated anywhere
305
+ if embedded_targets_missing_hosts_product_types == [:framework]
306
+ UI.warn 'The Podfile contains framework targets, for which the Podfile does not contain host targets (targets which embed the framework).' \
307
+ "\n" \
308
+ 'If this project is for doing framework development, you can ignore this message. Otherwise, add a target to the Podfile that embeds these frameworks to make this message go away (e.g. a test target).'
309
+ else
310
+ target_names = embedded_targets_missing_hosts.map do |target|
311
+ target.name.sub('Pods-', '') # Make the target names more recognizable to the user
312
+ end.join ', '
313
+ raise Informative, "Unable to find host target(s) for #{target_names}. Please add the host targets for the embedded targets to the Podfile." \
314
+ "\n" \
315
+ 'Certain kinds of targets require a host target. A host target is a "parent" target which embeds a "child" target. These are example types of targets that need a host target:' \
316
+ "\n- Framework" \
317
+ "\n- App Extension" \
318
+ "\n- Watch OS 1 Extension" \
319
+ "\n- Messages Extension (except when used with a Messages Application)"
320
+ end
321
+ end
322
+
323
+ target_mismatches = []
324
+ check_prop = lambda do |target_definition1, target_definition2, attr, msg|
325
+ attr1 = target_definition1.send(attr)
326
+ attr2 = target_definition2.send(attr)
327
+ if attr1 != attr2
328
+ target_mismatches << "- #{target_definition1.name} (#{attr1}) and #{target_definition2.name} (#{attr2}) #{msg}."
329
+ end
330
+ end
331
+ host_uuid_to_embedded_target_definitions.each do |uuid, target_definitions|
332
+ host_target_definition = target_definitions_by_uuid[uuid]
333
+ target_definitions.each do |target_definition|
334
+ check_prop.call(host_target_definition, target_definition, :platform, 'do not use the same platform')
335
+ check_prop.call(host_target_definition, target_definition, :uses_frameworks?, 'do not both set use_frameworks!')
336
+ check_prop.call(host_target_definition, target_definition, :swift_version, 'do not use the same Swift version')
337
+ end
338
+ end
339
+
340
+ unless target_mismatches.empty?
341
+ heading = 'Unable to integrate the following embedded targets with their respective host targets (a host target is a "parent" target which embeds a "child" target like a framework or extension):'
342
+ raise Informative, heading + "\n\n" + target_mismatches.sort.uniq.join("\n")
343
+ end
344
+ end
345
+
229
346
  # Creates the models that represent the targets generated by CocoaPods.
230
347
  #
231
348
  # @return [Array<AggregateTarget>]
232
349
  #
233
350
  def generate_targets
234
351
  specs_by_target = result.specs_by_target.reject { |td, _| td.abstract? }
352
+ check_pod_target_swift_versions(specs_by_target)
235
353
  pod_targets = generate_pod_targets(specs_by_target)
236
354
  aggregate_targets = specs_by_target.keys.map do |target_definition|
237
355
  generate_target(target_definition, pod_targets)
238
356
  end
357
+ if installation_options.integrate_targets?
358
+ # Copy embedded target pods that cannot have their pods embedded as frameworks to their host targets
359
+ embedded_targets = aggregate_targets.select(&:requires_host_target?).select(&:requires_frameworks?)
360
+ analyze_host_targets_in_podfile(aggregate_targets, embedded_targets)
361
+ aggregate_targets.each do |target|
362
+ copy_embedded_target_pod_targets_to_host(target, embedded_targets)
363
+ end
364
+ end
239
365
  aggregate_targets.each do |target|
240
366
  target.search_paths_aggregate_targets = aggregate_targets.select do |aggregate_target|
241
367
  target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition)
@@ -277,11 +403,45 @@ module Pod
277
403
  target.pod_targets = pod_targets.select do |pod_target|
278
404
  pod_target.target_definitions.include?(target_definition)
279
405
  end
406
+
280
407
  target
281
408
  end
282
409
 
410
+ # Verify that targets using a pod have the same swift version
411
+ #
412
+ # @param [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
413
+ # the resolved specifications grouped by target.
414
+ #
415
+ # @note raises Informative if targets using a pod do not have
416
+ # the same swift version
417
+ #
418
+ def check_pod_target_swift_versions(specs_by_target)
419
+ targets_by_spec = {}
420
+ specs_by_target.each do |target, specs|
421
+ specs.each do |spec|
422
+ (targets_by_spec[spec] ||= []) << target
423
+ end
424
+ end
425
+
426
+ error_message_for_target = lambda do |target|
427
+ "#{target.name} (Swift #{target.swift_version})"
428
+ end
429
+
430
+ error_messages = targets_by_spec.map do |spec, targets|
431
+ swift_targets = targets.reject { |target| target.swift_version.blank? }
432
+ next if swift_targets.empty? || swift_targets.uniq(&:swift_version).count == 1
433
+ target_errors = swift_targets.map(&error_message_for_target).join(', ')
434
+ "- #{spec.name} required by #{target_errors}"
435
+ end.compact
436
+
437
+ unless error_messages.empty?
438
+ raise Informative, 'The following pods are integrated into targets ' \
439
+ "that do not have the same Swift version:\n\n#{error_messages.join("\n")}"
440
+ end
441
+ end
442
+
283
443
  # Setup the pod targets for an aggregate target. Deduplicates resulting
284
- # targets by grouping by grouping by platform and subspec by their root
444
+ # targets by grouping by platform and subspec by their root
285
445
  # to create a {PodTarget} for each spec.
286
446
  #
287
447
  # @param [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
@@ -11,7 +11,7 @@ module Pod
11
11
 
12
12
  # @return [String] the PACKAGE emoji to use as prefix for every build phase aded to the user project
13
13
  #
14
- BUILD_PHASE_PREFIX = "\u{1F4E6} ".freeze
14
+ BUILD_PHASE_PREFIX = '[CP] '.freeze
15
15
 
16
16
  # @return [String] the name of the check manifest phase
17
17
  #
@@ -20,7 +20,12 @@ module Pod
20
20
  # @return [Array<Symbol>] the symbol types, which require that the pod
21
21
  # frameworks are embedded in the output directory / product bundle.
22
22
  #
23
- EMBED_FRAMEWORK_TARGET_TYPES = [:application, :unit_test_bundle, :app_extension, :watch_extension, :watch2_extension].freeze
23
+ # @note This does not include :app_extension or :watch_extension because
24
+ # these types must have their frameworks embedded in their host targets.
25
+ # For messages extensions, this only applies if it's embedded in a messages
26
+ # application.
27
+ #
28
+ EMBED_FRAMEWORK_TARGET_TYPES = [:application, :unit_test_bundle, :ui_test_bundle, :watch2_extension, :messages_application].freeze
24
29
 
25
30
  # @return [String] the name of the embed frameworks phase
26
31
  #
@@ -54,6 +59,7 @@ module Pod
54
59
 
55
60
  add_pods_library
56
61
  add_embed_frameworks_script_phase
62
+ remove_embed_frameworks_script_phase_from_embedded_targets
57
63
  add_copy_resources_script_phase
58
64
  add_check_manifest_lock_script_phase
59
65
  end
@@ -110,6 +116,21 @@ module Pod
110
116
  end
111
117
  end
112
118
 
119
+ # Removes the embed frameworks build phase from embedded targets
120
+ #
121
+ # @note Older versions of CocoaPods would add this build phase to embedded
122
+ # targets. They should be removed on upgrade because embedded targets
123
+ # will have their frameworks embedded in their host targets.
124
+ #
125
+ def remove_embed_frameworks_script_phase_from_embedded_targets
126
+ return unless target.requires_host_target?
127
+ native_targets.each do |native_target|
128
+ if AggregateTarget::EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES.include? native_target.symbol_type
129
+ remove_embed_frameworks_script_phase(native_target)
130
+ end
131
+ end
132
+ end
133
+
113
134
  def add_embed_frameworks_script_phase_to_target(native_target)
114
135
  phase = create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME)
115
136
  script_path = target.embed_frameworks_script_relative_path
@@ -121,7 +142,7 @@ module Pod
121
142
  # @param [PBXNativeTarget] native_target
122
143
  #
123
144
  def remove_embed_frameworks_script_phase(native_target)
124
- embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == EMBED_FRAMEWORK_PHASE_NAME }
145
+ embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(EMBED_FRAMEWORK_PHASE_NAME) }
125
146
  return unless embed_build_phase.present?
126
147
  native_target.build_phases.delete(embed_build_phase)
127
148
  end
@@ -157,10 +178,9 @@ module Pod
157
178
  native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase
158
179
  phase.shell_script = <<-SH.strip_heredoc
159
180
  diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
160
- if [[ $? != 0 ]] ; then
161
- cat << EOM
162
- error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
163
- EOM
181
+ if [ $? != 0 ] ; then
182
+ # print error to STDERR
183
+ echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
164
184
  exit 1
165
185
  fi
166
186
  SH
@@ -184,6 +204,7 @@ module Pod
184
204
  # directory / product bundle.
185
205
  #
186
206
  def native_targets_to_embed_in
207
+ return [] if target.requires_host_target?
187
208
  native_targets.select do |target|
188
209
  EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
189
210
  end
@@ -215,8 +236,7 @@ module Pod
215
236
  def create_or_update_build_phase(target, phase_name, phase_class = Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
216
237
  prefixed_phase_name = BUILD_PHASE_PREFIX + phase_name
217
238
  build_phases = target.build_phases.grep(phase_class)
218
- build_phases.find { |phase| phase.name == prefixed_phase_name } ||
219
- build_phases.find { |phase| phase.name == phase_name }.tap { |p| p.name = prefixed_phase_name if p } ||
239
+ build_phases.find { |phase| phase.name && phase.name.end_with?(phase_name) }.tap { |p| p.name = prefixed_phase_name if p } ||
220
240
  target.project.new(phase_class).tap do |phase|
221
241
  UI.message("Adding Build Phase '#{prefixed_phase_name}' to project.") do
222
242
  phase.name = prefixed_phase_name