sprout 0.7.153-darwin

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