sprout 0.7.219-i686-darwin10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprout might be problematic. Click here for more details.

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