sprout 0.7.191-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/TODO +12 -0
- data/bin/sprout +128 -0
- data/doc/Bundle +14 -0
- data/doc/Generator +35 -0
- data/doc/Library +63 -0
- data/doc/Task +21 -0
- data/doc/Tool +20 -0
- data/lib/corelib.swc +0 -0
- data/lib/platform.rb +109 -0
- data/lib/progress_bar.rb +330 -0
- data/lib/sprout.rb +466 -0
- data/lib/sprout/builder.rb +35 -0
- data/lib/sprout/commands/generate.rb +9 -0
- data/lib/sprout/general_tasks.rb +6 -0
- data/lib/sprout/generator.rb +6 -0
- data/lib/sprout/generator/base_mixins.rb +126 -0
- data/lib/sprout/generator/named_base.rb +227 -0
- data/lib/sprout/log.rb +46 -0
- data/lib/sprout/process_runner.rb +82 -0
- data/lib/sprout/project_model.rb +270 -0
- data/lib/sprout/remote_file_loader.rb +247 -0
- data/lib/sprout/remote_file_target.rb +96 -0
- data/lib/sprout/simple_resolver.rb +88 -0
- data/lib/sprout/tasks/gem_wrap_task.rb +192 -0
- data/lib/sprout/tasks/library_task.rb +113 -0
- data/lib/sprout/tasks/sftp_task.rb +248 -0
- data/lib/sprout/tasks/ssh_task.rb +153 -0
- data/lib/sprout/tasks/tool_task.rb +569 -0
- data/lib/sprout/tasks/zip_task.rb +158 -0
- data/lib/sprout/template_resolver.rb +207 -0
- data/lib/sprout/user.rb +370 -0
- data/lib/sprout/version.rb +10 -0
- data/lib/sprout/zip_util.rb +61 -0
- data/rakefile.rb +133 -0
- data/samples/gem_wrap/rakefile.rb +17 -0
- metadata +168 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2007 Pattern Park
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
This class has been commented out because the termios feature
|
24
|
+
that allows users to more securely enter passwords does not
|
25
|
+
work in a DOS shell.
|
26
|
+
|
27
|
+
It would be greatly appreciate if someone refactored this to either:
|
28
|
+
a) Get the same functionality in a cross-platform manner
|
29
|
+
b) Only require and use termios on systems that allow it
|
30
|
+
=end
|
31
|
+
|
32
|
+
#gem 'net-ssh', '1.1.4'
|
33
|
+
#gem 'net-sftp', '1.1.1'
|
34
|
+
|
35
|
+
require 'net/ssh'
|
36
|
+
require 'net/sftp'
|
37
|
+
|
38
|
+
#require 'termios'
|
39
|
+
|
40
|
+
module Sprout
|
41
|
+
class SFTPError < StandardError #:nodoc:
|
42
|
+
end
|
43
|
+
|
44
|
+
# The SFTPTask provides a simple rake task interface to the SFTP client RubyGem.
|
45
|
+
# This task can allow you to easily push build artifacts to a remote server
|
46
|
+
# with a single shell command.
|
47
|
+
class SFTPTask < Rake::Task
|
48
|
+
|
49
|
+
# Collection of files that should be transmitted to the remote server
|
50
|
+
attr_accessor :files
|
51
|
+
# Host name of the server to connect to with no protocol prefix, like: sub.yourhost.com
|
52
|
+
attr_accessor :host
|
53
|
+
# Username to send to the remote host. You will be prompted for this value if it is left null.
|
54
|
+
#
|
55
|
+
# NOTE: You should never check a file into version control with this field filled in, it is
|
56
|
+
# provided here as a convenience for getting set up.
|
57
|
+
attr_accessor :username
|
58
|
+
# The mode for transmitted files. Defaults to 0644
|
59
|
+
attr_accessor :file_mode
|
60
|
+
# the mode for created directories. Defaults to 0755
|
61
|
+
attr_accessor :dir_mode
|
62
|
+
# The local path to mask from transmitted files. This is key feature for automated file transmission.
|
63
|
+
# For example, if you are sending a file:
|
64
|
+
# bin/assets/img/SomeImage.jpg
|
65
|
+
# into a directory on your server like:
|
66
|
+
# /var/www/someproject/
|
67
|
+
# You don't necessarily want the 'bin' folder copied over, so you set the local_path to 'bin' like:
|
68
|
+
# t.local_path = 'bin'
|
69
|
+
# and your server will have the file uploaded to:
|
70
|
+
# /var/www/someproject/assets/img/SomeImage.jpg
|
71
|
+
attr_accessor :local_path
|
72
|
+
# The Remote base path where files should be transmitted, can be absolute or relative.
|
73
|
+
attr_accessor :remote_path
|
74
|
+
# Password to send to the remote host. You will be prompted for this value if it is left null.
|
75
|
+
#
|
76
|
+
# NOTE: You should never check a file into version control with this field filled in, it is
|
77
|
+
# provided here as a convenience for getting set up.
|
78
|
+
attr_accessor :password
|
79
|
+
|
80
|
+
def initialize(task_name, app)
|
81
|
+
super(task_name, app)
|
82
|
+
@name = name
|
83
|
+
@files = []
|
84
|
+
@dir_mode = 0755
|
85
|
+
@file_mode = 0644
|
86
|
+
@host = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.define_task(args, &block) # :nodoc:
|
90
|
+
t = super
|
91
|
+
yield t if block_given?
|
92
|
+
end
|
93
|
+
|
94
|
+
def execute(*args) # :nodoc:
|
95
|
+
if(@files.size == 0)
|
96
|
+
if(@local_path)
|
97
|
+
expr = @local_path + '/**/**/*'
|
98
|
+
@files = FileList[expr]
|
99
|
+
else
|
100
|
+
raise SFTPError.new('SFTP requires either a local_path or files to be transmitted')
|
101
|
+
end
|
102
|
+
else
|
103
|
+
if(!@local_path)
|
104
|
+
@local_path = ''
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if(@host.nil?)
|
109
|
+
raise SFTPError.new('SFTP requires non-nil host parameter')
|
110
|
+
end
|
111
|
+
|
112
|
+
if(@username.nil?)
|
113
|
+
print "Username: "
|
114
|
+
@username = $stdin.gets.chomp!
|
115
|
+
raise SFTPError.new('SFTP requires username parameter') unless @username
|
116
|
+
end
|
117
|
+
|
118
|
+
if(@password.nil?)
|
119
|
+
print "Password: "
|
120
|
+
@password = $stdin.gets.chomp!
|
121
|
+
# @password = Password.get
|
122
|
+
raise SFTPError.new('SFTP requires password parameter') unless @password
|
123
|
+
end
|
124
|
+
|
125
|
+
if(get_confirmation)
|
126
|
+
puts ">> Connecting to Remote Server: #{@username}@#{@host}:#{@remote_path}"
|
127
|
+
|
128
|
+
Net::SFTP.start(@host, @username, @password) do |sftp|
|
129
|
+
begin
|
130
|
+
dir = sftp.opendir(@remote_path)
|
131
|
+
rescue Net::SFTP::Operations::StatusException
|
132
|
+
puts "[ERROR] [#{@remote_path}] does not exist on the server"
|
133
|
+
return
|
134
|
+
end
|
135
|
+
for file in @files
|
136
|
+
next if File.stat(file).directory?
|
137
|
+
remote_file = remote_file_name(file)
|
138
|
+
put_file(file, remote_file, sftp)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
puts "[WARNING] Publish aborted by user request"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def put_file(local_file, remote_file, sftp) # :nodoc:
|
147
|
+
begin
|
148
|
+
create_remote_dir(remote_file, sftp)
|
149
|
+
|
150
|
+
if(file_changed(local_file, remote_file, sftp))
|
151
|
+
puts ">> Pushing #{local_file} to: #{remote_file}"
|
152
|
+
sftp.put_file(local_file, remote_file)
|
153
|
+
end
|
154
|
+
rescue Net::SFTP::Operations::StatusException => e
|
155
|
+
raise unless e.code == 2
|
156
|
+
sftp.put_file(local_file, remote_file)
|
157
|
+
sftp.setstat(remote_file, :permissions => @file_mode)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def create_remote_dir(path, sftp) # :nodoc:
|
162
|
+
begin
|
163
|
+
sftp.stat(File.dirname(path))
|
164
|
+
rescue Net::SFTP::Operations::StatusException => e
|
165
|
+
raise unless e.code == 2
|
166
|
+
dir = File.dirname(path.sub(@remote_path, ''))
|
167
|
+
parts = dir.split(File::SEPARATOR)
|
168
|
+
part = File.join(@remote_path, parts.shift)
|
169
|
+
while(part)
|
170
|
+
begin
|
171
|
+
sftp.stat(part)
|
172
|
+
rescue Net::SFTP::Operations::StatusException => e
|
173
|
+
raise unless e.code == 2
|
174
|
+
sftp.mkdir(part, :permissions => @dir_mode)
|
175
|
+
end
|
176
|
+
if(parts.size > 0)
|
177
|
+
part = File.join(part, parts.shift)
|
178
|
+
else
|
179
|
+
part = nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def file_changed(local_file, remote_file, sftp) # :nodoc:
|
186
|
+
local_stat = File.stat(local_file)
|
187
|
+
remote_stat = sftp.stat(remote_file)
|
188
|
+
time_difference = (local_stat.mtime > Time.at(remote_stat.mtime))
|
189
|
+
size_difference = (local_stat.size != remote_stat.size)
|
190
|
+
return (time_difference || size_difference)
|
191
|
+
end
|
192
|
+
|
193
|
+
def remote_file_name(file) # :nodoc:
|
194
|
+
return @remote_path + file.sub(@local_path, '')
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_confirmation # :nodoc:
|
198
|
+
puts "-----------------------------------------"
|
199
|
+
puts "Are you sure you want to publish #{@files.size} files to:"
|
200
|
+
puts "#{@username}@#{@host}:#{@remote_path}? [Yn]"
|
201
|
+
response = $stdin.gets.chomp!
|
202
|
+
return (response.downcase == 'y' || response == "")
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
=begin
|
208
|
+
# The following implementation does not work at all on DOS machines,
|
209
|
+
# is there a cross-platform way to solve the secure password prompt problem?
|
210
|
+
# Password handling snippet found at: http://www.caliban.org/ruby/ruby-password.shtml
|
211
|
+
class Password
|
212
|
+
|
213
|
+
def Password.get(message="Password: ")
|
214
|
+
begin
|
215
|
+
if $stdin.tty?
|
216
|
+
Password.echo false
|
217
|
+
print message if message
|
218
|
+
end
|
219
|
+
|
220
|
+
return $stdin.gets.chomp
|
221
|
+
ensure
|
222
|
+
if $stdin.tty?
|
223
|
+
Password.echo true
|
224
|
+
print "\n"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def Password.echo(on=true, masked=false)
|
230
|
+
term = Termios::getattr( $stdin )
|
231
|
+
|
232
|
+
if on
|
233
|
+
term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
|
234
|
+
else # off
|
235
|
+
term.c_lflag &= ~Termios::ECHO
|
236
|
+
term.c_lflag &= ~Termios::ICANON if masked
|
237
|
+
end
|
238
|
+
|
239
|
+
Termios::setattr( $stdin, Termios::TCSANOW, term )
|
240
|
+
end
|
241
|
+
end
|
242
|
+
=end
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
def sftp(args, &block)
|
247
|
+
Sprout::SFTPTask.define_task(args, &block)
|
248
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2007 Pattern Park
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
This class has been commented out because the termios feature
|
24
|
+
that allows users to more securely enter passwords does not
|
25
|
+
work in a DOS shell.
|
26
|
+
|
27
|
+
It would be greatly appreciate if someone refactored this to either:
|
28
|
+
a) Get the same functionality in a cross-platform manner
|
29
|
+
b) Only require and use termios on systems that allow it
|
30
|
+
=end
|
31
|
+
|
32
|
+
#gem 'net-ssh', '1.1.4'
|
33
|
+
#gem 'net-sftp', '1.1.1'
|
34
|
+
|
35
|
+
require 'net/ssh'
|
36
|
+
require 'net/sftp'
|
37
|
+
#require 'termios'
|
38
|
+
|
39
|
+
module Sprout
|
40
|
+
class SSHError < StandardError #:nodoc:
|
41
|
+
end
|
42
|
+
|
43
|
+
# The SSHTask allows you to execute arbitrary commands on a remote host.
|
44
|
+
#
|
45
|
+
# ssh :update_gem_index do |t|
|
46
|
+
# t.host = 'dev.projectsprouts.com'
|
47
|
+
# t.username = 'someUser'
|
48
|
+
# t.commands << 'cd /var/www/projectsprouts/current/gems'
|
49
|
+
# t.commands << 'gem generate_index -d .'
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
class SSHTask < Rake::Task
|
53
|
+
|
54
|
+
# Host name of the server to connect to with no protocol prefix, like: sub.yourhost.com
|
55
|
+
attr_accessor :host
|
56
|
+
# Array of commands that will be executed on the remote machine
|
57
|
+
attr_accessor :commands
|
58
|
+
# Username to send to the remote host. You will be prompted for this value if it is left null.
|
59
|
+
attr_accessor :username
|
60
|
+
# Password to send to the remote host. You will be prompted for this value if it is left null.
|
61
|
+
#
|
62
|
+
# NOTE: You should never check a file into version control with this field filled in, it is
|
63
|
+
# provided here as a convenience for getting set up.
|
64
|
+
attr_accessor :password
|
65
|
+
|
66
|
+
def initialize(task_name, app)
|
67
|
+
super(task_name, app)
|
68
|
+
@name = name
|
69
|
+
@host = nil
|
70
|
+
@queue = []
|
71
|
+
@commands = []
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.define_task(args, &block) # :nodoc:
|
75
|
+
t = super
|
76
|
+
yield t if block_given?
|
77
|
+
end
|
78
|
+
|
79
|
+
def execute(*args) # :nodoc:
|
80
|
+
if(@host.nil?)
|
81
|
+
throw SSHError.new('SSH requires a valid host parameter')
|
82
|
+
end
|
83
|
+
|
84
|
+
if(@username.nil?)
|
85
|
+
print "Username: "
|
86
|
+
@username = $stdin.gets.chomp!
|
87
|
+
raise SFTPError.new('SFTP requires username parameter') unless @username
|
88
|
+
end
|
89
|
+
|
90
|
+
if(@password.nil?)
|
91
|
+
print "Password: "
|
92
|
+
@password = $stdin.gets.chomp!
|
93
|
+
# @password = Password.get
|
94
|
+
raise SFTPError.new('SFTP requires password parameter') unless @password
|
95
|
+
end
|
96
|
+
|
97
|
+
puts ">> Connecting to Remote Server: #{@username}@#{@host}:#{@remote_path}"
|
98
|
+
Net::SSH.start(@host, @username, @password) do |session|
|
99
|
+
session.open_channel do |channel|
|
100
|
+
commands.each do |command|
|
101
|
+
puts ">> #{command}"
|
102
|
+
channel.exec command
|
103
|
+
end
|
104
|
+
channel.close
|
105
|
+
end
|
106
|
+
session.loop
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
=begin
|
113
|
+
# The following implementation does not work at all on DOS machines,
|
114
|
+
# is there a cross-platform way to solve the secure password prompt problem?
|
115
|
+
# Password handling snippet found at: http://www.caliban.org/ruby/ruby-password.shtml
|
116
|
+
class Password
|
117
|
+
|
118
|
+
def Password.get(message="Password: ")
|
119
|
+
begin
|
120
|
+
if $stdin.tty?
|
121
|
+
Password.echo false
|
122
|
+
print message if message
|
123
|
+
end
|
124
|
+
|
125
|
+
return $stdin.gets.chomp
|
126
|
+
ensure
|
127
|
+
if $stdin.tty?
|
128
|
+
Password.echo true
|
129
|
+
print "\n"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def Password.echo(on=true, masked=false)
|
135
|
+
term = Termios::getattr( $stdin )
|
136
|
+
|
137
|
+
if on
|
138
|
+
term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
|
139
|
+
else # off
|
140
|
+
term.c_lflag &= ~Termios::ECHO
|
141
|
+
term.c_lflag &= ~Termios::ICANON if masked
|
142
|
+
end
|
143
|
+
|
144
|
+
Termios::setattr( $stdin, Termios::TCSANOW, term )
|
145
|
+
end
|
146
|
+
end
|
147
|
+
=end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def ssh(args, &block)
|
152
|
+
Sprout::SSHTask.define_task(args, &block)
|
153
|
+
end
|
@@ -0,0 +1,569 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
|
4
|
+
class ToolTaskError < StandardError #:nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
# The ToolTask provides some base functionality for any Command Line Interface (CLI) tool.
|
8
|
+
# It also provides support for GUI tools that you would like to expose from the
|
9
|
+
# Command Line (Like the Flash Player for example).
|
10
|
+
#
|
11
|
+
# ToolTask extends Rake::FileTask, and should be thought of in the same way.
|
12
|
+
# Martin Fowler did a much better job of describing Rake and specifically FileTasks than
|
13
|
+
# I can in his (now classic) Rake article[http://martinfowler.com/articles/rake.html#FileTasks] from 2005.
|
14
|
+
#
|
15
|
+
# What this means is that most tool task instances should be named for the file that they will create.
|
16
|
+
# For example, an Sprout::MXMLCTask instance should be named for the SWF that it will generate.
|
17
|
+
#
|
18
|
+
# mxmlc 'bin/SomeProject.swf' => :corelib do |t|
|
19
|
+
# t.input = 'src/SomeProject.as'
|
20
|
+
# t.default_size = '800 600'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# In general, a tool task will only be executed if it's output file (name) does not exist or
|
24
|
+
# if the output file is older than any file identified as a prerequisite.
|
25
|
+
#
|
26
|
+
# Many of the compiler tasks take advantage of this feature by opting out of unnecessary compilation.
|
27
|
+
#
|
28
|
+
# Subclasses can add and configure command line parameters by calling the protected add_param method
|
29
|
+
# that is implemented on this class.
|
30
|
+
#
|
31
|
+
class ToolTask < Rake::FileTask
|
32
|
+
|
33
|
+
# Arguments to be prepended in front of the command line output
|
34
|
+
attr_accessor :prepended_args
|
35
|
+
# Arguments to appended at the end of the command line output
|
36
|
+
attr_accessor :appended_args
|
37
|
+
|
38
|
+
def initialize(name, app) # :nodoc:
|
39
|
+
super
|
40
|
+
@prepended_args = nil
|
41
|
+
@appended_args = nil
|
42
|
+
@default_gem_name = nil
|
43
|
+
@default_gem_path = nil
|
44
|
+
initialize_task
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.define_task(args, &block)
|
48
|
+
t = super
|
49
|
+
if(t.is_a?(ToolTask))
|
50
|
+
yield t if block_given?
|
51
|
+
t.define
|
52
|
+
t.prepare
|
53
|
+
end
|
54
|
+
return t
|
55
|
+
end
|
56
|
+
|
57
|
+
# Full name of the sprout tool gem that this tool task will use. For example, the MXMLCTask
|
58
|
+
# uses the sprout-flex3sdk-tool at the time of this writing, but will at some point
|
59
|
+
# change to use the sprout-flex3sdk-tool. You can combine this value with gem_version
|
60
|
+
# in order to specify exactly which gem your tool task is executing.
|
61
|
+
def gem_name
|
62
|
+
return @gem_name ||= @default_gem_name
|
63
|
+
end
|
64
|
+
|
65
|
+
def gem_name=(name)
|
66
|
+
@gem_name = name
|
67
|
+
end
|
68
|
+
|
69
|
+
# The exact gem version that you would like the ToolTask to execute. By default this value
|
70
|
+
# should be nil and will download the latest version of the gem that is available unless
|
71
|
+
# there is a version already installed on your system.
|
72
|
+
#
|
73
|
+
# This attribute could be an easy
|
74
|
+
# way to update your local gem to the latest version without leaving your build file,
|
75
|
+
# but it's primary purpose is to allow you to specify very specific versions of the tools
|
76
|
+
# that your project depends on. This way your team can rest assured that they are all
|
77
|
+
# working with the same tools.
|
78
|
+
def gem_version
|
79
|
+
return @gem_version ||= nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def gem_version=(version)
|
83
|
+
@gem_version = version
|
84
|
+
end
|
85
|
+
|
86
|
+
# The path inside the installed gem where an executable can be found. For the MXMLCTask, this
|
87
|
+
# value is 'bin/mxmlc'.
|
88
|
+
def gem_path
|
89
|
+
return @gem_path ||= @default_gem_path
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create a string that can be turned into a file
|
93
|
+
# that rdoc can parse to describe the customized
|
94
|
+
# or generated task using param name, type and
|
95
|
+
# description
|
96
|
+
def to_rdoc
|
97
|
+
result = ''
|
98
|
+
parts = self.class.to_s.split('::')
|
99
|
+
class_name = parts.pop
|
100
|
+
module_count = 0
|
101
|
+
while(module_name = parts.shift)
|
102
|
+
result << "module #{module_name}\n"
|
103
|
+
module_count += 1
|
104
|
+
end
|
105
|
+
|
106
|
+
result << "class #{class_name} < ToolTask\n"
|
107
|
+
|
108
|
+
params.each do |param|
|
109
|
+
result << param.to_rdoc
|
110
|
+
end
|
111
|
+
|
112
|
+
while((module_count -= 1) >= 0)
|
113
|
+
result << "end\nend\n"
|
114
|
+
end
|
115
|
+
|
116
|
+
return result
|
117
|
+
end
|
118
|
+
|
119
|
+
def execute(*args)
|
120
|
+
#puts ">> Executing #{File.basename(exe)} #{to_shell}"
|
121
|
+
exe = Sprout.get_executable(gem_name, gem_path, gem_version)
|
122
|
+
User.execute(exe, to_shell)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create a string that represents this configured tool for shell execution
|
126
|
+
def to_shell
|
127
|
+
result = []
|
128
|
+
result << @prepended_args unless @prepended_args.nil?
|
129
|
+
params.each do |param|
|
130
|
+
if(param.visible?)
|
131
|
+
result << param.to_shell
|
132
|
+
end
|
133
|
+
end
|
134
|
+
result << @appended_args unless @appended_args.nil?
|
135
|
+
return result.join(' ')
|
136
|
+
end
|
137
|
+
|
138
|
+
# An Array of all parameters that have been added to this Tool.
|
139
|
+
def params
|
140
|
+
@params ||= []
|
141
|
+
end
|
142
|
+
|
143
|
+
# Called after initialize and define, usually subclasses should
|
144
|
+
# only override define.
|
145
|
+
def prepare
|
146
|
+
# Get each added param to inject prerequisites as necessary
|
147
|
+
params.each do |param|
|
148
|
+
param.prepare
|
149
|
+
end
|
150
|
+
# Ensure there are no duplicates in the prerequisite collection
|
151
|
+
@prerequisites = prerequisites.uniq
|
152
|
+
end
|
153
|
+
|
154
|
+
def define
|
155
|
+
resolve_libraries(prerequisites)
|
156
|
+
end
|
157
|
+
|
158
|
+
# The default file expression to append to each PathParam
|
159
|
+
# in order to build file change prerequisites.
|
160
|
+
#
|
161
|
+
# Defaults to '/**/**/*'
|
162
|
+
#
|
163
|
+
def default_file_expression
|
164
|
+
@default_file_expression ||= '/**/**/*'
|
165
|
+
end
|
166
|
+
|
167
|
+
protected
|
168
|
+
|
169
|
+
def initialize_task
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate
|
173
|
+
params.each do |param|
|
174
|
+
param.validate
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# +add_param+ is the workhorse of the ToolTask.
|
179
|
+
# This method is used to add new shell parameters to the task.
|
180
|
+
# +name+ is a symbol or string that represents the parameter that you would like to add
|
181
|
+
# such as :debug or :source_path.
|
182
|
+
# +type+ is usually sent as a Ruby symbol and can be one of the following:
|
183
|
+
#
|
184
|
+
# [:string] Any string value
|
185
|
+
# [:boolean] true or false
|
186
|
+
# [:number] Any number
|
187
|
+
# [:file] Path to a file
|
188
|
+
# [:url] Basic URL
|
189
|
+
# [:path] Path to a directory
|
190
|
+
# [:files] Collection of files
|
191
|
+
# [:paths] Collection of directories
|
192
|
+
# [:strings] Collection of arbitrary strings
|
193
|
+
# [:urls] Collection of URLs
|
194
|
+
#
|
195
|
+
# Be sure to check out the Sprout::TaskParam class to learn more about
|
196
|
+
# block editing the parameters.
|
197
|
+
#
|
198
|
+
# Once parameters have been added using the +add_param+ method, clients
|
199
|
+
# can set and get those parameters from the newly created task.
|
200
|
+
#
|
201
|
+
def add_param(name, type, &block) # :yields: Sprout::TaskParam
|
202
|
+
name = name.to_s
|
203
|
+
|
204
|
+
# First ensure the named accessor doesn't yet exist...
|
205
|
+
if(param_hash[name])
|
206
|
+
raise ToolTaskError.new("TaskBase.add_param called with existing parameter name: #{name}")
|
207
|
+
end
|
208
|
+
|
209
|
+
param = create_param(type)
|
210
|
+
param.init do |p|
|
211
|
+
p.belongs_to = self
|
212
|
+
p.name = name
|
213
|
+
p.type = type
|
214
|
+
yield p if block_given?
|
215
|
+
end
|
216
|
+
|
217
|
+
param_hash[name] = param
|
218
|
+
params << param
|
219
|
+
end
|
220
|
+
|
221
|
+
# Alias an existing parameter with another name. For example, the
|
222
|
+
# existing parameter :source_path might be given an alias '-sp' as follows:
|
223
|
+
#
|
224
|
+
# add_param_alias(:sp, :source_path)
|
225
|
+
#
|
226
|
+
# Alias parameters cannot be configured differently from the parameter
|
227
|
+
# that they alias
|
228
|
+
#
|
229
|
+
def add_param_alias(name, other_param)
|
230
|
+
if(param_hash.has_key? other_param.to_s)
|
231
|
+
param_hash[name.to_s] = param_hash[other_param.to_s]
|
232
|
+
else
|
233
|
+
raise ToolTaskError.new("TaskBase.add_param_alis called with")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
protected
|
238
|
+
|
239
|
+
def create_param(type)
|
240
|
+
return eval("#{type.to_s.capitalize}Param.new")
|
241
|
+
end
|
242
|
+
|
243
|
+
def param_hash
|
244
|
+
@param_hash ||= {}
|
245
|
+
end
|
246
|
+
|
247
|
+
def respond_to?(name)
|
248
|
+
result = super
|
249
|
+
if(!result)
|
250
|
+
result = param_hash.has_key? name
|
251
|
+
end
|
252
|
+
return result
|
253
|
+
end
|
254
|
+
|
255
|
+
def clean_name(name)
|
256
|
+
name.gsub(/=$/, '')
|
257
|
+
end
|
258
|
+
|
259
|
+
def method_missing(name,*args)
|
260
|
+
name = name.to_s
|
261
|
+
cleaned = clean_name(name)
|
262
|
+
if(!respond_to?(cleaned))
|
263
|
+
raise NoMethodError.new("undefined method '#{name}' for #{self.class}")
|
264
|
+
end
|
265
|
+
param = param_hash[cleaned]
|
266
|
+
|
267
|
+
if(name =~ /=$/)
|
268
|
+
param.value = args.shift
|
269
|
+
elsif(param)
|
270
|
+
param.value
|
271
|
+
else
|
272
|
+
raise ToolTaskError.new("method_missing called with undefined parameter [#{name}]")
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Iterate over all prerequisites looking for any
|
277
|
+
# that are a LibraryTask.
|
278
|
+
# Concrete ToolTask implementations should
|
279
|
+
# override resolve_library in order to add
|
280
|
+
# the library sources or binaries appropriately.
|
281
|
+
def resolve_libraries(prerequisites)
|
282
|
+
prerequisites.each do |prereq|
|
283
|
+
instance = Rake::application[prereq]
|
284
|
+
if(instance.is_a?(LibraryTask))
|
285
|
+
resolve_library(instance)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Concrete ToolTasks should override this method
|
291
|
+
# and add any dependent libraries appropriately
|
292
|
+
def resolve_library(library_task)
|
293
|
+
end
|
294
|
+
|
295
|
+
# If the provided path contains spaces, wrap it in quotes so that
|
296
|
+
# shell tools won't choke on the spaces
|
297
|
+
def clean_path(path)
|
298
|
+
if(path.index(' '))
|
299
|
+
path = %{"#{path}"}
|
300
|
+
end
|
301
|
+
return path
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
#######################################################
|
307
|
+
# Parameter Implementations
|
308
|
+
|
309
|
+
# The base class for all ToolTask parameters. This class is extended by a variety
|
310
|
+
# of concrete implementations.
|
311
|
+
#
|
312
|
+
# At the time of this writing, only the :boolean TaskParam modifies the interface by
|
313
|
+
# adding the +show_on_false+ attribute.
|
314
|
+
#
|
315
|
+
# Some other helpful features are as follows:
|
316
|
+
#
|
317
|
+
# :file, :files, :path and :paths will all add any items that have been added to
|
318
|
+
# their values as file task prerequisites. This is especially helpful when writing
|
319
|
+
# rake tasks for Command Line Interface (CLI) compilers.
|
320
|
+
#
|
321
|
+
class TaskParam
|
322
|
+
attr_accessor :name
|
323
|
+
attr_accessor :type
|
324
|
+
attr_accessor :validator
|
325
|
+
attr_accessor :description
|
326
|
+
attr_accessor :visible
|
327
|
+
attr_accessor :hidden_name
|
328
|
+
attr_accessor :hidden_value
|
329
|
+
attr_accessor :required
|
330
|
+
attr_accessor :belongs_to
|
331
|
+
|
332
|
+
attr_writer :prefix
|
333
|
+
attr_writer :value
|
334
|
+
attr_writer :delimiter
|
335
|
+
attr_writer :shell_name
|
336
|
+
|
337
|
+
# Set the file_expression (blob) to append to each path
|
338
|
+
# in order to build the prerequisites FileList.
|
339
|
+
#
|
340
|
+
# Defaults to parent ToolTask.default_file_expression
|
341
|
+
attr_writer :file_expression
|
342
|
+
|
343
|
+
def init
|
344
|
+
yield self if block_given?
|
345
|
+
end
|
346
|
+
|
347
|
+
# By default, ToolParams only appear in the shell
|
348
|
+
# output when they are not nil
|
349
|
+
def visible?
|
350
|
+
@visible ||= value
|
351
|
+
end
|
352
|
+
|
353
|
+
def required?
|
354
|
+
(required == true)
|
355
|
+
end
|
356
|
+
|
357
|
+
def validate
|
358
|
+
if(required? && !visible?)
|
359
|
+
raise ToolTaskError.new("#{name} is required and must not be nil")
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def prepare
|
364
|
+
prepare_prerequisites
|
365
|
+
end
|
366
|
+
|
367
|
+
def prepare_prerequisites
|
368
|
+
end
|
369
|
+
|
370
|
+
# Should the param name be hidden from the shell?
|
371
|
+
# Used for params like 'input' on mxmlc
|
372
|
+
def hidden_name?
|
373
|
+
@hidden_name ||= false
|
374
|
+
end
|
375
|
+
|
376
|
+
# Should the param value be hidden from the shell?
|
377
|
+
# Usually used for Boolean toggles like '-debug'
|
378
|
+
def hidden_value?
|
379
|
+
@hidden_value ||= false
|
380
|
+
end
|
381
|
+
|
382
|
+
# Leading character for each parameter
|
383
|
+
# Can sometimes be an empty string,
|
384
|
+
# other times it's a double dash '--'
|
385
|
+
# but usually it's just a single dash '-'
|
386
|
+
def prefix
|
387
|
+
@prefix ||= '-'
|
388
|
+
end
|
389
|
+
|
390
|
+
def value
|
391
|
+
@value ||= nil
|
392
|
+
end
|
393
|
+
|
394
|
+
def shell_value
|
395
|
+
value.to_s
|
396
|
+
end
|
397
|
+
|
398
|
+
def file_expression # :nodoc:
|
399
|
+
@file_expression ||= belongs_to.default_file_expression
|
400
|
+
end
|
401
|
+
|
402
|
+
# ToolParams join their name/value pair with an
|
403
|
+
# equals sign by default, this can be modified
|
404
|
+
# To a space or whatever you wish
|
405
|
+
def delimiter
|
406
|
+
@delimiter ||= '='
|
407
|
+
end
|
408
|
+
|
409
|
+
# Return the name with a single leading dash
|
410
|
+
# and underscores replaced with dashes
|
411
|
+
def shell_name
|
412
|
+
@shell_name ||= prefix + name.split('_').join('-')
|
413
|
+
end
|
414
|
+
|
415
|
+
def to_shell
|
416
|
+
if(hidden_name?)
|
417
|
+
return shell_value
|
418
|
+
elsif(hidden_value?)
|
419
|
+
return shell_name
|
420
|
+
else
|
421
|
+
return "#{shell_name}#{delimiter}#{shell_value}"
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Create a string that can be turned into a file
|
426
|
+
# that rdoc can parse to describe the customized
|
427
|
+
# or generated task using param name, type and
|
428
|
+
# description
|
429
|
+
def to_rdoc
|
430
|
+
result = ''
|
431
|
+
parts = description.split("\n") unless description.nil?
|
432
|
+
result << "# #{parts.join("\n# ")}\n" unless description.nil?
|
433
|
+
result << "def #{name}=(#{type})\n @#{name} = #{type}\nend\n\n"
|
434
|
+
return result
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
438
|
+
|
439
|
+
# Concrete param object for :string values
|
440
|
+
class StringParam < TaskParam # :nodoc:
|
441
|
+
|
442
|
+
end
|
443
|
+
|
444
|
+
# Concrete param object for :symbol values
|
445
|
+
# like class names
|
446
|
+
class SymbolParam < TaskParam # :nodoc:
|
447
|
+
end
|
448
|
+
|
449
|
+
# Concrete param object for :url values
|
450
|
+
class UrlParam < TaskParam # :nodoc:
|
451
|
+
end
|
452
|
+
|
453
|
+
# Concrete param object for :number values
|
454
|
+
class NumberParam < TaskParam # :nodoc:
|
455
|
+
end
|
456
|
+
|
457
|
+
# Concrete param object for :file values
|
458
|
+
class FileParam < TaskParam # :nodoc:
|
459
|
+
def prepare_prerequisites
|
460
|
+
if(value && value != belongs_to.name.to_s)
|
461
|
+
file value
|
462
|
+
belongs_to.prerequisites << value
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
# Concrete param object for :path values
|
468
|
+
class PathParam < FileParam # :nodoc:
|
469
|
+
end
|
470
|
+
|
471
|
+
# Concrete param object for :boolean values
|
472
|
+
class BooleanParam < TaskParam # :nodoc:
|
473
|
+
attr_writer :show_on_false
|
474
|
+
|
475
|
+
def visible?
|
476
|
+
@visible ||= value
|
477
|
+
if(show_on_false)
|
478
|
+
return true unless value
|
479
|
+
else
|
480
|
+
return @visible
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def show_on_false
|
485
|
+
@show_on_false ||= false
|
486
|
+
end
|
487
|
+
|
488
|
+
def value
|
489
|
+
@value ||= false
|
490
|
+
end
|
491
|
+
|
492
|
+
end
|
493
|
+
|
494
|
+
# Concrete param object for collections of strings
|
495
|
+
class StringsParam < TaskParam # :nodoc:
|
496
|
+
|
497
|
+
# Files lists are initialized to an empty array by default
|
498
|
+
def value
|
499
|
+
@value ||= []
|
500
|
+
end
|
501
|
+
|
502
|
+
# By default, the FilesParams will not appear in the shell
|
503
|
+
# output if there are zero items in the collection
|
504
|
+
def visible?
|
505
|
+
@visible ||= (value && value.size > 0)
|
506
|
+
end
|
507
|
+
|
508
|
+
# Default delimiter is +=
|
509
|
+
# This is what will appear between each name/value pair as in:
|
510
|
+
# "source_path+=src source_path+=test source_path+=lib"
|
511
|
+
def delimiter
|
512
|
+
@delimiter ||= "+="
|
513
|
+
end
|
514
|
+
|
515
|
+
# Returns a shell formatted string of the collection
|
516
|
+
def to_shell
|
517
|
+
result = []
|
518
|
+
value.each do |str|
|
519
|
+
result << "#{shell_name}#{delimiter}#{str}"
|
520
|
+
end
|
521
|
+
return result.join(' ')
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
# Concrete param object for collections of symbols (like class names)
|
526
|
+
class SymbolsParam < StringsParam # :nodoc:
|
527
|
+
end
|
528
|
+
|
529
|
+
# Concrete param object for collections of files
|
530
|
+
class FilesParam < StringsParam # :nodoc:
|
531
|
+
|
532
|
+
def prepare
|
533
|
+
super
|
534
|
+
usr = User.new
|
535
|
+
path = nil
|
536
|
+
value.each_index do |index|
|
537
|
+
path = value[index]
|
538
|
+
value[index] = usr.clean_path path
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def prepare_prerequisites
|
543
|
+
value.each do |f|
|
544
|
+
file f
|
545
|
+
belongs_to.prerequisites << f
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
# Concrete param object for collections of paths
|
551
|
+
class PathsParam < FilesParam # :nodoc:
|
552
|
+
|
553
|
+
def prepare_prerequisites
|
554
|
+
usr = User.new
|
555
|
+
value.each do |f|
|
556
|
+
files = FileList[f + file_expression]
|
557
|
+
files.each do |req_file|
|
558
|
+
file req_file
|
559
|
+
belongs_to.prerequisites << req_file
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
# Concrete param object for collections of files
|
566
|
+
class UrlsParam < StringsParam # :nodoc:
|
567
|
+
end
|
568
|
+
|
569
|
+
end
|