rant 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +504 -0
- data/README +203 -0
- data/Rantfile +104 -0
- data/TODO +19 -0
- data/bin/rant +12 -0
- data/bin/rant-import +12 -0
- data/devel-notes +50 -0
- data/doc/configure.rdoc +40 -0
- data/doc/csharp.rdoc +74 -0
- data/doc/rant-import.rdoc +32 -0
- data/doc/rant.rdoc +24 -0
- data/doc/rantfile.rdoc +227 -0
- data/doc/rubyproject.rdoc +210 -0
- data/lib/rant.rb +9 -0
- data/lib/rant/cs_compiler.rb +334 -0
- data/lib/rant/import.rb +291 -0
- data/lib/rant/import/rubydoc.rb +125 -0
- data/lib/rant/import/rubypackage.rb +417 -0
- data/lib/rant/import/rubytest.rb +97 -0
- data/lib/rant/plugin/README +50 -0
- data/lib/rant/plugin/configure.rb +345 -0
- data/lib/rant/plugin/csharp.rb +275 -0
- data/lib/rant/plugin_methods.rb +41 -0
- data/lib/rant/rantenv.rb +217 -0
- data/lib/rant/rantfile.rb +664 -0
- data/lib/rant/rantlib.rb +1118 -0
- data/lib/rant/rantsys.rb +258 -0
- data/lib/rant/rantvar.rb +82 -0
- data/rantmethods.rb +79 -0
- data/run_import +7 -0
- data/run_rant +7 -0
- data/setup.rb +1360 -0
- data/test/Rantfile +2 -0
- data/test/plugin/configure/Rantfile +47 -0
- data/test/plugin/configure/test_configure.rb +58 -0
- data/test/plugin/csharp/Hello.cs +10 -0
- data/test/plugin/csharp/Rantfile +30 -0
- data/test/plugin/csharp/src/A.cs +8 -0
- data/test/plugin/csharp/src/B.cs +8 -0
- data/test/plugin/csharp/test_csharp.rb +99 -0
- data/test/project1/Rantfile +127 -0
- data/test/project1/test_project.rb +203 -0
- data/test/project2/buildfile +14 -0
- data/test/project2/rantfile.rb +20 -0
- data/test/project2/sub1/Rantfile +12 -0
- data/test/project2/test_project.rb +87 -0
- data/test/project_rb1/README +14 -0
- data/test/project_rb1/bin/wgrep +5 -0
- data/test/project_rb1/lib/wgrep.rb +56 -0
- data/test/project_rb1/rantfile.rb +30 -0
- data/test/project_rb1/test/tc_wgrep.rb +21 -0
- data/test/project_rb1/test/text +3 -0
- data/test/project_rb1/test_project_rb1.rb +153 -0
- data/test/test_env.rb +47 -0
- data/test/test_filetask.rb +57 -0
- data/test/test_lighttask.rb +49 -0
- data/test/test_metatask.rb +29 -0
- data/test/test_rant_interface.rb +65 -0
- data/test/test_sys.rb +61 -0
- data/test/test_task.rb +115 -0
- data/test/toplevel.rf +11 -0
- data/test/ts_all.rb +4 -0
- data/test/tutil.rb +95 -0
- metadata +133 -0
data/lib/rant/rantlib.rb
ADDED
@@ -0,0 +1,1118 @@
|
|
1
|
+
|
2
|
+
# rantlib.rb - The core of Rant.
|
3
|
+
#
|
4
|
+
# Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
9
|
+
|
10
|
+
require 'getoptlong'
|
11
|
+
require 'rant/rantvar'
|
12
|
+
require 'rant/rantenv'
|
13
|
+
require 'rant/rantfile'
|
14
|
+
require 'rant/rantsys'
|
15
|
+
|
16
|
+
module Rant
|
17
|
+
VERSION = '0.3.0'
|
18
|
+
|
19
|
+
# Those are the filenames for rantfiles.
|
20
|
+
# Case matters!
|
21
|
+
RANTFILES = [ "Rantfile",
|
22
|
+
"rantfile",
|
23
|
+
"Rantfile.rb",
|
24
|
+
"rantfile.rb",
|
25
|
+
]
|
26
|
+
|
27
|
+
# Names of plugins and imports for which code was loaded.
|
28
|
+
# Files that where loaded with the `import' commant are directly
|
29
|
+
# added; files loaded with the `plugin' command are prefixed with
|
30
|
+
# "plugin/".
|
31
|
+
CODE_IMPORTS = []
|
32
|
+
|
33
|
+
class RantAbortException < StandardError
|
34
|
+
end
|
35
|
+
|
36
|
+
class RantDoneException < StandardError
|
37
|
+
end
|
38
|
+
|
39
|
+
class RantfileException < StandardError
|
40
|
+
end
|
41
|
+
|
42
|
+
# This module is a namespace for generator classes.
|
43
|
+
module Generators
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# There is one problem with executing Rantfiles in a special context:
|
49
|
+
# In the top-level execution environment, there are some methods
|
50
|
+
# available which are not available to all objects. One example is the
|
51
|
+
# +include+ method.
|
52
|
+
#
|
53
|
+
# To (at least partially) solve this problem, we capture the `main'
|
54
|
+
# object here and delegate methods from RantContext#method_missing to
|
55
|
+
# this object.
|
56
|
+
Rant::MAIN_OBJECT = self
|
57
|
+
|
58
|
+
class Array
|
59
|
+
def arglist
|
60
|
+
self.shell_pathes.join(' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def shell_pathes
|
64
|
+
if ::Rant::Env.on_windows?
|
65
|
+
self.collect { |entry|
|
66
|
+
entry = entry.tr("/", "\\")
|
67
|
+
if entry.include? ' '
|
68
|
+
'"' + entry + '"'
|
69
|
+
else
|
70
|
+
entry
|
71
|
+
end
|
72
|
+
}
|
73
|
+
else
|
74
|
+
self.collect { |entry|
|
75
|
+
if entry.include? ' '
|
76
|
+
"'" + entry + "'"
|
77
|
+
else
|
78
|
+
entry
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module Rant::Lib
|
86
|
+
|
87
|
+
# Parses one string (elem) as it occurs in the array
|
88
|
+
# which is returned by caller.
|
89
|
+
# E.g.:
|
90
|
+
# p parse_caller_elem "/usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'"
|
91
|
+
# prints:
|
92
|
+
# {:method=>"irb_binding", :ln=>52, :file=>"/usr/local/lib/ruby/1.8/irb/workspace.rb"}
|
93
|
+
def parse_caller_elem elem
|
94
|
+
parts = elem.split(":")
|
95
|
+
rh = { :file => parts[0],
|
96
|
+
:ln => parts[1].to_i
|
97
|
+
}
|
98
|
+
=begin
|
99
|
+
# commented for better performance
|
100
|
+
meth = parts[2]
|
101
|
+
if meth && meth =~ /\`(\w+)'/
|
102
|
+
meth = $1
|
103
|
+
end
|
104
|
+
rh[:method] = meth
|
105
|
+
=end
|
106
|
+
rh
|
107
|
+
end
|
108
|
+
|
109
|
+
module_function :parse_caller_elem
|
110
|
+
|
111
|
+
# currently unused
|
112
|
+
class Caller
|
113
|
+
def self.[](i)
|
114
|
+
new(caller[i+1])
|
115
|
+
end
|
116
|
+
def initialize(clr)
|
117
|
+
@clr = clr
|
118
|
+
@file = @ln = nil
|
119
|
+
end
|
120
|
+
def file
|
121
|
+
unless @file
|
122
|
+
ca = Lib.parse_caller_elem(clr)
|
123
|
+
@file = ca[:file]
|
124
|
+
@ln = ca[:ln]
|
125
|
+
end
|
126
|
+
@file
|
127
|
+
end
|
128
|
+
def ln
|
129
|
+
unless @ln
|
130
|
+
ca = Lib.parse_caller_elem(clr)
|
131
|
+
@file = ca[:file]
|
132
|
+
@ln = ca[:ln]
|
133
|
+
end
|
134
|
+
@ln
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# The methods in this module are the public interface to Rant that can
|
140
|
+
# be used in Rantfiles.
|
141
|
+
module RantContext
|
142
|
+
include Rant::Generators
|
143
|
+
|
144
|
+
# Define a basic task.
|
145
|
+
def task targ, &block
|
146
|
+
rantapp.task(targ, &block)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Define a file task.
|
150
|
+
def file targ, &block
|
151
|
+
rantapp.file(targ, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Add code and/or prerequisites to existing task.
|
155
|
+
def enhance targ, &block
|
156
|
+
rantapp.enhance(targ, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
def desc(*args)
|
160
|
+
rantapp.desc(*args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def gen(*args, &block)
|
164
|
+
rantapp.gen(*args, &block)
|
165
|
+
end
|
166
|
+
|
167
|
+
def import(*args, &block)
|
168
|
+
rantapp.import(*args, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
def plugin(*args, &block)
|
172
|
+
rantapp.plugin(*args, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Look in the subdirectories, given by args,
|
176
|
+
# for rantfiles.
|
177
|
+
def subdirs *args
|
178
|
+
rantapp.subdirs(*args)
|
179
|
+
end
|
180
|
+
|
181
|
+
def source rantfile
|
182
|
+
rantapp.source(rantfile)
|
183
|
+
end
|
184
|
+
|
185
|
+
def sys *args
|
186
|
+
rantapp.sys(*args)
|
187
|
+
end
|
188
|
+
end # module RantContext
|
189
|
+
|
190
|
+
class RantAppContext
|
191
|
+
include Rant
|
192
|
+
include RantContext
|
193
|
+
|
194
|
+
def initialize(app)
|
195
|
+
@rantapp = app
|
196
|
+
end
|
197
|
+
|
198
|
+
def rantapp
|
199
|
+
@rantapp
|
200
|
+
end
|
201
|
+
|
202
|
+
def method_missing(sym, *args)
|
203
|
+
# See the documentation for Rant::MAIN_OBJECT why we're doing
|
204
|
+
# this...
|
205
|
+
# Note also that the +send+ method also invokes private
|
206
|
+
# methods, this is very important for our intent.
|
207
|
+
Rant::MAIN_OBJECT.send(sym, *args)
|
208
|
+
rescue NoMethodError
|
209
|
+
raise NameError, "NameError: undefined local " +
|
210
|
+
"variable or method `#{sym}' for main:Object", caller
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
module Rant
|
215
|
+
include RantContext
|
216
|
+
|
217
|
+
# In the class definition of Rant::RantApp, this will be set to a
|
218
|
+
# new application object.
|
219
|
+
@@rantapp = nil
|
220
|
+
|
221
|
+
class << self
|
222
|
+
|
223
|
+
# Run a new rant application in the current working directory.
|
224
|
+
# This has the same effect as running +rant+ from the
|
225
|
+
# commandline. You can give arguments as you would give them
|
226
|
+
# on the commandline. If no argument is given, ARGV will be
|
227
|
+
# used.
|
228
|
+
#
|
229
|
+
# This method returns 0 if the rant application was
|
230
|
+
# successfull and 1 on failure. So if you need your own rant
|
231
|
+
# startscript, it could look like:
|
232
|
+
#
|
233
|
+
# exit Rant.run
|
234
|
+
#
|
235
|
+
# This runs rant in the current directory, using the arguments
|
236
|
+
# given to your script and the exit code as suggested by the
|
237
|
+
# rant application.
|
238
|
+
#
|
239
|
+
# Or if you want rant to always be quiet with this script,
|
240
|
+
# use:
|
241
|
+
#
|
242
|
+
# exit Rant.run("--quiet", ARGV)
|
243
|
+
#
|
244
|
+
# Of course, you can invoke rant directly at the bottom of
|
245
|
+
# your rantfile, so you can run it directly with ruby.
|
246
|
+
def run(first_arg=nil, *other_args)
|
247
|
+
other_args = other_args.flatten
|
248
|
+
args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
|
249
|
+
if @@rantapp && !@@rantapp.ran?
|
250
|
+
@@rantapp.args.replace(args.flatten)
|
251
|
+
@@rantapp.run
|
252
|
+
else
|
253
|
+
@@rantapp = Rant::RantApp.new(args)
|
254
|
+
@@rantapp.run
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def rantapp
|
259
|
+
@@rantapp
|
260
|
+
end
|
261
|
+
|
262
|
+
def rantapp=(app)
|
263
|
+
@@rantapp = app
|
264
|
+
end
|
265
|
+
|
266
|
+
# "Clear" the current Rant application. After this call,
|
267
|
+
# Rant has the same state as immediately after startup.
|
268
|
+
def reset
|
269
|
+
@@rantapp = nil
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def rantapp
|
274
|
+
@@rantapp
|
275
|
+
end
|
276
|
+
|
277
|
+
# Pre 0.2.7: Manually making necessary methods module
|
278
|
+
# functions. Note that it caused problems with caller
|
279
|
+
# parsing when the Rantfile did a `require "rant"' (irb!).
|
280
|
+
#module_function :task, :file, :desc, :subdirs,
|
281
|
+
# :gen, :source, :enhance, :sys, :plugin
|
282
|
+
|
283
|
+
extend self
|
284
|
+
|
285
|
+
end # module Rant
|
286
|
+
|
287
|
+
class Rant::RantApp
|
288
|
+
include Rant::Console
|
289
|
+
|
290
|
+
# Important: We try to synchronize all tasks referenced indirectly
|
291
|
+
# by @rantfiles with the task hash @tasks. The task hash is
|
292
|
+
# intended for fast task lookup per task name.
|
293
|
+
|
294
|
+
# The RantApp class has no own state.
|
295
|
+
|
296
|
+
OPTIONS = [
|
297
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT,
|
298
|
+
"Print this help and exit." ],
|
299
|
+
[ "--version", "-V", GetoptLong::NO_ARGUMENT,
|
300
|
+
"Print version of Rant and exit." ],
|
301
|
+
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT,
|
302
|
+
"Print more messages to stderr." ],
|
303
|
+
[ "--quiet", "-q", GetoptLong::NO_ARGUMENT,
|
304
|
+
"Don't print commands." ],
|
305
|
+
[ "--err-commands", GetoptLong::NO_ARGUMENT,
|
306
|
+
"Print failed commands and their exit status." ],
|
307
|
+
[ "--directory","-C", GetoptLong::REQUIRED_ARGUMENT,
|
308
|
+
"Run rant in DIRECTORY." ],
|
309
|
+
[ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT,
|
310
|
+
"Process RANTFILE instead of standard rantfiles.\n" +
|
311
|
+
"Multiple files may be specified with this option" ],
|
312
|
+
[ "--force-run","-a", GetoptLong::REQUIRED_ARGUMENT,
|
313
|
+
"Force TARGET to be run, even if it isn't required.\n"],
|
314
|
+
[ "--tasks", "-T", GetoptLong::NO_ARGUMENT,
|
315
|
+
"Show a list of all described tasks and exit." ],
|
316
|
+
|
317
|
+
# "private" options intended for debugging, testing and
|
318
|
+
# internal use. A private option is distuingished from others
|
319
|
+
# by having +nil+ as description!
|
320
|
+
[ "--stop-after-load", GetoptLong::NO_ARGUMENT, nil ],
|
321
|
+
]
|
322
|
+
|
323
|
+
# Arguments, usually those given on commandline.
|
324
|
+
attr_reader :args
|
325
|
+
# A list of all Rantfiles used by this app.
|
326
|
+
attr_reader :rantfiles
|
327
|
+
# A list of target names to be forced (run even
|
328
|
+
# if not required). Each of these targets will be removed
|
329
|
+
# from this list after the first run.
|
330
|
+
#
|
331
|
+
# Forced targets will be run before other targets.
|
332
|
+
attr_reader :force_targets
|
333
|
+
# A list of all registered plugins.
|
334
|
+
attr_reader :plugins
|
335
|
+
# The context in which Rantfiles are loaded. RantContext methods
|
336
|
+
# may be called through an instance_eval on this object (e.g. from
|
337
|
+
# plugins).
|
338
|
+
attr_reader :context
|
339
|
+
# The [] and []= operators may be used to set/get values from this
|
340
|
+
# object (like a hash). It is intended to let the different
|
341
|
+
# modules, plugins and tasks to communicate to each other.
|
342
|
+
attr_reader :var
|
343
|
+
# A hash with all tasks. For fast task lookup use this hash with
|
344
|
+
# the taskname as key.
|
345
|
+
attr_reader :tasks
|
346
|
+
# A list with of all imports (code loaded with +import+).
|
347
|
+
attr_reader :imports
|
348
|
+
|
349
|
+
def initialize *args
|
350
|
+
@args = args.flatten
|
351
|
+
# Rantfiles will be loaded in the context of this object.
|
352
|
+
@context = RantAppContext.new(self)
|
353
|
+
@sys = ::Rant::SysObject.new(self)
|
354
|
+
Rant.rantapp ||= self
|
355
|
+
@rantfiles = []
|
356
|
+
@tasks = {}
|
357
|
+
@opts = {
|
358
|
+
:verbose => 0,
|
359
|
+
:quiet => false,
|
360
|
+
}
|
361
|
+
@arg_rantfiles = [] # rantfiles given in args
|
362
|
+
@arg_targets = [] # targets given in args
|
363
|
+
@force_targets = []
|
364
|
+
@ran = false
|
365
|
+
@done = false
|
366
|
+
@plugins = []
|
367
|
+
@var = Rant::RantVar::Space.new
|
368
|
+
@imports = []
|
369
|
+
|
370
|
+
@task_show = nil
|
371
|
+
@task_desc = nil
|
372
|
+
|
373
|
+
@orig_pwd = nil
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
# Just ensure that Rant.rantapp holds an RantApp after loading
|
378
|
+
# this file. The code in initialize will register the new app with
|
379
|
+
# Rant.rantapp= if necessary.
|
380
|
+
self.new
|
381
|
+
|
382
|
+
def [](opt)
|
383
|
+
@opts[opt]
|
384
|
+
end
|
385
|
+
|
386
|
+
def []=(opt, val)
|
387
|
+
case opt
|
388
|
+
when :directory
|
389
|
+
self.rootdir = val
|
390
|
+
else
|
391
|
+
@opts[opt] = val
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def rootdir
|
396
|
+
@opts[:directory].dup
|
397
|
+
end
|
398
|
+
|
399
|
+
def rootdir=(newdir)
|
400
|
+
if @ran
|
401
|
+
raise "rootdir of rant application can't " +
|
402
|
+
"be changed after calling `run'"
|
403
|
+
end
|
404
|
+
@opts[:directory] = newdir.dup
|
405
|
+
rootdir # return a dup of the new rootdir
|
406
|
+
end
|
407
|
+
|
408
|
+
def ran?
|
409
|
+
@ran
|
410
|
+
end
|
411
|
+
|
412
|
+
def done?
|
413
|
+
@done
|
414
|
+
end
|
415
|
+
|
416
|
+
# Returns 0 on success and 1 on failure.
|
417
|
+
def run
|
418
|
+
@ran = true
|
419
|
+
# remind pwd
|
420
|
+
@orig_pwd = Dir.pwd
|
421
|
+
# Process commandline.
|
422
|
+
process_args
|
423
|
+
# Set pwd.
|
424
|
+
opts_dir = @opts[:directory]
|
425
|
+
if opts_dir
|
426
|
+
unless test(?d, opts_dir)
|
427
|
+
abort("No such directory - #{opts_dir}")
|
428
|
+
end
|
429
|
+
opts_dir != @orig_pwd && Dir.chdir(opts_dir)
|
430
|
+
else
|
431
|
+
@opts[:directory] = @orig_pwd
|
432
|
+
end
|
433
|
+
# read rantfiles
|
434
|
+
load_rantfiles
|
435
|
+
|
436
|
+
raise Rant::RantDoneException if @opts[:stop_after_load]
|
437
|
+
|
438
|
+
# Notify plugins before running tasks
|
439
|
+
@plugins.each { |plugin| plugin.rant_start }
|
440
|
+
if @opts[:targets]
|
441
|
+
show_descriptions
|
442
|
+
raise Rant::RantDoneException
|
443
|
+
end
|
444
|
+
# run tasks
|
445
|
+
run_tasks
|
446
|
+
raise Rant::RantDoneException
|
447
|
+
rescue Rant::RantDoneException
|
448
|
+
@done = true
|
449
|
+
# Notify plugins
|
450
|
+
@plugins.each { |plugin| plugin.rant_done }
|
451
|
+
return 0
|
452
|
+
rescue Rant::RantfileException
|
453
|
+
err_msg "Invalid Rantfile: " + $!.message
|
454
|
+
$stderr.puts "rant aborted!"
|
455
|
+
return 1
|
456
|
+
rescue Rant::RantAbortException
|
457
|
+
$stderr.puts "rant aborted!"
|
458
|
+
return 1
|
459
|
+
rescue
|
460
|
+
err_msg $!.message, $!.backtrace
|
461
|
+
$stderr.puts "rant aborted!"
|
462
|
+
return 1
|
463
|
+
ensure
|
464
|
+
# TODO: exception handling!
|
465
|
+
@plugins.each { |plugin| plugin.rant_plugin_stop }
|
466
|
+
@plugins.each { |plugin| plugin.rant_quit }
|
467
|
+
# restore pwd
|
468
|
+
Dir.pwd != @orig_pwd && Dir.chdir(@orig_pwd)
|
469
|
+
Rant.rantapp = self.class.new
|
470
|
+
end
|
471
|
+
|
472
|
+
###### methods accessible through RantContext ####################
|
473
|
+
def show *args
|
474
|
+
@task_show = *args.join("\n")
|
475
|
+
end
|
476
|
+
|
477
|
+
def desc *args
|
478
|
+
if args.empty? || (args.size == 1 && args.first.nil?)
|
479
|
+
@task_desc = nil
|
480
|
+
else
|
481
|
+
@task_desc = args.join("\n")
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
def task targ, &block
|
486
|
+
prepare_task(targ, block) { |name,pre,blk|
|
487
|
+
Rant::Task.new(self, name, pre, &blk)
|
488
|
+
}
|
489
|
+
end
|
490
|
+
|
491
|
+
def file targ, &block
|
492
|
+
prepare_task(targ, block) { |name,pre,blk|
|
493
|
+
Rant::FileTask.new(self, name, pre, &blk)
|
494
|
+
}
|
495
|
+
end
|
496
|
+
|
497
|
+
def gen(*args, &block)
|
498
|
+
# retrieve caller info
|
499
|
+
clr = caller[1]
|
500
|
+
ch = Rant::Lib::parse_caller_elem(clr)
|
501
|
+
name = nil
|
502
|
+
pre = []
|
503
|
+
ln = ch[:ln] || 0
|
504
|
+
file = ch[:file]
|
505
|
+
# validate args
|
506
|
+
generator = args.shift
|
507
|
+
# Let modules/classes from the Generator namespace override
|
508
|
+
# other generators.
|
509
|
+
begin
|
510
|
+
if generator.is_a? Module
|
511
|
+
generator = ::Rant::Generators.const_get(generator.to_s)
|
512
|
+
end
|
513
|
+
rescue NameError, ArgumentError
|
514
|
+
end
|
515
|
+
unless generator.respond_to? :rant_generate
|
516
|
+
abort(pos_text(file, ln),
|
517
|
+
"First argument to `gen' has to be a task-generator.")
|
518
|
+
end
|
519
|
+
# ask generator to produce a task for this application
|
520
|
+
generator.rant_generate(self, ch, args, &block)
|
521
|
+
end
|
522
|
+
|
523
|
+
# Currently ignores block.
|
524
|
+
def import(*args, &block)
|
525
|
+
if block
|
526
|
+
warn_msg "import: currently ignoring block"
|
527
|
+
end
|
528
|
+
args.flatten.each { |arg|
|
529
|
+
unless String === arg
|
530
|
+
abort("import: currently " +
|
531
|
+
"only strings are allowed as arguments")
|
532
|
+
end
|
533
|
+
unless @imports.include? arg
|
534
|
+
unless Rant::CODE_IMPORTS.include? arg
|
535
|
+
begin
|
536
|
+
require "rant/import/#{arg}"
|
537
|
+
rescue LoadError => e
|
538
|
+
abort("No such import - #{arg}")
|
539
|
+
end
|
540
|
+
Rant::CODE_IMPORTS << arg.dup
|
541
|
+
end
|
542
|
+
@imports << arg.dup
|
543
|
+
end
|
544
|
+
}
|
545
|
+
end
|
546
|
+
|
547
|
+
def plugin(*args, &block)
|
548
|
+
# retrieve caller info
|
549
|
+
clr = caller[1]
|
550
|
+
ch = Rant::Lib::parse_caller_elem(clr)
|
551
|
+
name = nil
|
552
|
+
pre = []
|
553
|
+
ln = ch[:ln] || 0
|
554
|
+
file = ch[:file]
|
555
|
+
|
556
|
+
pl_name = args.shift
|
557
|
+
pl_name = pl_name.to_str if pl_name.respond_to? :to_str
|
558
|
+
pl_name = pl_name.to_s if pl_name.is_a? Symbol
|
559
|
+
unless pl_name.is_a? String
|
560
|
+
abort(pos_text(file, ln),
|
561
|
+
"Plugin name has to be a string or symbol.")
|
562
|
+
end
|
563
|
+
lc_pl_name = pl_name.downcase
|
564
|
+
import_name = "plugin/#{lc_pl_name}"
|
565
|
+
unless Rant::CODE_IMPORTS.include? import_name
|
566
|
+
begin
|
567
|
+
require "rant/plugin/#{lc_pl_name}"
|
568
|
+
Rant::CODE_IMPORTS << import_name
|
569
|
+
rescue LoadError
|
570
|
+
abort(pos_text(file, ln),
|
571
|
+
"no such plugin library - `#{lc_pl_name}'")
|
572
|
+
end
|
573
|
+
end
|
574
|
+
pl_class = nil
|
575
|
+
begin
|
576
|
+
pl_class = ::Rant::Plugin.const_get(pl_name)
|
577
|
+
rescue NameError, ArgumentError
|
578
|
+
abort(pos_text(file, ln),
|
579
|
+
"`#{pl_name}': no such plugin")
|
580
|
+
end
|
581
|
+
|
582
|
+
plugin = pl_class.rant_plugin_new(self, ch, *args, &block)
|
583
|
+
# TODO: check for rant_plugin?
|
584
|
+
@plugins << plugin
|
585
|
+
msg 2, "Plugin `#{plugin.rant_plugin_name}' registered."
|
586
|
+
plugin.rant_plugin_init
|
587
|
+
# return plugin instance
|
588
|
+
plugin
|
589
|
+
end
|
590
|
+
|
591
|
+
# Add block and prerequisites to the task specified by the
|
592
|
+
# name given as only key in targ.
|
593
|
+
# If there is no task with the given name, generate a warning
|
594
|
+
# and a new file task.
|
595
|
+
def enhance targ, &block
|
596
|
+
prepare_task(targ, block) { |name,pre,blk|
|
597
|
+
t = select_task { |t| t.name == name }
|
598
|
+
if t
|
599
|
+
t.enhance(pre, &blk)
|
600
|
+
return t
|
601
|
+
end
|
602
|
+
warn_msg "enhance \"#{name}\": no such task",
|
603
|
+
"Generating a new file task with the given name."
|
604
|
+
Rant::FileTask.new(self, name, pre, &blk)
|
605
|
+
}
|
606
|
+
end
|
607
|
+
|
608
|
+
def source rantfile
|
609
|
+
rf, is_new = rantfile_for_path(rantfile)
|
610
|
+
return false unless is_new
|
611
|
+
unless rf.exist?
|
612
|
+
abort("source: No such file to load - #{rantfile}")
|
613
|
+
end
|
614
|
+
load_file rf
|
615
|
+
true
|
616
|
+
end
|
617
|
+
|
618
|
+
# Search the given directories for Rantfiles.
|
619
|
+
def subdirs *args
|
620
|
+
args.flatten!
|
621
|
+
cinf = Rant::Lib::parse_caller_elem(caller[1])
|
622
|
+
ln = cinf[:ln] || 0
|
623
|
+
file = cinf[:file]
|
624
|
+
args.each { |arg|
|
625
|
+
if arg.is_a? Symbol
|
626
|
+
arg = arg.to_s
|
627
|
+
elsif arg.respond_to? :to_str
|
628
|
+
arg = arg.to_str
|
629
|
+
end
|
630
|
+
unless arg.is_a? String
|
631
|
+
abort(pos_text(file, ln),
|
632
|
+
"in `subdirs' command: arguments must be strings")
|
633
|
+
end
|
634
|
+
loaded = false
|
635
|
+
rantfiles_in_dir(arg).each { |f|
|
636
|
+
loaded = true
|
637
|
+
rf, is_new = rantfile_for_path(f)
|
638
|
+
if is_new
|
639
|
+
load_file rf
|
640
|
+
end
|
641
|
+
}
|
642
|
+
unless loaded || quiet?
|
643
|
+
warn_msg(pos_text(file, ln) + "; in `subdirs' command:",
|
644
|
+
"No Rantfile in subdir `#{arg}'.")
|
645
|
+
end
|
646
|
+
}
|
647
|
+
rescue SystemCallError => e
|
648
|
+
abort(pos_text(file, ln),
|
649
|
+
"in `subdirs' command: " + e.message)
|
650
|
+
end
|
651
|
+
|
652
|
+
def sys *args
|
653
|
+
if args.empty?
|
654
|
+
@sys
|
655
|
+
else
|
656
|
+
@sys.sh(*args)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
##################################################################
|
660
|
+
|
661
|
+
# Pop (remove and return) current pending task description.
|
662
|
+
def pop_desc
|
663
|
+
td = @task_desc
|
664
|
+
@task_desc = nil
|
665
|
+
td
|
666
|
+
end
|
667
|
+
|
668
|
+
# Prints msg as error message and throws a RantAbortException.
|
669
|
+
def abort *msg
|
670
|
+
err_msg(msg) unless msg.empty?
|
671
|
+
raise Rant::RantAbortException
|
672
|
+
end
|
673
|
+
|
674
|
+
def help
|
675
|
+
puts "rant [-f RANTFILE] [OPTIONS] tasks..."
|
676
|
+
puts
|
677
|
+
puts "Options are:"
|
678
|
+
print option_listing(OPTIONS)
|
679
|
+
raise Rant::RantDoneException
|
680
|
+
end
|
681
|
+
|
682
|
+
def show_descriptions
|
683
|
+
tlist = select_tasks { |t| t.description }
|
684
|
+
if tlist.empty?
|
685
|
+
msg "No described targets."
|
686
|
+
return
|
687
|
+
end
|
688
|
+
prefix = "rant "
|
689
|
+
infix = " # "
|
690
|
+
name_length = 0
|
691
|
+
tlist.each { |t|
|
692
|
+
if t.name.length > name_length
|
693
|
+
name_length = t.name.length
|
694
|
+
end
|
695
|
+
}
|
696
|
+
name_length < 7 && name_length = 7
|
697
|
+
cmd_length = prefix.length + name_length
|
698
|
+
tlist.each { |t|
|
699
|
+
print(prefix + t.name.ljust(name_length) + infix)
|
700
|
+
dt = t.description.sub(/\s+$/, "")
|
701
|
+
puts dt.sub("\n", "\n" + ' ' * cmd_length + infix + " ")
|
702
|
+
}
|
703
|
+
true
|
704
|
+
end
|
705
|
+
|
706
|
+
# Increase verbosity.
|
707
|
+
def more_verbose
|
708
|
+
@opts[:verbose] += 1
|
709
|
+
@opts[:quiet] = false
|
710
|
+
end
|
711
|
+
|
712
|
+
def verbose
|
713
|
+
@opts[:verbose]
|
714
|
+
end
|
715
|
+
|
716
|
+
def quiet?
|
717
|
+
@opts[:quiet]
|
718
|
+
end
|
719
|
+
|
720
|
+
def pos_text file, ln
|
721
|
+
t = "in file `#{file}'"
|
722
|
+
if ln && ln > 0
|
723
|
+
t << ", line #{ln}"
|
724
|
+
end
|
725
|
+
t + ": "
|
726
|
+
end
|
727
|
+
|
728
|
+
def msg *args
|
729
|
+
verbose_level = args[0]
|
730
|
+
if verbose_level.is_a? Integer
|
731
|
+
super(args[1..-1]) if verbose_level <= verbose
|
732
|
+
else
|
733
|
+
super
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
# Print a command message as would be done from a call to a
|
738
|
+
# Sys method.
|
739
|
+
def cmd_msg cmd
|
740
|
+
$stdout.puts cmd unless quiet?
|
741
|
+
end
|
742
|
+
|
743
|
+
###### public methods regarding plugins ##########################
|
744
|
+
# The preferred way for a plugin to report a warning.
|
745
|
+
def plugin_warn(*args)
|
746
|
+
warn_msg(*args)
|
747
|
+
end
|
748
|
+
# The preferred way for a plugin to report an error.
|
749
|
+
def plugin_err(*args)
|
750
|
+
err_msg(*args)
|
751
|
+
end
|
752
|
+
|
753
|
+
# Get the plugin with the given name or nil. Yields the plugin
|
754
|
+
# object if block given.
|
755
|
+
def plugin_named(name)
|
756
|
+
@plugins.each { |plugin|
|
757
|
+
if plugin.rant_plugin_name == name
|
758
|
+
yield plugin if block_given?
|
759
|
+
return plugin
|
760
|
+
end
|
761
|
+
}
|
762
|
+
nil
|
763
|
+
end
|
764
|
+
##################################################################
|
765
|
+
|
766
|
+
# All targets given on commandline, including those given
|
767
|
+
# with the -a option. The list will be in processing order.
|
768
|
+
def cmd_targets
|
769
|
+
@force_targets + @arg_targets
|
770
|
+
end
|
771
|
+
|
772
|
+
private
|
773
|
+
def have_any_task?
|
774
|
+
not @rantfiles.all? { |f| f.tasks.empty? }
|
775
|
+
end
|
776
|
+
|
777
|
+
def run_tasks
|
778
|
+
unless have_any_task?
|
779
|
+
abort("No tasks defined for this rant application!")
|
780
|
+
end
|
781
|
+
|
782
|
+
# Target selection strategy:
|
783
|
+
# Run tasks specified on commandline, if not given:
|
784
|
+
# run default task, if not given:
|
785
|
+
# run first defined task.
|
786
|
+
target_list = @force_targets + @arg_targets
|
787
|
+
# The target list is a list of strings, not Task objects!
|
788
|
+
if target_list.empty?
|
789
|
+
have_default = @rantfiles.any? { |f|
|
790
|
+
f.tasks.any? { |t| t.name == "default" }
|
791
|
+
}
|
792
|
+
if have_default
|
793
|
+
target_list << "default"
|
794
|
+
else
|
795
|
+
first = nil
|
796
|
+
@rantfiles.each { |f|
|
797
|
+
unless f.tasks.empty?
|
798
|
+
first = f.tasks.first.name
|
799
|
+
break
|
800
|
+
end
|
801
|
+
}
|
802
|
+
target_list << first
|
803
|
+
end
|
804
|
+
end
|
805
|
+
# Now, run all specified tasks in all rantfiles,
|
806
|
+
# rantfiles in reverse order.
|
807
|
+
opt = {}
|
808
|
+
matching_tasks = 0
|
809
|
+
target_list.each do |target|
|
810
|
+
matching_tasks = 0
|
811
|
+
if @force_targets.include?(target)
|
812
|
+
opt[:force] = true
|
813
|
+
@force_targets.delete(target)
|
814
|
+
end
|
815
|
+
(select_tasks { |t| t.name == target }).each { |t|
|
816
|
+
matching_tasks += 1
|
817
|
+
begin
|
818
|
+
t.invoke(opt)
|
819
|
+
rescue Rant::TaskFail => e
|
820
|
+
# TODO: Report failed dependancy.
|
821
|
+
abort("Task `#{e.tname}' fail.")
|
822
|
+
end
|
823
|
+
}
|
824
|
+
if matching_tasks == 0
|
825
|
+
abort("Don't know how to build `#{target}'.")
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
# Returns a list with all tasks for which yield
|
831
|
+
# returns true.
|
832
|
+
def select_tasks
|
833
|
+
selection = []
|
834
|
+
### pre 0.2.10 ##################
|
835
|
+
# @rantfile.reverse.each { |rf|
|
836
|
+
#################################
|
837
|
+
@rantfiles.each { |rf|
|
838
|
+
rf.tasks.each { |t|
|
839
|
+
selection << t if yield t
|
840
|
+
}
|
841
|
+
}
|
842
|
+
selection
|
843
|
+
end
|
844
|
+
public :select_tasks
|
845
|
+
|
846
|
+
# Returns an array (might be a MetaTask) with all tasks that have
|
847
|
+
# the given name.
|
848
|
+
def select_tasks_by_name name
|
849
|
+
s = @tasks[name]
|
850
|
+
case s
|
851
|
+
when nil: []
|
852
|
+
when Rant::Worker: [s]
|
853
|
+
else # assuming MetaTask
|
854
|
+
s
|
855
|
+
end
|
856
|
+
end
|
857
|
+
public :select_tasks_by_name
|
858
|
+
|
859
|
+
# Get the first task for which yield returns true. Returns nil if
|
860
|
+
# yield never returned true.
|
861
|
+
def select_task
|
862
|
+
@rantfiles.reverse.each { |rf|
|
863
|
+
rf.tasks.each { |t|
|
864
|
+
return t if yield t
|
865
|
+
}
|
866
|
+
}
|
867
|
+
nil
|
868
|
+
end
|
869
|
+
|
870
|
+
def load_rantfiles
|
871
|
+
# Take care: When rant isn't invoked from commandline,
|
872
|
+
# some "rant code" could already have run!
|
873
|
+
# We run the default Rantfiles only if no tasks where
|
874
|
+
# already defined and no Rantfile was given in args.
|
875
|
+
new_rf = []
|
876
|
+
@arg_rantfiles.each { |rf|
|
877
|
+
if test(?f, rf)
|
878
|
+
new_rf << rf
|
879
|
+
else
|
880
|
+
abort("No such file: " + rf)
|
881
|
+
end
|
882
|
+
}
|
883
|
+
if new_rf.empty? && !have_any_task?
|
884
|
+
# no Rantfiles given in args, no tasks defined,
|
885
|
+
# so let's look for the default files
|
886
|
+
new_rf = rantfiles_in_dir
|
887
|
+
end
|
888
|
+
new_rf.map! { |path|
|
889
|
+
rf, is_new = rantfile_for_path(path)
|
890
|
+
if is_new
|
891
|
+
load_file rf
|
892
|
+
end
|
893
|
+
rf
|
894
|
+
}
|
895
|
+
if @rantfiles.empty?
|
896
|
+
abort("No Rantfile in current directory (" + Dir.pwd + ")",
|
897
|
+
"looking for " + Rant::RANTFILES.join(", ") +
|
898
|
+
"; case matters!")
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
def load_file rantfile
|
903
|
+
msg 1, "source #{rantfile.path}"
|
904
|
+
begin
|
905
|
+
path = rantfile.absolute_path
|
906
|
+
@context.instance_eval(File.read(path), path)
|
907
|
+
rescue NameError => e
|
908
|
+
abort("Name error when loading `#{rantfile.path}':",
|
909
|
+
e.message, e.backtrace)
|
910
|
+
rescue LoadError => e
|
911
|
+
abort("Load error when loading `#{rantfile.path}':",
|
912
|
+
e.message, e.backtrace)
|
913
|
+
rescue ScriptError => e
|
914
|
+
abort("Script error when loading `#{rantfile.path}':",
|
915
|
+
e.message, e.backtrace)
|
916
|
+
end
|
917
|
+
unless @rantfiles.include?(rantfile)
|
918
|
+
@rantfiles << rantfile
|
919
|
+
end
|
920
|
+
end
|
921
|
+
private :load_file
|
922
|
+
|
923
|
+
# Get all rantfiles in dir.
|
924
|
+
# If dir is nil, look in current directory.
|
925
|
+
# Returns always an array with the pathes (not only the filenames)
|
926
|
+
# to the rantfiles.
|
927
|
+
def rantfiles_in_dir dir=nil
|
928
|
+
files = []
|
929
|
+
::Rant::RANTFILES.each { |rfn|
|
930
|
+
path = dir ? File.join(dir, rfn) : rfn
|
931
|
+
# We load don't accept rantfiles with pathes that differ
|
932
|
+
# only in case. This protects from loading the same file
|
933
|
+
# twice on case insensitive file systems.
|
934
|
+
unless files.find { |f| f.downcase == path.downcase }
|
935
|
+
files << path if test(?f, path)
|
936
|
+
end
|
937
|
+
}
|
938
|
+
files
|
939
|
+
end
|
940
|
+
|
941
|
+
def process_args
|
942
|
+
# WARNING: we currently have to fool getoptlong,
|
943
|
+
# by temporory changing ARGV!
|
944
|
+
# This could cause problems.
|
945
|
+
old_argv = ARGV.dup
|
946
|
+
ARGV.replace(@args.dup)
|
947
|
+
cmd_opts = GetoptLong.new(*OPTIONS.collect { |lst| lst[0..-2] })
|
948
|
+
cmd_opts.quiet = true
|
949
|
+
cmd_opts.each { |opt, value|
|
950
|
+
case opt
|
951
|
+
when "--verbose": more_verbose
|
952
|
+
when "--quiet"
|
953
|
+
@opts[:quiet] = true
|
954
|
+
@opts[:verbose] = -1
|
955
|
+
when "--err-commands"
|
956
|
+
@opts[:err_commands] = true
|
957
|
+
when "--version"
|
958
|
+
$stdout.puts "rant #{Rant::VERSION}"
|
959
|
+
raise Rant::RantDoneException
|
960
|
+
when "--help"
|
961
|
+
help
|
962
|
+
when "--directory"
|
963
|
+
@opts[:directory] = value
|
964
|
+
when "--rantfile"
|
965
|
+
@arg_rantfiles << value
|
966
|
+
when "--force-run"
|
967
|
+
@force_targets << value
|
968
|
+
when "--tasks"
|
969
|
+
@opts[:targets] = true
|
970
|
+
when "--stop-after-load"
|
971
|
+
@opts[:stop_after_load] = true
|
972
|
+
end
|
973
|
+
}
|
974
|
+
rescue GetoptLong::Error => e
|
975
|
+
abort(e.message)
|
976
|
+
ensure
|
977
|
+
rem_args = ARGV.dup
|
978
|
+
ARGV.replace(old_argv)
|
979
|
+
rem_args.each { |ra|
|
980
|
+
if ra =~ /(^[^=]+)=([^=]+)$/
|
981
|
+
msg 2, "Environment: #$1=#$2"
|
982
|
+
ENV[$1] = $2
|
983
|
+
else
|
984
|
+
@arg_targets << ra
|
985
|
+
end
|
986
|
+
}
|
987
|
+
end
|
988
|
+
|
989
|
+
def prepare_task(targ, block, clr = caller[2])
|
990
|
+
|
991
|
+
# Allow override of caller, usefull for plugins and libraries
|
992
|
+
# that define tasks.
|
993
|
+
if targ.is_a? Hash
|
994
|
+
targ.reject! { |k, v|
|
995
|
+
case k
|
996
|
+
when :__caller__
|
997
|
+
clr = v
|
998
|
+
true
|
999
|
+
else
|
1000
|
+
false
|
1001
|
+
end
|
1002
|
+
}
|
1003
|
+
end
|
1004
|
+
cinf = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr)
|
1005
|
+
|
1006
|
+
name, pre, file, ln = normalize_task_arg(targ, cinf)
|
1007
|
+
|
1008
|
+
file, is_new = rantfile_for_path(file)
|
1009
|
+
nt = yield(name, pre, block)
|
1010
|
+
nt.rantfile = file
|
1011
|
+
nt.line_number = ln
|
1012
|
+
nt.description = @task_desc
|
1013
|
+
@task_desc = nil
|
1014
|
+
file.tasks << nt
|
1015
|
+
hash_task nt
|
1016
|
+
nt
|
1017
|
+
end
|
1018
|
+
public :prepare_task
|
1019
|
+
|
1020
|
+
def hash_task task
|
1021
|
+
n = task.name
|
1022
|
+
et = @tasks[n]
|
1023
|
+
case et
|
1024
|
+
when nil
|
1025
|
+
@tasks[n] = task
|
1026
|
+
when Rant::Worker
|
1027
|
+
mt = Rant::MetaTask.new n
|
1028
|
+
mt << et << task
|
1029
|
+
@tasks[n] = mt
|
1030
|
+
else # assuming Rant::MetaTask
|
1031
|
+
et << task
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Tries to extract task name and prerequisites from the typical
|
1036
|
+
# argument to the +task+ command. +targ+ should be one of String,
|
1037
|
+
# Symbol or Hash. clr is the caller and is used for error
|
1038
|
+
# reporting and debugging.
|
1039
|
+
#
|
1040
|
+
# Returns four values, the first is a string which is the task name
|
1041
|
+
# and the second is an array with the prerequisites.
|
1042
|
+
# The third is the file name of +clr+, the fourth is the line number
|
1043
|
+
# of +clr+.
|
1044
|
+
def normalize_task_arg(targ, clr)
|
1045
|
+
# TODO: check the code calling this method so that we can
|
1046
|
+
# assume clr is already a hash
|
1047
|
+
ch = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr)
|
1048
|
+
name = nil
|
1049
|
+
pre = []
|
1050
|
+
ln = ch[:ln] || 0
|
1051
|
+
file = ch[:file]
|
1052
|
+
|
1053
|
+
# process and validate targ
|
1054
|
+
if targ.is_a? Hash
|
1055
|
+
if targ.empty?
|
1056
|
+
abort(pos_text(file, ln),
|
1057
|
+
"Empty hash as task argument, " +
|
1058
|
+
"task name required.")
|
1059
|
+
end
|
1060
|
+
if targ.size > 1
|
1061
|
+
abort(pos_text(file, ln),
|
1062
|
+
"Too many hash elements, " +
|
1063
|
+
"should only be one.")
|
1064
|
+
end
|
1065
|
+
targ.each_pair { |k,v|
|
1066
|
+
name = normalize_task_name(k, file, ln)
|
1067
|
+
pre = v
|
1068
|
+
}
|
1069
|
+
if pre.respond_to? :to_ary
|
1070
|
+
pre = pre.to_ary.dup
|
1071
|
+
pre.map! { |elem|
|
1072
|
+
normalize_task_name(elem, file, ln)
|
1073
|
+
}
|
1074
|
+
else
|
1075
|
+
pre = [normalize_task_name(pre, file, ln)]
|
1076
|
+
end
|
1077
|
+
else
|
1078
|
+
name = normalize_task_name(targ, file, ln)
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
[name, pre, file, ln]
|
1082
|
+
end
|
1083
|
+
public :normalize_task_arg
|
1084
|
+
|
1085
|
+
# Tries to make a task name out of arg and returns
|
1086
|
+
# the valid task name. If not possible, calls abort
|
1087
|
+
# with an appropriate error message using file and ln.
|
1088
|
+
def normalize_task_name(arg, file, ln)
|
1089
|
+
return arg if arg.is_a? String
|
1090
|
+
if Symbol === arg
|
1091
|
+
arg.to_s
|
1092
|
+
elsif arg.respond_to? :to_str
|
1093
|
+
arg.to_str
|
1094
|
+
else
|
1095
|
+
abort(pos_text(file, ln),
|
1096
|
+
"Task name has to be a string or symbol.")
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
# Returns a Rant::Rantfile object as first value
|
1101
|
+
# and a boolean value as second. If the second is true,
|
1102
|
+
# the rantfile was created and added, otherwise the rantfile
|
1103
|
+
# already existed.
|
1104
|
+
def rantfile_for_path path
|
1105
|
+
# TODO: optimization: File.expand_path is called very often
|
1106
|
+
# (don't forget the calls from Rant::Path#absolute_path)
|
1107
|
+
abs_path = File.expand_path(path)
|
1108
|
+
if @rantfiles.any? { |rf| rf.absolute_path == abs_path }
|
1109
|
+
file = @rantfiles.find { |rf| rf.absolute_path == abs_path }
|
1110
|
+
[file, false]
|
1111
|
+
else
|
1112
|
+
file = Rant::Rantfile.new(abs_path, abs_path)
|
1113
|
+
@rantfiles << file
|
1114
|
+
[file, true]
|
1115
|
+
end
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
end # class Rant::RantApp
|