cocoapods 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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