sprout 0.7.191-mswin32
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/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
|