cocoapods 0.37.2 → 0.38.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +136 -1
  3. data/lib/cocoapods.rb +0 -5
  4. data/lib/cocoapods/command.rb +3 -0
  5. data/lib/cocoapods/command/cache.rb +28 -0
  6. data/lib/cocoapods/command/cache/clean.rb +90 -0
  7. data/lib/cocoapods/command/cache/list.rb +69 -0
  8. data/lib/cocoapods/command/lib.rb +11 -4
  9. data/lib/cocoapods/command/list.rb +4 -4
  10. data/lib/cocoapods/command/outdated.rb +1 -10
  11. data/lib/cocoapods/command/project.rb +3 -2
  12. data/lib/cocoapods/command/spec.rb +0 -17
  13. data/lib/cocoapods/command/spec/cat.rb +1 -1
  14. data/lib/cocoapods/command/spec/create.rb +1 -0
  15. data/lib/cocoapods/command/spec/edit.rb +1 -1
  16. data/lib/cocoapods/command/spec/lint.rb +10 -4
  17. data/lib/cocoapods/config.rb +6 -0
  18. data/lib/cocoapods/downloader/cache.rb +48 -1
  19. data/lib/cocoapods/executable.rb +27 -6
  20. data/lib/cocoapods/gem_version.rb +1 -1
  21. data/lib/cocoapods/generator/copy_resources_script.rb +1 -0
  22. data/lib/cocoapods/generator/embed_frameworks_script.rb +23 -28
  23. data/lib/cocoapods/generator/header.rb +5 -1
  24. data/lib/cocoapods/generator/umbrella_header.rb +1 -1
  25. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +139 -33
  26. data/lib/cocoapods/generator/xcconfig/private_pod_xcconfig.rb +2 -2
  27. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +3 -3
  28. data/lib/cocoapods/installer.rb +64 -109
  29. data/lib/cocoapods/installer/analyzer.rb +167 -336
  30. data/lib/cocoapods/installer/analyzer/analysis_result.rb +46 -0
  31. data/lib/cocoapods/installer/analyzer/specs_state.rb +76 -0
  32. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +41 -0
  33. data/lib/cocoapods/installer/analyzer/target_inspector.rb +203 -0
  34. data/lib/cocoapods/installer/file_references_installer.rb +48 -13
  35. data/lib/cocoapods/installer/podfile_validator.rb +86 -0
  36. data/lib/cocoapods/installer/{hooks_context.rb → post_install_hooks_context.rb} +3 -3
  37. data/lib/cocoapods/installer/pre_install_hooks_context.rb +41 -0
  38. data/lib/cocoapods/installer/target_installer.rb +1 -7
  39. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +15 -17
  40. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +4 -4
  41. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +16 -16
  42. data/lib/cocoapods/sandbox/file_accessor.rb +20 -2
  43. data/lib/cocoapods/sandbox/path_list.rb +15 -13
  44. data/lib/cocoapods/sandbox/podspec_finder.rb +1 -0
  45. data/lib/cocoapods/sources_manager.rb +2 -0
  46. data/lib/cocoapods/target.rb +7 -37
  47. data/lib/cocoapods/target/aggregate_target.rb +25 -1
  48. data/lib/cocoapods/target/pod_target.rb +106 -10
  49. data/lib/cocoapods/user_interface.rb +26 -0
  50. data/lib/cocoapods/user_interface/error_report.rb +6 -0
  51. data/lib/cocoapods/validator.rb +22 -0
  52. metadata +21 -16
  53. data/lib/cocoapods/generator/target_environment_header.rb +0 -171
  54. data/lib/cocoapods/hooks/installer_representation.rb +0 -133
  55. data/lib/cocoapods/hooks/library_representation.rb +0 -93
  56. data/lib/cocoapods/hooks/pod_representation.rb +0 -70
@@ -0,0 +1,46 @@
1
+ module Pod
2
+ class Installer
3
+ class Analyzer
4
+ class AnalysisResult
5
+ # @return [SpecsState] the states of the Podfile specs.
6
+ #
7
+ attr_accessor :podfile_state
8
+
9
+ # @return [Hash{TargetDefinition => Array<Specification>}] the
10
+ # specifications grouped by target.
11
+ #
12
+ attr_accessor :specs_by_target
13
+
14
+ # @return [Array<Specification>] the specifications of the resolved
15
+ # version of Pods that should be installed.
16
+ #
17
+ attr_accessor :specifications
18
+
19
+ # @return [SpecsState] the states of the {Sandbox} respect the resolved
20
+ # specifications.
21
+ #
22
+ attr_accessor :sandbox_state
23
+
24
+ # @return [Array<Target>] The Podfile targets containing library
25
+ # dependencies.
26
+ #
27
+ attr_accessor :targets
28
+
29
+ # @return [Hash{TargetDefinition => Array<TargetInspectionResult>}] the
30
+ # results of inspecting the user targets
31
+ attr_accessor :target_inspections
32
+
33
+ # @return [Hash{String=>Symbol}] A hash representing all the user build
34
+ # configurations across all integration targets. Each key
35
+ # corresponds to the name of a configuration and its value to
36
+ # its type (`:debug` or `:release`).
37
+ #
38
+ def all_user_build_configurations
39
+ targets.reduce({}) do |result, target|
40
+ result.merge(target.user_build_configurations)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ module Pod
2
+ class Installer
3
+ class Analyzer
4
+ # This class represents the state of a collection of Pods.
5
+ #
6
+ # @note The names of the pods stored by this class are always the **root**
7
+ # name of the specification.
8
+ #
9
+ # @note The motivation for this class is to ensure that the names of the
10
+ # subspecs are added instead of the name of the Pods.
11
+ #
12
+ class SpecsState
13
+ # Initialize a new instance
14
+ #
15
+ # @param [Hash{Symbol=>String}] pods_by_state
16
+ # The name of the pods grouped by their state
17
+ # (`:added`, `:removed`, `:changed` or `:unchanged`).
18
+ #
19
+ def initialize(pods_by_state = nil)
20
+ @added = []
21
+ @deleted = []
22
+ @changed = []
23
+ @unchanged = []
24
+
25
+ if pods_by_state
26
+ @added = pods_by_state[:added] || []
27
+ @deleted = pods_by_state[:removed] || []
28
+ @changed = pods_by_state[:changed] || []
29
+ @unchanged = pods_by_state[:unchanged] || []
30
+ end
31
+ end
32
+
33
+ # @return [Array<String>] the names of the pods that were added.
34
+ #
35
+ attr_accessor :added
36
+
37
+ # @return [Array<String>] the names of the pods that were changed.
38
+ #
39
+ attr_accessor :changed
40
+
41
+ # @return [Array<String>] the names of the pods that were deleted.
42
+ #
43
+ attr_accessor :deleted
44
+
45
+ # @return [Array<String>] the names of the pods that were unchanged.
46
+ #
47
+ attr_accessor :unchanged
48
+
49
+ # Displays the state of each pod.
50
+ #
51
+ # @return [void]
52
+ #
53
+ def print
54
+ added .sort.each { |pod| UI.message('A'.green + " #{pod}", '', 2) }
55
+ deleted .sort.each { |pod| UI.message('R'.red + " #{pod}", '', 2) }
56
+ changed .sort.each { |pod| UI.message('M'.yellow + " #{pod}", '', 2) }
57
+ unchanged.sort.each { |pod| UI.message('-' + " #{pod}", '', 2) }
58
+ end
59
+
60
+ # Adds the name of a Pod to the give state.
61
+ #
62
+ # @param [String] name
63
+ # the name of the Pod.
64
+ #
65
+ # @param [Symbol] state
66
+ # the state of the Pod.
67
+ #
68
+ # @return [void]
69
+ #
70
+ def add_name(name, state)
71
+ send(state) << name
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,41 @@
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_accessor :target_definition
9
+
10
+ # @return [Pathname] the path of the user project that the
11
+ # #target_definition should integrate
12
+ #
13
+ attr_accessor :project_path
14
+
15
+ # @return [Array<String>] the uuid of the user's targets
16
+ #
17
+ attr_accessor :project_target_uuids
18
+
19
+ # @return [Hash{String=>Symbol}] A hash representing the user build
20
+ # configurations where each key corresponds to the name of a
21
+ # configuration and its value to its type (`:debug` or
22
+ # `:release`).
23
+ #
24
+ attr_accessor :build_configurations
25
+
26
+ # @return [Platform] the platform of the user targets
27
+ #
28
+ attr_accessor :platform
29
+
30
+ # @return [Array<String>] the architectures used by user's targets
31
+ #
32
+ attr_accessor :archs
33
+
34
+ # @return [Bool] whether frameworks are recommended for the integration
35
+ # due to the presence of Swift source in the user's targets
36
+ #
37
+ attr_accessor :recommends_frameworks
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,203 @@
1
+ module Pod
2
+ class Installer
3
+ class Analyzer
4
+ class TargetInspector
5
+ # @return [TargetDefinition] the target definition to inspect
6
+ #
7
+ attr_accessor :target_definition
8
+
9
+ # @return [Pathname] the root of the CocoaPods installation where the
10
+ # Podfile is located
11
+ attr_accessor :installation_root
12
+
13
+ # Initialize a new instance
14
+ #
15
+ # @param [TargetDefinition] target_definition
16
+ # @see #target_definition
17
+ #
18
+ # @param [Pathname] installation_root
19
+ # @see #installation_root
20
+ #
21
+ def initialize(target_definition, installation_root)
22
+ @target_definition = target_definition
23
+ @installation_root = installation_root
24
+ end
25
+
26
+ # Inspect the #target_definition
27
+ #
28
+ # @return [TargetInspectionResult]
29
+ #
30
+ def compute_results
31
+ project_path = compute_project_path
32
+ user_project = Xcodeproj::Project.open(project_path)
33
+ targets = compute_targets(user_project)
34
+
35
+ result = TargetInspectionResult.new
36
+ result.target_definition = target_definition
37
+ result.project_path = project_path
38
+ result.project_target_uuids = targets.map(&:uuid)
39
+ result.build_configurations = compute_build_configurations(targets)
40
+ result.platform = compute_platform(targets)
41
+ result.archs = compute_archs(targets)
42
+ result
43
+ end
44
+
45
+ #-----------------------------------------------------------------------#
46
+
47
+ private
48
+
49
+ # Returns the path of the user project that the #target_definition
50
+ # should integrate.
51
+ #
52
+ # @raise If the project is implicit and there are multiple projects.
53
+ #
54
+ # @raise If the path doesn't exits.
55
+ #
56
+ # @return [Pathname] the path of the user project.
57
+ #
58
+ def compute_project_path
59
+ if target_definition.user_project_path
60
+ path = installation_root + target_definition.user_project_path
61
+ path = "#{path}.xcodeproj" unless File.extname(path) == '.xcodeproj'
62
+ path = Pathname.new(path)
63
+ unless path.exist?
64
+ raise Informative, 'Unable to find the Xcode project ' \
65
+ "`#{path}` for the target `#{target_definition.label}`."
66
+ end
67
+ else
68
+ xcodeprojs = installation_root.children.select { |e| e.fnmatch('*.xcodeproj') }
69
+ if xcodeprojs.size == 1
70
+ path = xcodeprojs.first
71
+ else
72
+ raise Informative, 'Could not automatically select an Xcode project. ' \
73
+ "Specify one in your Podfile like so:\n\n" \
74
+ " xcodeproj 'path/to/Project.xcodeproj'\n"
75
+ end
76
+ end
77
+ path
78
+ end
79
+
80
+ # Returns a list of the targets from the project of #target_definition
81
+ # that needs to be integrated.
82
+ #
83
+ # @note The method first looks if there is a target specified with
84
+ # the `link_with` option of the {TargetDefinition}. Otherwise
85
+ # it looks for the target that has the same name of the target
86
+ # definition. Finally if no target was found the first
87
+ # encountered target is returned (it is assumed to be the one
88
+ # to integrate in simple projects).
89
+ #
90
+ # @param [Xcodeproj::Project] user_project
91
+ # the user project
92
+ #
93
+ # @return [Array<PBXNativeTarget>]
94
+ #
95
+ def compute_targets(user_project)
96
+ native_targets = user_project.native_targets
97
+ if link_with = target_definition.link_with
98
+ targets = native_targets.select { |t| link_with.include?(t.name) }
99
+ raise Informative, "Unable to find the targets named #{link_with.map { |x| "`#{x}`" }.to_sentence}" \
100
+ "to link with target definition `#{target_definition.name}`" if targets.empty?
101
+ elsif target_definition.link_with_first_target?
102
+ targets = [native_targets.first].compact
103
+ raise Informative, 'Unable to find a target' if targets.empty?
104
+ else
105
+ target = native_targets.find { |t| t.name == target_definition.name.to_s }
106
+ targets = [target].compact
107
+ raise Informative, "Unable to find a target named `#{target_definition.name}`" if targets.empty?
108
+ end
109
+ targets
110
+ end
111
+
112
+ # @param [Array<PBXNativeTarget] the user's targets of the project of
113
+ # #target_definition which needs to be integrated
114
+ #
115
+ # @return [Hash{String=>Symbol}] A hash representing the user build
116
+ # configurations where each key corresponds to the name of a
117
+ # configuration and its value to its type (`:debug` or `:release`).
118
+ #
119
+ def compute_build_configurations(user_targets)
120
+ if user_targets
121
+ user_targets.flat_map { |t| t.build_configurations.map(&:name) }.each_with_object({}) do |name, hash|
122
+ hash[name] = name == 'Debug' ? :debug : :release
123
+ end.merge(target_definition.build_configurations || {})
124
+ else
125
+ target_definition.build_configurations || {}
126
+ end
127
+ end
128
+
129
+ # @param [Array<PBXNativeTarget] the user's targets of the project of
130
+ # #target_definition which needs to be integrated
131
+ #
132
+ # @return [Platform] The platform of the user's targets
133
+ #
134
+ # @note This resolves to the lowest deployment target across the user
135
+ # targets.
136
+ #
137
+ # @todo Is assigning the platform to the target definition the best way
138
+ # to go?
139
+ #
140
+ def compute_platform(user_targets)
141
+ return target_definition.platform if target_definition.platform
142
+ name = nil
143
+ deployment_target = nil
144
+
145
+ user_targets.each do |target|
146
+ name ||= target.platform_name
147
+ raise Informative, 'Targets with different platforms' unless name == target.platform_name
148
+ if !deployment_target || deployment_target > Version.new(target.deployment_target)
149
+ deployment_target = Version.new(target.deployment_target)
150
+ end
151
+ end
152
+
153
+ target_definition.set_platform(name, deployment_target)
154
+ Platform.new(name, deployment_target)
155
+ end
156
+
157
+ # Computes the architectures relevant for the user's targets.
158
+ #
159
+ # @param [Array<PBXNativeTarget] the user's targets of the project of
160
+ # #target_definition which needs to be integrated
161
+ #
162
+ # @return [Array<String>]
163
+ #
164
+ def compute_archs(user_targets)
165
+ user_targets.flat_map do |target|
166
+ Array(target.common_resolved_build_setting('ARCHS'))
167
+ end.compact.uniq.sort
168
+ end
169
+
170
+ # Checks if any of the targets for the {TargetDefinition} computed before
171
+ # by #compute_user_project_targets is recommended to be build as a framework
172
+ # due the presence of Swift source code in any of the source build phases.
173
+ #
174
+ # @param [TargetDefinition] target_definition
175
+ # the target definition
176
+ #
177
+ # @param [Array<PBXNativeTarget>] native_targets
178
+ # the targets which are checked for presence of Swift source code
179
+ #
180
+ # @return [Boolean] Whether the user project targets to integrate into
181
+ # uses Swift
182
+ #
183
+ def compute_recommends_frameworks(target_definition, native_targets)
184
+ file_predicate = nil
185
+ file_predicate = proc do |file_ref|
186
+ if file_ref.respond_to?(:last_known_file_type)
187
+ file_ref.last_known_file_type == 'sourcecode.swift'
188
+ elsif file_ref.respond_to?(:files)
189
+ file_ref.files.any?(&file_predicate)
190
+ else
191
+ false
192
+ end
193
+ end
194
+ target_definition.platform.supports_dynamic_frameworks? || native_targets.any? do |target|
195
+ target.source_build_phase.files.any? do |build_file|
196
+ file_predicate.call(build_file.file_ref)
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -8,9 +8,9 @@ module Pod
8
8
  #
9
9
  attr_reader :sandbox
10
10
 
11
- # @return [Array<PodTarget>] The libraries of the installation.
11
+ # @return [Array<PodTarget>] The pod targets of the installation.
12
12
  #
13
- attr_reader :libraries
13
+ attr_reader :pod_targets
14
14
 
15
15
  # @return [Project] The Pods project.
16
16
  #
@@ -19,12 +19,12 @@ module Pod
19
19
  # Initialize a new instance
20
20
  #
21
21
  # @param [Sandbox] sandbox @see sandbox
22
- # @param [Array<PodTarget>] libraries @see libraries
23
- # @param [Project] libraries @see libraries
22
+ # @param [Array<PodTarget>] pod_targets @see pod_targets
23
+ # @param [Project] pods_project @see pod_project
24
24
  #
25
- def initialize(sandbox, libraries, pods_project)
25
+ def initialize(sandbox, pod_targets, pods_project)
26
26
  @sandbox = sandbox
27
- @libraries = libraries
27
+ @pod_targets = pod_targets
28
28
  @pods_project = pods_project
29
29
  end
30
30
 
@@ -113,18 +113,23 @@ module Pod
113
113
  #
114
114
  def link_headers
115
115
  UI.message '- Linking headers' do
116
- libraries.each do |library|
117
- library.file_accessors.each do |file_accessor|
116
+ pod_targets.each do |pod_target|
117
+ pod_target.file_accessors.each do |file_accessor|
118
+ framework_exp = /.framework\//
118
119
  headers_sandbox = Pathname.new(file_accessor.spec.root.name)
119
- library.build_headers.add_search_path(headers_sandbox, library.platform)
120
- sandbox.public_headers.add_search_path(headers_sandbox, library.platform)
120
+ pod_target.build_headers.add_search_path(headers_sandbox, pod_target.platform)
121
+ sandbox.public_headers.add_search_path(headers_sandbox, pod_target.platform)
121
122
 
122
123
  header_mappings(headers_sandbox, file_accessor, file_accessor.headers).each do |namespaced_path, files|
123
- library.build_headers.add_files(namespaced_path, files, library.platform)
124
+ pod_target.build_headers.add_files(namespaced_path, files.reject { |f| f.to_path =~ framework_exp }, pod_target.platform)
124
125
  end
125
126
 
126
127
  header_mappings(headers_sandbox, file_accessor, file_accessor.public_headers).each do |namespaced_path, files|
127
- sandbox.public_headers.add_files(namespaced_path, files, library.platform)
128
+ sandbox.public_headers.add_files(namespaced_path, files.reject { |f| f.to_path =~ framework_exp }, pod_target.platform)
129
+ end
130
+
131
+ vendored_frameworks_header_mappings(headers_sandbox, file_accessor).each do |namespaced_path, files|
132
+ sandbox.public_headers.add_files(namespaced_path, files, pod_target.platform)
128
133
  end
129
134
  end
130
135
  end
@@ -141,7 +146,7 @@ module Pod
141
146
  # specs platform combinations.
142
147
  #
143
148
  def file_accessors
144
- @file_accessors ||= libraries.map(&:file_accessors).flatten.compact
149
+ @file_accessors ||= pod_targets.map(&:file_accessors).flatten.compact
145
150
  end
146
151
 
147
152
  # Adds file references to the list of the paths returned by the file
@@ -208,6 +213,36 @@ module Pod
208
213
  mappings
209
214
  end
210
215
 
216
+ # Computes the destination sub-directory in the sandbox for headers
217
+ # from inside vendored frameworks.
218
+ #
219
+ # @param [Pathname] headers_sandbox
220
+ # The sandbox where the header links should be stored for this
221
+ # Pod.
222
+ #
223
+ # @param [Sandbox::FileAccessor] file_accessor
224
+ # The consumer file accessor for which the headers need to be
225
+ # linked.
226
+ #
227
+ def vendored_frameworks_header_mappings(headers_sandbox, file_accessor)
228
+ mappings = {}
229
+ file_accessor.vendored_frameworks.each do |framework|
230
+ headers_dir = Sandbox::FileAccessor.vendored_frameworks_headers_dir(framework)
231
+ headers = Sandbox::FileAccessor.vendored_frameworks_headers(framework)
232
+ framework_name = framework.basename(framework.extname)
233
+ dir = headers_sandbox + framework_name
234
+ headers.each do |header|
235
+ # the relative path of framework headers should be kept,
236
+ # not flattened like is done for most public headers.
237
+ relative_path = header.relative_path_from(headers_dir)
238
+ sub_dir = dir + relative_path.dirname
239
+ mappings[sub_dir] ||= []
240
+ mappings[sub_dir] << header
241
+ end
242
+ end
243
+ mappings
244
+ end
245
+
211
246
  #-----------------------------------------------------------------------#
212
247
  end
213
248
  end