cocoapods 0.5.1 → 0.6.0.rc1
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.
- data/CHANGELOG.md +229 -2
- data/README.md +50 -20
- data/bin/pod +3 -2
- data/lib/cocoapods.rb +23 -9
- data/lib/cocoapods/command.rb +71 -30
- data/lib/cocoapods/command/error_report.rb +102 -0
- data/lib/cocoapods/command/install.rb +27 -19
- data/lib/cocoapods/command/list.rb +51 -8
- data/lib/cocoapods/command/presenter.rb +61 -0
- data/lib/cocoapods/command/presenter/cocoa_pod.rb +123 -0
- data/lib/cocoapods/command/push.rb +102 -0
- data/lib/cocoapods/command/repo.rb +70 -14
- data/lib/cocoapods/command/search.rb +7 -10
- data/lib/cocoapods/command/setup.rb +76 -15
- data/lib/cocoapods/command/spec.rb +581 -97
- data/lib/cocoapods/config.rb +23 -26
- data/lib/cocoapods/dependency.rb +86 -40
- data/lib/cocoapods/downloader.rb +30 -18
- data/lib/cocoapods/downloader/git.rb +125 -15
- data/lib/cocoapods/downloader/http.rb +73 -0
- data/lib/cocoapods/downloader/mercurial.rb +3 -9
- data/lib/cocoapods/downloader/subversion.rb +3 -9
- data/lib/cocoapods/executable.rb +26 -3
- data/lib/cocoapods/generator/acknowledgements.rb +37 -0
- data/lib/cocoapods/generator/acknowledgements/markdown.rb +38 -0
- data/lib/cocoapods/generator/acknowledgements/plist.rb +63 -0
- data/lib/cocoapods/generator/copy_resources_script.rb +8 -4
- data/lib/cocoapods/generator/documentation.rb +99 -0
- data/lib/cocoapods/generator/dummy_source.rb +14 -0
- data/lib/cocoapods/installer.rb +140 -109
- data/lib/cocoapods/installer/target_installer.rb +78 -83
- data/lib/cocoapods/installer/user_project_integrator.rb +162 -0
- data/lib/cocoapods/local_pod.rb +240 -0
- data/lib/cocoapods/platform.rb +41 -18
- data/lib/cocoapods/podfile.rb +234 -21
- data/lib/cocoapods/project.rb +67 -0
- data/lib/cocoapods/resolver.rb +62 -32
- data/lib/cocoapods/sandbox.rb +63 -0
- data/lib/cocoapods/source.rb +42 -20
- data/lib/cocoapods/specification.rb +294 -271
- data/lib/cocoapods/specification/set.rb +10 -28
- data/lib/cocoapods/specification/statistics.rb +112 -0
- metadata +124 -11
- data/lib/cocoapods/xcodeproj_pods.rb +0 -111
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'xcodeproj/workspace'
|
2
|
+
require 'xcodeproj/project'
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
class Installer
|
6
|
+
|
7
|
+
class UserProjectIntegrator
|
8
|
+
include Pod::Config::Mixin
|
9
|
+
|
10
|
+
def initialize(podfile)
|
11
|
+
@podfile = podfile
|
12
|
+
end
|
13
|
+
|
14
|
+
def integrate!
|
15
|
+
create_workspace!
|
16
|
+
|
17
|
+
# Only need to write out the user's project if any of the target
|
18
|
+
# integrators actually did some work.
|
19
|
+
target_integrators.map(&:integrate!)
|
20
|
+
end
|
21
|
+
|
22
|
+
def workspace_path
|
23
|
+
@podfile.workspace || raise(Informative, "Could not automatically select an Xcode workspace. " \
|
24
|
+
"Specify one in your Podfile.")
|
25
|
+
end
|
26
|
+
|
27
|
+
def pods_project_path
|
28
|
+
config.project_root + "Pods/Pods.xcodeproj"
|
29
|
+
end
|
30
|
+
|
31
|
+
def target_integrators
|
32
|
+
@target_integrators ||= @podfile.target_definitions.values.map do |definition|
|
33
|
+
TargetIntegrator.new(definition) unless definition.empty?
|
34
|
+
end.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def user_project_paths
|
38
|
+
@podfile.target_definitions.values.map do |td|
|
39
|
+
next if td.empty?
|
40
|
+
td.user_project.path #|| raise(Informative, "Could not resolve the Xcode project in which the " \
|
41
|
+
# "`#{td.name}' target should be integrated.")
|
42
|
+
end.compact
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_workspace!
|
46
|
+
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
|
47
|
+
[pods_project_path, *user_project_paths].each do |project_path|
|
48
|
+
project_path = project_path.relative_path_from(config.project_root).to_s
|
49
|
+
workspace << project_path unless workspace.include?(project_path)
|
50
|
+
end
|
51
|
+
unless workspace_path.exist? || config.silent?
|
52
|
+
puts "[!] From now on use `#{workspace_path.basename}'."
|
53
|
+
end
|
54
|
+
workspace.save_as(workspace_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
class TargetIntegrator
|
58
|
+
attr_reader :target_definition
|
59
|
+
|
60
|
+
def initialize(target_definition)
|
61
|
+
@target_definition = target_definition
|
62
|
+
end
|
63
|
+
|
64
|
+
def inspect
|
65
|
+
"#<#{self.class} for target `#{@target_definition.label}'>"
|
66
|
+
end
|
67
|
+
|
68
|
+
def integrate!
|
69
|
+
return if targets.empty?
|
70
|
+
|
71
|
+
unless Config.instance.silent?
|
72
|
+
# TODO let's just use ActiveSupport.
|
73
|
+
plural = targets.size > 1
|
74
|
+
puts "-> Integrating `#{@target_definition.lib_name}' into target#{'s' if plural} " \
|
75
|
+
"`#{targets.map(&:name).join(', ')}' of Xcode project `#{user_project_path.basename}'.".green
|
76
|
+
end
|
77
|
+
|
78
|
+
add_xcconfig_base_configuration
|
79
|
+
add_pods_library
|
80
|
+
add_copy_resources_script_phase
|
81
|
+
user_project.save_as(@target_definition.user_project.path)
|
82
|
+
end
|
83
|
+
|
84
|
+
def user_project_path
|
85
|
+
if path = @target_definition.user_project.path
|
86
|
+
unless path.exist?
|
87
|
+
raise Informative, "The Xcode project `#{path}' does not exist."
|
88
|
+
end
|
89
|
+
path
|
90
|
+
else
|
91
|
+
raise Informative, "Could not automatically select an Xcode project.\n" \
|
92
|
+
"Specify one in your Podfile like so:\n\n" \
|
93
|
+
" xcodeproj 'path/to/XcodeProject'"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def user_project
|
98
|
+
@user_project ||= Xcodeproj::Project.new(user_project_path)
|
99
|
+
end
|
100
|
+
|
101
|
+
# This returns a list of the targets from the user’s project to which
|
102
|
+
# this Pods static library should be linked. If no explicit target was
|
103
|
+
# specified, then the first encountered target is assumed.
|
104
|
+
#
|
105
|
+
# In addition this will only return targets that do **not** already
|
106
|
+
# have the Pods library in their frameworks build phase.
|
107
|
+
#
|
108
|
+
# @return [Array<PBXNativeTarget>] Returns the list of targets that
|
109
|
+
# the Pods lib should be linked with.
|
110
|
+
def targets
|
111
|
+
@targets ||= begin
|
112
|
+
if link_with = @target_definition.link_with
|
113
|
+
# Find explicitly linked targets.
|
114
|
+
user_project.targets.select do |target|
|
115
|
+
link_with.include? target.name
|
116
|
+
end
|
117
|
+
elsif @target_definition.name != :default
|
118
|
+
# Find the target with the matching name.
|
119
|
+
target = user_project.targets.find { |target| target.name == @target_definition.name.to_s }
|
120
|
+
raise Informative, "Unable to find a target named `#{@target_definition.name.to_s}'" unless target
|
121
|
+
[target]
|
122
|
+
else
|
123
|
+
# Default to the first, which in a simple project is probably an app target.
|
124
|
+
[user_project.targets.first]
|
125
|
+
end.reject do |target|
|
126
|
+
# Reject any target that already has this Pods library in one of its frameworks build phases
|
127
|
+
target.frameworks_build_phases.any? do |phase|
|
128
|
+
phase.files.any? { |file| file.name == @target_definition.lib_name }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_xcconfig_base_configuration
|
135
|
+
xcconfig = user_project.files.new('path' => @target_definition.xcconfig_relative_path)
|
136
|
+
targets.each do |target|
|
137
|
+
target.build_configurations.each do |config|
|
138
|
+
config.base_configuration = xcconfig
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_pods_library
|
144
|
+
pods_library = user_project.group("Frameworks").files.new_static_library(@target_definition.label)
|
145
|
+
targets.each do |target|
|
146
|
+
target.frameworks_build_phases.each { |build_phase| build_phase << pods_library }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_copy_resources_script_phase
|
151
|
+
targets.each do |target|
|
152
|
+
phase = target.shell_script_build_phases.new
|
153
|
+
phase.name = 'Copy Pods Resources'
|
154
|
+
phase.shell_script = %{"#{@target_definition.copy_resources_script_relative_path}"\n}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module Pod
|
2
|
+
class LocalPod
|
3
|
+
attr_reader :top_specification, :specifications
|
4
|
+
attr_reader :sandbox
|
5
|
+
|
6
|
+
def initialize(specification, sandbox, platform)
|
7
|
+
@top_specification, @sandbox = specification.top_level_parent, sandbox
|
8
|
+
@top_specification.activate_platform(platform)
|
9
|
+
@specifications = [] << specification
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_podspec(podspec, sandbox, platform)
|
13
|
+
new(Specification.from_file(podspec), sandbox, platform)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Method to add the specifications sharing the same top level
|
17
|
+
# parent. With this information the local pod can determine the
|
18
|
+
# paths to clean and avoid duplication in file processing.
|
19
|
+
# Adding specifications is idempotent.
|
20
|
+
def add_specification(spec)
|
21
|
+
raise Informative, "[Local Pod] Attempt to add a specification from another pod" unless spec.top_level_parent == top_specification
|
22
|
+
spec.activate_platform(platform)
|
23
|
+
@specifications << spec unless @specifications.include?(spec)
|
24
|
+
end
|
25
|
+
|
26
|
+
def root
|
27
|
+
@sandbox.root + top_specification.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def subspecs
|
31
|
+
specifications.reject{|s| s.parent.nil? }
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
result = top_specification.to_s
|
36
|
+
result << " [LOCAL]" if top_specification.local?
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def name
|
41
|
+
top_specification.name
|
42
|
+
end
|
43
|
+
|
44
|
+
def platform
|
45
|
+
top_specification.active_platform
|
46
|
+
end
|
47
|
+
|
48
|
+
# Installation methods
|
49
|
+
|
50
|
+
def create
|
51
|
+
root.mkpath unless exists?
|
52
|
+
end
|
53
|
+
|
54
|
+
def exists?
|
55
|
+
root.exist?
|
56
|
+
end
|
57
|
+
|
58
|
+
def chdir(&block)
|
59
|
+
create
|
60
|
+
Dir.chdir(root, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def implode
|
64
|
+
root.rmtree if exists?
|
65
|
+
end
|
66
|
+
|
67
|
+
# It deletes all the files identified by clean_files, then it removes
|
68
|
+
# all the empty folders or symlinks.
|
69
|
+
def clean
|
70
|
+
clean_files.each { |path| FileUtils.rm_rf(path) }
|
71
|
+
|
72
|
+
# Get all the directories. Then sort them from the longest
|
73
|
+
# to the shortest, so a directory will be empty if its
|
74
|
+
# subdirs where empty. We need to delete the symlinks because
|
75
|
+
# it might prevent a bundle from being deleted
|
76
|
+
dirs = Dir.glob(root + "**/*", File::FNM_DOTMATCH)
|
77
|
+
dirs = dirs.reject { |d| d.end_with?('.', '..') || !File.directory?(d) }.sort_by(&:length).reverse
|
78
|
+
dirs.each { |d| FileUtils.rm_rf(d) if File.symlink?(d) || (Dir.entries(d) == %w[ . .. ]) }
|
79
|
+
end
|
80
|
+
|
81
|
+
# File attributes
|
82
|
+
|
83
|
+
def prefix_header_file
|
84
|
+
root + top_specification.prefix_header_file if top_specification.prefix_header_file
|
85
|
+
end
|
86
|
+
|
87
|
+
def source_files(relative = true)
|
88
|
+
chained_expanded_paths(:source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => relative)
|
89
|
+
end
|
90
|
+
|
91
|
+
def header_files
|
92
|
+
source_files.select { |f| f.extname == '.h' }
|
93
|
+
end
|
94
|
+
|
95
|
+
def resources(relative = true)
|
96
|
+
chained_expanded_paths(:resources, :relative_to_sandbox => relative)
|
97
|
+
end
|
98
|
+
|
99
|
+
def clean_files
|
100
|
+
all_files = Dir.glob(root + "**/*", File::FNM_DOTMATCH).map { |f| root + f }.reject { |p| p.directory? }
|
101
|
+
all_files - used_files
|
102
|
+
end
|
103
|
+
|
104
|
+
def used_files
|
105
|
+
source_files(false) + resources(false) + preserve_paths + [ readme_file, license_file, prefix_header_file ]
|
106
|
+
end
|
107
|
+
|
108
|
+
# TODO: implement case insensitive search
|
109
|
+
def preserve_paths
|
110
|
+
chained_expanded_paths(:preserve_paths) + expanded_paths(%w[ *.podspec notice* NOTICE* CREDITS* ])
|
111
|
+
end
|
112
|
+
|
113
|
+
def readme_file
|
114
|
+
expanded_paths(%w[ README{*,.*} readme{*,.*} ]).first
|
115
|
+
end
|
116
|
+
|
117
|
+
def license_file
|
118
|
+
if top_specification.license && top_specification.license[:file]
|
119
|
+
root + top_specification.license[:file]
|
120
|
+
else
|
121
|
+
expanded_paths(%w[ LICENSE{*,.*} licence{*,.*} ]).first
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def license_text
|
126
|
+
if (license_hash = top_specification.license)
|
127
|
+
if (result = license_hash[:text])
|
128
|
+
result
|
129
|
+
elsif license_file
|
130
|
+
result = IO.read(license_file)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def xcconfig
|
136
|
+
specifications.map { |s| s.xcconfig }.reduce(:merge)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Method used by documentation generator. It return the source files
|
140
|
+
# of all the specs.
|
141
|
+
def all_specs_public_header_files
|
142
|
+
#TODO: merge with #221
|
143
|
+
specs = top_specification.recursive_subspecs << top_specification
|
144
|
+
specs.map { |s| expanded_paths(s.source_files, :glob => '*.{h}') }.compact.flatten.select { |f| f.extname == '.h' }.uniq
|
145
|
+
end
|
146
|
+
|
147
|
+
# Integration methods
|
148
|
+
|
149
|
+
def link_headers
|
150
|
+
copy_header_mappings.each do |namespaced_path, files|
|
151
|
+
@sandbox.add_header_files(namespaced_path, files)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_to_target(target)
|
156
|
+
sources_files_by_specification.each do | spec, files |
|
157
|
+
files.each do |file|
|
158
|
+
# TODO: Xcodeproj::Project::Object::PBXNativeTarget#add_source_file is quite slow
|
159
|
+
# The issus appears to be related to the find call in line 107.
|
160
|
+
target.add_source_file(file, nil, spec.compiler_flags.strip)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def requires_arc?
|
166
|
+
top_specification.requires_arc
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def implementation_files
|
172
|
+
source_files.select { |f| f.extname != '.h' }
|
173
|
+
end
|
174
|
+
|
175
|
+
def relative_root
|
176
|
+
root.relative_path_from(@sandbox.root)
|
177
|
+
end
|
178
|
+
|
179
|
+
# TODO this is being overriden in the RestKit 0.9.4 spec, need to do
|
180
|
+
# something with that, and this method also still exists in Specification.
|
181
|
+
#
|
182
|
+
# This is not overriden anymore in specification refactor and the code
|
183
|
+
# Pod::Specification#copy_header_mapping can be moved here.
|
184
|
+
def copy_header_mappings
|
185
|
+
search_path_headers = header_files - headers_excluded_from_search_paths
|
186
|
+
search_path_headers.inject({}) do |mappings, from|
|
187
|
+
from_without_prefix = from.relative_path_from(relative_root)
|
188
|
+
to = top_specification.header_dir + top_specification.copy_header_mapping(from_without_prefix)
|
189
|
+
(mappings[to.dirname] ||= []) << from
|
190
|
+
mappings
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# returns an hash where the source_files are groupped by specification.
|
195
|
+
# If the same file is required by two specifications the one at the
|
196
|
+
# higher level in the inheritance chain wins.
|
197
|
+
def sources_files_by_specification
|
198
|
+
files_by_spec = {}
|
199
|
+
processed_files = []
|
200
|
+
specifications.sort_by { |s| s.name.length }.each do |spec|
|
201
|
+
files = []
|
202
|
+
expanded_paths(spec.source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => true).each do | file |
|
203
|
+
files << file unless processed_files.include?(file)
|
204
|
+
end
|
205
|
+
files_by_spec[spec] = files
|
206
|
+
processed_files += files
|
207
|
+
end
|
208
|
+
files_by_spec
|
209
|
+
end
|
210
|
+
|
211
|
+
def headers_excluded_from_search_paths
|
212
|
+
chained_expanded_paths(:exclude_header_search_paths, :glob => '*.h', :relative_to_sandbox => true)
|
213
|
+
end
|
214
|
+
|
215
|
+
def chained_expanded_paths(accessor, options = {})
|
216
|
+
specifications.map { |s| expanded_paths(s.send(accessor), options) }.compact.flatten.uniq
|
217
|
+
end
|
218
|
+
|
219
|
+
def expanded_paths(patterns, options = {})
|
220
|
+
raise Informative, "[Local Pod] Attempt to resolve paths for non existent pod." unless exists?
|
221
|
+
|
222
|
+
patterns = [ patterns ] if patterns.is_a? String
|
223
|
+
patterns.map do |pattern|
|
224
|
+
pattern = root + pattern
|
225
|
+
|
226
|
+
if pattern.directory? && options[:glob]
|
227
|
+
pattern += options[:glob]
|
228
|
+
end
|
229
|
+
|
230
|
+
pattern.glob.map do |file|
|
231
|
+
if options[:relative_to_sandbox]
|
232
|
+
file.relative_path_from(@sandbox.root)
|
233
|
+
else
|
234
|
+
file
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end.flatten
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
data/lib/cocoapods/platform.rb
CHANGED
@@ -1,42 +1,65 @@
|
|
1
1
|
module Pod
|
2
2
|
class Platform
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
def self.ios
|
4
|
+
new :ios
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.osx
|
8
|
+
new :osx
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :deployment_target
|
12
|
+
|
13
|
+
def initialize(symbolic_name = nil, deployment_target = nil)
|
6
14
|
@symbolic_name = symbolic_name
|
7
|
-
|
15
|
+
if deployment_target
|
16
|
+
version = deployment_target.is_a?(Hash) ? deployment_target[:deployment_target] : deployment_target # backwards compatibility from 0.6
|
17
|
+
@deployment_target = Pod::Version.create(version)
|
18
|
+
end
|
8
19
|
end
|
9
|
-
|
20
|
+
|
10
21
|
def name
|
11
22
|
@symbolic_name
|
12
23
|
end
|
13
|
-
|
24
|
+
|
25
|
+
def deployment_target= (version)
|
26
|
+
@deployment_target = Pod::Version.create(version)
|
27
|
+
end
|
28
|
+
|
14
29
|
def ==(other_platform_or_symbolic_name)
|
15
30
|
if other_platform_or_symbolic_name.is_a?(Symbol)
|
16
31
|
@symbolic_name == other_platform_or_symbolic_name
|
17
32
|
else
|
18
|
-
self == (other_platform_or_symbolic_name.name)
|
33
|
+
self.name == (other_platform_or_symbolic_name.name) && self.deployment_target == other_platform_or_symbolic_name.deployment_target
|
19
34
|
end
|
20
35
|
end
|
21
|
-
|
36
|
+
|
37
|
+
def supports?(other)
|
38
|
+
return true if @symbolic_name.nil? || other.nil?
|
39
|
+
os_check = @symbolic_name == other.name
|
40
|
+
version_check = (deployment_target.nil? || other.deployment_target.nil? || deployment_target >= other.deployment_target)
|
41
|
+
os_check && version_check
|
42
|
+
end
|
43
|
+
|
22
44
|
def to_s
|
23
|
-
|
45
|
+
case @symbolic_name
|
46
|
+
when :ios
|
47
|
+
'iOS' + (deployment_target ? " #{deployment_target}" : '')
|
48
|
+
when :osx
|
49
|
+
'OS X' + (deployment_target ? " #{deployment_target}" : '')
|
50
|
+
else
|
51
|
+
'iOS - OS X'
|
52
|
+
end
|
24
53
|
end
|
25
|
-
|
54
|
+
|
26
55
|
def to_sym
|
27
56
|
name
|
28
57
|
end
|
29
|
-
|
58
|
+
|
30
59
|
def nil?
|
31
60
|
name.nil?
|
32
61
|
end
|
33
|
-
|
34
|
-
def deployment_target
|
35
|
-
if (opt = options[:deployment_target])
|
36
|
-
Pod::Version.new(opt)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
62
|
+
|
40
63
|
def requires_legacy_ios_archs?
|
41
64
|
return unless deployment_target
|
42
65
|
(name == :ios) && (deployment_target < Pod::Version.new("4.3"))
|