xcocoapods 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6303 -0
  3. data/LICENSE +28 -0
  4. data/README.md +80 -0
  5. data/bin/pod +56 -0
  6. data/bin/sandbox-pod +168 -0
  7. data/lib/cocoapods.rb +73 -0
  8. data/lib/cocoapods/command.rb +175 -0
  9. data/lib/cocoapods/command/cache.rb +28 -0
  10. data/lib/cocoapods/command/cache/clean.rb +90 -0
  11. data/lib/cocoapods/command/cache/list.rb +69 -0
  12. data/lib/cocoapods/command/env.rb +66 -0
  13. data/lib/cocoapods/command/init.rb +128 -0
  14. data/lib/cocoapods/command/install.rb +45 -0
  15. data/lib/cocoapods/command/ipc.rb +19 -0
  16. data/lib/cocoapods/command/ipc/list.rb +40 -0
  17. data/lib/cocoapods/command/ipc/podfile.rb +31 -0
  18. data/lib/cocoapods/command/ipc/podfile_json.rb +30 -0
  19. data/lib/cocoapods/command/ipc/repl.rb +51 -0
  20. data/lib/cocoapods/command/ipc/spec.rb +29 -0
  21. data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
  22. data/lib/cocoapods/command/lib.rb +11 -0
  23. data/lib/cocoapods/command/lib/create.rb +105 -0
  24. data/lib/cocoapods/command/lib/lint.rb +121 -0
  25. data/lib/cocoapods/command/list.rb +39 -0
  26. data/lib/cocoapods/command/options/project_directory.rb +36 -0
  27. data/lib/cocoapods/command/options/repo_update.rb +34 -0
  28. data/lib/cocoapods/command/outdated.rb +140 -0
  29. data/lib/cocoapods/command/repo.rb +29 -0
  30. data/lib/cocoapods/command/repo/add.rb +103 -0
  31. data/lib/cocoapods/command/repo/lint.rb +82 -0
  32. data/lib/cocoapods/command/repo/list.rb +93 -0
  33. data/lib/cocoapods/command/repo/push.rb +281 -0
  34. data/lib/cocoapods/command/repo/remove.rb +36 -0
  35. data/lib/cocoapods/command/repo/update.rb +28 -0
  36. data/lib/cocoapods/command/setup.rb +103 -0
  37. data/lib/cocoapods/command/spec.rb +112 -0
  38. data/lib/cocoapods/command/spec/cat.rb +51 -0
  39. data/lib/cocoapods/command/spec/create.rb +283 -0
  40. data/lib/cocoapods/command/spec/edit.rb +87 -0
  41. data/lib/cocoapods/command/spec/env_spec.rb +53 -0
  42. data/lib/cocoapods/command/spec/lint.rb +137 -0
  43. data/lib/cocoapods/command/spec/which.rb +43 -0
  44. data/lib/cocoapods/command/update.rb +101 -0
  45. data/lib/cocoapods/config.rb +347 -0
  46. data/lib/cocoapods/core_overrides.rb +1 -0
  47. data/lib/cocoapods/downloader.rb +190 -0
  48. data/lib/cocoapods/downloader/cache.rb +233 -0
  49. data/lib/cocoapods/downloader/request.rb +86 -0
  50. data/lib/cocoapods/downloader/response.rb +16 -0
  51. data/lib/cocoapods/executable.rb +222 -0
  52. data/lib/cocoapods/external_sources.rb +57 -0
  53. data/lib/cocoapods/external_sources/abstract_external_source.rb +205 -0
  54. data/lib/cocoapods/external_sources/downloader_source.rb +30 -0
  55. data/lib/cocoapods/external_sources/path_source.rb +55 -0
  56. data/lib/cocoapods/external_sources/podspec_source.rb +54 -0
  57. data/lib/cocoapods/gem_version.rb +5 -0
  58. data/lib/cocoapods/generator/acknowledgements.rb +107 -0
  59. data/lib/cocoapods/generator/acknowledgements/markdown.rb +44 -0
  60. data/lib/cocoapods/generator/acknowledgements/plist.rb +94 -0
  61. data/lib/cocoapods/generator/app_target_helper.rb +244 -0
  62. data/lib/cocoapods/generator/bridge_support.rb +22 -0
  63. data/lib/cocoapods/generator/constant.rb +19 -0
  64. data/lib/cocoapods/generator/copy_resources_script.rb +230 -0
  65. data/lib/cocoapods/generator/dummy_source.rb +31 -0
  66. data/lib/cocoapods/generator/embed_frameworks_script.rb +215 -0
  67. data/lib/cocoapods/generator/header.rb +103 -0
  68. data/lib/cocoapods/generator/info_plist_file.rb +116 -0
  69. data/lib/cocoapods/generator/module_map.rb +99 -0
  70. data/lib/cocoapods/generator/prefix_header.rb +60 -0
  71. data/lib/cocoapods/generator/umbrella_header.rb +46 -0
  72. data/lib/cocoapods/hooks_manager.rb +132 -0
  73. data/lib/cocoapods/installer.rb +703 -0
  74. data/lib/cocoapods/installer/analyzer.rb +972 -0
  75. data/lib/cocoapods/installer/analyzer/analysis_result.rb +87 -0
  76. data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +98 -0
  77. data/lib/cocoapods/installer/analyzer/pod_variant.rb +67 -0
  78. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +157 -0
  79. data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
  80. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +240 -0
  81. data/lib/cocoapods/installer/analyzer/specs_state.rb +84 -0
  82. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +53 -0
  83. data/lib/cocoapods/installer/analyzer/target_inspector.rb +260 -0
  84. data/lib/cocoapods/installer/installation_options.rb +158 -0
  85. data/lib/cocoapods/installer/pod_source_installer.rb +202 -0
  86. data/lib/cocoapods/installer/pod_source_preparer.rb +77 -0
  87. data/lib/cocoapods/installer/podfile_validator.rb +139 -0
  88. data/lib/cocoapods/installer/post_install_hooks_context.rb +132 -0
  89. data/lib/cocoapods/installer/pre_install_hooks_context.rb +51 -0
  90. data/lib/cocoapods/installer/source_provider_hooks_context.rb +34 -0
  91. data/lib/cocoapods/installer/user_project_integrator.rb +250 -0
  92. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +463 -0
  93. data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
  94. data/lib/cocoapods/installer/xcode.rb +8 -0
  95. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +416 -0
  96. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +181 -0
  97. data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +84 -0
  98. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +334 -0
  99. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +777 -0
  100. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +116 -0
  101. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +86 -0
  102. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +256 -0
  103. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer_helper.rb +68 -0
  104. data/lib/cocoapods/installer/xcode/target_validator.rb +147 -0
  105. data/lib/cocoapods/open-uri.rb +33 -0
  106. data/lib/cocoapods/project.rb +414 -0
  107. data/lib/cocoapods/resolver.rb +585 -0
  108. data/lib/cocoapods/resolver/lazy_specification.rb +79 -0
  109. data/lib/cocoapods/sandbox.rb +404 -0
  110. data/lib/cocoapods/sandbox/file_accessor.rb +444 -0
  111. data/lib/cocoapods/sandbox/headers_store.rb +146 -0
  112. data/lib/cocoapods/sandbox/path_list.rb +220 -0
  113. data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +85 -0
  114. data/lib/cocoapods/sandbox/podspec_finder.rb +23 -0
  115. data/lib/cocoapods/sources_manager.rb +157 -0
  116. data/lib/cocoapods/target.rb +261 -0
  117. data/lib/cocoapods/target/aggregate_target.rb +338 -0
  118. data/lib/cocoapods/target/build_settings.rb +1075 -0
  119. data/lib/cocoapods/target/pod_target.rb +559 -0
  120. data/lib/cocoapods/user_interface.rb +459 -0
  121. data/lib/cocoapods/user_interface/error_report.rb +187 -0
  122. data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
  123. data/lib/cocoapods/validator.rb +981 -0
  124. metadata +533 -0
@@ -0,0 +1,240 @@
1
+ module Pod
2
+ class Installer
3
+ class Analyzer
4
+ # Analyze the sandbox to detect which Pods should be removed, and which
5
+ # ones should be reinstalled.
6
+ #
7
+ # The logic is the following:
8
+ #
9
+ # Added
10
+ # - If not present in the sandbox lockfile.
11
+ # - The directory of the Pod doesn't exits.
12
+ #
13
+ # Changed
14
+ # - The version of the Pod changed.
15
+ # - The SHA of the specification file changed.
16
+ # - The specific installed (sub)specs of the same Pod changed.
17
+ # - The specification is from an external source and the
18
+ # installation process is in update mode.
19
+ # - The directory of the Pod is empty.
20
+ # - The Pod has been pre-downloaded.
21
+ #
22
+ # Removed
23
+ # - If a specification is present in the lockfile but not in the resolved
24
+ # specs.
25
+ #
26
+ # Unchanged
27
+ # - If none of the above conditions match.
28
+ #
29
+ class SandboxAnalyzer
30
+ # @return [Sandbox] The sandbox to analyze.
31
+ #
32
+ attr_reader :sandbox
33
+
34
+ # @return [Array<Specifications>] The specifications returned by the
35
+ # resolver.
36
+ #
37
+ attr_reader :specs
38
+
39
+ # @return [Bool] Whether the installation is performed in update mode.
40
+ #
41
+ attr_reader :update_mode
42
+
43
+ alias_method :update_mode?, :update_mode
44
+
45
+ # Init a new SandboxAnalyzer
46
+ #
47
+ # @param [Sandbox] sandbox @see sandbox
48
+ # @param [Array<Specifications>] specs @see specs
49
+ # @param [Bool] update_mode @see update_mode
50
+ #
51
+ def initialize(sandbox, specs, update_mode)
52
+ @sandbox = sandbox
53
+ @specs = specs
54
+ @update_mode = update_mode
55
+ end
56
+
57
+ # Performs the analysis to the detect the state of the sandbox respect
58
+ # to the resolved specifications.
59
+ #
60
+ # @return [void]
61
+ #
62
+ def analyze
63
+ state = SpecsState.new
64
+ if sandbox_manifest
65
+ all_names = (resolved_pods + sandbox_pods).uniq.sort
66
+ all_names.sort.each do |name|
67
+ state.add_name(name, pod_state(name))
68
+ end
69
+ else
70
+ state.added.merge(resolved_pods)
71
+ end
72
+ state
73
+ end
74
+
75
+ #---------------------------------------------------------------------#
76
+
77
+ private
78
+
79
+ # @!group Pod state
80
+
81
+ # Returns the state of the Pod with the given name.
82
+ #
83
+ # @param [String] pod
84
+ # the name of the Pod.
85
+ #
86
+ # @return [Symbol] The state
87
+ #
88
+ def pod_state(pod)
89
+ return :added if pod_added?(pod)
90
+ return :deleted if pod_deleted?(pod)
91
+ return :changed if pod_changed?(pod)
92
+ :unchanged
93
+ end
94
+
95
+ # Returns whether the Pod with the given name should be installed.
96
+ #
97
+ # @note A Pod whose folder doesn't exists is considered added.
98
+ #
99
+ # @param [String] pod
100
+ # the name of the Pod.
101
+ #
102
+ # @return [Bool] Whether the Pod is added.
103
+ #
104
+ def pod_added?(pod)
105
+ return true if resolved_pods.include?(pod) && !sandbox_pods.include?(pod)
106
+ return true if !folder_exist?(pod) && !sandbox.local?(pod)
107
+ false
108
+ end
109
+
110
+ # Returns whether the Pod with the given name should be removed from
111
+ # the installation.
112
+ #
113
+ # @param [String] pod
114
+ # the name of the Pod.
115
+ #
116
+ # @return [Bool] Whether the Pod is deleted.
117
+ #
118
+ def pod_deleted?(pod)
119
+ return true if !resolved_pods.include?(pod) && sandbox_pods.include?(pod)
120
+ false
121
+ end
122
+
123
+ # Returns whether the Pod with the given name should be considered
124
+ # changed and thus should be reinstalled.
125
+ #
126
+ # @note In update mode, as there is no way to know if a remote source
127
+ # hash changed the Pods from external
128
+ # sources are always marked as changed.
129
+ #
130
+ # @note A Pod whose folder is empty is considered changed.
131
+ #
132
+ # @param [String] pod
133
+ # the name of the Pod.
134
+ #
135
+ # @return [Bool] Whether the Pod is changed.
136
+ #
137
+ def pod_changed?(pod)
138
+ spec = root_spec(pod)
139
+ return true if spec.version != sandbox_version(pod)
140
+ return true if spec.checksum != sandbox_checksum(pod)
141
+ return true if resolved_spec_names(pod) != sandbox_spec_names(pod)
142
+ return true if sandbox.predownloaded?(pod)
143
+ return true if folder_empty?(pod)
144
+ false
145
+ end
146
+
147
+ #---------------------------------------------------------------------#
148
+
149
+ private
150
+
151
+ # @!group Private helpers
152
+
153
+ # @return [Lockfile] The manifest to use for the sandbox.
154
+ #
155
+ def sandbox_manifest
156
+ sandbox.manifest
157
+ end
158
+
159
+ #--------------------------------------#
160
+
161
+ # @return [Array<String>] The name of the resolved Pods.
162
+ #
163
+ def resolved_pods
164
+ specs.map { |spec| spec.root.name }.uniq
165
+ end
166
+
167
+ # @return [Array<String>] The name of the Pods stored in the sandbox
168
+ # manifest.
169
+ #
170
+ def sandbox_pods
171
+ sandbox_manifest.pod_names.map { |name| Specification.root_name(name) }.uniq
172
+ end
173
+
174
+ # @return [Array<String>] The name of the resolved specifications
175
+ # (includes subspecs).
176
+ #
177
+ # @param [String] pod
178
+ # the name of the Pod.
179
+ #
180
+ def resolved_spec_names(pod)
181
+ specs.select { |s| s.root.name == pod }.map(&:name).uniq.sort
182
+ end
183
+
184
+ # @return [Array<String>] The name of the specifications stored in the
185
+ # sandbox manifest (includes subspecs).
186
+ #
187
+ # @param [String] pod
188
+ # the name of the Pod.
189
+ #
190
+ def sandbox_spec_names(pod)
191
+ sandbox_manifest.pod_names.select { |name| Specification.root_name(name) == pod }.uniq.sort
192
+ end
193
+
194
+ # @return [Specification] The root specification for the Pod with the
195
+ # given name.
196
+ #
197
+ # @param [String] pod
198
+ # the name of the Pod.
199
+ #
200
+ def root_spec(pod)
201
+ specs.find { |s| s.root.name == pod }.root
202
+ end
203
+
204
+ #--------------------------------------#
205
+
206
+ # @return [Version] The version of Pod with the given name stored in
207
+ # the sandbox.
208
+ #
209
+ # @param [String] pod
210
+ # the name of the Pod.
211
+ #
212
+ def sandbox_version(pod)
213
+ sandbox_manifest.version(pod)
214
+ end
215
+
216
+ # @return [String] The checksum of the specification of the Pod with
217
+ # the given name stored in the sandbox.
218
+ #
219
+ # @param [String] pod
220
+ # the name of the Pod.
221
+ #
222
+ def sandbox_checksum(pod)
223
+ sandbox_manifest.checksum(pod)
224
+ end
225
+
226
+ #--------------------------------------#
227
+
228
+ def folder_exist?(pod)
229
+ sandbox.pod_dir(pod).exist?
230
+ end
231
+
232
+ def folder_empty?(pod)
233
+ Dir.glob(sandbox.pod_dir(pod) + '*').empty?
234
+ end
235
+
236
+ #---------------------------------------------------------------------#
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,84 @@
1
+ require 'set'
2
+
3
+ module Pod
4
+ class Installer
5
+ class Analyzer
6
+ # This class represents the state of a collection of Pods.
7
+ #
8
+ # @note The names of the pods stored by this class are always the **root**
9
+ # name of the specification.
10
+ #
11
+ # @note The motivation for this class is to ensure that the names of the
12
+ # subspecs are added instead of the name of the Pods.
13
+ #
14
+ class SpecsState
15
+ # @return [Set<String>] the names of the pods that were added.
16
+ #
17
+ attr_reader :added
18
+
19
+ # @return [Set<String>] the names of the pods that were changed.
20
+ #
21
+ attr_reader :changed
22
+
23
+ # @return [Set<String>] the names of the pods that were deleted.
24
+ #
25
+ attr_reader :deleted
26
+
27
+ # @return [Set<String>] the names of the pods that were unchanged.
28
+ #
29
+ attr_reader :unchanged
30
+
31
+ # Initialize a new instance
32
+ #
33
+ # @param [Hash{Symbol=>String}] pods_by_state
34
+ # The name of the pods grouped by their state
35
+ # (`:added`, `:removed`, `:changed` or `:unchanged`).
36
+ #
37
+ def initialize(pods_by_state = nil)
38
+ @added = Set.new
39
+ @deleted = Set.new
40
+ @changed = Set.new
41
+ @unchanged = Set.new
42
+
43
+ if pods_by_state
44
+ {
45
+ :added => :added,
46
+ :changed => :changed,
47
+ :removed => :deleted,
48
+ :unchanged => :unchanged,
49
+ }.each do |state, spec_state|
50
+ Array(pods_by_state[state]).each do |name|
51
+ add_name(name, spec_state)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # Displays the state of each pod.
58
+ #
59
+ # @return [void]
60
+ #
61
+ def print
62
+ added .sort.each { |pod| UI.message('A'.green + " #{pod}", '', 2) }
63
+ deleted .sort.each { |pod| UI.message('R'.red + " #{pod}", '', 2) }
64
+ changed .sort.each { |pod| UI.message('M'.yellow + " #{pod}", '', 2) }
65
+ unchanged.sort.each { |pod| UI.message('-' + " #{pod}", '', 2) }
66
+ end
67
+
68
+ # Adds the name of a Pod to the give state.
69
+ #
70
+ # @param [String] name
71
+ # the name of the Pod.
72
+ #
73
+ # @param [Symbol] state
74
+ # the state of the Pod.
75
+ #
76
+ # @return [void]
77
+ #
78
+ def add_name(name, state)
79
+ send(state) << Specification.root_name(name)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,53 @@
1
+ module Pod
2
+ class Installer
3
+ class Analyzer
4
+ class TargetInspectionResult
5
+ # @return [TargetDefinition] the target definition, whose project was
6
+ # inspected
7
+ #
8
+ attr_reader :target_definition
9
+
10
+ # @return [Xcodeproj::Project] the user's Xcode project
11
+ #
12
+ attr_reader :project
13
+
14
+ # @return [Array<String>] the uuid of the user's targets
15
+ #
16
+ attr_reader :project_target_uuids
17
+
18
+ # @return [Hash{String=>Symbol}] A hash representing the user build
19
+ # configurations where each key corresponds to the name of a
20
+ # configuration and its value to its type (`:debug` or
21
+ # `:release`).
22
+ #
23
+ attr_reader :build_configurations
24
+
25
+ # @return [Platform] the platform of the user targets
26
+ #
27
+ attr_reader :platform
28
+
29
+ # @return [Array<String>] the architectures used by user's targets
30
+ #
31
+ attr_reader :archs
32
+
33
+ # Initialize a new instance
34
+ #
35
+ # @param [TargetDefinition] target_definition @see #target_definition
36
+ # @param [Xcodeproj::Project] project @see #project
37
+ # @param [Array<String>] project_target_uuids @see #project_target_uuids
38
+ # @param [Hash{String=>Symbol}] build_configurations @see #build_configurations
39
+ # @param [Platform] platform @see #platform
40
+ # @param [Array<String>] archs @see #archs
41
+ #
42
+ def initialize(target_definition, project, project_target_uuids, build_configurations, platform, archs)
43
+ @target_definition = target_definition
44
+ @project = project
45
+ @project_target_uuids = project_target_uuids
46
+ @build_configurations = build_configurations
47
+ @platform = platform
48
+ @archs = archs
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,260 @@
1
+ require 'active_support/core_ext/array/conversions'
2
+
3
+ module Pod
4
+ class Installer
5
+ class Analyzer
6
+ class TargetInspector
7
+ PLATFORM_INFO_URL = 'https://guides.cocoapods.org/syntax/podfile.html#platform'.freeze
8
+
9
+ # @return [TargetDefinition] the target definition to inspect
10
+ #
11
+ attr_reader :target_definition
12
+
13
+ # @return [Pathname] the root of the CocoaPods installation where the
14
+ # Podfile is located
15
+ #
16
+ attr_reader :installation_root
17
+
18
+ # Initialize a new instance
19
+ #
20
+ # @param [TargetDefinition] target_definition
21
+ # @see #target_definition
22
+ #
23
+ # @param [Pathname] installation_root
24
+ # @see #installation_root
25
+ #
26
+ def initialize(target_definition, installation_root)
27
+ @target_definition = target_definition
28
+ @installation_root = installation_root
29
+ end
30
+
31
+ # Inspect the #target_definition
32
+ #
33
+ # @raise If no `user_project` is set
34
+ #
35
+ # @return [TargetInspectionResult] the result of the inspection of the target definition within the user project
36
+ #
37
+ def compute_results(user_project)
38
+ raise ArgumentError, 'Cannot compute results without a user project set' unless user_project
39
+
40
+ targets = compute_targets(user_project)
41
+ project_target_uuids = targets.map(&:uuid)
42
+ build_configurations = compute_build_configurations(targets)
43
+ platform = compute_platform(targets)
44
+ archs = compute_archs(targets)
45
+ swift_version = compute_swift_version_from_targets(targets)
46
+
47
+ result = TargetInspectionResult.new(target_definition, user_project, project_target_uuids,
48
+ build_configurations, platform, archs)
49
+ result.target_definition.swift_version = swift_version
50
+ result
51
+ end
52
+
53
+ # Returns the path of the user project that the #target_definition
54
+ # should integrate.
55
+ #
56
+ # @raise If the project is implicit and there are multiple projects.
57
+ #
58
+ # @raise If the path doesn't exits.
59
+ #
60
+ # @return [Pathname] the path of the user project.
61
+ #
62
+ def compute_project_path
63
+ if target_definition.user_project_path
64
+ path = installation_root + target_definition.user_project_path
65
+ path = "#{path}.xcodeproj" unless File.extname(path) == '.xcodeproj'
66
+ path = Pathname.new(path)
67
+ unless path.exist?
68
+ raise Informative, 'Unable to find the Xcode project ' \
69
+ "`#{path}` for the target `#{target_definition.label}`."
70
+ end
71
+ else
72
+ xcodeprojs = installation_root.children.select { |e| e.fnmatch('*.xcodeproj') }
73
+ if xcodeprojs.size == 1
74
+ path = xcodeprojs.first
75
+ else
76
+ raise Informative, 'Could not automatically select an Xcode project. ' \
77
+ "Specify one in your Podfile like so:\n\n" \
78
+ " project 'path/to/Project.xcodeproj'\n"
79
+ end
80
+ end
81
+ path
82
+ end
83
+
84
+ #-----------------------------------------------------------------------#
85
+
86
+ private
87
+
88
+ # Returns a list of the targets from the project of #target_definition
89
+ # that needs to be integrated.
90
+ #
91
+ # @note The method first looks if there is a target specified with
92
+ # the `link_with` option of the {TargetDefinition}. Otherwise
93
+ # it looks for the target that has the same name of the target
94
+ # definition. Finally if no target was found the first
95
+ # encountered target is returned (it is assumed to be the one
96
+ # to integrate in simple projects).
97
+ #
98
+ # @param [Xcodeproj::Project] user_project
99
+ # the user project
100
+ #
101
+ # @return [Array<PBXNativeTarget>]
102
+ #
103
+ def compute_targets(user_project)
104
+ native_targets = user_project.native_targets
105
+ target = native_targets.find { |t| t.name == target_definition.name.to_s }
106
+ unless target
107
+ found = native_targets.map { |t| "`#{t.name}`" }.to_sentence
108
+ raise Informative, "Unable to find a target named `#{target_definition.name}`, did find #{found}."
109
+ end
110
+ [target]
111
+ end
112
+
113
+ # @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
114
+ # #target_definition which needs to be integrated
115
+ #
116
+ # @return [Hash{String=>Symbol}] A hash representing the user build
117
+ # configurations where each key corresponds to the name of a
118
+ # configuration and its value to its type (`:debug` or `:release`).
119
+ #
120
+ def compute_build_configurations(user_targets)
121
+ if user_targets
122
+ user_targets.flat_map { |t| t.build_configurations.map(&:name) }.each_with_object({}) do |name, hash|
123
+ hash[name] = name == 'Debug' ? :debug : :release
124
+ end.merge(target_definition.build_configurations || {})
125
+ else
126
+ target_definition.build_configurations || {}
127
+ end
128
+ end
129
+
130
+ # @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
131
+ # #target_definition which needs to be integrated
132
+ #
133
+ # @return [Platform] The platform of the user's targets
134
+ #
135
+ # @note This resolves to the lowest deployment target across the user
136
+ # targets.
137
+ #
138
+ # @todo Is assigning the platform to the target definition the best way
139
+ # to go?
140
+ #
141
+ def compute_platform(user_targets)
142
+ return target_definition.platform if target_definition.platform
143
+ name = nil
144
+ deployment_target = nil
145
+
146
+ user_targets.each do |target|
147
+ name ||= target.platform_name
148
+ raise Informative, 'Targets with different platforms' unless name == target.platform_name
149
+ if !deployment_target || deployment_target > Version.new(target.deployment_target)
150
+ deployment_target = Version.new(target.deployment_target)
151
+ end
152
+ end
153
+
154
+ unless name
155
+ raise Informative,
156
+ "Unable to determine the platform for the `#{target_definition.name}` target."
157
+ end
158
+
159
+ UI.warn "Automatically assigning platform `#{name}` with version `#{deployment_target}` " \
160
+ "on target `#{target_definition.name}` because no platform was specified. " \
161
+ "Please specify a platform for this target in your Podfile. See `#{PLATFORM_INFO_URL}`."
162
+
163
+ target_definition.set_platform(name, deployment_target)
164
+ Platform.new(name, deployment_target)
165
+ end
166
+
167
+ # Computes the architectures relevant for the user's targets.
168
+ #
169
+ # @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
170
+ # #target_definition which needs to be integrated
171
+ #
172
+ # @return [Array<String>]
173
+ #
174
+ def compute_archs(user_targets)
175
+ user_targets.flat_map do |target|
176
+ Array(target.common_resolved_build_setting('ARCHS'))
177
+ end.compact.uniq.sort
178
+ end
179
+
180
+ # Checks if any of the targets for the {TargetDefinition} computed before
181
+ # by #compute_user_project_targets is recommended to be build as a framework
182
+ # due the presence of Swift source code in any of the source build phases.
183
+ #
184
+ # @param [TargetDefinition] target_definition
185
+ # the target definition
186
+ #
187
+ # @param [Array<PBXNativeTarget>] native_targets
188
+ # the targets which are checked for presence of Swift source code
189
+ #
190
+ # @return [Boolean] Whether the user project targets to integrate into
191
+ # uses Swift
192
+ #
193
+ def compute_recommends_frameworks(target_definition, native_targets)
194
+ file_predicate = nil
195
+ file_predicate = proc do |file_ref|
196
+ if file_ref.respond_to?(:last_known_file_type)
197
+ file_ref.last_known_file_type == 'sourcecode.swift'
198
+ elsif file_ref.respond_to?(:files)
199
+ file_ref.files.any?(&file_predicate)
200
+ else
201
+ false
202
+ end
203
+ end
204
+ target_definition.platform.supports_dynamic_frameworks? || native_targets.any? do |target|
205
+ target.source_build_phase.files.any? do |build_file|
206
+ file_predicate.call(build_file.file_ref)
207
+ end
208
+ end
209
+ end
210
+
211
+ # Compute the Swift version for the target build configurations. If more
212
+ # than one Swift version is defined for a given target, then it will raise.
213
+ #
214
+ # @param [Array<PBXNativeTarget>] targets
215
+ # the targets that are checked for Swift versions.
216
+ #
217
+ # @return [String] the targets Swift version or nil
218
+ #
219
+ def compute_swift_version_from_targets(targets)
220
+ versions_to_targets = targets.inject({}) do |memo, target|
221
+ # User project may have an xcconfig that specifies the `SWIFT_VERSION`. We first check if that is true and
222
+ # that the xcconfig file actually exists. After the first integration the xcconfig set is most probably
223
+ # the one that was generated from CocoaPods. See https://github.com/CocoaPods/CocoaPods/issues/7731 for
224
+ # more details.
225
+ resolve_against_xcconfig = target.build_configuration_list.build_configurations.all? do |bc|
226
+ !bc.base_configuration_reference.nil? && File.exist?(bc.base_configuration_reference.real_path)
227
+ end
228
+ versions = target.resolved_build_setting('SWIFT_VERSION', resolve_against_xcconfig).values
229
+ versions.each do |version|
230
+ memo[version] = [] if memo[version].nil?
231
+ memo[version] << target.name unless memo[version].include? target.name
232
+ end
233
+ memo
234
+ end
235
+
236
+ case versions_to_targets.count
237
+ when 0
238
+ nil
239
+ when 1
240
+ versions_to_targets.keys.first
241
+ else
242
+ target_version_pairs = versions_to_targets.map do |version_names, target_names|
243
+ target_names.map { |target_name| [target_name, version_names] }
244
+ end
245
+
246
+ sorted_pairs = target_version_pairs.flat_map { |i| i }.sort_by do |target_name, version_name|
247
+ "#{target_name} #{version_name}"
248
+ end
249
+
250
+ formatted_output = sorted_pairs.map do |target, version_name|
251
+ "#{target}: Swift #{version_name}"
252
+ end.join("\n")
253
+
254
+ raise Informative, "There may only be up to 1 unique SWIFT_VERSION per target. Found target(s) with multiple Swift versions:\n#{formatted_output}"
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end