sprout 0.7.153-darwin

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sprout.rb ADDED
@@ -0,0 +1,456 @@
1
+ require 'rubygems'
2
+ require 'archive/tar/minitar'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+
6
+ $:.push(File.dirname(__FILE__))
7
+ require 'progress_bar'
8
+ require 'sprout/log'
9
+ require 'sprout/user'
10
+ require 'sprout/zip_util'
11
+ require 'sprout/remote_file_target'
12
+ require 'sprout/remote_file_loader'
13
+ require 'sprout/simple_resolver'
14
+ require 'sprout/template_resolver'
15
+
16
+ require 'rubygems/installer'
17
+ require 'rubygems/source_info_cache'
18
+ require 'rubygems/version'
19
+ require 'rubygems/digest/md5'
20
+
21
+ require 'sprout/project_model'
22
+ require 'sprout/builder'
23
+ require 'sprout/version'
24
+ require 'sprout/tasks/tool_task'
25
+ require 'sprout/general_tasks'
26
+
27
+ module Sprout
28
+ class SproutError < StandardError #:nodoc:
29
+ end
30
+
31
+ # Sprouts is an open-source, cross-platform project generation and configuration tool
32
+ # for ActionScript 2, ActionScript 3, Adobe AIR and Flex projects. It is built on top
33
+ # of Ruby Gems, Rails Generators and is intended to work on any platform that Ruby runs
34
+ # on including specifically, Windows XP, Windows Vista, Cygwin, OS X and Linux.
35
+ #
36
+ # Sprouts can be separated into some core concepts as follows:
37
+ #
38
+ # ----
39
+ # == Tools
40
+ # :include: ../doc/Tool
41
+ #
42
+ # ----
43
+ # == Libraries
44
+ # :include: ../doc/Library
45
+ #
46
+ # ----
47
+ # == Bundles
48
+ # :include: ../doc/Bundle
49
+ #
50
+ # ----
51
+ # == Generators
52
+ # :include: ../doc/Generator
53
+ #
54
+ # ----
55
+ # == Tasks
56
+ # :include: ../doc/Task
57
+ #
58
+ # ----
59
+ # == Sprout
60
+ #
61
+ # Tools, Libraries and Bundles are distributed as RubyGems and given a specific gem name suffix. For some examples:
62
+ # sprout-flex2sdk-tool
63
+ # sprout-asunit-library
64
+ # sprout-as3-bundle
65
+ #
66
+ # The Sprout application provides shared functionality for each of the different types of Sprouts.
67
+ #
68
+ # The Sprout command line tool primarily provides access to project generators from any sprout bundle that is available
69
+ # to your system, either locally or from the network.
70
+ #
71
+ # When executed from the system path, this class will download and install a named bundle, and execute a +project+
72
+ # generator within that bundle. Following is an example:
73
+ #
74
+ # sprout -n as3 SomeProject
75
+ #
76
+ # The previous command will download and install the latest version of the sprout-as3-bundle gem and initiate the
77
+ # project_generator with a single argument of 'SomeProject'. If the string passed to the -n parameter begins with
78
+ # 'sprout-' it will be unmodified for the lookup. For example:
79
+ #
80
+ # spout -n sprout-as3-bundle SomeProject
81
+ #
82
+ # will not have duplicate strings prepended or suffixed.
83
+ #
84
+ # ----
85
+ # Some additional resources or references:
86
+ #
87
+ # Rake:
88
+ # http://rake.rubyforge.org
89
+ # http://martinfowler.com/articles/rake.html
90
+ #
91
+ # RubyGems:
92
+ # * http://www.rubygems.org
93
+ # * http://www.linuxjournal.com/article/8967
94
+ #
95
+ # Ruby Raven (Mostly Inspiration)
96
+ # * http://raven.rubyforge.org
97
+ #
98
+ class Sprout
99
+ @@default_rakefiles = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
100
+
101
+ @@name = 'Sprouts'
102
+ @@cache = 'cache'
103
+ @@lib = 'lib'
104
+ @@spec = 'sprout.spec'
105
+ @@home = File.expand_path(File.dirname(File.dirname(__FILE__)))
106
+
107
+ # Execute a generator that is available locally or from the network.
108
+ # * +sprout_name+ A full gem name (ex., sprout-as3-bundle) that contains a generator that matches +generator_name+
109
+ # * +generator_name+ A string like 'project' or 'class' that maps to a generator
110
+ # * +params+ Arbitrary parameters to pass to the generator
111
+ # * +project_path+ Optional parameter. Will default to the nearest folder that contains a valid Rakefile.
112
+ # This Rakefile will usually be loaded by the referenced Generator, and it should have a configured ProjectModel
113
+ # defined in it.
114
+ def self.generate(sprout_name, generator_name, params, project_path=nil)
115
+ Rails::Generator::Base.use_sprout_sources!(sprout_name, project_path)
116
+ generator = Rails::Generator::Base.instance(generator_name, params)
117
+ generator.command(:create).invoke!
118
+ end
119
+
120
+ # Remove all installed RubyGems that begin with the string 'sprout' and clear the local sprout cache
121
+ def self.remove_all
122
+ # Set up sudo prefix if not on win machine
123
+ # Only show confirmation if there is at least one installed sprout gem
124
+ confirmation = false
125
+ count = 0
126
+ # For each sprout found, remove it!
127
+ Rails::Generator::GemGeneratorSource.new().each_sprout do |sprout|
128
+ count += 1
129
+ command = "#{get_gem_preamble} uninstall -x -a -i -q #{sprout.name}"
130
+
131
+ if(!confirmation)
132
+ break unless confirmation = remove_gems_confirmation
133
+ end
134
+ raise ">> Exited with errors: #{$?}" unless system(command)
135
+ end
136
+
137
+ if(confirmation)
138
+ puts "All Sprout gems have been successfully uninstalled"
139
+ elsif(count > 0)
140
+ puts "Some Sprout gems have been left on the system"
141
+ else
142
+ puts "No Sprout gems were found on this system"
143
+ end
144
+
145
+ # Now clear out the cache
146
+ cache = File.dirname(File.dirname(Sprout.sprout_cache))
147
+
148
+ if(File.exists?(cache))
149
+ puts "\n[WARNING]\n\nAbout to irrevocably destroy the sprout cache at:\n\n#{cache}\n\n"
150
+ puts "Are you absolutely sure? [Yn]"
151
+ response = $stdin.gets.chomp!
152
+ if(response.downcase.index('y'))
153
+ FileUtils.rm_rf(cache)
154
+ else
155
+ puts "Leaving the Sprout file cache in tact...."
156
+ end
157
+ else
158
+ puts "No cached files found on this system"
159
+ end
160
+
161
+ puts "To completely remove sprouts now, run:"
162
+ puts " #{get_gem_preamble} uninstall sprout"
163
+ end
164
+
165
+ # Build up the platform-specific preamble required
166
+ # to call the gem binary from Kernel.execute
167
+ def self.get_gem_preamble
168
+ usr = User.new()
169
+ if(!usr.is_a?(WinUser))
170
+ # Everyone but Win and Cygwin users get 'sudo '
171
+ return "sudo gem"
172
+ elsif(!usr.is_a?(CygwinUser))
173
+ # We're in the DOS Shell
174
+ return "ruby #{get_executable_from_path('gem')}"
175
+ end
176
+ # We're either a CygwinUser or some other non-sudo supporter
177
+ return 'gem'
178
+ end
179
+
180
+ # Retrieve the full path to an executable that is
181
+ # available in the system path
182
+ def self.get_executable_from_path(exe)
183
+ path = ENV['PATH']
184
+ file_path = nil
185
+ path.split(get_path_delimiter).each do |p|
186
+ file_path = File.join(p, exe)
187
+ # file_path = file_path.split("/").join("\\")
188
+ # file_path = file_path.split("\\").join("/")
189
+ if(File.exists?(file_path))
190
+ return User.clean_path(file_path)
191
+ end
192
+ end
193
+ return nil
194
+ end
195
+
196
+ def self.get_path_delimiter
197
+ usr = User.new
198
+ if(usr.is_a?(WinUser) && !usr.is_a?(CygwinUser))
199
+ return ';'
200
+ else
201
+ return ':'
202
+ end
203
+ end
204
+
205
+ def self.remove_gems_confirmation
206
+ msg =<<EOF
207
+ About to uninstall all RubyGems that match 'sprout-'....
208
+ Are you sure you want to do this? [Yn]
209
+ EOF
210
+ puts msg
211
+ response = $stdin.gets.chomp!
212
+ if(response.downcase.index('y'))
213
+ return true
214
+ end
215
+ return false
216
+ end
217
+
218
+ # Retrieve the file target to an executable by sprout name. Usually, these are tool sprouts.
219
+ # * +name+ Full sprout gem name that contains an executable file
220
+ # * +archive_path+ Optional parameter for tools that contain more than one executable, or for
221
+ # when you don't want to use the default executable presented by the tool. For example, the Flex 2 SDK
222
+ # has many executables, when this method is called for them, one might use something like:
223
+ # Sprout::Sprout.get_executable('sprout-flex2sdk-tool', 'bin/mxmlc')
224
+ # * +version+ Optional parameter to specify a particular gem version for this executable
225
+ def self.get_executable(name, archive_path=nil, version=nil)
226
+ target = self.sprout(name, version)
227
+ if(archive_path)
228
+ # If caller sent in a relative path to an executable (e.g., bin/mxmlc), use it
229
+ exe = File.join(target.installed_path, archive_path)
230
+ if(User.new.is_a?(WinUser) && !archive_path.match(/.exe$/))
231
+ # If we're on Win (even Cygwin), add .exe to support custom binaries (see sprout-flex2sdk-tool)
232
+ exe << '.exe'
233
+ end
234
+ elsif(target.url)
235
+ # Otherwise, use the default path to an executable if the RemoteFileTarget has a url prop
236
+ exe = File.join(target.installed_path, target.archive_path)
237
+ else
238
+ # Otherwise attempt to run the feature from the system path
239
+ exe = target.archive_path
240
+ end
241
+
242
+ if(File.exists?(exe) && File.stat(exe).mode != 33261)
243
+ File.chmod 0755, exe
244
+ end
245
+
246
+ return exe
247
+ end
248
+
249
+ # Allows us to easily download and install RubyGem sprouts by name and
250
+ # version.
251
+ # Returns a RubyGem Gem Spec[http://rubygems.org/read/chapter/20]
252
+ # when installation is complete. If the installed gem has a Ruby file
253
+ # configured to 'autorequire', that file will also be required by this
254
+ # method so that any provided Ruby functionality will be immediately
255
+ # available to client scripts. If the installed gem contains a
256
+ # 'sprout.spec' file, any RemoteFileTargets will be resolved synchronously
257
+ # and those files will be available in the Sprout::Sprout.cache.
258
+ #
259
+ def self.sprout(name, version=nil)
260
+ name = sprout_to_gem_name(name)
261
+ gem_spec = self.find_gem_spec(name, version)
262
+ sprout_spec_path = File.join(gem_spec.full_gem_path, @@spec)
263
+
264
+ if(gem_spec.autorequire)
265
+ $:.push(File.join(gem_spec.full_gem_path, 'lib'))
266
+ require gem_spec.autorequire
267
+ end
268
+ if(File.exists?(sprout_spec_path))
269
+ # Ensure the requisite files get downloaded and unpacked
270
+ Builder.build(sprout_spec_path, gem_file_cache(gem_spec.name, gem_spec.version))
271
+ else
272
+ return gem_spec
273
+ end
274
+ end
275
+
276
+ # Return sprout-#{name}-bundle for any name that does not begin with 'sprout-'. This was used early on in development
277
+ # but should possibly be removed as we move forward and try to support arbitrary RubyGems.
278
+ def self.sprout_to_gem_name(name)
279
+ if(!name.match(/^sprout-/))
280
+ name = "sprout-#{name}-bundle"
281
+ end
282
+ return name
283
+ end
284
+
285
+ # Return the home directory for this Sprout installation
286
+ def self.home
287
+ return @@home
288
+ end
289
+
290
+ # Return the location on disk where this installation of Sprouts stores it's cached files.
291
+ # If the currently installed version of Sprouts were 0.7 and your system username were 'foo'
292
+ # this would return the following locations:
293
+ # * +OSX+ /Users/foo/Library/Sprouts/cache/0.7
294
+ # * +Windows+ C:/Documents And Settings/foo/Local Settings/Application Data/Sprouts/cache/0.7
295
+ # * +Linux+ ~/.sprouts/cache/0.7
296
+ def self.sprout_cache
297
+ home = User.application_home(@@name)
298
+ return File.join(home, @@cache, "#{VERSION::MAJOR}.#{VERSION::MINOR}")
299
+ end
300
+
301
+ # Return the +sprout_cache+ combined with the passed in +name+ and +version+ so that you will get
302
+ # a cache location for a specific gem.
303
+ def self.gem_file_cache(name, version)
304
+ return File.join(sprout_cache, "#{name}-#{version}")
305
+ end
306
+
307
+ # Retrieve the RubyGems gem spec for a particular gem +name+ that meets the provided +requirements+.
308
+ # +requirements+ are provided as a string value like:
309
+ # '>= 0.0.1'
310
+ # or
311
+ # '0.0.1'
312
+ # This method will actually download and install the provided gem by +name+ and +requirements+ if
313
+ # it is not found locally on the system.
314
+ def self.find_gem_spec(name, requirements=nil, recursed=false)
315
+ specs = Gem::cache.search(/.*#{name}$/)
316
+ requirement = nil
317
+ if(requirements)
318
+ requirement = Gem::Requirement.new(requirements)
319
+ end
320
+ specs.each do |spec|
321
+ if(requirements)
322
+ if(requirement.satisfied_by?(spec.version))
323
+ return spec
324
+ end
325
+ else
326
+ return spec
327
+ end
328
+ end
329
+
330
+ if(recursed)
331
+ raise SproutError.new("Gem Spec not found for #{name} #{requirements}")
332
+ else
333
+ msg = ">> Loading gem [#{name}]"
334
+ msg << " #{requirements}" if requirements
335
+ msg << " from #{gem_sources.join(', ')} with it's dependencies"
336
+ Log.puts msg
337
+ parts = []
338
+ parts << "ins"
339
+ parts << "-r"
340
+ parts << name
341
+ # This url should be removed once released, released gems should be hosted from the rubyforge
342
+ # project, and development gems will be hosted on our domain.
343
+ parts << "--source #{gem_sources.join(' --source ')}" if(Log.debug || name.index('sprout-'))
344
+ parts << "-v #{requirements}" unless requirements.nil?
345
+
346
+ self.load_gem(parts.join(" "))
347
+ Gem::cache.refresh!
348
+ return find_gem_spec(name, requirements, true)
349
+ end
350
+ end
351
+
352
+ def self.load_gem(args)
353
+ # This must use a 'system' call because RubyGems
354
+ # sends an 'exit'?
355
+ system("#{get_gem_preamble} #{args}")
356
+ end
357
+
358
+ ##
359
+ # List of files to ignore when copying project templates
360
+ # These files will not be copied
361
+ @@COPY_IGNORE_FILES = ['.', '..', '.svn', '.DS_Store', 'CVS', '.cvs' 'Thumbs.db', '__MACOSX', '.Trashes', 'Desktop DB', 'Desktop DF']
362
+ # Do not copy files found in the ignore_files list
363
+ def self.ignore_file? file
364
+ @@COPY_IGNORE_FILES.each do |name|
365
+ if(name == file)
366
+ return true
367
+ end
368
+ end
369
+ return false
370
+ end
371
+
372
+ def self.gem_sources=(sources) # :nodoc:
373
+ if(sources.is_a?(String))
374
+ # TODO: Clean up the string that is sent in,
375
+ # maybe even split space or comma-delimited?
376
+ sources = [sources]
377
+ end
378
+ @@gem_sources = sources
379
+ end
380
+
381
+ # TODO: Should be updated after release so that all gems are
382
+ # loaded form rubyforge instead of projectsprouts, only development
383
+ # gems will continue to be hosted at this default domain.
384
+ def self.gem_sources # :nodoc:
385
+ @@gem_sources ||= ['http://gems.rubyforge.org']
386
+ end
387
+
388
+ def self.project_name=(name) # :nodoc:
389
+ @@project_name = name
390
+ end
391
+
392
+ # Return the current project_name assuming someone has already set it, otherwise return an empty string
393
+ def self.project_name
394
+ @@project_name ||= ''
395
+ end
396
+
397
+ def self.project_path=(path) # :nodoc:
398
+ @@project_rakefile = child_rakefile(path)
399
+ @@project_path = path
400
+ end
401
+
402
+ # project_path should step backward in the file system
403
+ # until it encounters a rakefile. The parent directory
404
+ # of that rakefile should be returned.
405
+ # If no rakefile is found, it should return Dir.pwd
406
+ def self.project_path
407
+ @@project_path ||= get_implicit_project_path(Dir.pwd)
408
+ end
409
+
410
+ # Return the rakefile in the current +project_path+
411
+ def self.project_rakefile
412
+ return @@project_rakefile ||= nil
413
+ end
414
+
415
+ private
416
+
417
+ # Look in the provided +dir+ for files that meet the criteria to be a valid Rakefile.
418
+ def self.child_rakefile(dir)
419
+ @@default_rakefiles.each do |file|
420
+ rake_path = File.join(dir, file)
421
+ if(File.exists?(rake_path))
422
+ return rake_path
423
+ end
424
+ end
425
+ return nil
426
+ end
427
+
428
+ def self.get_implicit_project_path(path)
429
+ # We have recursed to the root of the filesystem, return nil
430
+ if(path.nil? || path == '/' || path.match(/[A-Z]\:\//))
431
+ return Dir.pwd
432
+ end
433
+ # Look for a rakefile as a child of the current path
434
+ if(child_rakefile(path))
435
+ return path
436
+ end
437
+ # No rakefile and no root found, check in parent dir
438
+ return Sprout.get_implicit_project_path(File.dirname(path))
439
+ end
440
+
441
+ end
442
+ end
443
+
444
+ # Set an array of URLs to use as gem repositories when loading Sprout gems.
445
+ # Any rakefile that requires the sprout gem can use this method as follows:
446
+ #
447
+ # set_sources ['http://gems.yourdomain.com']
448
+ #
449
+ def set_sources(sources)
450
+ Sprout::Sprout.gem_sources = sources
451
+ end
452
+
453
+ # Helper method that will download and install remote sprouts by name and version
454
+ def sprout(name, version=nil)
455
+ Sprout::Sprout.sprout(name, version)
456
+ end