sprout 0.7.191-mswin32

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