rant 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/NEWS +14 -0
  2. data/README +13 -7
  3. data/Rantfile +11 -0
  4. data/doc/md5.rdoc +49 -0
  5. data/doc/rantfile.rdoc +1 -1
  6. data/lib/rant/coregen.rb +193 -0
  7. data/lib/rant/import/archive/zip.rb +2 -0
  8. data/lib/rant/import/archive.rb +10 -2
  9. data/lib/rant/import/autoclean.rb +16 -7
  10. data/lib/rant/import/c/dependencies.rb +1 -1
  11. data/lib/rant/import/directedrule.rb +2 -2
  12. data/lib/rant/import/md5.rb +16 -0
  13. data/lib/rant/import/metadata.rb +162 -0
  14. data/lib/rant/import/nodes/default.rb +490 -0
  15. data/lib/rant/import/nodes/signed.rb +84 -0
  16. data/lib/rant/import/package/zip.rb +2 -0
  17. data/lib/rant/import/rubydoc.rb +5 -1
  18. data/lib/rant/import/rubypackage.rb +2 -1
  19. data/lib/rant/import/signature/md5.rb +38 -0
  20. data/lib/rant/import/signedfile.rb +235 -0
  21. data/lib/rant/import/subfile.rb +1 -1
  22. data/lib/rant/import.rb +5 -1
  23. data/lib/rant/node.rb +165 -0
  24. data/lib/rant/plugin/csharp.rb +2 -0
  25. data/lib/rant/rantlib.rb +64 -9
  26. data/lib/rant/rantsys.rb +39 -27
  27. data/lib/rant/rantvar.rb +32 -2
  28. data/misc/TODO +66 -0
  29. data/test/import/c/dependencies/test_on_the_fly.rb +52 -0
  30. data/test/import/metadata/Rantfile +16 -0
  31. data/test/import/metadata/sub/Rantfile +17 -0
  32. data/test/import/metadata/test_metadata.rb +126 -0
  33. data/test/import/nodes/signed/Rantfile +89 -0
  34. data/test/import/nodes/signed/sub1/Rantfile +6 -0
  35. data/test/import/nodes/signed/test_signed.rb +455 -0
  36. data/test/import/package/md5.rf +10 -0
  37. data/test/import/package/test_package.rb +127 -1
  38. data/test/import/signeddirectory/Rantfile +15 -0
  39. data/test/import/signeddirectory/test_signeddirectory.rb +84 -0
  40. data/test/import/signedfile/Rantfile +90 -0
  41. data/test/import/signedfile/sub1/Rantfile +4 -0
  42. data/test/import/signedfile/test_signedfile.rb +338 -0
  43. data/test/project1/Rantfile +0 -9
  44. data/test/project1/test_project.rb +2 -0
  45. data/test/project_rb1/test_project_rb1.rb +27 -10
  46. data/test/rant-import/test_rant-import.rb +46 -9
  47. data/test/subdirs/sub2/sub/rantfile +0 -5
  48. data/test/subdirs/test_subdirs.rb +0 -9
  49. data/test/test_examples.rb +131 -3
  50. data/test/test_filelist.rb +44 -0
  51. data/test/test_sys.rb +19 -1
  52. data/test/test_task.rb +2 -2
  53. data/test/tutil.rb +9 -3
  54. metadata +34 -4
  55. data/lib/rant/rantfile.rb +0 -897
  56. data/test/test_lighttask.rb +0 -68
@@ -0,0 +1,235 @@
1
+
2
+ # signedfile.rb - File tasks with checksum change recognition.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ module Rant
7
+ def self.init_import_signedfile(rac, *rest)
8
+ rac.import "signature/md5" unless rac.var._get("__signature__")
9
+ rac.import "metadata" unless rac.var._get("__metadata__")
10
+ end
11
+ module Generators
12
+ class SignedFile
13
+ include Node
14
+
15
+ def self.rant_gen(rac, ch, args, &block)
16
+ unless args.size == 1
17
+ rac.abort_at(ch, "SignedFile: too many arguments")
18
+ end
19
+ rac.prepare_task(args.first, block, ch) { |name,pre,blk|
20
+ self.new(rac, name, pre, &blk)
21
+ }
22
+ end
23
+
24
+ def initialize(rac, name, prerequisites, &block)
25
+ super()
26
+ @rac = rac
27
+ @name = name
28
+ @pre = prerequisites or
29
+ raise ArgumentError, "prerequisites required"
30
+ @block = block
31
+ @run = false
32
+ @success = nil
33
+ @needed_blk = nil
34
+ end
35
+ def prerequisites
36
+ @pre
37
+ end
38
+ alias deps prerequisites
39
+ # first prerequisite
40
+ def source
41
+ @pre.first.to_s
42
+ end
43
+ def has_actions?
44
+ !!@block
45
+ end
46
+ def <<(pre)
47
+ @pre << pre
48
+ end
49
+ def invoked?
50
+ !@success.nil?
51
+ end
52
+ def fail?
53
+ @success == false
54
+ end
55
+ def done?
56
+ @success
57
+ end
58
+ def enhance(deps = nil, &blk)
59
+ @pre.concat(deps) if deps
60
+ if @block
61
+ if blk
62
+ first_block = @block
63
+ @block = lambda { |t|
64
+ first_block[t]
65
+ blk[t]
66
+ }
67
+ end
68
+ else
69
+ @block = blk
70
+ end
71
+ end
72
+ def needed?
73
+ invoke(:needed? => true)
74
+ end
75
+ def needed(&blk)
76
+ @needed_blk = blk
77
+ end
78
+ def invoke(opt = INVOKE_OPT)
79
+ return circular_dep if @run
80
+ @run = true
81
+ begin
82
+ return if done?
83
+ goto_task_home
84
+ @cur_checksums = []
85
+ @sigs = @rac.var._get("__signature__")
86
+ key = "prerequisites_sig_#{@sigs.name}"
87
+ target_key = "target_sig_#{@sigs.name}"
88
+ up = signed_process_prerequisites(opt)
89
+ up ||= opt[:force]
90
+ if @needed_blk
91
+ up = true if @needed_blk.call(self)
92
+ end
93
+ @cur_checksums.sort!
94
+ check_str = @cur_checksums.join
95
+ @cur_checksums = nil
96
+ metadata = @rac.var._get("__metadata__")
97
+ old_check_str = metadata.fetch(key, @name)
98
+ old_target_str = metadata.fetch(target_key, @name)
99
+ # check explicitely for plain file, thus allow the
100
+ # target of a SignedFile to be a directory ;)
101
+ if test(?f, @name)
102
+ target_str = @sigs.signature_for_file(@name)
103
+ else
104
+ target_str = ""
105
+ up ||= !File.exist?(@name)
106
+ end
107
+ check_str_changed = old_check_str != check_str
108
+ target_changed = old_target_str != target_str
109
+ up ||= check_str_changed || target_changed
110
+ return up if opt[:needed?]
111
+ return false unless up
112
+ # run action and save checksums
113
+ run
114
+ goto_task_home
115
+ target_str = test(?f, @name) ?
116
+ @sigs.signature_for_file(@name) : ""
117
+ target_changed = target_str != old_target_str
118
+ if target_changed
119
+ metadata.set(target_key, target_str, @name)
120
+ end
121
+ if check_str_changed
122
+ metadata.set(key, check_str, @name)
123
+ end
124
+ return target_changed
125
+ rescue TaskFail => e
126
+ raise
127
+ rescue Exception => e
128
+ self.fail(nil, e)
129
+ ensure
130
+ @sigs = nil
131
+ @run = false
132
+ end
133
+ end
134
+ def each_target
135
+ goto_task_home
136
+ yield @name
137
+ end
138
+ def timestamp
139
+ File.exist?(@name) ? File.mtime(@name) : T0
140
+ end
141
+ def signature
142
+ goto_task_home
143
+ sigs = @rac.var._get("__signature__")
144
+ md = @rac.var._get("__metadata__")
145
+ key = "target_sig_#{sigs.name}"
146
+ md.fetch(key, @name)
147
+ end
148
+ private
149
+ # returns true if update required
150
+ def signed_process_prerequisites(opt)
151
+ up = false
152
+ # set with already handled prerequisites, don't
153
+ # handle on prerequisite multiple times
154
+ handled = {@name => true}
155
+ my_subdir = project_subdir
156
+ @pre.each { |dep|
157
+ dep_str = dep.to_rant_target
158
+ next if handled.include? dep_str
159
+ if Node === dep
160
+ up = true if handle_node(dep, dep_str, opt)
161
+ else
162
+ tasks = @rac.resolve(dep_str, my_subdir)
163
+ if tasks.empty?
164
+ if test(?d, dep_str)
165
+ handle_dir(dep_str)
166
+ elsif File.exist?(dep_str)
167
+ handle_file(dep_str)
168
+ else
169
+ rac.err_msg @rac.pos_text(rantfile.path, line_number),
170
+ "in prerequisites: no such file or task: `#{dep_str}'"
171
+ self.fail
172
+ end
173
+ else
174
+ tasks.each { |t|
175
+ up = true if handle_node(t, dep_str, opt)
176
+ }
177
+ end
178
+ end
179
+ handled[dep_str] = true
180
+ }
181
+ up
182
+ end
183
+ def handle_node(node, dep_str, opt)
184
+ up = node.invoke(opt)
185
+ if node.respond_to? :signature
186
+ @cur_checksums << node.signature
187
+ elsif test(?f, dep_str)
188
+ # calculate checksum for plain file
189
+ handle_file(dep_str)
190
+ elsif File.exist?(dep_str)
191
+ @cur_checksums << @sigs.signature_for_string(dep_str)
192
+ end
193
+ goto_task_home
194
+ up
195
+ end
196
+ def handle_file(path)
197
+ @cur_checksums << @sigs.signature_for_file(path)
198
+ end
199
+ def handle_dir(path)
200
+ @cur_checksums << @sigs.signature_for_string(path)
201
+ end
202
+ end # class SignedFile
203
+
204
+ class AutoSubSignedFile < SignedFile
205
+ include AutoInvokeDirNode
206
+ end
207
+
208
+ class SignedDirectory < SignedFile
209
+ def respond_to?(meth)
210
+ if meth == :signature
211
+ @block
212
+ else
213
+ super
214
+ end
215
+ end
216
+ def signature
217
+ goto_task_home
218
+ sigs = @rac.var._get("__signature__")
219
+ md = @rac.var._get("__metadata__")
220
+ key = "prerequisites_sig_#{sigs.name}"
221
+ md.fetch(key, @name)
222
+ end
223
+ private
224
+ def run
225
+ @rac.cx.sys.mkdir @name unless test ?d, @name
226
+ if @block
227
+ @block.arity == 0 ? @block.call : @block[self]
228
+ goto_task_home
229
+ # for compatibility with mtime based tasks
230
+ @rac.cx.sys.touch @name
231
+ end
232
+ end
233
+ end # class SignedDirectory
234
+ end # module Generators
235
+ end # module Rant
@@ -49,7 +49,7 @@ class Rant::Generators::SubFile
49
49
  pre << dirp
50
50
  end
51
51
  rac.cx.desc file_desc
52
- ::Rant::FileTask.new(rac, name, pre, &blk)
52
+ rac.node_factory.new_file(rac, name, pre, blk)
53
53
  }
54
54
  end
55
55
  end
data/lib/rant/import.rb CHANGED
@@ -103,6 +103,10 @@ module Rant
103
103
  @imports.concat(@rantapp.imports)
104
104
  @plugins.concat(@rantapp.plugins.map { |p| p.name })
105
105
  end
106
+
107
+ #unless @imports.include? "nodes/default"
108
+ #@imports.unshift "nodes/default"
109
+ #end
106
110
 
107
111
  if File.exist?(@mono_fn) && !@force
108
112
  abort("#{@mono_fn} exists. Rant won't overwrite this file.",
@@ -298,7 +302,7 @@ EOF
298
302
  next if line =~ /^#! ?(\/|\\)?\w/
299
303
  # uncomment line if +uncomment+ directive given
300
304
  if line =~ /^\s*#.*#\s?rant-import:\s?uncomment\s*$/
301
- line.sub! '#', ''
305
+ line.sub!(/#/, '')
302
306
  end
303
307
  # skip line if +remove+ directive given
304
308
  next if line =~ /#\s?rant-import:\s?remove\s*$/
data/lib/rant/node.rb ADDED
@@ -0,0 +1,165 @@
1
+
2
+ # node.rb - Base of Rant nodes.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ module Rant
7
+
8
+ class TaskFail < StandardError
9
+ def initialize(task, orig, msg)
10
+ @task = task
11
+ @orig = orig
12
+ @msg = msg
13
+ end
14
+ def exception
15
+ self
16
+ end
17
+ def task
18
+ @task
19
+ end
20
+ def tname
21
+ @task ? @task.name : nil
22
+ end
23
+ # the exception which caused the task to fail
24
+ def orig
25
+ @orig
26
+ end
27
+ def msg
28
+ @msg
29
+ end
30
+ end
31
+
32
+ class Rantfile
33
+ attr_reader :tasks, :path
34
+ attr_accessor :project_subdir
35
+ def initialize(path)
36
+ @path = path or raise ArgumentError, "path required"
37
+ @tasks = []
38
+ @project_subdir = nil
39
+ end
40
+ def to_s
41
+ @path
42
+ end
43
+ end # class Rantfile
44
+
45
+ # Any +object+ is considered a _task_ if
46
+ # <tt>Rant::Node === object</tt> is true.
47
+ #
48
+ # Most important classes including this module are the Rant::Task
49
+ # class and the Rant::FileTask class.
50
+ module Node
51
+
52
+ INVOKE_OPT = {}.freeze
53
+
54
+ T0 = Time.at(0).freeze
55
+
56
+ # Name of the task, this is always a string.
57
+ attr_reader :name
58
+ # A reference to the Rant compiler this task belongs to.
59
+ attr_reader :rac
60
+ # Description for this task.
61
+ attr_accessor :description
62
+ # The rantfile this task was defined in.
63
+ # Should be a Rant::Rantfile instance.
64
+ attr_accessor :rantfile
65
+ # The linenumber in rantfile where this task was defined.
66
+ attr_accessor :line_number
67
+ # The directory in which this task was defined, relative to
68
+ # the projects root directory.
69
+ attr_accessor :project_subdir
70
+
71
+ def initialize
72
+ @description = nil
73
+ @rantfile = nil
74
+ @line_number = nil
75
+ @run = false
76
+ @project_subdir = ""
77
+ end
78
+
79
+ # Returns the name of this task.
80
+ def to_s
81
+ name
82
+ end
83
+
84
+ def to_rant_target
85
+ name
86
+ end
87
+
88
+ # Basically project_subdir/name
89
+ #
90
+ # The Rant compiler (or application) references tasks by their
91
+ # full_name.
92
+ def full_name
93
+ sd = project_subdir
94
+ sd.empty? ? name : File.join(sd, name)
95
+ end
96
+
97
+ # Change current working directory to the directory this task
98
+ # was defined in.
99
+ #
100
+ # Important for subclasses: Call this method always before
101
+ # invoking code from Rantfiles (e.g. task action blocks).
102
+ def goto_task_home
103
+ @rac.goto_project_dir project_subdir
104
+ end
105
+
106
+ def done?
107
+ @done
108
+ end
109
+
110
+ def needed?
111
+ !done?
112
+ end
113
+
114
+ # True during invoke. Used to encounter circular dependencies.
115
+ def run?
116
+ @run
117
+ end
118
+
119
+ # +opt+ is a Hash and shouldn't be modified.
120
+ # All objects implementing the Rant::Node protocol should
121
+ # know about the following +opt+ values:
122
+ # <tt>:needed?</tt>::
123
+ # Just check if this task is needed. Should do the same
124
+ # as calling Node#needed?
125
+ # <tt>:force</tt>::
126
+ # Run task action even if needed? is false.
127
+ # Returns true if task action was run.
128
+ def invoke(opt = INVOKE_OPT)
129
+ return circular_dep if run?
130
+ @run = true
131
+ begin
132
+ return needed? if opt[:needed?]
133
+ self.run if opt[:force] || self.needed?
134
+ ensure
135
+ @run = false
136
+ end
137
+ end
138
+
139
+ # Cause task to fail. Usually called from inside the block
140
+ # given to +act+.
141
+ def fail msg = nil, orig = nil
142
+ raise TaskFail.new(self, orig, msg)
143
+ end
144
+
145
+ # Change pwd to task home directory and yield for each created
146
+ # file/directory.
147
+ #
148
+ # Override in subclasses if your task instances create files.
149
+ def each_target
150
+ end
151
+
152
+ def run
153
+ return unless @block
154
+ goto_task_home
155
+ @block.arity == 0 ? @block.call : @block[self]
156
+ end
157
+ private :run
158
+
159
+ def circular_dep
160
+ rac.warn_msg "Circular dependency on task `#{full_name}'."
161
+ false
162
+ end
163
+ private :circular_dep
164
+ end # module Node
165
+ end
@@ -3,6 +3,8 @@
3
3
 
4
4
  require 'rant/plugin_methods'
5
5
  require 'rant/cs_compiler'
6
+ # TODO
7
+ require 'rant/import/nodes/default'
6
8
 
7
9
  module Rant
8
10
 
data/lib/rant/rantlib.rb CHANGED
@@ -10,8 +10,10 @@
10
10
  require 'getoptlong'
11
11
  require 'rant/rantvar'
12
12
  require 'rant/rantenv'
13
- require 'rant/rantfile'
14
13
  require 'rant/rantsys'
14
+ require 'rant/node'
15
+ require 'rant/import/nodes/default' # could be optimized away
16
+ require 'rant/coregen'
15
17
 
16
18
  # There is one problem with executing Rantfiles in a special context:
17
19
  # In the top-level execution environment, there are some methods
@@ -23,11 +25,19 @@ require 'rant/rantsys'
23
25
  # this object.
24
26
  Rant::MAIN_OBJECT = self
25
27
 
26
- unless Process::Status.method_defined?(:success?)
28
+ unless Process::Status.method_defined?(:success?) # new in 1.8.2
27
29
  class Process::Status
28
30
  def success?; exitstatus == 0; end
29
31
  end
30
32
  end
33
+ unless Regexp.respond_to? :union # new in 1.8.1
34
+ def Regexp.union(*patterns)
35
+ # let's hope it comes close to ruby-1.8.1 and upwards...
36
+ return /(?!)/ if patterns.empty?
37
+ # i guess the options are lost
38
+ Regexp.new(patterns.join("|"))
39
+ end
40
+ end
31
41
  if RUBY_VERSION < "1.8.2"
32
42
  class Array
33
43
  def flatten
@@ -54,6 +64,13 @@ if RUBY_VERSION < "1.8.2"
54
64
  end
55
65
  end
56
66
  end
67
+ if RUBY_VERSION < "1.8.1"
68
+ module FileUtils
69
+ def fu_list(arg)
70
+ arg.respond_to?(:to_ary) ? arg.to_ary : [arg]
71
+ end
72
+ end
73
+ end
57
74
 
58
75
  class Array
59
76
 
@@ -194,12 +211,12 @@ class RantAppContext
194
211
  include RantContext
195
212
 
196
213
  def initialize(app)
197
- @rac = app
214
+ @__rac__ = app
198
215
  end
199
216
 
200
217
  # +rac+ stands for "rant compiler"
201
218
  def rac
202
- @rac
219
+ @__rac__
203
220
  end
204
221
 
205
222
  def method_missing(sym, *args)
@@ -271,6 +288,16 @@ end # module Rant
271
288
  class Rant::RantApp
272
289
  include Rant::Console
273
290
 
291
+ class AutoLoadNodeFactory
292
+ def initialize(rac)
293
+ @rac = rac
294
+ end
295
+ def method_missing(sym, *args, &block)
296
+ @rac.import "nodes/default"
297
+ @rac.node_factory.send(sym, *args, &block)
298
+ end
299
+ end
300
+
274
301
  # Important: We try to synchronize all tasks referenced indirectly
275
302
  # by @rantfiles with the task hash @tasks. The task hash is
276
303
  # intended for fast task lookup per task name.
@@ -305,6 +332,7 @@ class Rant::RantApp
305
332
  # internal use. A private option is distuingished from others
306
333
  # by having +nil+ as description!
307
334
 
335
+ [ "--import", "-i", GetoptLong::REQUIRED_ARGUMENT, nil ],
308
336
  [ "--stop-after-load", GetoptLong::NO_ARGUMENT, nil ],
309
337
  # Print caller to $stderr on abort.
310
338
  [ "--trace-abort", GetoptLong::NO_ARGUMENT, nil ],
@@ -347,6 +375,8 @@ class Rant::RantApp
347
375
  # Note: Might change before 1.0
348
376
  attr_reader :resolve_hooks
349
377
 
378
+ attr_accessor :node_factory
379
+
350
380
  def initialize(*args)
351
381
  unless args.empty?
352
382
  STDERR.puts caller[0]
@@ -381,6 +411,8 @@ class Rant::RantApp
381
411
  @orig_pwd = nil
382
412
  @current_subdir = ""
383
413
  @resolve_hooks = []
414
+
415
+ @node_factory = AutoLoadNodeFactory.new(self)
384
416
  end
385
417
 
386
418
  def [](opt)
@@ -531,6 +563,8 @@ class Rant::RantApp
531
563
  return 1
532
564
  ensure
533
565
  # TODO: exception handling!
566
+ hooks = var._get("__at_return__")
567
+ hooks.each { |hook| hook.call } if hooks
534
568
  @plugins.each { |plugin| plugin.rant_plugin_stop }
535
569
  @plugins.each { |plugin| plugin.rant_quit }
536
570
  # restore pwd
@@ -550,13 +584,13 @@ class Rant::RantApp
550
584
 
551
585
  def task(targ, &block)
552
586
  prepare_task(targ, block) { |name,pre,blk|
553
- Rant::Task.new(self, name, pre, &blk)
587
+ @node_factory.new_task(self, name, pre, blk)
554
588
  }
555
589
  end
556
590
 
557
591
  def file(targ, &block)
558
592
  prepare_task(targ, block) { |name,pre,blk|
559
- Rant::FileTask.new(self, name, pre, &blk)
593
+ @node_factory.new_file(self, name, pre, blk)
560
594
  }
561
595
  end
562
596
 
@@ -594,6 +628,8 @@ class Rant::RantApp
594
628
  end
595
629
  Rant::CODE_IMPORTS << arg.dup
596
630
  end
631
+ init_msg = "init_import_#{arg.gsub(/[^\w]/, '__')}"
632
+ Rant.send init_msg, self if Rant.respond_to? init_msg
597
633
  @imports << arg.dup
598
634
  end
599
635
  }
@@ -661,7 +697,7 @@ class Rant::RantApp
661
697
  end
662
698
  warn_msg "enhance \"#{name}\": no such task",
663
699
  "Generating a new file task with the given name."
664
- Rant::FileTask.new(self, name, pre, &blk)
700
+ @node_factory.new_file(self, name, pre, blk)
665
701
  }
666
702
  end
667
703
 
@@ -924,7 +960,7 @@ class Rant::RantApp
924
960
  tn = nil
925
961
  prepare_task(targ, block, ch) { |name,pre,blk|
926
962
  tn = name
927
- Rant::FileTask.new(self, name, pre, &blk)
963
+ @node_factory.new_file(self, name, pre, blk)
928
964
  }
929
965
  build(tn)
930
966
  elsif target.respond_to? :to_rant_target
@@ -938,7 +974,7 @@ class Rant::RantApp
938
974
  # create a file task
939
975
  ch ||= Rant::Lib.parse_caller_elem(caller[1])
940
976
  prepare_task(rt, block, ch) { |name,pre,blk|
941
- Rant::FileTask.new(self, name, pre, &blk)
977
+ @node_factory.new_file(self, name, pre, blk)
942
978
  }
943
979
  build(rt)
944
980
  else
@@ -1017,6 +1053,18 @@ class Rant::RantApp
1017
1053
  end
1018
1054
  public :at_resolve
1019
1055
 
1056
+ # block will be called before this rac returns from #run
1057
+ # pwd will be the projects root directory
1058
+ def at_return(&block)
1059
+ hooks = var._get("__at_return__")
1060
+ if hooks
1061
+ hooks << block
1062
+ else
1063
+ var._set("__at_return__", [block])
1064
+ end
1065
+ end
1066
+ public :at_return
1067
+
1020
1068
  # Returns a list with all tasks for which yield
1021
1069
  # returns true.
1022
1070
  def select_tasks
@@ -1130,6 +1178,8 @@ class Rant::RantApp
1130
1178
  @arg_rantfiles << value
1131
1179
  when "--force-run"
1132
1180
  @force_targets << value
1181
+ when "--import"
1182
+ import value
1133
1183
  else
1134
1184
  # simple switch
1135
1185
  @opts[opt.sub(/^--/, '').tr('-', "_").to_sym] = true
@@ -1305,6 +1355,11 @@ class Rant::RantApp
1305
1355
  msg << orig.backtrace[0..4] unless ch
1306
1356
  end
1307
1357
  end
1358
+ if e.msg && !e.msg.empty?
1359
+ ch = get_ch_from_backtrace(e.backtrace)
1360
+ t_msg.unshift(e.msg)
1361
+ t_msg.unshift(pos_text(ch[:file], ch[:ln])) if ch
1362
+ end
1308
1363
  err_msg msg unless msg.empty?
1309
1364
  err_msg t_msg
1310
1365
  end