vendor 0.0.4 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG.md +22 -0
  2. data/Gemfile.lock +9 -1
  3. data/Guardfile +12 -0
  4. data/LICENSE +2 -0
  5. data/Readme.markdown +39 -23
  6. data/TODO.md +26 -0
  7. data/VERSION +1 -0
  8. data/lib/vendor.rb +6 -0
  9. data/lib/vendor/api.rb +61 -7
  10. data/lib/vendor/cli/app.rb +4 -4
  11. data/lib/vendor/cli/console.rb +7 -0
  12. data/lib/vendor/spec.rb +98 -0
  13. data/lib/vendor/templates/Vendorfile +3 -1
  14. data/lib/vendor/templates/vendorspec +15 -10
  15. data/lib/vendor/vendor_file.rb +5 -4
  16. data/lib/vendor/vendor_file/dependency_graph.rb +135 -0
  17. data/lib/vendor/vendor_file/dsl.rb +2 -0
  18. data/lib/vendor/vendor_file/library/base.rb +178 -29
  19. data/lib/vendor/vendor_file/library/git.rb +5 -1
  20. data/lib/vendor/vendor_file/library/local.rb +11 -1
  21. data/lib/vendor/vendor_file/library/remote.rb +134 -2
  22. data/lib/vendor/vendor_file/loader.rb +13 -11
  23. data/lib/vendor/vendor_spec/builder.rb +4 -7
  24. data/lib/vendor/version.rb +172 -1
  25. data/lib/vendor/xcode/project.rb +213 -4
  26. data/lib/vendor/xcode/proxy.rb +1 -0
  27. data/lib/vendor/xcode/proxy/pbx_frameworks_build_phase.rb +6 -0
  28. data/lib/vendor/xcode/proxy/pbx_reference_proxy.rb +7 -0
  29. data/lib/vendor/xcode/proxy/pbx_resources_build_phase.rb +8 -0
  30. data/lib/vendor/xcode/proxy/pbx_shell_script_build_phase.rb +8 -0
  31. data/lib/vendor/xcode/proxy/pbx_sources_build_phase.rb +6 -0
  32. data/spec/lib/vendor/api_spec.rb +54 -0
  33. data/spec/lib/vendor/spec_spec.rb +121 -0
  34. data/spec/lib/vendor/vendor_file/dependency_graph_spec.rb +129 -0
  35. data/spec/lib/vendor/vendor_file/library/base_spec.rb +174 -14
  36. data/spec/lib/vendor/vendor_file/library/remote_spec.rb +154 -4
  37. data/spec/lib/vendor/vendor_file/loader_spec.rb +4 -2
  38. data/spec/lib/vendor/vendor_spec/builder_spec.rb +2 -2
  39. data/spec/lib/vendor/version_spec.rb +168 -0
  40. data/spec/lib/vendor/xcode/project_spec.rb +175 -4
  41. data/spec/lib/vendor_spec.rb +15 -0
  42. data/spec/spec_helper.rb +3 -2
  43. data/spec/support/api_stubs.rb +57 -0
  44. data/spec/support/resources/cache/base/{DKBenchmark-Manifest → DKBenchmark-0.1-Manifest}/data/DKBenchmark.h +0 -0
  45. data/spec/support/resources/cache/base/{DKBenchmark-Manifest → DKBenchmark-0.1-Manifest}/data/DKBenchmark.m +0 -0
  46. data/spec/support/resources/cache/base/DKBenchmark-0.1-Manifest/vendor.json +1 -0
  47. data/spec/support/resources/cache/base/{DKBenchmark-Vendorspec → DKBenchmark-0.1-Nothing}/DKBenchmark.h +0 -0
  48. data/spec/support/resources/cache/base/{DKBenchmark-Vendorspec → DKBenchmark-0.1-Nothing}/DKBenchmark.m +0 -0
  49. data/spec/support/resources/cache/base/DKBenchmark-0.1-Nothing/DKBenchmark.vendorspec +16 -0
  50. data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.h +18 -0
  51. data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.m +73 -0
  52. data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.vendorspec +24 -0
  53. data/spec/support/resources/projects/MultipleTargets/MultipleTargets.xcodeproj/project.pbxproj +624 -0
  54. data/spec/support/resources/projects/RestKitProject/RestKitProject.xcodeproj/project.pbxproj +479 -0
  55. data/spec/support/resources/projects/UtilityApplication/UtilityApplication.xcodeproj/project.pbxproj +16 -7
  56. data/spec/support/resources/vendors/DKBenchmark/DKBenchmark.vendorspec +24 -8
  57. data/spec/support/resources/vendors/DKBenchmarkUnsafe/DKBenchmark.vendorspec +17 -8
  58. data/vendor.gemspec +4 -2
  59. metadata +93 -39
  60. data/lib/vendor/vendor_spec/dsl.rb +0 -39
  61. data/lib/vendor/vendor_spec/loader.rb +0 -23
  62. data/spec/lib/vendor/vendor_spec/dsl_spec.rb +0 -67
  63. data/spec/lib/vendor/vendor_spec/loader_spec.rb +0 -41
  64. data/spec/support/resources/cache/base/DKBenchmark-Manifest/vendor.json +0 -1
  65. data/spec/support/resources/cache/base/DKBenchmark-Vendorspec/DKBenchmark.vendorspec +0 -11
@@ -1,3 +1,5 @@
1
- source :vendorforge
1
+ # This is an example Vendorfile for you to start editing and add your
2
+ # own dependencies. Check out http://github.com/keithpitt/vendor for
3
+ # more information on how to use this file.
2
4
 
3
5
  lib "DKBenchmark", "0.2"
@@ -1,15 +1,20 @@
1
1
  # This is a generated version of the vendorspec. Please change the
2
- # values to suit your library. Refer to the documentation for a list of
3
- # all possible attributes.
2
+ # values to suit your library.
4
3
 
5
- name "<%= name %>"
6
- version "0.1"
4
+ Vendor::Spec.new do |s|
7
5
 
8
- authors "<%= username %>"
9
- email "<%= email %>"
10
- homepage "HOMEPAGE GOES HERE"
11
- description "DESCRIPTION GOES HERE"
6
+ s.name "<%= name %>"
7
+ s.version "0.1"
12
8
 
13
- github "GITHUB LINK GOES HERE"
9
+ s.authors "<%= username %>"
10
+ s.email "<%= email %>"
11
+ s.description "DESCRIPTION GOES HERE"
14
12
 
15
- files `git ls-files`.split("\n")
13
+ # s.homepage "HOMEPAGE URL GOES HERE"
14
+ # s.source "SOURCE URL GOES HERE"
15
+
16
+ s.files `git ls-files`.split("\n")
17
+
18
+ # s.dependency "JSONKit", "0.5"
19
+
20
+ end
@@ -2,10 +2,11 @@ module Vendor
2
2
 
3
3
  module VendorFile
4
4
 
5
- autoload :Source, "vendor/vendor_file/source"
6
- autoload :Loader, "vendor/vendor_file/loader"
7
- autoload :DSL, "vendor/vendor_file/dsl"
8
- autoload :Library, "vendor/vendor_file/library"
5
+ autoload :Source, "vendor/vendor_file/source"
6
+ autoload :Loader, "vendor/vendor_file/loader"
7
+ autoload :DSL, "vendor/vendor_file/dsl"
8
+ autoload :Library, "vendor/vendor_file/library"
9
+ autoload :DependencyGraph, "vendor/vendor_file/dependency_graph"
9
10
 
10
11
  end
11
12
 
@@ -0,0 +1,135 @@
1
+ module Vendor
2
+ module VendorFile
3
+
4
+ class DependencyGraph
5
+
6
+ attr_accessor :libraries
7
+ attr_reader :libraries_to_install
8
+
9
+ def initialize(libs = nil)
10
+ self.libraries = libs
11
+ end
12
+
13
+ def libraries=(value)
14
+ if value
15
+ # We want our own array, not a pointer to the old one, but we
16
+ # can keep the contents, thats fine.
17
+ @libraries = [ *value ]
18
+ else
19
+ @libraries = nil
20
+ end
21
+ end
22
+
23
+ def dependency_graph
24
+ map = {}
25
+ _touched = {}
26
+ graph = []
27
+ @libraries.each do |lib|
28
+ if (x = _map(lib, map, _touched))
29
+ graph << x
30
+ end
31
+ end
32
+
33
+ return graph, map
34
+ end
35
+
36
+ def version_conflicts?
37
+ graph, map = dependency_graph
38
+
39
+ # The version conflict detector is pretty stupid, pretty much if
40
+ # two libraries are trying to include a lib of different
41
+ # versions:
42
+ # a requires b v1.0
43
+ # c requires b v1.1
44
+ # Then things will blow up.
45
+
46
+ @libraries_to_install = []
47
+
48
+ map.keys.sort.each do |name|
49
+
50
+ libs = map[name]
51
+
52
+ # Only populate the "targets" element if there is a specific
53
+ # target to add to.
54
+ found_targets = libs.find_all { |l| l.targets if l.targets }.map &:targets
55
+ targets = found_targets.empty? ? nil : found_targets.flatten.compact.uniq
56
+
57
+ version_to_install = libs.first
58
+
59
+ # Check for conflicts and try to resolve
60
+ if libs.length > 1
61
+
62
+ # Sort the versions, starting with the latest version first
63
+ versions = libs.sort.reverse
64
+
65
+ # This code is a little yucky, but what it does, is it tries
66
+ # to find the best version match for every other version in
67
+ # the loop. So, lets say we have versions => 0.1 and ~> 0.1,
68
+ # and we have an array of [ "0.1", "0.1.1" ], try and find
69
+ # the higest versiont that each likes. Once we know that, we
70
+ # uniqify the results. If we have more than 1, that means we
71
+ # have a conflict.
72
+ vvs = versions.map(&:version)
73
+ matched_versions = libs.map do |l|
74
+ l.version_matches_any?(vvs)
75
+ end.inject([]) do |uniqs, obj|
76
+ if uniqs.all? { |e| e != obj }
77
+ uniqs << obj
78
+ end
79
+ uniqs
80
+ end
81
+
82
+ if matched_versions.length > 1
83
+ version_to_install = nil
84
+ else
85
+ # Find the version we've recomended and install that one.
86
+ y = matched_versions.first
87
+ version_to_install = versions.find do |x|
88
+ x.version == y.to_s
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ # Are there multiple versions to install?
95
+ unless version_to_install
96
+ Vendor.ui.error "Multiple versions detected for #{name} (#{versions.map(&:version).join(', ')})"
97
+
98
+ # A semi-meaningfull error
99
+ libs.each do |v|
100
+ if v.parent
101
+ Vendor.ui.error " #{v.description} is required by #{v.parent.description}"
102
+ else
103
+ Vendor.ui.error " #{v.description} is required in the Vendorfile"
104
+ end
105
+ end
106
+
107
+ exit
108
+ else
109
+ @libraries_to_install << [ version_to_install, targets ]
110
+ end
111
+ end
112
+
113
+ false
114
+ end
115
+
116
+ private
117
+
118
+ def _map(library, map, _touched)
119
+ unless _touched[library.description]
120
+ # Add the library to the map
121
+ map[library.name] ||= []
122
+ map[library.name] << library
123
+
124
+ # So we don't traverse this object again
125
+ _touched[library.description] = true
126
+
127
+ # Return the graph
128
+ [ library.description, library.dependencies.map { |l| _map(l, map, _touched) }.compact ]
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+ end
@@ -15,6 +15,8 @@ module Vendor
15
15
  end
16
16
 
17
17
  def source(source)
18
+ Vendor.ui.warn "Having multiple vendor sources is currently not supported. It will be in a future release."
19
+
18
20
  @sources << Vendor::VendorFile::Source.new(source)
19
21
  end
20
22
 
@@ -6,32 +6,51 @@ module Vendor
6
6
 
7
7
  class Base
8
8
 
9
+ attr_accessor :parent
10
+
9
11
  attr_accessor :name
10
12
  attr_accessor :targets
11
13
  attr_accessor :require
14
+ attr_accessor :version
12
15
 
13
16
  def initialize(attributes = {})
14
17
  @source_tree = :group
18
+ @targets = [ :all ]
15
19
  attributes.each { |k, v| self.send("#{k}=", v) }
16
20
  end
17
21
 
18
22
  def targets=(value)
19
- @targets = [ *value ]
23
+ if value
24
+ @targets = [ *value ]
25
+ end
20
26
  end
21
27
 
22
28
  alias :target= :targets=
23
29
 
24
30
  def cache_path
25
- @cache_path ||= File.join(Vendor.library_path, self.class.name.split('::').last.downcase, name)
31
+ File.join(Vendor.library_path, self.class.name.split('::').last.downcase, name)
26
32
  end
27
33
 
28
34
  def download
29
35
  # Do nothing by default, leave that up to the implementation
30
36
  end
31
37
 
32
- def install(project)
33
- Vendor.ui.debug "Installing #{name} into #{project} (source_tree = #{@source_tree})"
38
+ # This method sucks. What we should be doing is passing a library to the Xcode project class
39
+ # to install. We shouldn't be interacting with it like this. Really, VendorFile::Library should
40
+ # extend Xcode::Library or something. That was its a little more modular.
41
+ def install(project, options = {})
42
+ # If the cache doesn't exist, download it
43
+ download unless cache_exists?
34
44
 
45
+ Vendor.ui.info %{Installing #{display_name}}
46
+
47
+ # Combine the local targets, with those targets specified in the options. Also
48
+ # for sanity reasons, flatten and uniqify them.
49
+ if @targets || options[:targets]
50
+ install_targets = [ @targets, options[:targets] ].compact.flatten.uniq
51
+ end
52
+
53
+ # The destination in the XCode project
35
54
  destination = "Vendor/#{name}"
36
55
 
37
56
  # Remove the group, and recreate
@@ -39,53 +58,183 @@ module Vendor
39
58
 
40
59
  # Install the files back into the project
41
60
  files.each do |file|
42
- Vendor.ui.debug "Copying file #{file} to #{destination}"
43
-
44
- project.add_file :targets => targets, :path => destination,
61
+ project.add_file :targets => install_targets, :path => destination,
45
62
  :file => file, :source_tree => @source_tree
46
63
  end
64
+
65
+ # Add frameworks
66
+ frameworks.each do |framework|
67
+ project.add_framework framework, :targets => install_targets
68
+ end
69
+
70
+ # Add compiler flags
71
+ build_settings.each do |build_setting|
72
+ project.add_build_setting build_setting[0], build_setting[1], :targets => install_targets, :from => self
73
+ end
47
74
  end
48
75
 
49
- def files
50
- unless File.exist?(cache_path)
51
- Vendor.ui.error "Could not find libray `#{name}` at path `#{cache_path}`"
52
- return []
76
+ def dependencies
77
+ # If the cache doesn't exist, download it
78
+ download unless cache_exists?
79
+
80
+ # Find the dependencies
81
+ dependencies = if manifest
82
+ manifest['dependencies']
83
+ elsif vendor_spec
84
+ vendor_spec.dependencies
53
85
  end
54
86
 
55
- # Try and find a vendorspec in the cached folder
56
- vendor_spec = Dir[File.join(cache_path, "*.vendorspec")].first
87
+ # Create remote objects to represent the dependencies
88
+ (dependencies || []).map do |d|
89
+ Vendor::VendorFile::Library::Remote.new(:name => d[0], :version => d[1], :parent => self, :targets => @targets)
90
+ end
91
+ end
92
+
93
+ def frameworks
94
+ # If the cache doesn't exist, download it
95
+ download unless cache_exists?
96
+
97
+ # Find the frameworks
98
+ frameworks = if manifest
99
+ manifest['frameworks']
100
+ elsif vendor_spec
101
+ vendor_spec.frameworks
102
+ end
103
+
104
+ frameworks || []
105
+ end
57
106
 
58
- # Try and find a manifest (a built vendor file)
59
- manifest = File.join(cache_path, "vendor.json")
107
+ def build_settings
108
+ # If the cache doesn't exist, download it
109
+ download unless cache_exists?
110
+
111
+ # Find the build settings
112
+ build_settings = if manifest
113
+ manifest['build_settings']
114
+ elsif vendor_spec
115
+ vendor_spec.build_settings
116
+ end
117
+
118
+ build_settings || []
119
+ end
120
+
121
+ def files
122
+ # If the cache doesn't exist, download it
123
+ download unless cache_exists?
60
124
 
61
125
  # Calculate the files we need to add. There are 3 different types
62
126
  # of installation:
63
127
  # 1) Installation from a manifest (a built lib)
64
128
  # 2) Loading the .vendorspec and seeing what files needed to be added
65
129
  # 3) Try to be smart and try and find files to install
66
- install_files = if manifest && File.exist?(manifest)
130
+ install_files = if manifest
131
+ manifest['files'].map do |file|
132
+ File.join(cache_path, "data", file)
133
+ end
134
+ elsif vendor_spec
135
+ vendor_spec.files.map do |file|
136
+ File.join(cache_path, file)
137
+ end
138
+ else
139
+ location = [ cache_path, self.require, "**/*.*" ].compact
140
+ Dir[ File.join *location ]
141
+ end
67
142
 
68
- json = JSON.parse(File.read(manifest))
69
- json['files'].map { |file| File.join(cache_path, "data", file) }
70
-
71
- elsif vendor_spec && File.exist?(vendor_spec)
143
+ # Remove files that are within folders with a ".", such as ".bundle"
144
+ # and ".frameworks"
145
+ install_files.reject do |file|
146
+ file.gsub(cache_path, "") =~ /\/?[^\/]+\.[^\/]+\//
147
+ end
148
+ end
72
149
 
73
- loader = Vendor::VendorSpec::Loader.new
74
- loader.load vendor_spec
75
- loader.dsl.files.map { |file| File.join(cache_path, file) }
150
+ def version
151
+ if @version
152
+ @version
153
+ elsif manifest
154
+ manifest['version']
155
+ elsif vendor_spec
156
+ vendor_spec.version
157
+ end
158
+ end
76
159
 
77
- else
160
+ def version_matches_any?(other_versions)
161
+ # If we have an equality matcher, we need sort through
162
+ # the versions, and try and find the best match
163
+ wants = Vendor::Version.new(version)
78
164
 
79
- location = [ cache_path, self.require, "**/*.*" ].compact
80
- Dir[ File.join *location ]
165
+ # Sort them from the latest versions first
166
+ versions = other_versions.map{|v| Vendor::Version.create(v) }.sort.reverse
81
167
 
168
+ # We don't want to include pre-releases if the wants version
169
+ # isn't a pre-release itself. If we're after "2.5.alpha", then
170
+ # we should be able to include that, however if we're asking for
171
+ # "2.5", then pre-releases shouldn't be included.
172
+ unless wants.prerelease?
173
+ versions = versions.reject { |v| v.prerelease? }
82
174
  end
83
175
 
84
- # Remove files that are within folders with a ".", such as ".bundle"
85
- # and ".frameworks"
86
- install_files.reject { |file| file =~ /\/?[^\/]+\.[^\/]+\// }
176
+ versions.find { |has| wants == has }
177
+ end
178
+
179
+ def <=>(other)
180
+ v = other.respond_to?(:version) ? other.version : other
181
+ Vendor::Version.create(version) <=> Vendor::Version.create(v)
182
+ end
183
+
184
+ def ==(other)
185
+ other.name == name && other.version == version
186
+ end
187
+
188
+ def display_name
189
+ description
87
190
  end
88
191
 
192
+ def description
193
+ [ name, version ].compact.join(" ")
194
+ end
195
+
196
+ private
197
+
198
+ def cache_exists?
199
+ File.exist?(cache_path)
200
+ end
201
+
202
+ def vendor_spec
203
+ # Cache the vendor spec
204
+ return @vendor_spec if @vendor_spec
205
+
206
+ # Try and find a vendorspec in the cached folder
207
+ if cache_path
208
+ file = Dir[File.join(cache_path, "*.vendorspec")].first
209
+
210
+ if file && File.exist?(file)
211
+ @vendor_spec = Vendor::Spec.load(file)
212
+ else
213
+ false
214
+ end
215
+ else
216
+ false
217
+ end
218
+ end
219
+
220
+ def manifest
221
+ # Cache the manifest
222
+ return @manifest if @manifest
223
+
224
+ # Try and find a manifest (a built vendor file)
225
+ if cache_path
226
+ file = File.join(cache_path, "vendor.json")
227
+
228
+ if File.exist?(file)
229
+ @manifest = JSON.parse(File.read(file))
230
+ else
231
+ false
232
+ end
233
+ else
234
+ false
235
+ end
236
+ end
237
+
89
238
  end
90
239
 
91
240
  end