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,158 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+
3
+ module Pod
4
+ class Installer
5
+ # Represents the installation options the user can customize via a
6
+ # `Podfile`.
7
+ #
8
+ class InstallationOptions
9
+ # Parses installation options from a podfile.
10
+ #
11
+ # @param [Podfile] podfile the podfile to parse installation options
12
+ # from.
13
+ #
14
+ # @raise [Informative] if `podfile` does not specify a `CocoaPods`
15
+ # install.
16
+ #
17
+ # @return [Self]
18
+ #
19
+ def self.from_podfile(podfile)
20
+ name, options = podfile.installation_method
21
+ unless name.downcase == 'cocoapods'
22
+ raise Informative, "Currently need to specify a `cocoapods` install, you chose `#{name}`."
23
+ end
24
+ new(options)
25
+ end
26
+
27
+ # Defines a new installation option.
28
+ #
29
+ # @param [#to_s] name the name of the option.
30
+ #
31
+ # @param default the default value for the option.
32
+ #
33
+ # @param [Boolean] boolean whether the option has a boolean value.
34
+ #
35
+ # @return [void]
36
+ #
37
+ # @!macro [attach] option
38
+ #
39
+ # @note this option defaults to $2.
40
+ #
41
+ # @return the $1 $0 for installation.
42
+ #
43
+ def self.option(name, default, boolean: true)
44
+ name = name.to_s
45
+ raise ArgumentError, "The `#{name}` option is already defined" if defaults.key?(name)
46
+ defaults[name] = default
47
+ attr_accessor name
48
+ alias_method "#{name}?", name if boolean
49
+ end
50
+
51
+ # @return [Hash<Symbol,Object>] all known installation options and their
52
+ # default values.
53
+ #
54
+ def self.defaults
55
+ @defaults ||= {}
56
+ end
57
+
58
+ # @return [Array<Symbol>] the names of all known installation options.
59
+ #
60
+ def self.all_options
61
+ defaults.keys
62
+ end
63
+
64
+ # Initializes the installation options with a hash of options from a
65
+ # Podfile.
66
+ #
67
+ # @param [Hash] options the options to parse.
68
+ #
69
+ # @raise [Informative] if `options` contains any unknown keys.
70
+ #
71
+ def initialize(options = {})
72
+ options = ActiveSupport::HashWithIndifferentAccess.new(options)
73
+ unknown_keys = options.keys - self.class.all_options.map(&:to_s)
74
+ raise Informative, "Unknown installation options: #{unknown_keys.to_sentence}." unless unknown_keys.empty?
75
+ self.class.defaults.each do |key, default|
76
+ value = options.fetch(key, default)
77
+ send("#{key}=", value)
78
+ end
79
+ end
80
+
81
+ # @param [Boolean] include_defaults whether values that match the default
82
+ # for their option should be included. Defaults to `true`.
83
+ #
84
+ # @return [Hash] the options, keyed by option name.
85
+ #
86
+ def to_h(include_defaults: true)
87
+ self.class.defaults.reduce(ActiveSupport::HashWithIndifferentAccess.new) do |hash, (option, default)|
88
+ value = send(option)
89
+ hash[option] = value if include_defaults || value != default
90
+ hash
91
+ end
92
+ end
93
+
94
+ def ==(other)
95
+ other.is_a?(self.class) && to_h == other.to_h
96
+ end
97
+
98
+ alias_method :eql, :==
99
+
100
+ def hash
101
+ to_h.hash
102
+ end
103
+
104
+ option :clean, true
105
+ option :deduplicate_targets, true
106
+ option :deterministic_uuids, true
107
+ option :integrate_targets, true
108
+ option :lock_pod_sources, true
109
+ option :warn_for_multiple_pod_sources, true
110
+ option :share_schemes_for_development_pods, false
111
+
112
+ module Mixin
113
+ module ClassMethods
114
+ # Delegates the creation of {#installation_options} to the `Podfile`
115
+ # returned by the given block.
116
+ #
117
+ # @param blk a block that returns the `Podfile` to create
118
+ # installation options from.
119
+ #
120
+ # @return [Void]
121
+ #
122
+ def delegate_installation_options(&blk)
123
+ define_method(:installation_options) do
124
+ @installation_options ||= InstallationOptions.from_podfile(instance_eval(&blk))
125
+ end
126
+ end
127
+
128
+ # Delegates the installation options attributes directly to
129
+ # {#installation_options}.
130
+ #
131
+ # @return [Void]
132
+ #
133
+ def delegate_installation_option_attributes!
134
+ define_method(:respond_to_missing?) do |name, *args|
135
+ installation_options.respond_to?(name, *args) || super
136
+ end
137
+
138
+ define_method(:method_missing) do |name, *args, &blk|
139
+ if installation_options.respond_to?(name)
140
+ installation_options.send(name, *args, &blk)
141
+ else
142
+ super
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ # @return [InstallationOptions] The installation options.
149
+ #
150
+ attr_accessor :installation_options
151
+
152
+ def self.included(mod)
153
+ mod.extend(ClassMethods)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,202 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
3
+ module Pod
4
+ class Installer
5
+ # Controller class responsible of installing the activated specifications
6
+ # of a single Pod.
7
+ #
8
+ # @note This class needs to consider all the activated specs of a Pod.
9
+ #
10
+ class PodSourceInstaller
11
+ UNENCRYPTED_PROTOCOLS = %w(http git).freeze
12
+
13
+ # @return [Sandbox] The installation target.
14
+ #
15
+ attr_reader :sandbox
16
+
17
+ # @return [Hash{Symbol=>Array}] The specifications that need to be
18
+ # installed grouped by platform.
19
+ #
20
+ attr_reader :specs_by_platform
21
+
22
+ # @return [Boolean] Whether the installer is allowed to touch the cache.
23
+ #
24
+ attr_reader :can_cache
25
+ alias_method :can_cache?, :can_cache
26
+
27
+ # Initialize a new instance
28
+ #
29
+ # @param [Sandbox] sandbox @see #sandbox
30
+ # @param [Hash{Symbol=>Array}] specs_by_platform @see #specs_by_platform
31
+ # @param [Boolean] can_cache @see #can_cache
32
+ #
33
+ def initialize(sandbox, specs_by_platform, can_cache: true)
34
+ @sandbox = sandbox
35
+ @specs_by_platform = specs_by_platform
36
+ @can_cache = can_cache
37
+ end
38
+
39
+ # @return [String] A string suitable for debugging.
40
+ #
41
+ def inspect
42
+ "<#{self.class} sandbox=#{sandbox.root} pod=#{root_spec.name}"
43
+ end
44
+
45
+ # @return [String] The name of the pod this installer is installing.
46
+ #
47
+ def name
48
+ root_spec.name
49
+ end
50
+
51
+ #-----------------------------------------------------------------------#
52
+
53
+ public
54
+
55
+ # @!group Installation
56
+
57
+ # Creates the target in the Pods project and the relative support files.
58
+ #
59
+ # @return [void]
60
+ #
61
+ def install!
62
+ download_source unless predownloaded? || local?
63
+ PodSourcePreparer.new(root_spec, root).prepare! if local?
64
+ end
65
+
66
+ # Cleans the installations if appropriate.
67
+ #
68
+ # @return [void]
69
+ #
70
+ def clean!
71
+ clean_installation unless local?
72
+ end
73
+
74
+ # Locks the source files if appropriate.
75
+ #
76
+ # @return [void]
77
+ #
78
+ def lock_files!(file_accessors)
79
+ return if local?
80
+ FileUtils.chmod('u-w', source_files(file_accessors))
81
+ end
82
+
83
+ # Unlocks the source files if appropriate.
84
+ #
85
+ # @return [void]
86
+ #
87
+ def unlock_files!(file_accessors)
88
+ return if local?
89
+ FileUtils.chmod('u+w', source_files(file_accessors))
90
+ end
91
+
92
+ #-----------------------------------------------------------------------#
93
+
94
+ private
95
+
96
+ # @!group Installation Steps
97
+
98
+ # Downloads the source of the Pod.
99
+ #
100
+ # @return [void]
101
+ #
102
+ def download_source
103
+ verify_source_is_secure(root_spec)
104
+ download_result = Downloader.download(download_request, root, :can_cache => can_cache?)
105
+
106
+ if (specific_source = download_result.checkout_options) && specific_source != root_spec.source
107
+ sandbox.store_checkout_source(root_spec.name, specific_source)
108
+ end
109
+ end
110
+
111
+ # Verify the source of the spec is secure, which is used to show a warning to the user if that isn't the case
112
+ # This method doesn't verify all protocols, but currently only prohibits unencrypted 'http://' and 'git://''
113
+ # connections.
114
+ #
115
+ # @return [void]
116
+ #
117
+ def verify_source_is_secure(root_spec)
118
+ return if root_spec.source.nil? || (root_spec.source[:http].nil? && root_spec.source[:git].nil?)
119
+ source = if !root_spec.source[:http].nil?
120
+ URI(root_spec.source[:http].to_s)
121
+ elsif !root_spec.source[:git].nil?
122
+ git_source = root_spec.source[:git].to_s
123
+ return unless git_source =~ /^#{URI.regexp}$/
124
+ URI(git_source)
125
+ end
126
+ if UNENCRYPTED_PROTOCOLS.include?(source.scheme)
127
+ UI.warn "'#{root_spec.name}' uses the unencrypted '#{source.scheme}' protocol to transfer the Pod. " \
128
+ 'Please be sure you\'re in a safe network with only trusted hosts. ' \
129
+ 'Otherwise, please reach out to the library author to notify them of this security issue.'
130
+ end
131
+ end
132
+
133
+ def download_request
134
+ Downloader::Request.new(
135
+ :spec => root_spec,
136
+ :released => released?,
137
+ )
138
+ end
139
+
140
+ # Removes all the files not needed for the installation according to the
141
+ # specs by platform.
142
+ #
143
+ # @return [void]
144
+ #
145
+ def clean_installation
146
+ cleaner = Sandbox::PodDirCleaner.new(root, specs_by_platform)
147
+ cleaner.clean!
148
+ end
149
+
150
+ #-----------------------------------------------------------------------#
151
+
152
+ private
153
+
154
+ # @!group Convenience methods.
155
+
156
+ # @return [Array<Specifications>] the specification of the Pod used in
157
+ # this installation.
158
+ #
159
+ def specs
160
+ specs_by_platform.values.flatten
161
+ end
162
+
163
+ # @return [Specification] the root specification of the Pod.
164
+ #
165
+ def root_spec
166
+ specs.first.root
167
+ end
168
+
169
+ # @return [Pathname] the folder where the source of the Pod is located.
170
+ #
171
+ def root
172
+ sandbox.pod_dir(root_spec.name)
173
+ end
174
+
175
+ # @return [Boolean] whether the source has been pre downloaded in the
176
+ # resolution process to retrieve its podspec.
177
+ #
178
+ def predownloaded?
179
+ sandbox.predownloaded_pods.include?(root_spec.name)
180
+ end
181
+
182
+ # @return [Boolean] whether the pod uses the local option and thus
183
+ # CocoaPods should not interfere with the files of the user.
184
+ #
185
+ def local?
186
+ sandbox.local?(root_spec.name)
187
+ end
188
+
189
+ def released?
190
+ !local? && !predownloaded? && sandbox.specification(root_spec.name) != root_spec
191
+ end
192
+
193
+ # @return [Array<Pathname>] The paths of the source files
194
+ #
195
+ def source_files(file_accessors)
196
+ file_accessors.flat_map(&:source_files)
197
+ end
198
+
199
+ #-----------------------------------------------------------------------#
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,77 @@
1
+ module Pod
2
+ class Installer
3
+ # Controller class responsible of executing the prepare command
4
+ # of a single Pod.
5
+ #
6
+ class PodSourcePreparer
7
+ # @return [Specification] the root specification of the Pod.
8
+ #
9
+ attr_reader :spec
10
+
11
+ # @return [Pathname] the folder where the source of the Pod is located.
12
+ #
13
+ attr_reader :path
14
+
15
+ # Initialize a new instance
16
+ #
17
+ # @param [Specification] spec the root specification of the Pod.
18
+ # @param [Pathname] path the folder where the source of the Pod is located.
19
+ #
20
+ def initialize(spec, path)
21
+ raise "Given spec isn't a root spec, but must be." unless spec.root?
22
+ @spec = spec
23
+ @path = path
24
+ end
25
+
26
+ #-----------------------------------------------------------------------#
27
+
28
+ public
29
+
30
+ # @!group Preparation
31
+
32
+ # Executes the prepare command if there is one.
33
+ #
34
+ # @return [void]
35
+ #
36
+ def prepare!
37
+ run_prepare_command
38
+ end
39
+
40
+ #-----------------------------------------------------------------------#
41
+
42
+ private
43
+
44
+ # @!group Preparation Steps
45
+
46
+ extend Executable
47
+ executable :bash
48
+
49
+ # Runs the prepare command bash script of the spec.
50
+ #
51
+ # @note Unsets the `CDPATH` env variable before running the
52
+ # shell script to avoid issues with relative paths
53
+ # (issue #1694).
54
+ #
55
+ # @return [void]
56
+ #
57
+ def run_prepare_command
58
+ return unless spec.prepare_command
59
+ UI.section(' > Running prepare command', '', 1) do
60
+ Dir.chdir(path) do
61
+ begin
62
+ ENV.delete('CDPATH')
63
+ ENV['COCOAPODS_VERSION'] = Pod::VERSION
64
+ prepare_command = spec.prepare_command.strip_heredoc.chomp
65
+ full_command = "\nset -e\n" + prepare_command
66
+ bash!('-c', full_command)
67
+ ensure
68
+ ENV.delete('COCOAPODS_VERSION')
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ #-----------------------------------------------------------------------#
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,139 @@
1
+ module Pod
2
+ class Installer
3
+ # Validate the podfile before installing to catch errors and
4
+ # problems
5
+ #
6
+ class PodfileValidator
7
+ # @return [Podfile] The podfile being validated
8
+ #
9
+ attr_reader :podfile
10
+
11
+ # @return [Array<String>] any errors that have occured during the validation
12
+ #
13
+ attr_reader :errors
14
+
15
+ # @return [Array<String>] any warnings that have occured during the validation
16
+ #
17
+ attr_reader :warnings
18
+
19
+ # Initialize a new instance
20
+ #
21
+ # @param [Podfile] podfile
22
+ # The podfile to validate
23
+ #
24
+ # @param [Analyzer::PodfileDependencyCache] podfile_dependency_cache
25
+ # An (optional) cache of all the dependencies in the podfile
26
+ #
27
+ def initialize(podfile, podfile_dependency_cache = Analyzer::PodfileDependencyCache.from_podfile(podfile))
28
+ @podfile = podfile
29
+ @podfile_dependency_cache = podfile_dependency_cache
30
+ @errors = []
31
+ @warnings = []
32
+ @validated = false
33
+ end
34
+
35
+ # Validate the podfile
36
+ # Errors are added to the errors array
37
+ #
38
+ def validate
39
+ validate_pod_directives
40
+ validate_no_abstract_only_pods!
41
+ validate_dependencies_are_present!
42
+ validate_no_duplicate_targets!
43
+
44
+ @validated = true
45
+ end
46
+
47
+ # Wether the podfile is valid is not
48
+ # NOTE: Will execute `validate` if the podfile
49
+ # has not yet been validated
50
+ #
51
+ def valid?
52
+ validate unless @validated
53
+
54
+ @validated && errors.empty?
55
+ end
56
+
57
+ # A message describing any errors in the
58
+ # validation
59
+ #
60
+ def message
61
+ errors.join("\n")
62
+ end
63
+
64
+ private
65
+
66
+ def add_error(error)
67
+ errors << error
68
+ end
69
+
70
+ def add_warning(warning)
71
+ warnings << warning
72
+ end
73
+
74
+ def validate_pod_directives
75
+ @podfile_dependency_cache.podfile_dependencies.each do |dependency|
76
+ validate_conflicting_external_sources!(dependency)
77
+ end
78
+ end
79
+
80
+ def validate_conflicting_external_sources!(dependency)
81
+ external_source = dependency.external_source
82
+ return false if external_source.nil?
83
+
84
+ available_downloaders = Downloader.downloader_class_by_key.keys
85
+ specified_downloaders = external_source.select { |key| available_downloaders.include?(key) }
86
+ if specified_downloaders.size > 1
87
+ add_error "The dependency `#{dependency.name}` specifies more than one download strategy(#{specified_downloaders.keys.join(',')})." \
88
+ 'Only one is allowed'
89
+ end
90
+
91
+ pod_spec_or_path = external_source[:podspec].present? || external_source[:path].present?
92
+ if pod_spec_or_path && specified_downloaders.size > 0
93
+ add_error "The dependency `#{dependency.name}` specifies `podspec` or `path` in combination with other" \
94
+ ' download strategies. This is not allowed'
95
+ end
96
+ end
97
+
98
+ # Warns the user if the podfile is empty.
99
+ #
100
+ # @note The workspace is created in any case and all the user projects
101
+ # are added to it, however the projects are not integrated as
102
+ # there is no way to discern between target definitions which are
103
+ # empty and target definitions which just serve the purpose to
104
+ # wrap other ones. This is not an issue because empty target
105
+ # definitions generate empty libraries.
106
+ #
107
+ # @return [void]
108
+ #
109
+ def validate_dependencies_are_present!
110
+ if @podfile_dependency_cache.target_definition_list.all?(&:empty?)
111
+ add_warning 'The Podfile does not contain any dependencies.'
112
+ end
113
+ end
114
+
115
+ # Verifies that no dependencies in the Podfile will end up not being built
116
+ # at all. In other words, all dependencies _must_ belong to a non-abstract
117
+ # target, or be inherited by a target where `inheritance == complete`.
118
+ #
119
+ def validate_no_abstract_only_pods!
120
+ all_dependencies = @podfile_dependency_cache.podfile_dependencies
121
+ concrete_dependencies = @podfile_dependency_cache.target_definition_list.reject(&:abstract?).flat_map { |td| @podfile_dependency_cache.target_definition_dependencies(td) }
122
+ abstract_only_dependencies = all_dependencies - concrete_dependencies
123
+ abstract_only_dependencies.each do |dep|
124
+ add_error "The dependency `#{dep}` is not used in any concrete target."
125
+ end
126
+ end
127
+
128
+ def validate_no_duplicate_targets!
129
+ @podfile_dependency_cache.target_definition_list.group_by { |td| [td.name, td.user_project_path] }.
130
+ each do |(name, project), definitions|
131
+ next unless definitions.size > 1
132
+ error = "The target `#{name}` is declared twice"
133
+ error << " for the project `#{project}`" if project
134
+ add_error(error << '.')
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end