vendor 0.0.4 → 0.1

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 (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