trema 0.3.21 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.mono.rant DELETED
@@ -1,4107 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # .mono.rant - Monolithic rant script, autogenerated by rant-import 0.5.7.
4
- #
5
- # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
6
- #
7
- # This program is free software.
8
- # You can distribute/modify this program under the terms of
9
- # the GNU LGPL, Lesser General Public License version 2.1.
10
-
11
-
12
- require 'getoptlong'
13
-
14
-
15
- require 'rbconfig'
16
-
17
- unless Process::Status.method_defined?(:success?) # new in 1.8.2
18
- class Process::Status
19
- def success?; exitstatus == 0; end
20
- end
21
- end
22
- unless Regexp.respond_to? :union # new in 1.8.1
23
- def Regexp.union(*patterns)
24
- return /(?!)/ if patterns.empty?
25
- Regexp.new(patterns.join("|"))
26
- end
27
- end
28
- if RUBY_VERSION < "1.8.2"
29
- class Array
30
- undef_method :flatten, :flatten!
31
- def flatten
32
- cp = self.dup
33
- cp.flatten!
34
- cp
35
- end
36
- def flatten!
37
- res = []
38
- flattened = false
39
- self.each { |e|
40
- if e.respond_to? :to_ary
41
- res.concat(e.to_ary)
42
- flattened = true
43
- else
44
- res << e
45
- end
46
- }
47
- if flattened
48
- replace(res)
49
- flatten!
50
- self
51
- end
52
- end
53
- end
54
- end
55
-
56
- class String
57
- def _rant_sub_ext(ext, new_ext = nil)
58
- if new_ext
59
- self.sub(/#{Regexp.escape ext}$/, new_ext)
60
- else
61
- self.sub(/(\.[^.]*$)|$/, ".#{ext}")
62
- end
63
- end
64
- end
65
-
66
- module Rant
67
- VERSION = '0.5.7'
68
-
69
- @__rant_no_value__ = Object.new.freeze
70
- def self.__rant_no_value__
71
- @__rant_no_value__
72
- end
73
-
74
- module Env
75
- OS = ::Config::CONFIG['target']
76
- RUBY = ::Config::CONFIG['ruby_install_name']
77
- RUBY_BINDIR = ::Config::CONFIG['bindir']
78
- RUBY_EXE = File.join(RUBY_BINDIR, RUBY + ::Config::CONFIG["EXEEXT"])
79
-
80
- @@zip_bin = false
81
- @@tar_bin = false
82
-
83
- if OS =~ /mswin/i
84
- def on_windows?; true; end
85
- else
86
- def on_windows?; false; end
87
- end
88
-
89
- def have_zip?
90
- if @@zip_bin == false
91
- @@zip_bin = find_bin "zip"
92
- end
93
- !@@zip_bin.nil?
94
- end
95
- def have_tar?
96
- if @@tar_bin == false
97
- @@tar_bin = find_bin "tar"
98
- end
99
- !@@tar_bin.nil?
100
- end
101
- def pathes
102
- path = ENV[on_windows? ? "Path" : "PATH"]
103
- return [] unless path
104
- path.split(on_windows? ? ";" : ":")
105
- end
106
- def find_bin bin_name
107
- if on_windows?
108
- bin_name_exe = nil
109
- if bin_name !~ /\.[^\.]{1,3}$/i
110
- bin_name_exe = bin_name + ".exe"
111
- end
112
- pathes.each { |dir|
113
- file = File.join(dir, bin_name)
114
- return file if test(?f, file)
115
- if bin_name_exe
116
- file = File.join(dir, bin_name_exe)
117
- return file if test(?f, file)
118
- end
119
- }
120
- else
121
- pathes.each { |dir|
122
- file = File.join(dir, bin_name)
123
- return file if test(?x, file)
124
- }
125
- end
126
- nil
127
- end
128
- def shell_path path
129
- if on_windows?
130
- path = path.tr("/", "\\")
131
- if path.include? ' '
132
- '"' + path + '"'
133
- else
134
- path
135
- end
136
- else
137
- if path.include? ' '
138
- "'" + path + "'"
139
- else
140
- path
141
- end
142
- end
143
- end
144
- extend self
145
- end # module Env
146
-
147
- module Sys
148
- def sp(arg)
149
- if arg.respond_to? :to_ary
150
- arg.to_ary.map{ |e| sp e }.join(' ')
151
- else
152
- _escaped_path arg
153
- end
154
- end
155
- def escape(arg)
156
- if arg.respond_to? :to_ary
157
- arg.to_ary.map{ |e| escape e }.join(' ')
158
- else
159
- _escaped arg
160
- end
161
- end
162
- if Env.on_windows?
163
- def _escaped_path(path)
164
- _escaped(path.to_s.tr("/", "\\"))
165
- end
166
- def _escaped(arg)
167
- sarg = arg.to_s
168
- return sarg unless sarg.include?(" ")
169
- sarg << "\\" if sarg[-1].chr == "\\"
170
- "\"#{sarg}\""
171
- end
172
- def regular_filename(fn)
173
- fn.to_str.tr("\\", "/").gsub(%r{/{2,}}, "/")
174
- end
175
- else
176
- def _escaped_path(path)
177
- path.to_s.gsub(/(?=\s)/, "\\")
178
- end
179
- alias _escaped _escaped_path
180
- def regular_filename(fn)
181
- fn.to_str.gsub(%r{/{2,}}, "/")
182
- end
183
- end
184
- private :_escaped_path
185
- private :_escaped
186
- def split_all(path)
187
- names = regular_filename(path).split(%r{/})
188
- names[0] = "/" if names[0] && names[0].empty?
189
- names
190
- end
191
- extend self
192
- end # module Sys
193
-
194
-
195
- ROOT_RANTFILE = "root.rant"
196
- SUB_RANTFILE = "sub.rant"
197
- RANTFILES = [ "Rantfile", "rantfile", ROOT_RANTFILE ]
198
-
199
- CODE_IMPORTS = []
200
-
201
- class RantAbortException < StandardError
202
- end
203
-
204
- class RantDoneException < StandardError
205
- end
206
-
207
- class Error < StandardError
208
- end
209
-
210
- module Generators
211
- end
212
-
213
- module RantVar
214
-
215
- class Error < Rant::Error
216
- end
217
-
218
- class ConstraintError < Error
219
-
220
- attr_reader :constraint, :val
221
-
222
- def initialize(constraint, val, msg = nil)
223
- @msg = msg
224
- @constraint = constraint
225
- @val = val
226
- end
227
-
228
- def message
229
- val_desc = @val.inspect
230
- val_desc[7..-1] = "..." if val_desc.length > 10
231
- "#{val_desc} doesn't match constraint: #@constraint"
232
- end
233
- end
234
-
235
- class NotAConstraintFactoryError < Error
236
- attr_reader :obj
237
- def initialize(obj, msg = nil)
238
- @msg = msg
239
- @obj = obj
240
- end
241
- def message
242
- obj_desc = @obj.inspect
243
- obj_desc[7..-1] = "..." if obj_desc.length > 10
244
- "#{obj_desc} is not a valid constraint factory"
245
- end
246
- end
247
-
248
- class InvalidVidError < Error
249
- def initialize(vid, msg = nil)
250
- @msg = msg
251
- @vid = vid
252
- end
253
- def message
254
- vid_desc = @vid.inspect
255
- vid_desc[7..-1] = "..." if vid_desc.length > 10
256
- "#{vid_desc} is not a valid var identifier"
257
- end
258
- end
259
-
260
- class InvalidConstraintError < Error
261
- end
262
-
263
- class QueryError < Error
264
- end
265
-
266
- class Space
267
-
268
- @@env_ref = Object.new
269
-
270
- def initialize
271
- @store = {}
272
- @constraints = {}
273
- end
274
-
275
- def query(*args, &block)
276
- case args.size
277
- when 0
278
- raise QueryError, "no arguments", caller
279
- when 1
280
- arg = args.first
281
- if Hash === arg
282
- if arg.size == 1
283
- arg.each { |k,v|
284
- self[k] = v if self[k].nil?
285
- }
286
- self
287
- else
288
- init_all arg
289
- end
290
- else
291
- self[arg]
292
- end
293
- when 2, 3
294
- vid, cf, val = *args
295
- constrain vid,
296
- get_factory(cf).rant_constraint
297
- self[vid] = val if val
298
- else
299
- raise QueryError, "too many arguments"
300
- end
301
- end
302
-
303
- def restrict vid, ct, *ct_args
304
- if vid.respond_to? :to_ary
305
- vid.to_ary.each { |v| restrict(v, ct, *ct_args) }
306
- else
307
- constrain vid,
308
- get_factory(ct).rant_constraint(*ct_args)
309
- end
310
- self
311
- end
312
-
313
- def get_factory id
314
- if String === id || Symbol === id
315
- id = Constraints.const_get(id) rescue nil
316
- end
317
- unless id.respond_to? :rant_constraint
318
- raise NotAConstraintFactoryError.new(id), caller
319
- end
320
- id
321
- end
322
- private :get_factory
323
-
324
- def [](vid)
325
- vid = RantVar.valid_vid vid
326
- val = @store[vid]
327
- val.equal?(@@env_ref) ? ENV[vid] : val
328
- end
329
-
330
- def []=(vid, val)
331
- vid = RantVar.valid_vid(vid)
332
- c = @constraints[vid]
333
- if @store[vid] == @@env_ref
334
- ENV[vid] = c ? c.filter(val) : val
335
- else
336
- @store[vid] = c ? c.filter(val) : val
337
- end
338
- end
339
-
340
- def env(*vars)
341
- vars.flatten.each { |var|
342
- vid = RantVar.valid_vid(var)
343
- cur_val = @store[vid]
344
- next if cur_val == @@env_ref
345
- ENV[vid] = cur_val unless cur_val.nil?
346
- @store[vid] = @@env_ref
347
- }
348
- nil
349
- end
350
-
351
- def set_all hash
352
- unless Hash === hash
353
- raise QueryError,
354
- "set_all argument has to be a hash"
355
- end
356
- hash.each_pair { |k, v|
357
- self[k] = v
358
- }
359
- end
360
-
361
- def init_all hash
362
- unless Hash === hash
363
- raise QueryError,
364
- "init_all argument has to be a hash"
365
- end
366
- hash.each_pair { |k, v|
367
- self[k] = v if self[k].nil?
368
- }
369
- end
370
-
371
- def constrain vid, constraint
372
- vid = RantVar.valid_vid(vid)
373
- unless RantVar.valid_constraint? constraint
374
- raise InvalidConstraintError, constraint
375
- end
376
- @constraints[vid] = constraint
377
- if @store.member? vid
378
- begin
379
- val = @store[vid]
380
- @store[vid] = constraint.filter(@store[vid])
381
- rescue
382
- @store[vid] = constraint.default
383
- raise ConstraintError.new(constraint, val)
384
- end
385
- else
386
- @store[vid] = constraint.default
387
- end
388
- end
389
-
390
- def has_var?(vid)
391
- !self[vid].nil?
392
- end
393
-
394
- def _set(vid, val) #:nodoc:
395
- @store[vid] = val
396
- end
397
-
398
- def _get(vid) #:nodoc:
399
- @store[vid]
400
- end
401
-
402
- def _init(vid, val) #:nodoc:
403
- @store[vid] ||= val
404
- end
405
-
406
- end # class Space
407
-
408
- module Constraint
409
- def matches? val
410
- filter val
411
- true
412
- rescue
413
- return false
414
- end
415
- end
416
-
417
- def valid_vid(obj)
418
- case obj
419
- when String: obj
420
- when Symbol: obj.to_s
421
- else
422
- if obj.respond_to? :to_str
423
- obj.to_str
424
- else
425
- raise InvalidVidError.new(obj)
426
- end
427
- end
428
- end
429
-
430
- def valid_constraint?(obj)
431
- obj.respond_to?(:filter) &&
432
- obj.respond_to?(:matches?) &&
433
- obj.respond_to?(:default)
434
- end
435
-
436
- module_function :valid_constraint?, :valid_vid
437
-
438
- module Constraints
439
- class AutoList
440
- include Constraint
441
- class << self
442
- alias rant_constraint new
443
- end
444
- def filter(val)
445
- if val.respond_to? :to_ary
446
- val.to_ary
447
- elsif val.nil?
448
- raise ConstraintError.new(self, val)
449
- else
450
- [val]
451
- end
452
- end
453
- def default
454
- []
455
- end
456
- def to_s
457
- "list or single, non-nil value"
458
- end
459
- end
460
- end # module Constraints
461
- end # module RantVar
462
- end # module Rant
463
-
464
-
465
- require 'fileutils'
466
-
467
-
468
- module Rant
469
- def FileList(arg)
470
- if arg.respond_to?(:to_rant_filelist)
471
- arg.to_rant_filelist
472
- elsif arg.respond_to?(:to_ary)
473
- FileList.new(arg.to_ary)
474
- else
475
- raise TypeError,
476
- "cannot convert #{arg.class} into Rant::FileList"
477
- end
478
- end
479
- module_function :FileList
480
- class FileList
481
- include Enumerable
482
-
483
- ESC_SEPARATOR = Regexp.escape(File::SEPARATOR)
484
- ESC_ALT_SEPARATOR = File::ALT_SEPARATOR ?
485
- Regexp.escape(File::ALT_SEPARATOR) : nil
486
-
487
- class << self
488
- def [](*patterns)
489
- new.hide_dotfiles.include(*patterns)
490
- end
491
- def glob(*patterns)
492
- fl = new.hide_dotfiles.ignore(".", "..").include(*patterns)
493
- if block_given? then yield fl else fl end
494
- end
495
- def glob_all(*patterns)
496
- fl = new.ignore(".", "..").include(*patterns)
497
- if block_given? then yield fl else fl end
498
- end
499
- end
500
-
501
- def initialize(store = [])
502
- @pending = false
503
- @def_glob_dotfiles = true
504
- @items = store
505
- @ignore_rx = nil
506
- @keep = {}
507
- @actions = []
508
- end
509
- alias _object_dup dup
510
- private :_object_dup
511
- def dup
512
- c = _object_dup
513
- c.items = @items.dup
514
- c.actions = @actions.dup
515
- c.ignore_rx = @ignore_rx.dup if @ignore_rx
516
- c.instance_variable_set(:@keep, @keep.dup)
517
- c
518
- end
519
- def copy
520
- c = _object_dup
521
- c.items = @items.map { |entry| entry.dup }
522
- c.actions = @actions.dup
523
- c.ignore_rx = @ignore_rx.dup if @ignore_rx
524
- h_keep = {}
525
- @keep.each_key { |entry| h_keep[entry] = true }
526
- c.instance_variable_set(:@keep, h_keep)
527
- c
528
- end
529
- def glob_dotfiles?
530
- @def_glob_dotfiles
531
- end
532
- def glob_dotfiles=(flag)
533
- @def_glob_dotfiles = flag ? true : false
534
- end
535
- def hide_dotfiles
536
- @def_glob_dotfiles = false
537
- self
538
- end
539
- def glob_dotfiles
540
- @def_glob_dotfiles = true
541
- self
542
- end
543
-
544
- protected
545
- attr_accessor :actions, :items
546
- attr_accessor :pending
547
- attr_accessor :ignore_rx
548
-
549
- public
550
- def each(&block)
551
- resolve if @pending
552
- @items.each(&block)
553
- self
554
- end
555
- def to_ary
556
- resolve if @pending
557
- @items
558
- end
559
- alias to_a to_ary
560
- alias entries to_ary # entries: defined in Enumerable
561
- def to_rant_filelist
562
- self
563
- end
564
- def +(other)
565
- if other.respond_to? :to_rant_filelist
566
- c = other.to_rant_filelist.dup
567
- c.actions.concat(@actions)
568
- c.items.concat(@items)
569
- c.pending = !c.actions.empty?
570
- c
571
- elsif other.respond_to? :to_ary
572
- c = dup
573
- c.actions <<
574
- [:apply_ary_method_1, :concat, other.to_ary.dup]
575
- c.pending = true
576
- c
577
- else
578
- raise TypeError,
579
- "cannot add #{other.class} to Rant::FileList"
580
- end
581
- end
582
- def <<(file)
583
- @actions << [:apply_ary_method_1, :push, file]
584
- @keep[file] = true
585
- @pending = true
586
- self
587
- end
588
- def keep(entry)
589
- @keep[entry] = true
590
- @items << entry
591
- self
592
- end
593
- def concat(ary)
594
- if @pending
595
- ary = ary.to_ary.dup
596
- @actions << [:apply_ary_method_1, :concat, ary]
597
- else
598
- ix = ignore_rx and ary = ary.to_ary.reject { |f| f =~ ix }
599
- @items.concat(ary)
600
- end
601
- self
602
- end
603
- def size
604
- resolve if @pending
605
- @items.size
606
- end
607
- alias length size
608
- def empty?
609
- resolve if @pending
610
- @items.empty?
611
- end
612
- def join(sep = ' ')
613
- resolve if @pending
614
- @items.join(sep)
615
- end
616
- def pop
617
- resolve if @pending
618
- @items.pop
619
- end
620
- def push(entry)
621
- resolve if @pending
622
- @items.push(entry) if entry !~ ignore_rx
623
- self
624
- end
625
- def shift
626
- resolve if @pending
627
- @items.shift
628
- end
629
- def unshift(entry)
630
- resolve if @pending
631
- @items.unshift(entry) if entry !~ ignore_rx
632
- self
633
- end
634
- if Object.method_defined?(:fcall) || Object.method_defined?(:funcall) # in Ruby 1.9 like __send__
635
- @@__send_private__ = Object.method_defined?(:fcall) ? :fcall : :funcall
636
- def resolve
637
- @pending = false
638
- @actions.each{ |action| self.__send__(@@__send_private__, *action) }.clear
639
- ix = ignore_rx
640
- if ix
641
- @items.reject! { |f| f =~ ix && !@keep[f] }
642
- end
643
- self
644
- end
645
- else
646
- def resolve
647
- @pending = false
648
- @actions.each{ |action| self.__send__(*action) }.clear
649
- ix = ignore_rx
650
- if ix
651
- @items.reject! { |f| f =~ ix && !@keep[f] }
652
- end
653
- self
654
- end
655
- end
656
- def include(*pats)
657
- @def_glob_dotfiles ? glob_all(*pats) : glob_unix(*pats)
658
- end
659
- alias glob include
660
- def glob_unix(*patterns)
661
- patterns.flatten.each { |pat|
662
- @actions << [:apply_glob_unix, pat]
663
- }
664
- @pending = true
665
- self
666
- end
667
- def glob_all(*patterns)
668
- patterns.flatten.each { |pat|
669
- @actions << [:apply_glob_all, pat]
670
- }
671
- @pending = true
672
- self
673
- end
674
- if RUBY_VERSION < "1.8.2"
675
- FN_DOTFILE_RX_ = ESC_ALT_SEPARATOR ?
676
- /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)\..*
677
- ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x :
678
- /(^|#{ESC_SEPARATOR}+)\..* (#{ESC_SEPARATOR}+|$)/x
679
- def apply_glob_unix(pattern)
680
- inc_files = Dir.glob(pattern)
681
- unless pattern =~ /(^|\/)\./
682
- inc_files.reject! { |fn| fn =~ FN_DOTFILE_RX_ }
683
- end
684
- @items.concat(inc_files)
685
- end
686
- else
687
- def apply_glob_unix(pattern)
688
- @items.concat(Dir.glob(pattern))
689
- end
690
- end
691
- private :apply_glob_unix
692
- def apply_glob_all(pattern)
693
- @items.concat(Dir.glob(pattern, File::FNM_DOTMATCH))
694
- end
695
- private :apply_glob_all
696
- def exclude(*patterns)
697
- patterns.each { |pat|
698
- if Regexp === pat
699
- @actions << [:apply_exclude_rx, pat]
700
- else
701
- @actions << [:apply_exclude, pat]
702
- end
703
- }
704
- @pending = true
705
- self
706
- end
707
- def ignore(*patterns)
708
- patterns.each { |pat|
709
- add_ignore_rx(Regexp === pat ? pat : mk_all_rx(pat))
710
- }
711
- @pending = true
712
- self
713
- end
714
- def add_ignore_rx(rx)
715
- @ignore_rx =
716
- if @ignore_rx
717
- Regexp.union(@ignore_rx, rx)
718
- else
719
- rx
720
- end
721
- end
722
- private :add_ignore_rx
723
- def apply_exclude(pattern)
724
- @items.reject! { |elem|
725
- File.fnmatch?(pattern, elem, File::FNM_DOTMATCH) && !@keep[elem]
726
- }
727
- end
728
- private :apply_exclude
729
- def apply_exclude_rx(rx)
730
- @items.reject! { |elem|
731
- elem =~ rx && !@keep[elem]
732
- }
733
- end
734
- private :apply_exclude_rx
735
- def exclude_name(*names)
736
- names.each { |name|
737
- @actions << [:apply_exclude_rx, mk_all_rx(name)]
738
- }
739
- @pending = true
740
- self
741
- end
742
- alias shun exclude_name
743
- if File::ALT_SEPARATOR
744
- def mk_all_rx(file)
745
- /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)#{Regexp.escape(file)}
746
- ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x
747
- end
748
- else
749
- def mk_all_rx(file)
750
- /(^|#{ESC_SEPARATOR}+)#{Regexp.escape(file)}
751
- (#{ESC_SEPARATOR}+|$)/x
752
- end
753
- end
754
- private :mk_all_rx
755
- def exclude_path(*patterns)
756
- patterns.each { |pat|
757
- @actions << [:apply_exclude_path, pat]
758
- }
759
- @pending = true
760
- self
761
- end
762
- def apply_exclude_path(pattern)
763
- flags = File::FNM_DOTMATCH|File::FNM_PATHNAME
764
- @items.reject! { |elem|
765
- File.fnmatch?(pattern, elem, flags) && !@keep[elem]
766
- }
767
- end
768
- private :apply_exclude
769
- def select(&block)
770
- d = dup
771
- d.actions << [:apply_select, block]
772
- d.pending = true
773
- d
774
- end
775
- alias find_all select
776
- def apply_select blk
777
- @items = @items.select(&blk)
778
- end
779
- private :apply_select
780
- def map(&block)
781
- d = dup
782
- d.actions << [:apply_ary_method, :map!, block]
783
- d.pending = true
784
- d
785
- end
786
- alias collect map
787
- def sub_ext(ext, new_ext=nil)
788
- map { |f| f._rant_sub_ext ext, new_ext }
789
- end
790
- def ext(ext_str)
791
- sub_ext(ext_str)
792
- end
793
- def arglist
794
- Rant::Sys.sp to_ary
795
- end
796
- alias to_s arglist
797
- alias object_inspect inspect
798
- def uniq!
799
- @actions << [:apply_ary_method, :uniq!]
800
- @pending = true
801
- self
802
- end
803
- def sort!
804
- @actions << [:apply_ary_method, :sort!]
805
- @pending = true
806
- self
807
- end
808
- def map!(&block)
809
- @actions << [:apply_ary_method, :map!, block]
810
- @pending = true
811
- self
812
- end
813
- def reject!(&block)
814
- @actions << [:apply_ary_method, :reject!, block]
815
- @pending = true
816
- self
817
- end
818
- private
819
- def apply_ary_method(meth, block=nil)
820
- @items.send meth, &block
821
- end
822
- def apply_ary_method_1(meth, arg1, block=nil)
823
- @items.send meth, arg1, &block
824
- end
825
- end # class FileList
826
- end # module Rant
827
-
828
- if RUBY_VERSION == "1.8.3"
829
- module FileUtils
830
- METHODS = singleton_methods - %w(private_module_function
831
- commands options have_option? options_of collect_method)
832
- module Verbose
833
- class << self
834
- public(*::FileUtils::METHODS)
835
- end
836
- public(*::FileUtils::METHODS)
837
- end
838
- end
839
- end
840
-
841
- if RUBY_VERSION < "1.8.1"
842
- module FileUtils
843
- undef_method :fu_list
844
- def fu_list(arg)
845
- arg.respond_to?(:to_ary) ? arg.to_ary : [arg]
846
- end
847
- end
848
- end
849
-
850
- module Rant
851
- class RacFileList < FileList
852
-
853
- attr_reader :subdir
854
- attr_reader :basedir
855
-
856
- def initialize(rac, store = [])
857
- super(store)
858
- @rac = rac
859
- @subdir = @rac.current_subdir
860
- @basedir = Dir.pwd
861
- @ignore_hash = nil
862
- @add_ignore_args = []
863
- update_ignore_rx
864
- end
865
- def dup
866
- c = super
867
- c.instance_variable_set(
868
- :@add_ignore_args, @add_ignore_args.dup)
869
- c
870
- end
871
- def copy
872
- c = super
873
- c.instance_variable_set(
874
- :@add_ignore_args, @add_ignore_args.map { |e| e.dup })
875
- c
876
- end
877
- alias filelist_ignore ignore
878
- def ignore(*patterns)
879
- @add_ignore_args.concat patterns
880
- self
881
- end
882
- def ignore_rx
883
- update_ignore_rx
884
- @ignore_rx
885
- end
886
- alias filelist_resolve resolve
887
- def resolve
888
- Sys.cd(@basedir) { filelist_resolve }
889
- end
890
- def each_cd(&block)
891
- old_pwd = Dir.pwd
892
- Sys.cd(@basedir)
893
- filelist_resolve if @pending
894
- @items.each(&block)
895
- ensure
896
- Sys.cd(old_pwd)
897
- end
898
- private
899
- def update_ignore_rx
900
- ri = @rac.var[:ignore]
901
- ri = ri ? (ri + @add_ignore_args) : @add_ignore_args
902
- rh = ri.hash
903
- unless rh == @ignore_hash
904
- @ignore_rx = nil
905
- filelist_ignore(*ri)
906
- @ignore_hash = rh
907
- end
908
- end
909
- end # class RacFileList
910
-
911
- class MultiFileList
912
-
913
- attr_reader :cur_list
914
-
915
- def initialize(rac)
916
- @rac = rac
917
- @cur_list = RacFileList.new(@rac)
918
- @lists = [@cur_list]
919
- end
920
-
921
- def each_entry(&block)
922
- @lists.each { |list|
923
- list.each_cd(&block)
924
- }
925
- end
926
-
927
- def add(filelist)
928
- @cur_list = filelist
929
- @lists << filelist
930
- self
931
- end
932
-
933
- def method_missing(sym, *args, &block)
934
- if @cur_list && @cur_list.respond_to?(sym)
935
- if @cur_list.subdir == @rac.current_subdir
936
- @cur_list.send(sym, *args, &block)
937
- else
938
- add(RacFileList.new(@rac))
939
- @cur_list.send(sym, *args, &block)
940
- end
941
- else
942
- super
943
- end
944
- end
945
- end # class MultiFileList
946
-
947
- class CommandError < StandardError
948
- attr_reader :cmd
949
- attr_reader :status
950
- def initialize(cmd, status=nil, msg=nil)
951
- @msg = msg
952
- @cmd = cmd
953
- @status = status
954
- end
955
- def message
956
- if !@msg && cmd
957
- if status
958
- "Command failed with status #{status.exitstatus}:\n" +
959
- "[#{cmd}]"
960
- else
961
- "Command failed:\n[#{cmd}]"
962
- end
963
- else
964
- @msg
965
- end
966
- end
967
- end
968
-
969
- module Sys
970
- include ::FileUtils::Verbose
971
-
972
- @symlink_supported = true
973
- class << self
974
- attr_accessor :symlink_supported
975
- end
976
-
977
- def fu_output_message(msg) #:nodoc:
978
- end
979
- private :fu_output_message
980
-
981
- def fu_each_src_dest(src, *rest)
982
- src = src.to_ary if src.respond_to? :to_ary
983
- super(src, *rest)
984
- end
985
- private :fu_each_src_dest
986
-
987
- def sh(*cmd_args, &block)
988
- cmd_args.flatten!
989
- cmd = cmd_args.join(" ")
990
- fu_output_message cmd
991
- success = system(*cmd_args)
992
- if block_given?
993
- block[$?]
994
- elsif !success
995
- raise CommandError.new(cmd, $?)
996
- end
997
- end
998
-
999
- def ruby(*args, &block)
1000
- if args.empty?
1001
- sh(Env::RUBY_EXE, '', &block)
1002
- else
1003
- sh(args.unshift(Env::RUBY_EXE), &block)
1004
- end
1005
- end
1006
- def cd(dir, &block)
1007
- fu_output_message "cd #{dir}"
1008
- orig_pwd = Dir.pwd
1009
- Dir.chdir dir
1010
- if block
1011
- begin
1012
- block.arity == 0 ? block.call : block.call(Dir.pwd)
1013
- ensure
1014
- fu_output_message "cd -"
1015
- Dir.chdir orig_pwd
1016
- end
1017
- else
1018
- self
1019
- end
1020
- end
1021
-
1022
- def safe_ln(src, dest)
1023
- dest = dest.to_str
1024
- src = src.respond_to?(:to_ary) ? src.to_ary : src.to_str
1025
- unless Sys.symlink_supported
1026
- cp(src, dest)
1027
- else
1028
- begin
1029
- ln(src, dest)
1030
- rescue Exception # SystemCallError # Errno::EOPNOTSUPP
1031
- Sys.symlink_supported = false
1032
- cp(src, dest)
1033
- end
1034
- end
1035
- end
1036
-
1037
- def ln_f(src, dest)
1038
- ln(src, dest, :force => true)
1039
- end
1040
-
1041
- def split_path(str)
1042
- str.split(Env.on_windows? ? ";" : ":")
1043
- end
1044
-
1045
- if Env.on_windows?
1046
- def root_dir?(path)
1047
- path == "/" || path == "\\" ||
1048
- path =~ %r{\A[a-zA-Z]+:(\\|/)\Z}
1049
- end
1050
- def absolute_path?(path)
1051
- path =~ %r{\A([a-zA-Z]+:)?(/|\\)}
1052
- end
1053
- else
1054
- def root_dir?(path)
1055
- path == "/"
1056
- end
1057
- def absolute_path?(path)
1058
- path =~ %r{\A/}
1059
- end
1060
- end
1061
-
1062
- extend self
1063
-
1064
- if RUBY_VERSION >= "1.8.4" # needed by 1.9.0, too
1065
- class << self
1066
- public(*::FileUtils::METHODS)
1067
- end
1068
- public(*::FileUtils::METHODS)
1069
- end
1070
-
1071
- end # module Sys
1072
-
1073
- class SysObject
1074
- include Sys
1075
- def initialize(rant)
1076
- @rant = rant or
1077
- raise ArgumentError, "rant application required"
1078
- end
1079
- def ignore(*patterns)
1080
- @rant.var[:ignore].concat(patterns)
1081
- nil
1082
- end
1083
- def filelist(arg = Rant.__rant_no_value__)
1084
- if Rant.__rant_no_value__.equal?(arg)
1085
- RacFileList.new(@rant)
1086
- elsif arg.respond_to?(:to_rant_filelist)
1087
- arg.to_rant_filelist
1088
- elsif arg.respond_to?(:to_ary)
1089
- RacFileList.new(@rant, arg.to_ary)
1090
- else
1091
- raise TypeError,
1092
- "cannot convert #{arg.class} into Rant::FileList"
1093
- end
1094
- end
1095
- def [](*patterns)
1096
- RacFileList.new(@rant).hide_dotfiles.include(*patterns)
1097
- end
1098
- def glob(*patterns, &block)
1099
- fl = RacFileList.new(@rant).hide_dotfiles.include(*patterns)
1100
- fl.ignore(".", "..")
1101
- if block_given? then yield fl else fl end
1102
- end
1103
- def glob_all(*patterns, &block)
1104
- fl = RacFileList.new(@rant).include(*patterns)
1105
- fl.ignore(".", "..") # use case: "*.*" as pattern
1106
- if block_given? then yield fl else fl end
1107
- end
1108
- def expand_path(path)
1109
- File.expand_path(@rant.project_to_fs_path(path))
1110
- end
1111
- private
1112
- def fu_output_message(cmd)
1113
- @rant.cmd_msg cmd
1114
- end
1115
- end
1116
-
1117
-
1118
- class TaskFail < StandardError
1119
- def initialize(task, orig, msg)
1120
- @task = task
1121
- @orig = orig
1122
- @msg = msg
1123
- end
1124
- def exception
1125
- self
1126
- end
1127
- def task
1128
- @task
1129
- end
1130
- def tname
1131
- @task ? @task.name : nil
1132
- end
1133
- def orig
1134
- @orig
1135
- end
1136
- def msg
1137
- @msg
1138
- end
1139
- end
1140
-
1141
- class Rantfile
1142
- attr_reader :tasks, :path
1143
- attr_accessor :project_subdir
1144
- def initialize(path)
1145
- @path = path or raise ArgumentError, "path required"
1146
- @tasks = []
1147
- @project_subdir = nil
1148
- end
1149
- alias to_s path
1150
- alias to_str path
1151
- end # class Rantfile
1152
-
1153
- module Node
1154
-
1155
- INVOKE_OPT = {}.freeze
1156
-
1157
- T0 = Time.at(0).freeze
1158
-
1159
- attr_reader :name
1160
- attr_reader :rac
1161
- attr_accessor :description
1162
- attr_accessor :rantfile
1163
- attr_accessor :line_number
1164
- attr_accessor :project_subdir
1165
-
1166
- def initialize
1167
- @description = nil
1168
- @rantfile = nil
1169
- @line_number = nil
1170
- @run = false
1171
- @project_subdir = ""
1172
- @success = nil
1173
- end
1174
-
1175
- def reference_name
1176
- sd = rac.current_subdir
1177
- case sd
1178
- when "": full_name
1179
- when project_subdir: name
1180
- else "@#{full_name}".sub(/^@#{Regexp.escape sd}\//, '')
1181
- end
1182
- end
1183
-
1184
- alias to_s reference_name
1185
- alias to_rant_target name
1186
-
1187
- def full_name
1188
- sd = project_subdir
1189
- sd.empty? ? name : File.join(sd, name)
1190
- end
1191
-
1192
- def ch
1193
- {:file => rantfile.to_str, :ln => line_number}
1194
- end
1195
-
1196
- def goto_task_home
1197
- @rac.goto_project_dir project_subdir
1198
- end
1199
-
1200
- def file_target?
1201
- false
1202
- end
1203
-
1204
- def done?
1205
- @success
1206
- end
1207
-
1208
- def needed?
1209
- invoke(:needed? => true)
1210
- end
1211
-
1212
- def run?
1213
- @run
1214
- end
1215
-
1216
- def invoke(opt = INVOKE_OPT)
1217
- return circular_dep if run?
1218
- @run = true
1219
- begin
1220
- return !done? if opt[:needed?]
1221
- self.run if !done?
1222
- @success = true
1223
- ensure
1224
- @run = false
1225
- end
1226
- end
1227
-
1228
- def fail msg = nil, orig = nil
1229
- raise TaskFail.new(self, orig, msg)
1230
- end
1231
-
1232
- def each_target
1233
- end
1234
-
1235
- def has_actions?
1236
- defined? @block and @block
1237
- end
1238
-
1239
- def dry_run
1240
- text = "Executing #{name.dump}"
1241
- text << " [NOOP]" unless has_actions?
1242
- @rac.cmd_msg text
1243
- action_descs.each { |ad|
1244
- @rac.cmd_print " - "
1245
- @rac.cmd_msg ad.sub(/\n$/, '').gsub(/\n/, "\n ")
1246
- }
1247
- end
1248
-
1249
- private
1250
- def run
1251
- goto_task_home
1252
- return if @rac.running_task(self)
1253
- return unless has_actions?
1254
- @receiver.pre_run(self) if defined? @receiver and @receiver
1255
- @block.arity == 0 ? @block.call : @block[self] if @block
1256
- end
1257
-
1258
- def action_descs
1259
- descs = []
1260
- if defined? @receiver and @receiver
1261
- descs.concat(@receiver.pre_action_descs)
1262
- end
1263
- @block ? descs << action_block_desc : descs
1264
- end
1265
-
1266
- def action_block_desc
1267
- @block.inspect =~ /^#<Proc:[\da-z]+@(.+):(\d+)>$/i
1268
- fn, ln = $1, $2
1269
- "Ruby Proc at #{fn.sub(/^#{Regexp.escape @rac.rootdir}\//, '')}:#{ln}"
1270
- end
1271
-
1272
- def circular_dep
1273
- rac.warn_msg "Circular dependency on task `#{full_name}'."
1274
- false
1275
- end
1276
- end # module Node
1277
-
1278
-
1279
- def self.init_import_nodes__default(rac, *rest)
1280
- rac.node_factory = DefaultNodeFactory.new
1281
- end
1282
-
1283
- class DefaultNodeFactory
1284
- def new_task(rac, name, pre, blk)
1285
- Task.new(rac, name, pre, &blk)
1286
- end
1287
- def new_file(rac, name, pre, blk)
1288
- FileTask.new(rac, name, pre, &blk)
1289
- end
1290
- def new_dir(rac, name, pre, blk)
1291
- DirTask.new(rac, name, pre, &blk)
1292
- end
1293
- def new_source(rac, name, pre, blk)
1294
- SourceNode.new(rac, name, pre, &blk)
1295
- end
1296
- def new_custom(rac, name, pre, blk)
1297
- UserTask.new(rac, name, pre, &blk)
1298
- end
1299
- def new_auto_subfile(rac, name, pre, blk)
1300
- AutoSubFileTask.new(rac, name, pre, &blk)
1301
- end
1302
- end
1303
-
1304
- class Task
1305
- include Node
1306
-
1307
- attr_accessor :receiver
1308
-
1309
- def initialize(rac, name, prerequisites = [], &block)
1310
- super()
1311
- @rac = rac or raise ArgumentError, "rac not given"
1312
- @name = name or raise ArgumentError, "name not given"
1313
- @pre = prerequisites || []
1314
- @pre_resolved = false
1315
- @block = block
1316
- @run = false
1317
- @receiver = nil
1318
- end
1319
-
1320
- def prerequisites
1321
- @pre.collect { |pre| pre.to_s }
1322
- end
1323
- alias deps prerequisites
1324
-
1325
- def source
1326
- @pre.first.to_s
1327
- end
1328
-
1329
- def has_actions?
1330
- @block or @receiver && @receiver.has_pre_action?
1331
- end
1332
-
1333
- def <<(pre)
1334
- @pre_resolved = false
1335
- @pre << pre
1336
- end
1337
-
1338
- def invoked?
1339
- !@success.nil?
1340
- end
1341
-
1342
- def fail?
1343
- @success == false
1344
- end
1345
-
1346
- def enhance(deps = nil, &blk)
1347
- if deps
1348
- @pre_resolved = false
1349
- @pre.concat deps
1350
- end
1351
- if @block
1352
- if blk
1353
- first_block = @block
1354
- @block = lambda { |t|
1355
- first_block[t]
1356
- blk[t]
1357
- }
1358
- end
1359
- else
1360
- @block = blk
1361
- end
1362
- end
1363
-
1364
- def invoke(opt = INVOKE_OPT)
1365
- return circular_dep if @run
1366
- @run = true
1367
- begin
1368
- return if done?
1369
- internal_invoke opt
1370
- ensure
1371
- @run = false
1372
- end
1373
- end
1374
-
1375
- def internal_invoke(opt, ud_init = true)
1376
- goto_task_home
1377
- update = ud_init || opt[:force]
1378
- dep = nil
1379
- uf = false
1380
- each_dep { |dep|
1381
- if dep.respond_to? :timestamp
1382
- handle_timestamped(dep, opt) && update = true
1383
- elsif Node === dep
1384
- handle_node(dep, opt) && update = true
1385
- else
1386
- dep, uf = handle_non_node(dep, opt)
1387
- uf && update = true
1388
- dep
1389
- end
1390
- }
1391
- if @receiver
1392
- goto_task_home
1393
- update = true if @receiver.update?(self)
1394
- end
1395
- return update if opt[:needed?]
1396
- run if update
1397
- @success = true
1398
- update
1399
- rescue StandardError => e
1400
- @success = false
1401
- self.fail(nil, e)
1402
- end
1403
- private :internal_invoke
1404
-
1405
- def handle_node(dep, opt)
1406
- dep.invoke opt
1407
- end
1408
-
1409
- def handle_timestamped(dep, opt)
1410
- dep.invoke opt
1411
- end
1412
-
1413
- def handle_non_node(dep, opt)
1414
- @rac.err_msg "Unknown task `#{dep}',",
1415
- "referenced in `#{rantfile.path}', line #{@line_number}!"
1416
- self.fail
1417
- end
1418
-
1419
- def each_dep
1420
- t = nil
1421
- if @pre_resolved
1422
- return @pre.each { |t| yield(t) }
1423
- end
1424
- my_full_name = full_name
1425
- my_project_subdir = project_subdir
1426
- @pre.map! { |t|
1427
- if Node === t
1428
- if t.full_name == my_full_name
1429
- nil
1430
- else
1431
- yield(t)
1432
- t
1433
- end
1434
- else
1435
- t = t.to_s if Symbol === t
1436
- if t == my_full_name #TODO
1437
- nil
1438
- else
1439
- selection = @rac.resolve t,
1440
- my_project_subdir
1441
- if selection.empty?
1442
- yield(t)
1443
- else
1444
- selection.each { |st| yield(st) }
1445
- selection
1446
- end
1447
- end
1448
- end
1449
- }
1450
- if @pre.kind_of? Rant::FileList
1451
- @pre.resolve
1452
- else
1453
- @pre.flatten!
1454
- @pre.compact!
1455
- end
1456
- @pre_resolved = true
1457
- end
1458
- end # class Task
1459
-
1460
- class UserTask < Task
1461
-
1462
- def initialize(*args)
1463
- super
1464
- @block = nil
1465
- @needed = nil
1466
- @target_files = nil
1467
- yield self if block_given?
1468
- end
1469
-
1470
- def act(&block)
1471
- @block = block
1472
- end
1473
-
1474
- def needed(&block)
1475
- @needed = block
1476
- end
1477
-
1478
- def file_target?
1479
- @target_files and @target_files.include? @name
1480
- end
1481
-
1482
- def each_target(&block)
1483
- goto_task_home
1484
- @target_files.each(&block) if @target_files
1485
- end
1486
-
1487
- def file_target(*args)
1488
- args.flatten!
1489
- args << @name if args.empty?
1490
- if @target_files
1491
- @target_files.concat(args)
1492
- else
1493
- @target_files = args
1494
- end
1495
- end
1496
-
1497
- def invoke(opt = INVOKE_OPT)
1498
- return circular_dep if @run
1499
- @run = true
1500
- begin
1501
- return if done?
1502
- internal_invoke(opt, ud_init_by_needed)
1503
- ensure
1504
- @run = false
1505
- end
1506
- end
1507
-
1508
- private
1509
- def ud_init_by_needed
1510
- if @needed
1511
- goto_task_home
1512
- @needed.arity == 0 ? @needed.call : @needed[self]
1513
- end
1514
- end
1515
- end # class UserTask
1516
-
1517
- class FileTask < Task
1518
-
1519
- def initialize(*args)
1520
- super
1521
- @ts = T0
1522
- end
1523
-
1524
- def file_target?
1525
- true
1526
- end
1527
-
1528
- def invoke(opt = INVOKE_OPT)
1529
- return circular_dep if @run
1530
- @run = true
1531
- begin
1532
- return if done?
1533
- goto_task_home
1534
- if File.exist? @name
1535
- @ts = File.mtime @name
1536
- internal_invoke opt, false
1537
- else
1538
- @ts = T0
1539
- internal_invoke opt, true
1540
- end
1541
- ensure
1542
- @run = false
1543
- end
1544
- end
1545
-
1546
- def timestamp(opt = INVOKE_OPT)
1547
- File.exist?(@name) ? File.mtime(@name) : T0
1548
- end
1549
-
1550
- def handle_node(dep, opt)
1551
- return true if dep.file_target? && dep.invoke(opt)
1552
- if File.exist? dep.name
1553
- File.mtime(dep.name) > @ts
1554
- elsif !dep.file_target?
1555
- @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1556
- "in prerequisites: no such file: `#{dep.full_name}'"
1557
- self.fail
1558
- end
1559
- end
1560
-
1561
- def handle_timestamped(dep, opt)
1562
- return true if dep.invoke opt
1563
- dep.timestamp(opt) > @ts
1564
- end
1565
-
1566
- def handle_non_node(dep, opt)
1567
- goto_task_home # !!??
1568
- unless File.exist? dep
1569
- @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1570
- "in prerequisites: no such file or task: `#{dep}'"
1571
- self.fail
1572
- end
1573
- [dep, File.mtime(dep) > @ts]
1574
- end
1575
-
1576
- def each_target
1577
- goto_task_home
1578
- yield name
1579
- end
1580
- end # class FileTask
1581
-
1582
- module AutoInvokeDirNode
1583
- private
1584
- def run
1585
- goto_task_home
1586
- return if @rac.running_task(self)
1587
- dir = File.dirname(name)
1588
- @rac.build dir unless dir == "." || dir == "/"
1589
- return unless @block
1590
- @block.arity == 0 ? @block.call : @block[self]
1591
- end
1592
- end
1593
-
1594
- class AutoSubFileTask < FileTask
1595
- include AutoInvokeDirNode
1596
- end
1597
-
1598
- class DirTask < Task
1599
-
1600
- def initialize(*args)
1601
- super
1602
- @ts = T0
1603
- @isdir = nil
1604
- end
1605
-
1606
- def invoke(opt = INVOKE_OPT)
1607
- return circular_dep if @run
1608
- @run = true
1609
- begin
1610
- return if done?
1611
- goto_task_home
1612
- @isdir = test(?d, @name)
1613
- if @isdir
1614
- @ts = @block ? test(?M, @name) : Time.now
1615
- internal_invoke opt, false
1616
- else
1617
- @ts = T0
1618
- internal_invoke opt, true
1619
- end
1620
- ensure
1621
- @run = false
1622
- end
1623
- end
1624
-
1625
- def file_target?
1626
- true
1627
- end
1628
-
1629
- def handle_node(dep, opt)
1630
- return true if dep.file_target? && dep.invoke(opt)
1631
- if File.exist? dep.name
1632
- File.mtime(dep.name) > @ts
1633
- elsif !dep.file_target?
1634
- @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1635
- "in prerequisites: no such file: `#{dep.full_name}'"
1636
- self.fail
1637
- end
1638
- end
1639
-
1640
- def handle_timestamped(dep, opt)
1641
- return @block if dep.invoke opt
1642
- @block && dep.timestamp(opt) > @ts
1643
- end
1644
-
1645
- def handle_non_node(dep, opt)
1646
- goto_task_home
1647
- unless File.exist? dep
1648
- @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1649
- "in prerequisites: no such file or task: `#{dep}'"
1650
- self.fail
1651
- end
1652
- [dep, @block && File.mtime(dep) > @ts]
1653
- end
1654
-
1655
- def run
1656
- return if @rac.running_task(self)
1657
- @rac.sys.mkdir @name unless @isdir
1658
- if @block
1659
- @block.arity == 0 ? @block.call : @block[self]
1660
- goto_task_home
1661
- @rac.sys.touch @name
1662
- end
1663
- end
1664
-
1665
- def each_target
1666
- goto_task_home
1667
- yield name
1668
- end
1669
- end # class DirTask
1670
-
1671
- class SourceNode
1672
- include Node
1673
- def initialize(rac, name, prerequisites = [])
1674
- super()
1675
- @rac = rac
1676
- @name = name or raise ArgumentError, "name not given"
1677
- @pre = prerequisites
1678
- @run = false
1679
- @ts = nil
1680
- end
1681
- def prerequisites
1682
- @pre
1683
- end
1684
- def timestamp(opt = INVOKE_OPT)
1685
- return @ts if @ts
1686
- goto_task_home
1687
- if File.exist?(@name)
1688
- @ts = File.mtime @name
1689
- else
1690
- rac.abort_at(ch, "SourceNode: no such file -- #@name")
1691
- end
1692
- sd = project_subdir
1693
- @pre.each { |f|
1694
- nodes = rac.resolve f, sd
1695
- if nodes.empty?
1696
- if File.exist? f
1697
- mtime = File.mtime f
1698
- @ts = mtime if mtime > @ts
1699
- else
1700
- rac.abort_at(ch,
1701
- "SourceNode: no such file -- #{f}")
1702
- end
1703
- else
1704
- nodes.each { |node|
1705
- node.invoke(opt)
1706
- if node.respond_to? :timestamp
1707
- node_ts = node.timestamp(opt)
1708
- goto_task_home
1709
- @ts = node_ts if node_ts > @ts
1710
- else
1711
- rac.abort_at(ch,
1712
- "SourceNode can't depend on #{node.name}")
1713
- end
1714
- }
1715
- end
1716
- }
1717
- @ts
1718
- end
1719
- def invoke(opt = INVOKE_OPT)
1720
- false
1721
- end
1722
- def related_sources
1723
- @pre
1724
- end
1725
- end # class SourceNode
1726
-
1727
- module Generators
1728
- class Task
1729
- def self.rant_gen(rac, ch, args, &block)
1730
- unless args.size == 1
1731
- rac.abort("Task takes only one argument " +
1732
- "which has to be like one given to the " +
1733
- "`task' function")
1734
- end
1735
- rac.prepare_task(args.first, nil, ch) { |name,pre,blk|
1736
- rac.node_factory.new_custom(rac, name, pre, block)
1737
- }
1738
- end
1739
- end
1740
- class Directory
1741
- def self.rant_gen(rac, ch, args, &block)
1742
- case args.size
1743
- when 1
1744
- name, pre = rac.normalize_task_arg(args.first, ch)
1745
- self.task(rac, ch, name, pre, &block)
1746
- when 2
1747
- basedir = args.shift
1748
- if basedir.respond_to? :to_str
1749
- basedir = basedir.to_str
1750
- else
1751
- rac.abort_at(ch,
1752
- "Directory: basedir argument has to be a string.")
1753
- end
1754
- name, pre = rac.normalize_task_arg(args.first, ch)
1755
- self.task(rac, ch, name, pre, basedir, &block)
1756
- else
1757
- rac.abort_at(ch, "Directory takes one argument, " +
1758
- "which should be like one given to the `task' command.")
1759
- end
1760
- end
1761
-
1762
- def self.task(rac, ch, name, prerequisites=[], basedir=nil, &block)
1763
- dirs = ::Rant::Sys.split_all(name)
1764
- if dirs.empty?
1765
- rac.abort_at(ch,
1766
- "Not a valid directory name: `#{name}'")
1767
- end
1768
- path = basedir
1769
- last_task = nil
1770
- task_block = nil
1771
- desc_for_last = rac.pop_desc
1772
- dirs.each { |dir|
1773
- pre = [path]
1774
- pre.compact!
1775
- if dir.equal?(dirs.last)
1776
- rac.cx.desc desc_for_last
1777
-
1778
- dp = prerequisites.dup
1779
- pre.each { |elem| dp << elem }
1780
- pre = dp
1781
-
1782
- task_block = block
1783
- end
1784
- path = path.nil? ? dir : File.join(path, dir)
1785
- last_task = rac.prepare_task({:__caller__ => ch,
1786
- path => pre}, task_block) { |name,pre,blk|
1787
- rac.node_factory.new_dir(rac, name, pre, blk)
1788
- }
1789
- }
1790
- last_task
1791
- end
1792
- end # class Directory
1793
- class SourceNode
1794
- def self.rant_gen(rac, ch, args)
1795
- unless args.size == 1
1796
- rac.abort_at(ch, "SourceNode takes one argument.")
1797
- end
1798
- if block_given?
1799
- rac.abort_at(ch, "SourceNode doesn't take a block.")
1800
- end
1801
- rac.prepare_task(args.first, nil, ch) { |name, pre, blk|
1802
- rac.node_factory.new_source(rac, name, pre, blk)
1803
- }
1804
- end
1805
- end
1806
- class Rule
1807
- def self.rant_gen(rac, ch, args, &block)
1808
- unless args.size == 1
1809
- rac.abort_at(ch, "Rule takes only one argument.")
1810
- end
1811
- rac.abort_at(ch, "Rule: block required.") unless block
1812
- arg = args.first
1813
- target = nil
1814
- src_arg = nil
1815
- if Symbol === arg
1816
- target = ".#{arg}"
1817
- elsif arg.respond_to? :to_str
1818
- target = arg.to_str
1819
- elsif Regexp === arg
1820
- target = arg
1821
- elsif Hash === arg && arg.size == 1
1822
- arg.each_pair { |target, src_arg| }
1823
- src_arg = src_arg.to_str if src_arg.respond_to? :to_str
1824
- target = target.to_str if target.respond_to? :to_str
1825
- src_arg = ".#{src_arg}" if Symbol === src_arg
1826
- target = ".#{target}" if Symbol === target
1827
- else
1828
- rac.abort_at(ch, "Rule argument " +
1829
- "has to be a hash with one key-value pair.")
1830
- end
1831
- esc_target = nil
1832
- target_rx = case target
1833
- when String
1834
- esc_target = Regexp.escape(target)
1835
- /#{esc_target}$/
1836
- when Regexp
1837
- target
1838
- else
1839
- rac.abort_at(ch, "rule target has " +
1840
- "to be a string or regular expression")
1841
- end
1842
- src_proc = case src_arg
1843
- when String, Array
1844
- unless String === target
1845
- rac.abort(ch, "rule target has to be " +
1846
- "a string if source is a string")
1847
- end
1848
- if src_arg.kind_of? String
1849
- lambda { |name|
1850
- name.sub(/#{esc_target}$/, src_arg)
1851
- }
1852
- else
1853
- lambda { |name|
1854
- src_arg.collect { |s_src|
1855
- s_src = ".#{s_src}" if Symbol === s_src
1856
- name.sub(/#{esc_target}$/, s_src)
1857
- }
1858
- }
1859
- end
1860
- when Proc: src_arg
1861
- when nil: lambda { |name| [] }
1862
- else
1863
- rac.abort_at(ch, "rule source has to be a " +
1864
- "String, Array or Proc")
1865
- end
1866
- rac.resolve_hooks <<
1867
- (block.arity == 2 ? Hook : FileHook).new(
1868
- rac, ch, target_rx, src_proc, block)
1869
- nil
1870
- end
1871
- class Hook
1872
- attr_accessor :target_rx
1873
- def initialize(rant, ch, target_rx, src_proc, block)
1874
- @rant = rant
1875
- @ch = ch
1876
- @target_rx = target_rx
1877
- @src_proc = src_proc
1878
- @block = block
1879
- end
1880
- def call(target, rel_project_dir)
1881
- if @target_rx =~ target
1882
- have_src = true
1883
- src = @src_proc[target]
1884
- if src.respond_to? :to_ary
1885
- have_src = src.to_ary.all? { |s|
1886
- have_src?(rel_project_dir, s)
1887
- }
1888
- else
1889
- have_src = have_src?(rel_project_dir, src)
1890
- end
1891
- if have_src
1892
- create_nodes(rel_project_dir, target, src)
1893
- end
1894
- end
1895
- end
1896
- alias [] call
1897
- private
1898
- def have_src?(rel_project_dir, name)
1899
- return true unless
1900
- @rant.rec_save_resolve(name, self, rel_project_dir).empty?
1901
- test(?e, @rant.abs_path(rel_project_dir, name))
1902
- end
1903
- def create_nodes(rel_project_dir, target, deps)
1904
- @rant.goto_project_dir rel_project_dir
1905
- case nodes = @block[target, deps]
1906
- when Array: nodes
1907
- when Node: [nodes]
1908
- else
1909
- @rant.abort_at(@ch, "Block has to " +
1910
- "return Node or array of Nodes.")
1911
- end
1912
- end
1913
- end
1914
- class FileHook < Hook
1915
- private
1916
- def have_src?(rel_project_dir, name)
1917
- test(?e, @rant.abs_path(rel_project_dir, name)) or
1918
- @rant.rec_save_resolve(name, self, rel_project_dir
1919
- ).any? { |t| t.file_target? }
1920
- end
1921
- def create_nodes(rel_project_dir, target, deps)
1922
- @rant.goto_project_dir rel_project_dir
1923
- t = @rant.file(:__caller__ => @ch,
1924
- target => deps, &@block)
1925
- [t]
1926
- end
1927
- end
1928
- end # class Rule
1929
- class Action
1930
- def self.rant_gen(rac, ch, args, &block)
1931
- case args.size
1932
- when 0:
1933
- unless (rac[:tasks] || rac[:stop_after_load])
1934
- yield
1935
- end
1936
- when 1:
1937
- rx = args.first
1938
- unless rx.kind_of? Regexp
1939
- rac.abort_at(ch, "Action: argument has " +
1940
- "to be a regular expression.")
1941
- end
1942
- rac.resolve_hooks << self.new(rac, block, rx)
1943
- nil
1944
- else
1945
- rac.abort_at(ch, "Action: too many arguments.")
1946
- end
1947
- end
1948
- def initialize(rant, block, rx)
1949
- @rant = rant
1950
- @subdir = @rant.current_subdir
1951
- @block = block
1952
- @rx = rx
1953
- end
1954
- def call(target, rel_project_dir)
1955
- if target =~ @rx
1956
- @rant.resolve_hooks.delete(self)
1957
- @rant.goto_project_dir @subdir
1958
- @block.call
1959
- @rant.resolve(target, rel_project_dir)
1960
- end
1961
- end
1962
- alias [] call
1963
- end
1964
- end # module Generators
1965
- end # module Rant
1966
-
1967
- Rant::MAIN_OBJECT = self
1968
-
1969
- class String
1970
- alias sub_ext _rant_sub_ext
1971
- def to_rant_target
1972
- self
1973
- end
1974
- end
1975
-
1976
- module Rant::Lib
1977
- def parse_caller_elem(elem)
1978
- return { :file => "", :ln => 0 } unless elem
1979
- if elem =~ /^(.+):(\d+)(?::|$)/
1980
- { :file => $1, :ln => $2.to_i }
1981
- else
1982
- $stderr.puts "parse_caller_elem: #{elem.inspect}"
1983
- { :file => elem, :ln => 0 }
1984
- end
1985
-
1986
- end
1987
- module_function :parse_caller_elem
1988
- end # module Lib
1989
-
1990
- module Rant::Console
1991
- RANT_PREFIX = "rant: "
1992
- ERROR_PREFIX = "[ERROR] "
1993
- WARN_PREFIX = "[WARNING] "
1994
- def msg_prefix
1995
- if defined? @msg_prefix and @msg_prefix
1996
- @msg_prefix
1997
- else
1998
- RANT_PREFIX
1999
- end
2000
- end
2001
- def msg(*text)
2002
- pre = msg_prefix
2003
- $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2004
- end
2005
- def vmsg(importance, *text)
2006
- msg(*text) if verbose >= importance
2007
- end
2008
- def err_msg(*text)
2009
- pre = msg_prefix + ERROR_PREFIX
2010
- $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2011
- end
2012
- def warn_msg(*text)
2013
- pre = msg_prefix + WARN_PREFIX
2014
- $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2015
- end
2016
- def ask_yes_no text
2017
- $stderr.print msg_prefix + text + " [y|n] "
2018
- case $stdin.readline
2019
- when /y|yes/i: true
2020
- when /n|no/i: false
2021
- else
2022
- $stderr.puts(' ' * msg_prefix.length +
2023
- "Please answer with `yes' or `no'")
2024
- ask_yes_no text
2025
- end
2026
- end
2027
- def prompt text
2028
- $stderr.print msg_prefix + text
2029
- input = $stdin.readline
2030
- input ? input.chomp : input
2031
- end
2032
- def option_listing opts
2033
- rs = ""
2034
- opts.each { |lopt, *opt_a|
2035
- if opt_a.size == 2
2036
- mode, desc = opt_a
2037
- else
2038
- sopt, mode, desc = opt_a
2039
- end
2040
- next unless desc # "private" option
2041
- optstr = ""
2042
- arg = nil
2043
- if mode != GetoptLong::NO_ARGUMENT
2044
- if desc =~ /(\b[A-Z_]{2,}\b)/
2045
- arg = $1
2046
- end
2047
- end
2048
- if lopt
2049
- optstr << lopt
2050
- if arg
2051
- optstr << " " << arg
2052
- end
2053
- optstr = optstr.ljust(30)
2054
- end
2055
- if sopt
2056
- optstr << " " unless optstr.empty?
2057
- optstr << sopt
2058
- if arg
2059
- optstr << " " << arg
2060
- end
2061
- end
2062
- rs << " #{optstr}\n"
2063
- rs << " #{desc.split("\n").join("\n ")}\n"
2064
- }
2065
- rs
2066
- end
2067
- extend self
2068
- end # module Rant::Console
2069
-
2070
- module RantContext
2071
- include Rant::Generators
2072
-
2073
- Env = Rant::Env
2074
- FileList = Rant::FileList
2075
-
2076
- def task(targ, &block)
2077
- rant.task(targ, &block)
2078
- end
2079
-
2080
- def file(targ, &block)
2081
- rant.file(targ, &block)
2082
- end
2083
-
2084
- def enhance(targ, &block)
2085
- rant.enhance(targ, &block)
2086
- end
2087
-
2088
- def desc(*args)
2089
- rant.desc(*args)
2090
- end
2091
-
2092
- def gen(*args, &block)
2093
- rant.gen(*args, &block)
2094
- end
2095
-
2096
- def import(*args, &block)
2097
- rant.import(*args, &block)
2098
- end
2099
-
2100
- def plugin(*args, &block)
2101
- rant.plugin(*args, &block)
2102
- end
2103
-
2104
- def subdirs(*args)
2105
- rant.subdirs(*args)
2106
- end
2107
-
2108
- def source(opt, rantfile = nil)
2109
- rant.source(opt, rantfile)
2110
- end
2111
-
2112
- def sys(*args, &block)
2113
- rant.sys(*args, &block)
2114
- end
2115
-
2116
- def var(*args, &block)
2117
- rant.var(*args, &block)
2118
- end
2119
-
2120
- def make(*args, &block)
2121
- rant.make(*args, &block)
2122
- end
2123
-
2124
- end # module RantContext
2125
-
2126
- class RantAppContext
2127
- include RantContext
2128
-
2129
- def initialize(app)
2130
- @__rant__ = app
2131
- end
2132
-
2133
- def rant
2134
- @__rant__
2135
- end
2136
-
2137
- def method_missing(sym, *args)
2138
- Rant::MAIN_OBJECT.send(sym, *args)
2139
- rescue NoMethodError
2140
- raise NameError, "NameError: undefined local " +
2141
- "variable or method `#{sym}' for main:Object", caller
2142
- end
2143
- end
2144
-
2145
- module Rant
2146
-
2147
- @__rant__ = nil
2148
- class << self
2149
-
2150
- def run(first_arg=nil, *other_args)
2151
- other_args = other_args.flatten
2152
- args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
2153
- if rant && !rant.run?
2154
- rant.run(args.flatten)
2155
- else
2156
- @__rant__ = Rant::RantApp.new
2157
- rant.run(args)
2158
- end
2159
- end
2160
-
2161
- def rant
2162
- @__rant__
2163
- end
2164
- end
2165
-
2166
- end # module Rant
2167
-
2168
- class Rant::RantApp
2169
- include Rant::Console
2170
-
2171
- class AutoLoadNodeFactory
2172
- def initialize(rant)
2173
- @rant = rant
2174
- end
2175
- def method_missing(sym, *args, &block)
2176
- @rant.import "nodes/default"
2177
- @rant.node_factory.send(sym, *args, &block)
2178
- end
2179
- end
2180
-
2181
-
2182
-
2183
- OPTIONS = [
2184
- [ "--help", "-h", GetoptLong::NO_ARGUMENT,
2185
- "Print this help and exit." ],
2186
- [ "--version", "-V", GetoptLong::NO_ARGUMENT,
2187
- "Print version of Rant and exit." ],
2188
- [ "--verbose", "-v", GetoptLong::NO_ARGUMENT,
2189
- "Print more messages to stderr." ],
2190
- [ "--quiet", "-q", GetoptLong::NO_ARGUMENT,
2191
- "Don't print commands." ],
2192
- [ "--err-commands", GetoptLong::NO_ARGUMENT,
2193
- "Print failed commands and their exit status." ],
2194
- [ "--directory","-C", GetoptLong::REQUIRED_ARGUMENT,
2195
- "Run rant in DIRECTORY." ],
2196
- [ "--cd-parent","-c", GetoptLong::NO_ARGUMENT,
2197
- "Run rant in parent directory with Rantfile." ],
2198
- [ "--look-up", "-u", GetoptLong::NO_ARGUMENT,
2199
- "Look in parent directories for root Rantfile." ],
2200
- [ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT,
2201
- "Process RANTFILE instead of standard rantfiles.\n" +
2202
- "Multiple files may be specified with this option." ],
2203
- [ "--force-run","-a", GetoptLong::REQUIRED_ARGUMENT,
2204
- "Force rebuild of TARGET and all dependencies." ],
2205
- [ "--dry-run", "-n", GetoptLong::NO_ARGUMENT,
2206
- "Print info instead of actually executing actions." ],
2207
- [ "--tasks", "-T", GetoptLong::NO_ARGUMENT,
2208
- "Show a list of all described tasks and exit." ],
2209
-
2210
-
2211
- [ "--import", "-i", GetoptLong::REQUIRED_ARGUMENT, nil ],
2212
- [ "--stop-after-load", GetoptLong::NO_ARGUMENT, nil ],
2213
- [ "--trace-abort", GetoptLong::NO_ARGUMENT, nil ],
2214
- ]
2215
-
2216
- ROOT_DIR_ID = "@"
2217
- ESCAPE_ID = "\\"
2218
-
2219
- attr_reader :args
2220
- attr_reader :rantfiles
2221
- attr_reader :force_targets
2222
- attr_reader :plugins
2223
- attr_reader :context
2224
- alias cx context
2225
- attr_reader :tasks
2226
- attr_reader :imports
2227
- attr_reader :current_subdir
2228
- attr_reader :resolve_hooks
2229
- attr_reader :rootdir
2230
-
2231
- attr_accessor :node_factory
2232
-
2233
- def initialize
2234
- @args = []
2235
- @context = RantAppContext.new(self)
2236
- @sys = ::Rant::SysObject.new(self)
2237
- @rantfiles = []
2238
- @tasks = {}
2239
- @opts = {
2240
- :verbose => 0,
2241
- :quiet => false,
2242
- }
2243
- @rootdir = Dir.pwd # root directory of project
2244
- @arg_rantfiles = [] # rantfiles given in args
2245
- @arg_targets = [] # targets given in args
2246
- @force_targets = [] # targets given with -a option
2247
- @run = false # run method was called at least once
2248
- @done = false # run method was successful
2249
- @plugins = []
2250
- @var = Rant::RantVar::Space.new
2251
- @var.query :ignore, :AutoList, []
2252
- @imports = []
2253
-
2254
- @task_desc = nil
2255
- @last_build_subdir = ""
2256
-
2257
- @current_subdir = ""
2258
- @resolve_hooks = []
2259
-
2260
- @node_factory = AutoLoadNodeFactory.new(self)
2261
- end
2262
-
2263
- def [](opt)
2264
- @opts[opt]
2265
- end
2266
-
2267
- def []=(opt, val)
2268
- @opts[opt] = val
2269
- end
2270
-
2271
- def expand_path(subdir, path)
2272
- case path
2273
- when nil: subdir.dup
2274
- when "": subdir.dup
2275
- when /^@/: path.sub(/^@/, '')
2276
- else
2277
- path = path.sub(/^\\(?=@)/, '')
2278
- if subdir.empty?
2279
- path
2280
- else
2281
- File.join(subdir, path)
2282
- end
2283
- end
2284
- end
2285
- def resolve_root_ref(path)
2286
- return File.join(@rootdir, path[1..-1]) if path =~ /^@/
2287
- path.sub(/^\\(?=@)/, '')
2288
- end
2289
- def project_to_fs_path(path)
2290
- sub = expand_path(@current_subdir, path)
2291
- sub.empty? ? @rootdir : File.join(@rootdir, sub)
2292
- end
2293
- def abs_path(subdir, fn)
2294
- return fn if Rant::Sys.absolute_path?(fn)
2295
- path = File.join(@rootdir, subdir, fn)
2296
- path.gsub!(%r{/+}, "/")
2297
- path.sub!(%r{/$}, "") if path.length > 1
2298
- path
2299
- end
2300
- def goto(dir)
2301
- goto_project_dir(expand_path(@current_subdir, dir))
2302
- end
2303
- def goto_project_dir(dir='')
2304
- @current_subdir = dir
2305
- abs_path = @current_subdir.empty? ?
2306
- @rootdir : File.join(@rootdir, @current_subdir)
2307
- unless Dir.pwd == abs_path
2308
- Dir.chdir abs_path
2309
- vmsg 1, "in #{abs_path}"
2310
- end
2311
- end
2312
-
2313
- def run?
2314
- @run
2315
- end
2316
-
2317
- def done?
2318
- @done
2319
- end
2320
-
2321
- def run(*args)
2322
- @run = true
2323
- @args.concat(args.flatten)
2324
- orig_pwd = @rootdir = Dir.pwd
2325
- process_args
2326
- Dir.chdir(@rootdir) rescue abort $!.message
2327
- load_rantfiles
2328
-
2329
- raise Rant::RantDoneException if @opts[:stop_after_load]
2330
-
2331
- @plugins.each { |plugin| plugin.rant_start }
2332
- if @opts[:tasks]
2333
- show_descriptions
2334
- raise Rant::RantDoneException
2335
- end
2336
- run_tasks
2337
- raise Rant::RantDoneException
2338
- rescue Rant::RantDoneException
2339
- @done = true
2340
- @plugins.each { |plugin| plugin.rant_done }
2341
- return 0
2342
- rescue Rant::RantAbortException
2343
- $stderr.puts "rant aborted!"
2344
- return 1
2345
- rescue Exception => e
2346
- ch = get_ch_from_backtrace(e.backtrace)
2347
- if ch && !@opts[:trace_abort]
2348
- err_msg(pos_text(ch[:file], ch[:ln]), e.message)
2349
- else
2350
- err_msg e.message, e.backtrace[0..4]
2351
- end
2352
- $stderr.puts "rant aborted!"
2353
- return 1
2354
- ensure
2355
- Dir.chdir @rootdir if test ?d, @rootdir
2356
- hooks = var._get("__at_return__")
2357
- hooks.each { |hook| hook.call } if hooks
2358
- @plugins.each { |plugin| plugin.rant_plugin_stop }
2359
- @plugins.each { |plugin| plugin.rant_quit }
2360
- Dir.chdir orig_pwd
2361
- end
2362
-
2363
-
2364
- def desc(*args)
2365
- if args.empty? || (args.size == 1 && args.first.nil?)
2366
- @task_desc = nil
2367
- else
2368
- @task_desc = args.join("\n")
2369
- end
2370
- end
2371
-
2372
- def task(targ, &block)
2373
- prepare_task(targ, block) { |name,pre,blk|
2374
- @node_factory.new_task(self, name, pre, blk)
2375
- }
2376
- end
2377
-
2378
- def file(targ, &block)
2379
- prepare_task(targ, block) { |name,pre,blk|
2380
- @node_factory.new_file(self, name, pre, blk)
2381
- }
2382
- end
2383
-
2384
- def gen(*args, &block)
2385
- ch = Rant::Lib::parse_caller_elem(caller[1])
2386
- generator = args.shift
2387
- unless generator.respond_to? :rant_gen
2388
- abort_at(ch,
2389
- "gen: First argument has to be a task-generator.")
2390
- end
2391
- generator.rant_gen(self, ch, args, &block)
2392
- end
2393
-
2394
- def import(*args, &block)
2395
- ch = Rant::Lib::parse_caller_elem(caller[1])
2396
- if block
2397
- warn_msg pos_text(ch[:file], ch[:ln]),
2398
- "import: ignoring block"
2399
- end
2400
- args.flatten.each { |arg|
2401
- unless String === arg
2402
- abort_at(ch, "import: only strings allowed as arguments")
2403
- end
2404
- unless @imports.include? arg
2405
- unless Rant::CODE_IMPORTS.include? arg
2406
- begin
2407
- vmsg 2, "import #{arg}"
2408
- require "rant/import/#{arg}"
2409
- rescue LoadError => e
2410
- abort_at(ch, "No such import - #{arg}")
2411
- end
2412
- Rant::CODE_IMPORTS << arg.dup
2413
- end
2414
- init_msg = "init_import_#{arg.gsub(/[^\w]/, '__')}"
2415
- Rant.send init_msg, self if Rant.respond_to? init_msg
2416
- @imports << arg.dup
2417
- end
2418
- }
2419
- end
2420
-
2421
- def plugin(*args, &block)
2422
- clr = caller[1]
2423
- ch = Rant::Lib::parse_caller_elem(clr)
2424
- name = nil
2425
- pre = []
2426
- ln = ch[:ln] || 0
2427
- file = ch[:file]
2428
-
2429
- pl_name = args.shift
2430
- pl_name = pl_name.to_str if pl_name.respond_to? :to_str
2431
- pl_name = pl_name.to_s if pl_name.is_a? Symbol
2432
- unless pl_name.is_a? String
2433
- abort(pos_text(file, ln),
2434
- "Plugin name has to be a string or symbol.")
2435
- end
2436
- lc_pl_name = pl_name.downcase
2437
- import_name = "plugin/#{lc_pl_name}"
2438
- unless Rant::CODE_IMPORTS.include? import_name
2439
- begin
2440
- require "rant/plugin/#{lc_pl_name}"
2441
- Rant::CODE_IMPORTS << import_name
2442
- rescue LoadError
2443
- abort(pos_text(file, ln),
2444
- "no such plugin library -- #{lc_pl_name}")
2445
- end
2446
- end
2447
- pl_class = nil
2448
- begin
2449
- pl_class = ::Rant::Plugin.const_get(pl_name)
2450
- rescue NameError, ArgumentError
2451
- abort(pos_text(file, ln),
2452
- "no such plugin -- #{pl_name}")
2453
- end
2454
-
2455
- plugin = pl_class.rant_plugin_new(self, ch, *args, &block)
2456
- @plugins << plugin
2457
- vmsg 2, "Plugin `#{plugin.rant_plugin_name}' registered."
2458
- plugin.rant_plugin_init
2459
- plugin
2460
- end
2461
-
2462
- def enhance(targ, &block)
2463
- prepare_task(targ, block) { |name,pre,blk|
2464
- t = resolve(name).last
2465
- if t
2466
- unless t.respond_to? :enhance
2467
- abort("Can't enhance task `#{name}'")
2468
- end
2469
- t.enhance(pre, &blk)
2470
- return t
2471
- end
2472
- warn_msg "enhance \"#{name}\": no such task",
2473
- "Generating a new file task with the given name."
2474
- @node_factory.new_file(self, name, pre, blk)
2475
- }
2476
- end
2477
-
2478
- def source(opt, rantfile = nil)
2479
- unless rantfile
2480
- rantfile = opt
2481
- opt = nil
2482
- end
2483
- make_rf = opt != :n && opt != :now
2484
- rf, is_new = rantfile_for_path(rantfile)
2485
- return false unless is_new
2486
- make rantfile if make_rf
2487
- unless File.exist? rf.path
2488
- abort("source: No such file -- #{rantfile}")
2489
- end
2490
-
2491
- load_file rf
2492
- end
2493
-
2494
- def subdirs(*args)
2495
- args.flatten!
2496
- ch = Rant::Lib::parse_caller_elem(caller[1])
2497
- args.each { |arg|
2498
- if arg.respond_to? :to_str
2499
- arg = arg.to_str
2500
- else
2501
- abort_at(ch, "subdirs: arguments must be strings")
2502
- end
2503
- loaded = false
2504
- prev_subdir = @current_subdir
2505
- begin
2506
- goto arg
2507
- if test(?f, Rant::SUB_RANTFILE)
2508
- path = Rant::SUB_RANTFILE
2509
- else
2510
- path = rantfile_in_dir
2511
- end
2512
- if path
2513
- if defined? @initial_subdir and
2514
- @initial_subdir == @current_subdir
2515
- rf, is_new = rantfile_for_path(path, false)
2516
- @rantfiles.unshift rf if is_new
2517
- else
2518
- rf, is_new = rantfile_for_path(path)
2519
- end
2520
- load_file rf if is_new
2521
- elsif !@opts[:no_warn_subdir]
2522
- warn_msg(pos_text(ch[:file], ch[:ln]),
2523
- "subdirs: No Rantfile in subdir `#{arg}'.")
2524
- end
2525
- ensure
2526
- goto_project_dir prev_subdir
2527
- end
2528
- }
2529
- rescue SystemCallError => e
2530
- abort_at(ch, "subdirs: " + e.message)
2531
- end
2532
-
2533
- def sys(*args, &block)
2534
- args.empty? ? @sys : @sys.sh(*args, &block)
2535
- end
2536
-
2537
- def var(*args, &block)
2538
- args.empty? ? @var : @var.query(*args, &block)
2539
- end
2540
-
2541
- def pop_desc
2542
- td = @task_desc
2543
- @task_desc = nil
2544
- td
2545
- end
2546
-
2547
- def abort(*msg)
2548
- err_msg(msg) unless msg.empty?
2549
- $stderr.puts caller if @opts[:trace_abort]
2550
- raise Rant::RantAbortException
2551
- end
2552
-
2553
- def abort_at(ch, *msg)
2554
- err_msg(pos_text(ch[:file], ch[:ln]), msg)
2555
- $stderr.puts caller if @opts[:trace_abort]
2556
- raise Rant::RantAbortException
2557
- end
2558
-
2559
- def show_help
2560
- puts "rant [-f Rantfile] [Options] [targets]"
2561
- puts
2562
- puts "Options are:"
2563
- print option_listing(OPTIONS)
2564
- end
2565
-
2566
- def show_descriptions
2567
- tlist = select_tasks { |t| t.description }
2568
- def_target = target_list.first
2569
- if tlist.empty?
2570
- puts "rant # => " + list_task_names(
2571
- resolve(def_target)).join(', ')
2572
- msg "No described tasks."
2573
- return
2574
- end
2575
- prefix = "./build.rb "
2576
- infix = " # "
2577
- name_length = (tlist.map{ |t| t.to_s.length } << 7).max
2578
- cmd_length = prefix.length + name_length
2579
- unless tlist.first.to_s == def_target
2580
- defaults = list_task_names(
2581
- resolve(def_target)).join(', ')
2582
- puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}"
2583
- end
2584
- tlist.each { |t|
2585
- print(prefix + t.to_s.ljust(name_length) + infix)
2586
- dt = t.description.sub(/\s+$/, "")
2587
- puts dt.gsub(/\n/, "\n" + ' ' * cmd_length + infix + " ")
2588
- }
2589
- true
2590
- end
2591
-
2592
- def list_task_names(*tasks)
2593
- rsl = []
2594
- tasks.flatten.each { |t|
2595
- if t.respond_to?(:has_actions?) && t.has_actions?
2596
- rsl << t
2597
- elsif t.respond_to? :prerequisites
2598
- if t.prerequisites.empty?
2599
- rsl << t
2600
- else
2601
- t.prerequisites.each { |pre|
2602
- rsl.concat(list_task_names(
2603
- resolve(pre, t.project_subdir)))
2604
- }
2605
- end
2606
- else
2607
- rsl << t
2608
- end
2609
- }
2610
- rsl
2611
- end
2612
- private :list_task_names
2613
-
2614
- def verbose
2615
- @opts[:verbose]
2616
- end
2617
-
2618
- def quiet?
2619
- @opts[:quiet]
2620
- end
2621
-
2622
- def pos_text(file, ln)
2623
- t = "in file `#{file}'"
2624
- t << ", line #{ln}" if ln && ln > 0
2625
- t << ": "
2626
- end
2627
-
2628
- def cmd_msg(cmd)
2629
- puts cmd unless quiet?
2630
- end
2631
-
2632
- def cmd_print(text)
2633
- print text unless quiet?
2634
- $stdout.flush
2635
- end
2636
-
2637
- def cmd_targets
2638
- @force_targets + @arg_targets
2639
- end
2640
-
2641
- def running_task(task)
2642
- if @current_subdir != @last_build_subdir
2643
- cmd_msg "(in #{@current_subdir.empty? ?
2644
- @rootdir : @current_subdir})"
2645
- @last_build_subdir = @current_subdir
2646
- end
2647
- if @opts[:dry_run]
2648
- task.dry_run
2649
- true
2650
- end
2651
- end
2652
-
2653
- private
2654
- def have_any_task?
2655
- !@tasks.empty?
2656
- end
2657
-
2658
- def target_list
2659
- if !have_any_task? && @resolve_hooks.empty?
2660
- abort("No tasks defined for this rant application!")
2661
- end
2662
-
2663
- target_list = @force_targets + @arg_targets
2664
- if target_list.empty?
2665
- def_tasks = resolve "default"
2666
- unless def_tasks.empty?
2667
- target_list << "default"
2668
- else
2669
- @rantfiles.each { |f|
2670
- first = f.tasks.first
2671
- if first
2672
- target_list << first.reference_name
2673
- break
2674
- end
2675
- }
2676
- end
2677
- end
2678
- target_list
2679
- end
2680
-
2681
- def run_tasks
2682
- target_list.each { |target|
2683
- if build(target) == 0
2684
- abort("Don't know how to make `#{target}'.")
2685
- end
2686
- }
2687
- end
2688
-
2689
- def make(target, *args, &block)
2690
- ch = nil
2691
- if target.respond_to? :to_hash
2692
- targ = target.to_hash
2693
- ch = Rant::Lib.parse_caller_elem(caller[1])
2694
- abort_at(ch, "make: too many arguments") unless args.empty?
2695
- tn = nil
2696
- prepare_task(targ, block, ch) { |name,pre,blk|
2697
- tn = name
2698
- @node_factory.new_file(self, name, pre, blk)
2699
- }
2700
- build(tn)
2701
- elsif target.respond_to? :to_rant_target
2702
- rt = target.to_rant_target
2703
- opt = args.shift
2704
- unless args.empty?
2705
- ch ||= Rant::Lib.parse_caller_elem(caller[1])
2706
- abort_at(ch, "make: too many arguments")
2707
- end
2708
- if block
2709
- ch ||= Rant::Lib.parse_caller_elem(caller[1])
2710
- prepare_task(rt, block, ch) { |name,pre,blk|
2711
- @node_factory.new_file(self, name, pre, blk)
2712
- }
2713
- build(rt)
2714
- else
2715
- build(rt, opt||{})
2716
- end
2717
- elsif target.respond_to? :rant_gen
2718
- ch = Rant::Lib.parse_caller_elem(caller[1])
2719
- rv = target.rant_gen(self, ch, args, &block)
2720
- unless rv.respond_to? :to_rant_target
2721
- abort_at(ch, "make: invalid generator return value")
2722
- end
2723
- build(rv.to_rant_target)
2724
- rv
2725
- else
2726
- ch = Rant::Lib.parse_caller_elem(caller[1])
2727
- abort_at(ch,
2728
- "make: generator or target as first argument required.")
2729
- end
2730
- end
2731
- public :make
2732
-
2733
- def build(target, opt = {})
2734
- opt[:force] = true if @force_targets.delete(target)
2735
- opt[:dry_run] = @opts[:dry_run]
2736
- matching_tasks = 0
2737
- old_subdir = @current_subdir
2738
- old_pwd = Dir.pwd
2739
- resolve(target).each { |t|
2740
- unless opt[:type] == :file && !t.file_target?
2741
- matching_tasks += 1
2742
- begin
2743
- t.invoke(opt)
2744
- rescue Rant::TaskFail => e
2745
- err_task_fail(e)
2746
- abort
2747
- end
2748
- end
2749
- }
2750
- @current_subdir = old_subdir
2751
- Dir.chdir old_pwd
2752
- matching_tasks
2753
- end
2754
- public :build
2755
-
2756
- def resolve(task_name, rel_project_dir = @current_subdir)
2757
- s = @tasks[expand_path(rel_project_dir, task_name)]
2758
- case s
2759
- when nil
2760
- @resolve_hooks.each { |s|
2761
- s = s[task_name, rel_project_dir]
2762
- return s if s
2763
- }
2764
- []
2765
- when Rant::Node: [s]
2766
- else # assuming list of tasks
2767
- s
2768
- end
2769
- end
2770
- public :resolve
2771
-
2772
- def rec_save_resolve(task_name, excl_hook, rel_project_dir = @current_subdir)
2773
- s = @tasks[expand_path(rel_project_dir, task_name)]
2774
- case s
2775
- when nil
2776
- @resolve_hooks.each { |s|
2777
- next if s == excl_hook
2778
- s = s[task_name, rel_project_dir]
2779
- return s if s
2780
- }
2781
- []
2782
- when Rant::Node: [s]
2783
- else
2784
- s
2785
- end
2786
- end
2787
- public :rec_save_resolve
2788
-
2789
- def at_resolve(&block)
2790
- @resolve_hooks << block if block
2791
- end
2792
- public :at_resolve
2793
-
2794
- def at_return(&block)
2795
- hooks = var._get("__at_return__")
2796
- if hooks
2797
- hooks << block
2798
- else
2799
- var._set("__at_return__", [block])
2800
- end
2801
- end
2802
- public :at_return
2803
-
2804
- def select_tasks
2805
- selection = []
2806
- @rantfiles.each { |rf|
2807
- rf.tasks.each { |t|
2808
- selection << t if yield t
2809
- }
2810
- }
2811
- selection
2812
- end
2813
- public :select_tasks
2814
-
2815
- def load_rantfiles
2816
- unless @arg_rantfiles.empty?
2817
- @arg_rantfiles.each { |fn|
2818
- if test(?f, fn)
2819
- rf, is_new = rantfile_for_path(fn)
2820
- load_file rf if is_new
2821
- else
2822
- abort "No such file -- #{fn}"
2823
- end
2824
- }
2825
- return
2826
- end
2827
- return if have_any_task?
2828
- fn = rantfile_in_dir
2829
- if @opts[:cd_parent]
2830
- old_root = @rootdir
2831
- until fn or @rootdir == "/"
2832
- @rootdir = File.dirname(@rootdir)
2833
- fn = rantfile_in_dir(@rootdir)
2834
- end
2835
- if @rootdir != old_root and fn
2836
- Dir.chdir @rootdir
2837
- cmd_msg "(in #@rootdir)"
2838
- end
2839
- end
2840
- if fn
2841
- rf, is_new = rantfile_for_path(fn)
2842
- load_file rf if is_new
2843
- return
2844
- end
2845
- have_sub_rantfile = test(?f, Rant::SUB_RANTFILE)
2846
- if have_sub_rantfile || @opts[:look_up]
2847
- cur_dir = Dir.pwd
2848
- until cur_dir == "/"
2849
- cur_dir = File.dirname(cur_dir)
2850
- Dir.chdir cur_dir
2851
- fn = rantfile_in_dir
2852
- if fn
2853
- @initial_subdir = @rootdir.sub(
2854
- /^#{Regexp.escape cur_dir}\//, '')
2855
- @rootdir = cur_dir
2856
- cmd_msg "(root is #@rootdir, in #@initial_subdir)"
2857
- @last_build_subdir = @initial_subdir
2858
- rf, is_new = rantfile_for_path(fn)
2859
- load_file rf if is_new
2860
- goto_project_dir @initial_subdir
2861
- if have_sub_rantfile
2862
- rf, is_new = rantfile_for_path(
2863
- Rant::SUB_RANTFILE, false)
2864
- if is_new
2865
- @rantfiles.unshift rf
2866
- load_file rf
2867
- end
2868
- end
2869
- break
2870
- end
2871
- end
2872
- end
2873
- if @rantfiles.empty?
2874
- abort("No Rantfile found, looking for:",
2875
- Rant::RANTFILES.join(", "))
2876
- end
2877
- end
2878
-
2879
- def load_file(rantfile)
2880
- vmsg 1, "source #{rantfile}"
2881
- @context.instance_eval(File.read(rantfile), rantfile)
2882
- end
2883
- private :load_file
2884
-
2885
- def rantfile_in_dir(dir=nil)
2886
- ::Rant::RANTFILES.each { |rfn|
2887
- path = dir ? File.join(dir, rfn) : rfn
2888
- return path if test ?f, path
2889
- }
2890
- nil
2891
- end
2892
-
2893
- def process_args
2894
- old_argv = ARGV.dup
2895
- ARGV.replace(@args.dup)
2896
- cmd_opts = GetoptLong.new(*OPTIONS.collect { |lst| lst[0..-2] })
2897
- cmd_opts.quiet = true
2898
- cmd_opts.each { |opt, value|
2899
- case opt
2900
- when "--verbose": @opts[:verbose] += 1
2901
- when "--version"
2902
- puts "rant #{Rant::VERSION}"
2903
- raise Rant::RantDoneException
2904
- when "--help"
2905
- show_help
2906
- raise Rant::RantDoneException
2907
- when "--directory"
2908
- @rootdir = File.expand_path(value)
2909
- when "--rantfile"
2910
- @arg_rantfiles << value
2911
- when "--force-run"
2912
- @force_targets << value
2913
- when "--import"
2914
- import value
2915
- else
2916
- @opts[opt.sub(/^--/, '').tr('-', "_").to_sym] = true
2917
- end
2918
- }
2919
- rescue GetoptLong::Error => e
2920
- abort(e.message)
2921
- ensure
2922
- rem_args = ARGV.dup
2923
- ARGV.replace(old_argv)
2924
- rem_args.each { |ra|
2925
- if ra =~ /(^[^=]+)=([^=]+)$/
2926
- vmsg 2, "var: #$1=#$2"
2927
- @var[$1] = $2
2928
- else
2929
- @arg_targets << ra
2930
- end
2931
- }
2932
- end
2933
-
2934
- def prepare_task(targ, block, clr = caller[2])
2935
-
2936
- if targ.is_a? Hash
2937
- targ.reject! { |k, v| clr = v if k == :__caller__ }
2938
- end
2939
- ch = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr)
2940
-
2941
- name, pre = normalize_task_arg(targ, ch)
2942
-
2943
- file, is_new = rantfile_for_path(ch[:file])
2944
- nt = yield(name, pre, block)
2945
- nt.rantfile = file
2946
- nt.project_subdir = @current_subdir
2947
- nt.line_number = ch[:ln]
2948
- nt.description = @task_desc
2949
- @task_desc = nil
2950
- file.tasks << nt
2951
- hash_task nt
2952
- nt
2953
- end
2954
- public :prepare_task
2955
-
2956
- def hash_task(task)
2957
- n = task.full_name
2958
- et = @tasks[n]
2959
- case et
2960
- when nil
2961
- @tasks[n] = task
2962
- when Rant::Node
2963
- mt = [et, task]
2964
- @tasks[n] = mt
2965
- else # assuming list of tasks
2966
- et << task
2967
- end
2968
- end
2969
-
2970
- def normalize_task_arg(targ, ch)
2971
- name = nil
2972
- pre = []
2973
-
2974
- if targ.is_a? Hash
2975
- if targ.empty?
2976
- abort_at(ch, "Empty hash as task argument, " +
2977
- "task name required.")
2978
- end
2979
- if targ.size > 1
2980
- abort_at(ch, "Too many hash elements, " +
2981
- "should only be one.")
2982
- end
2983
- targ.each_pair { |k,v|
2984
- name = normalize_task_name(k, ch)
2985
- pre = v
2986
- }
2987
- unless ::Rant::FileList === pre
2988
- if pre.respond_to? :to_ary
2989
- pre = pre.to_ary.dup
2990
- pre.map! { |elem|
2991
- normalize_task_name(elem, ch)
2992
- }
2993
- else
2994
- pre = [normalize_task_name(pre, ch)]
2995
- end
2996
- end
2997
- else
2998
- name = normalize_task_name(targ, ch)
2999
- end
3000
-
3001
- [name, pre]
3002
- end
3003
- public :normalize_task_arg
3004
-
3005
- def normalize_task_name(arg, ch)
3006
- return arg if arg.is_a? String
3007
- if Symbol === arg
3008
- arg.to_s
3009
- elsif arg.respond_to? :to_str
3010
- arg.to_str
3011
- else
3012
- abort_at(ch, "Task name has to be a string or symbol.")
3013
- end
3014
- end
3015
-
3016
- def rantfile_for_path(path, register=true)
3017
- abs_path = File.expand_path(path)
3018
- file = @rantfiles.find { |rf| rf.path == abs_path }
3019
- if file
3020
- [file, false]
3021
- else
3022
- file = Rant::Rantfile.new abs_path
3023
- file.project_subdir = @current_subdir
3024
- @rantfiles << file if register
3025
- [file, true]
3026
- end
3027
- end
3028
-
3029
- def get_ch_from_backtrace(backtrace)
3030
- backtrace.each { |clr|
3031
- ch = ::Rant::Lib.parse_caller_elem(clr)
3032
- if ::Rant::Env.on_windows?
3033
- return ch if @rantfiles.any? { |rf|
3034
- rf.path.tr("\\", "/").sub(/^\w\:/, '') ==
3035
- ch[:file].tr("\\", "/").sub(/^\w\:/, '')
3036
- }
3037
- else
3038
- return ch if @rantfiles.any? { |rf|
3039
- rf.path == ch[:file]
3040
- }
3041
- end
3042
- }
3043
- nil
3044
- end
3045
-
3046
- def err_task_fail(e)
3047
- msg = []
3048
- t_msg = ["Task `#{e.tname}' fail."]
3049
- orig = e
3050
- loop { orig = orig.orig; break unless Rant::TaskFail === orig }
3051
- if orig && orig != e && !(Rant::RantAbortException === orig)
3052
- ch = get_ch_from_backtrace(orig.backtrace)
3053
- msg << pos_text(ch[:file], ch[:ln]) if ch
3054
- unless Rant::CommandError === orig && !@opts[:err_commands]
3055
- msg << orig.message
3056
- msg << orig.backtrace[0..4] unless ch
3057
- end
3058
- end
3059
- if e.msg && !e.msg.empty?
3060
- ch = get_ch_from_backtrace(e.backtrace)
3061
- t_msg.unshift(e.msg)
3062
- t_msg.unshift(pos_text(ch[:file], ch[:ln])) if ch
3063
- end
3064
- err_msg msg unless msg.empty?
3065
- err_msg t_msg
3066
- end
3067
- end # class Rant::RantApp
3068
-
3069
-
3070
-
3071
-
3072
- module Rant end
3073
- module Rant::C
3074
- module Include
3075
- def parse_includes(src)
3076
- if src.respond_to? :to_str
3077
- src = src.to_str
3078
- else
3079
- raise ArgumentError, "src has to be a string"
3080
- end
3081
- s_includes = []
3082
- l_includes = []
3083
- in_block_comment = false
3084
- prev_line = nil
3085
- src.each { |line|
3086
- line.chomp!
3087
- if block_start_i = line.index("/*")
3088
- c_start_i = line.index("//")
3089
- if !c_start_i || block_start_i < c_start_i
3090
- if block_end_i = line.index("*/")
3091
- if block_end_i > block_start_i
3092
- line[block_start_i..block_end_i+1] = ""
3093
- end
3094
- end
3095
- end
3096
- end
3097
- if prev_line
3098
- line = prev_line << line
3099
- prev_line = nil
3100
- end
3101
- if line =~ /\\$/
3102
- prev_line = line.chomp[0...line.length-1]
3103
- end
3104
- if in_block_comment
3105
- in_block_comment = false if line =~ %r|\*/|
3106
- next
3107
- end
3108
- case line
3109
- when /\s*#\s*include\s+"([^"]+)"/
3110
- l_includes << $1
3111
- when /\s*#\s*include\s+<([^>]+)>/
3112
- s_includes << $1
3113
- when %r|(?!//)[^/]*/\*|
3114
- in_block_comment = true
3115
- end
3116
- }
3117
- [s_includes, l_includes]
3118
- end
3119
- module_function :parse_includes
3120
- end # module Include
3121
- end # module Rant::C
3122
-
3123
- module Rant::Generators::C end
3124
- class Rant::Generators::C::Dependencies
3125
- def self.rant_gen(rac, ch, args, &block)
3126
- c_files, out_fn, include_pathes, opts = nil
3127
- if block
3128
- rac.warn_msg "C::Dependencies: ignoring block"
3129
- end
3130
- case args.size
3131
- when 0 # noop
3132
- when 1
3133
- farg = args.first
3134
- Hash === farg ? (opts = farg) : (out_fn = farg)
3135
- when 2
3136
- out_fn = args.first
3137
- opts = args[1]
3138
- else
3139
- rac.abort_at(ch,
3140
- "C::Dependencies takes one or two arguments.")
3141
- end
3142
- correct_case = false
3143
- if opts
3144
- if opts.respond_to? :to_hash
3145
- opts = opts.to_hash
3146
- else
3147
- rac.abort_at(ch,
3148
- "C::Dependencies: second argument has to be a hash.")
3149
- end
3150
- opts.each { |k, v|
3151
- case k
3152
- when :sources
3153
- c_files = v
3154
- when :search, :search_pathes, :include_pathes
3155
- include_pathes =
3156
- if v.respond_to? :to_str
3157
- [v.to_str]
3158
- else
3159
- v
3160
- end
3161
- when :correct_case
3162
- correct_case = !!v
3163
- else
3164
- rac.abort_at(ch,
3165
- "C::Dependencies: no such option -- #{k}")
3166
- end
3167
- }
3168
- end
3169
- out_fn ||= "c_dependencies"
3170
- c_files ||= rac.cx.sys["**/*.{c,cpp,cc,h,hpp}"]
3171
- include_pathes ||= ["."]
3172
- if out_fn.respond_to? :to_str
3173
- out_fn = out_fn.to_str
3174
- else
3175
- rac.abort_at(ch, "filename has to be a string")
3176
- end
3177
- unless ::Rant::FileList === c_files
3178
- if c_files.respond_to? :to_ary
3179
- c_files = c_files.to_ary
3180
- else
3181
- rac.abort_at(ch, "sources has to be a list of files")
3182
- end
3183
- end
3184
- unless ::Rant::FileList === include_pathes
3185
- if include_pathes.respond_to? :to_ary
3186
- include_pathes = include_pathes.to_ary
3187
- else
3188
- rac.abort_at(ch,
3189
- "search has to be a list of directories")
3190
- end
3191
- end
3192
- rac.cx.file({:__caller__ => ch, out_fn => c_files}) do |t|
3193
- tmp_rac = ::Rant::RantApp.new
3194
- depfile_ts = Time.at(0)
3195
- if File.exist? t.name
3196
- tmp_rac.source(t.name)
3197
- depfile_ts = File.mtime(t.name)
3198
- end
3199
- rf_str = ""
3200
- c_files.each { |cf|
3201
- f_task = nil
3202
- unless test(?f, cf)
3203
- rac.warn_msg "#{t.name}: no such file -- #{cf}"
3204
- next
3205
- end
3206
- f_task = tmp_rac.tasks[cf.to_str]
3207
- deps = f_task ? f_task.prerequisites : nil
3208
- if !deps or File.mtime(cf) > depfile_ts
3209
- rac.cmd_msg "scanning #{cf}"
3210
- std_includes, local_includes =
3211
- ::Rant::C::Include.parse_includes(File.read(cf))
3212
- deps = []
3213
- (std_includes + local_includes).each { |fn|
3214
- path = existing_file(
3215
- include_pathes, fn, correct_case)
3216
- deps << path if path
3217
- }
3218
- end
3219
- rf_str << file_deps(cf, deps) << "\n"
3220
- }
3221
- rac.vmsg 1, "writing C source dependencies to #{t.name}"
3222
- open(t.name, "w") { |f|
3223
- f.puts
3224
- f.puts "# #{t.name}"
3225
- f.puts "# C source dependencies generated by Rant #{Rant::VERSION}"
3226
- f.puts "# WARNING: Modifications to this file will get lost!"
3227
- f.puts
3228
- f.write rf_str
3229
- }
3230
- end
3231
- end
3232
- def self.existing_file(dirs, fn, correct_case)
3233
- dirs.each { |dir|
3234
- path = dir == "." ? fn : File.join(dir, fn)
3235
- if test ?f, path
3236
- return path unless correct_case
3237
- found_file = File.basename(fn)
3238
- found_in_dir = File.dirname(File.join(dir, fn))
3239
- Dir.entries(found_in_dir).each { |dentry|
3240
- return File.join(found_in_dir, dentry) if dentry.downcase == found_file.downcase
3241
- }
3242
- end
3243
- }
3244
- nil
3245
- end
3246
- def self.file_deps(target, deps)
3247
- s = "gen SourceNode, #{target.to_str.dump} => "
3248
- s << "[#{ deps.map{ |fn| fn.to_str.dump }.join(', ')}]"
3249
- end
3250
- end # class Rant::Generators::C::Dependencies
3251
-
3252
-
3253
-
3254
- class Rant::Generators::DirectedRule
3255
- def self.rant_gen(rac, ch, args, &block)
3256
- unless args.size == 1
3257
- rac.abort_at(ch, "DirectedRule takes one arguments.")
3258
- end
3259
- h = args.first
3260
- if h.respond_to? :to_hash
3261
- h = h.to_hash
3262
- else
3263
- rac.abort_at(ch, "Argument has to be a hash.")
3264
- end
3265
- ts_h, dir_h = nil, nil
3266
- h.each { |k, v| v.respond_to?(:to_ary) ?
3267
- dir_h = { k => v } :
3268
- ts_h = { k => v }
3269
- }
3270
- unless dir_h
3271
- rac.abort_at(ch,
3272
- "Source directory argument has to be a list.")
3273
- end
3274
- target, source = nil, nil
3275
- ts_h.each { |target, source| }
3276
- target_dir, source_dirs = nil, nil
3277
- dir_h.each { |target_dir, source_dirs| }
3278
- if target_dir.respond_to? :to_str
3279
- target_dir = target_dir.to_str
3280
- else
3281
- rac.abort_at(ch, "String required as target directory.")
3282
- end
3283
- if source_dirs.respond_to? :to_ary
3284
- source_dirs = source_dirs.to_ary
3285
- elsif source_dirs.respond_to? :to_str
3286
- source_dirs = [source_dirs.to_str]
3287
- else
3288
- rac.abort_at(ch,
3289
- "List of strings or string required for source directories.")
3290
- end
3291
- target = ".#{target}" if Symbol === target
3292
- source = ".#{source}" if Symbol === source
3293
- if target.respond_to? :to_str
3294
- target = target.to_str
3295
- else
3296
- rac.abort_at(ch, "target has to be a string")
3297
- end
3298
- if source.respond_to? :to_str
3299
- source = source.to_str
3300
- else
3301
- rac.abort_at(ch, "source has to be a string or symbol")
3302
- end
3303
- blk = self.new(rac, ch, target_dir, source_dirs,
3304
- target, source, &block)
3305
- blk.define_hook
3306
- blk
3307
- end
3308
- def initialize(rac, ch, target_dir, source_dirs,
3309
- target, source, &block)
3310
- @rac = rac
3311
- @ch = ch
3312
- @source_dirs = source_dirs
3313
- @target_dir = target_dir
3314
- @target = target.sub(/^\./, '')
3315
- @target_rx = /#{Regexp.escape(target)}$/
3316
- @source = source.sub(/^\./, '')
3317
- @esc_target_dir = Regexp.escape(target_dir)
3318
- @block = block
3319
- end
3320
- def [](name, rel_project_dir)
3321
- if name =~ /^#@esc_target_dir\// && name =~ @target_rx
3322
- fn = File.basename(name)
3323
- src_fn = fn.sub_ext(@source)
3324
- src = nil
3325
- @source_dirs.each { |d|
3326
- path = File.join(d, src_fn)
3327
- (src = path) && break if test(?e, path)
3328
- }
3329
- if src
3330
- [@rac.prepare_task({name => src}, @block, @ch) { |name,pre,blk|
3331
- @rac.node_factory.new_auto_subfile(@rac, name, pre, blk)
3332
- }]
3333
- else
3334
- nil
3335
- end
3336
- else
3337
- nil
3338
- end
3339
- end
3340
- def define_hook
3341
- @rac.resolve_hooks << self
3342
- end
3343
- def each_target(&block)
3344
- @rac.cx.sys["#@target_dir/*"].each { |entry|
3345
- yield entry if entry =~ @target_rx
3346
- }
3347
- end
3348
- def candidates
3349
- sources.map { |src|
3350
- File.join(@target_dir, File.basename(src).sub_ext(@target))
3351
- }
3352
- end
3353
- def sources
3354
- cl = []
3355
- @source_dirs.each { |dir|
3356
- cl.concat(@rac.cx.sys["#{dir}/*.#@source"])
3357
- }
3358
- cl
3359
- end
3360
- end # class Rant::Generators::DirectedRule
3361
-
3362
-
3363
-
3364
- class Rant::Generators::Clean
3365
- def self.rant_gen(rac, ch, args, &block)
3366
- if args.size > 1
3367
- rac.abort_at(ch, "Clean doesn't take more than one argument.")
3368
- end
3369
- tname = args.first || "clean"
3370
-
3371
- case rac.var[tname]
3372
- when nil
3373
- rac.var[tname] = Rant::MultiFileList.new(rac)
3374
- when Rant::RacFileList
3375
- ml = Rant::MultiFileList.new(rac)
3376
- rac.var[tname] = ml.add(rac.var[tname])
3377
- when Rant::MultiFileList
3378
- else
3379
- rac.abort_at(ch,
3380
- "var `#{tname}' already exists.",
3381
- "Clean uses var with the same name as the task name.")
3382
- end
3383
-
3384
- rac.task :__caller__ => ch, tname => [] do |t|
3385
- rac.var[tname].each_entry { |entry|
3386
- if test ?e, entry
3387
- if test ?f, entry
3388
- rac.cx.sys.rm_f entry
3389
- else
3390
- rac.cx.sys.rm_rf entry
3391
- end
3392
- end
3393
- }
3394
- end
3395
- end
3396
- end # class Rant::Generators::Clean
3397
-
3398
-
3399
-
3400
-
3401
- module Rant; end
3402
- module Rant::Archive; end
3403
- module Rant::Archive::Minitar; end
3404
-
3405
- class Rant::Archive::Minitar::PosixHeader
3406
- FIELDS = %w(name mode uid gid size mtime checksum typeflag linkname) +
3407
- %w(magic version uname gname devmajor devminor prefix)
3408
-
3409
- FIELDS.each { |field| attr_reader field.intern }
3410
-
3411
- HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155"
3412
- HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155"
3413
-
3414
- def self.new_from_stream(stream)
3415
- data = stream.read(512)
3416
- fields = data.unpack(HEADER_UNPACK_FORMAT)
3417
- name = fields.shift
3418
- mode = fields.shift.oct
3419
- uid = fields.shift.oct
3420
- gid = fields.shift.oct
3421
- size = fields.shift.oct
3422
- mtime = fields.shift.oct
3423
- checksum = fields.shift.oct
3424
- typeflag = fields.shift
3425
- linkname = fields.shift
3426
- magic = fields.shift
3427
- version = fields.shift.oct
3428
- uname = fields.shift
3429
- gname = fields.shift
3430
- devmajor = fields.shift.oct
3431
- devminor = fields.shift.oct
3432
- prefix = fields.shift
3433
-
3434
- empty = (data == "\0" * 512)
3435
-
3436
- new(:name => name, :mode => mode, :uid => uid, :gid => gid,
3437
- :size => size, :mtime => mtime, :checksum => checksum,
3438
- :typeflag => typeflag, :magic => magic, :version => version,
3439
- :uname => uname, :gname => gname, :devmajor => devmajor,
3440
- :devminor => devminor, :prefix => prefix, :empty => empty)
3441
- end
3442
-
3443
- def initialize(vals)
3444
- unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
3445
- raise ArgumentError
3446
- end
3447
-
3448
- vals[:mtime] ||= 0
3449
- vals[:checksum] ||= ""
3450
- vals[:typeflag] ||= "0"
3451
- vals[:magic] ||= "ustar"
3452
- vals[:version] ||= "00"
3453
-
3454
- FIELDS.each do |field|
3455
- instance_variable_set("@#{field}", vals[field.intern])
3456
- end
3457
- @empty = vals[:empty]
3458
- end
3459
-
3460
- def empty?
3461
- @empty
3462
- end
3463
-
3464
- def to_s
3465
- update_checksum
3466
- header(@checksum)
3467
- end
3468
-
3469
- def update_checksum
3470
- hh = header(" " * 8)
3471
- @checksum = oct(calculate_checksum(hh), 6)
3472
- end
3473
-
3474
- private
3475
- def oct(num, len)
3476
- if num.nil?
3477
- "\0" * (len + 1)
3478
- else
3479
- "%0#{len}o" % num
3480
- end
3481
- end
3482
-
3483
- def calculate_checksum(hdr)
3484
- hdr.unpack("C*").inject { |aa, bb| aa + bb }
3485
- end
3486
-
3487
- def header(chksum)
3488
- arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
3489
- oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
3490
- uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
3491
- str = arr.pack(HEADER_PACK_FORMAT)
3492
- str + "\0" * ((512 - str.size) % 512)
3493
- end
3494
- end
3495
-
3496
- require 'fileutils'
3497
- require 'find'
3498
-
3499
- module Rant::Archive::Minitar
3500
- VERSION = "0.5.1"
3501
-
3502
- class NonSeekableStream < StandardError; end
3503
- class BlockRequired < ArgumentError; end
3504
- class ClosedStream < StandardError; end
3505
- class FileNameTooLong < StandardError; end
3506
- class UnexpectedEOF < StandardError; end
3507
-
3508
- class Writer
3509
- class RestrictedStream
3510
- def initialize(anIO)
3511
- @io = anIO
3512
- end
3513
-
3514
- def write(data)
3515
- @io.write(data)
3516
- end
3517
- end
3518
-
3519
- class BoundedStream < Rant::Archive::Minitar::Writer::RestrictedStream
3520
- class FileOverflow < RuntimeError; end
3521
-
3522
- attr_reader :limit
3523
- attr_reader :written
3524
-
3525
- def initialize(io, limit)
3526
- @io = io
3527
- @limit = limit
3528
- @written = 0
3529
- end
3530
-
3531
- def write(data)
3532
- raise FileOverflow if (data.size + @written) > @limit
3533
- @io.write(data)
3534
- @written += data.size
3535
- data.size
3536
- end
3537
- end
3538
-
3539
- def self.open(anIO)
3540
- writer = Writer.new(anIO)
3541
-
3542
- return writer unless block_given?
3543
-
3544
- begin
3545
- res = yield writer
3546
- ensure
3547
- writer.close
3548
- end
3549
-
3550
- res
3551
- end
3552
-
3553
- def initialize(anIO)
3554
- @io = anIO
3555
- @closed = false
3556
- end
3557
-
3558
- def add_file_simple(name, opts = {}) # :yields BoundedStream:
3559
- raise Rant::Archive::Minitar::BlockRequired unless block_given?
3560
- raise Rant::Archive::Minitar::ClosedStream if @closed
3561
-
3562
- name, prefix = split_name(name)
3563
-
3564
- header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
3565
- :size => opts[:size], :gid => opts[:gid], :uid => opts[:uid],
3566
- :prefix => prefix }
3567
- header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3568
- @io.write(header)
3569
-
3570
- os = BoundedStream.new(@io, opts[:size])
3571
- yield os
3572
-
3573
- min_padding = opts[:size] - os.written
3574
- @io.write("\0" * min_padding)
3575
- remainder = (512 - (opts[:size] % 512)) % 512
3576
- @io.write("\0" * remainder)
3577
- end
3578
-
3579
- def add_file(name, opts = {}) # :yields RestrictedStream, +opts+:
3580
- raise Rant::Archive::Minitar::BlockRequired unless block_given?
3581
- raise Rant::Archive::Minitar::ClosedStream if @closed
3582
- raise Rant::Archive::Minitar::NonSeekableStream unless @io.respond_to?(:pos=)
3583
-
3584
- name, prefix = split_name(name)
3585
- init_pos = @io.pos
3586
- @io.write("\0" * 512) # placeholder for the header
3587
-
3588
- yield RestrictedStream.new(@io), opts
3589
-
3590
- size = @io.pos - (init_pos + 512)
3591
- remainder = (512 - (size % 512)) % 512
3592
- @io.write("\0" * remainder)
3593
-
3594
- final_pos = @io.pos
3595
- @io.pos = init_pos
3596
-
3597
- header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
3598
- :size => size, :gid => opts[:gid], :uid => opts[:uid],
3599
- :prefix => prefix }
3600
- header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3601
- @io.write(header)
3602
- @io.pos = final_pos
3603
- end
3604
-
3605
- def mkdir(name, opts = {})
3606
- raise ClosedStream if @closed
3607
- name, prefix = split_name(name)
3608
- header = { :name => name, :mode => opts[:mode], :typeflag => "5",
3609
- :size => 0, :gid => opts[:gid], :uid => opts[:uid],
3610
- :mtime => opts[:mtime], :prefix => prefix }
3611
- header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3612
- @io.write(header)
3613
- nil
3614
- end
3615
-
3616
- def flush
3617
- raise ClosedStream if @closed
3618
- @io.flush if @io.respond_to?(:flush)
3619
- end
3620
-
3621
- def close
3622
- return if @closed
3623
- @io.write("\0" * 1024)
3624
- @closed = true
3625
- end
3626
-
3627
- private
3628
- def split_name(name)
3629
- raise FileNameTooLong if name.size > 256
3630
- if name.size <= 100
3631
- prefix = ""
3632
- else
3633
- parts = name.split(/\//)
3634
- newname = parts.pop
3635
-
3636
- nxt = ""
3637
-
3638
- loop do
3639
- nxt = parts.pop
3640
- break if newname.size + 1 + nxt.size > 100
3641
- newname = "#{nxt}/#{newname}"
3642
- end
3643
-
3644
- prefix = (parts + [nxt]).join("/")
3645
-
3646
- name = newname
3647
-
3648
- raise FileNameTooLong if name.size > 100 || prefix.size > 155
3649
- end
3650
- return name, prefix
3651
- end
3652
- end
3653
-
3654
- class Reader
3655
- module InvalidEntryStream
3656
- def read(len = nil); raise ClosedStream; end
3657
- def getc; raise ClosedStream; end
3658
- def rewind; raise ClosedStream; end
3659
- end
3660
-
3661
- class EntryStream
3662
- Rant::Archive::Minitar::PosixHeader::FIELDS.each do |field|
3663
- attr_reader field.intern
3664
- end
3665
-
3666
- def initialize(header, anIO)
3667
- @io = anIO
3668
- @name = header.name
3669
- @mode = header.mode
3670
- @uid = header.uid
3671
- @gid = header.gid
3672
- @size = header.size
3673
- @mtime = header.mtime
3674
- @checksum = header.checksum
3675
- @typeflag = header.typeflag
3676
- @linkname = header.linkname
3677
- @magic = header.magic
3678
- @version = header.version
3679
- @uname = header.uname
3680
- @gname = header.gname
3681
- @devmajor = header.devmajor
3682
- @devminor = header.devminor
3683
- @prefix = header.prefix
3684
- @read = 0
3685
- @orig_pos = @io.pos
3686
- end
3687
-
3688
- def read(len = nil)
3689
- return nil if @read >= @size
3690
- len ||= @size - @read
3691
- max_read = [len, @size - @read].min
3692
- ret = @io.read(max_read)
3693
- @read += ret.size
3694
- ret
3695
- end
3696
-
3697
- def getc
3698
- return nil if @read >= @size
3699
- ret = @io.getc
3700
- @read += 1 if ret
3701
- ret
3702
- end
3703
-
3704
- def directory?
3705
- @typeflag == "5"
3706
- end
3707
- alias_method :directory, :directory?
3708
-
3709
- def file?
3710
- @typeflag == "0"
3711
- end
3712
- alias_method :file, :file?
3713
-
3714
- def eof?
3715
- @read >= @size
3716
- end
3717
-
3718
- def pos
3719
- @read
3720
- end
3721
-
3722
- def rewind
3723
- raise NonSeekableStream unless @io.respond_to?(:pos=)
3724
- @io.pos = @orig_pos
3725
- @read = 0
3726
- end
3727
-
3728
- def bytes_read
3729
- @read
3730
- end
3731
-
3732
- def full_name
3733
- if @prefix != ""
3734
- File.join(@prefix, @name)
3735
- else
3736
- @name
3737
- end
3738
- end
3739
-
3740
- def close
3741
- invalidate
3742
- end
3743
-
3744
- private
3745
- def invalidate
3746
- extend InvalidEntryStream
3747
- end
3748
- end
3749
-
3750
- def self.open(anIO)
3751
- reader = Reader.new(anIO)
3752
-
3753
- return reader unless block_given?
3754
-
3755
- begin
3756
- res = yield reader
3757
- ensure
3758
- reader.close
3759
- end
3760
-
3761
- res
3762
- end
3763
-
3764
- def initialize(anIO)
3765
- @io = anIO
3766
- @init_pos = anIO.pos
3767
- end
3768
-
3769
- def each(&block)
3770
- each_entry(&block)
3771
- end
3772
-
3773
- def rewind
3774
- if @init_pos == 0
3775
- raise NonSeekableStream unless @io.respond_to?(:rewind)
3776
- @io.rewind
3777
- else
3778
- raise NonSeekableStream unless @io.respond_to?(:pos=)
3779
- @io.pos = @init_pos
3780
- end
3781
- end
3782
-
3783
- def each_entry
3784
- loop do
3785
- return if @io.eof?
3786
-
3787
- header = Rant::Archive::Minitar::PosixHeader.new_from_stream(@io)
3788
- return if header.empty?
3789
-
3790
- entry = EntryStream.new(header, @io)
3791
- size = entry.size
3792
-
3793
- yield entry
3794
-
3795
- skip = (512 - (size % 512)) % 512
3796
-
3797
- if @io.respond_to?(:seek)
3798
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
3799
- else
3800
- pending = size - entry.bytes_read
3801
- while pending > 0
3802
- bread = @io.read([pending, 4096].min).size
3803
- raise UnexpectedEOF if @io.eof?
3804
- pending -= bread
3805
- end
3806
- end
3807
- @io.read(skip) # discard trailing zeros
3808
- entry.close
3809
- end
3810
- end
3811
-
3812
- def close
3813
- end
3814
- end
3815
-
3816
- class Input
3817
- include Enumerable
3818
-
3819
- def self.open(input)
3820
- stream = Input.new(input)
3821
- return stream unless block_given?
3822
-
3823
- begin
3824
- res = yield stream
3825
- ensure
3826
- stream.close
3827
- end
3828
-
3829
- res
3830
- end
3831
-
3832
- def initialize(input)
3833
- if input.respond_to?(:read)
3834
- @io = input
3835
- else
3836
- @io = open(input, "rb")
3837
- end
3838
- @tarreader = Rant::Archive::Minitar::Reader.new(@io)
3839
- end
3840
-
3841
- def each(&block)
3842
- @tarreader.each { |entry| yield entry }
3843
- ensure
3844
- @tarreader.rewind
3845
- end
3846
-
3847
- def extract_entry(destdir, entry) # :yields action, name, stats:
3848
- stats = {
3849
- :current => 0,
3850
- :currinc => 0,
3851
- :entry => entry
3852
- }
3853
-
3854
- if entry.directory?
3855
- dest = File.join(destdir, entry.full_name)
3856
-
3857
- yield :dir, entry.full_name, stats if block_given?
3858
-
3859
- if Rant::Archive::Minitar.dir?(dest)
3860
- begin
3861
- FileUtils.chmod(entry.mode, dest)
3862
- rescue Exception
3863
- nil
3864
- end
3865
- else
3866
- FileUtils.mkdir_p(dest, :mode => entry.mode)
3867
- FileUtils.chmod(entry.mode, dest)
3868
- end
3869
-
3870
- fsync_dir(dest)
3871
- fsync_dir(File.join(dest, ".."))
3872
- return
3873
- else # it's a file
3874
- destdir = File.join(destdir, File.dirname(entry.full_name))
3875
- FileUtils.mkdir_p(destdir, :mode => 0755)
3876
-
3877
- destfile = File.join(destdir, File.basename(entry.full_name))
3878
- FileUtils.chmod(0600, destfile) rescue nil # Errno::ENOENT
3879
-
3880
- yield :file_start, entry.full_name, stats if block_given?
3881
-
3882
- File.open(destfile, "wb", entry.mode) do |os|
3883
- loop do
3884
- data = entry.read(4096)
3885
- break unless data
3886
-
3887
- stats[:currinc] = os.write(data)
3888
- stats[:current] += stats[:currinc]
3889
-
3890
- yield :file_progress, entry.full_name, stats if block_given?
3891
- end
3892
- os.fsync
3893
- end
3894
-
3895
- FileUtils.chmod(entry.mode, destfile)
3896
- fsync_dir(File.dirname(destfile))
3897
- fsync_dir(File.join(File.dirname(destfile), ".."))
3898
-
3899
- yield :file_done, entry.full_name, stats if block_given?
3900
- end
3901
- end
3902
-
3903
- def tar
3904
- @tarreader
3905
- end
3906
-
3907
- def close
3908
- @io.close
3909
- @tarreader.close
3910
- end
3911
-
3912
- private
3913
- def fsync_dir(dirname)
3914
- dir = open(dirname, 'rb')
3915
- dir.fsync
3916
- rescue # ignore IOError if it's an unpatched (old) Ruby
3917
- nil
3918
- ensure
3919
- dir.close if dir rescue nil
3920
- end
3921
- end
3922
-
3923
- class Output
3924
- def self.open(output)
3925
- stream = Output.new(output)
3926
- return stream unless block_given?
3927
-
3928
- begin
3929
- res = yield stream
3930
- ensure
3931
- stream.close
3932
- end
3933
-
3934
- res
3935
- end
3936
-
3937
- def initialize(output)
3938
- if output.respond_to?(:write)
3939
- @io = output
3940
- else
3941
- @io = ::File.open(output, "wb")
3942
- end
3943
- @tarwriter = Rant::Archive::Minitar::Writer.new(@io)
3944
- end
3945
-
3946
- def tar
3947
- @tarwriter
3948
- end
3949
-
3950
- def close
3951
- @tarwriter.close
3952
- @io.close
3953
- end
3954
- end
3955
-
3956
- class << self
3957
- def dir?(path)
3958
- File.directory?((path[-1] == ?/) ? path : "#{path}/")
3959
- end
3960
-
3961
- def open(dest, mode = "r", &block)
3962
- case mode
3963
- when "r"
3964
- Input.open(dest, &block)
3965
- when "w"
3966
- Output.open(dest, &block)
3967
- else
3968
- raise "Unknown open mode for Rant::Archive::Minitar.open."
3969
- end
3970
- end
3971
-
3972
- def pack_file(entry, outputter) #:yields action, name, stats:
3973
- outputter = outputter.tar if outputter.kind_of?(Rant::Archive::Minitar::Output)
3974
-
3975
- stats = {}
3976
-
3977
- if entry.kind_of?(Hash)
3978
- name = entry[:name]
3979
-
3980
- entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
3981
- else
3982
- name = entry
3983
- end
3984
-
3985
- name = name.sub(%r{\./}, '')
3986
- stat = File.stat(name)
3987
- stats[:mode] ||= stat.mode
3988
- stats[:mtime] ||= stat.mtime
3989
- stats[:size] = stat.size
3990
-
3991
- if RUBY_PLATFORM =~ /win32/
3992
- stats[:uid] = nil
3993
- stats[:gid] = nil
3994
- else
3995
- stats[:uid] ||= stat.uid
3996
- stats[:gid] ||= stat.gid
3997
- end
3998
-
3999
- case
4000
- when File.file?(name)
4001
- outputter.add_file_simple(name, stats) do |os|
4002
- stats[:current] = 0
4003
- yield :file_start, name, stats if block_given?
4004
- File.open(name, "rb") do |ff|
4005
- until ff.eof?
4006
- stats[:currinc] = os.write(ff.read(4096))
4007
- stats[:current] += stats[:currinc]
4008
- yield :file_progress, name, stats if block_given?
4009
- end
4010
- end
4011
- yield :file_done, name, stats if block_given?
4012
- end
4013
- when dir?(name)
4014
- yield :dir, name, stats if block_given?
4015
- outputter.mkdir(name, stats)
4016
- else
4017
- raise "Don't yet know how to pack this type of file."
4018
- end
4019
- end
4020
-
4021
- def pack(src, dest, recurse_dirs = true, &block)
4022
- Output.open(dest) do |outp|
4023
- if src.kind_of?(Array)
4024
- src.each do |entry|
4025
- pack_file(entry, outp, &block)
4026
- if dir?(entry) and recurse_dirs
4027
- Dir["#{entry}/**/**"].each do |ee|
4028
- pack_file(ee, outp, &block)
4029
- end
4030
- end
4031
- end
4032
- else
4033
- Find.find(src) do |entry|
4034
- pack_file(entry, outp, &block)
4035
- end
4036
- end
4037
- end
4038
- end
4039
-
4040
- def unpack(src, dest, files = [], &block)
4041
- Input.open(src) do |inp|
4042
- if File.exist?(dest) and (not dir?(dest))
4043
- raise "Can't unpack to a non-directory."
4044
- elsif not File.exist?(dest)
4045
- FileUtils.mkdir_p(dest)
4046
- end
4047
-
4048
- inp.each do |entry|
4049
- if files.empty? or files.include?(entry.full_name)
4050
- inp.extract_entry(dest, entry, &block)
4051
- end
4052
- end
4053
- end
4054
- end
4055
- end
4056
- end
4057
-
4058
- module Rant
4059
- module Sys
4060
- def unpack_tgz(archive, opts={})
4061
- output_dir = opts[:to] || opts[:in] || "."
4062
- mkpath output_dir unless test ?d, output_dir
4063
- if Env.have_tar?
4064
- sh "tar", "-xzf", archive, "-C", output_dir
4065
- else
4066
- minitar_unpack(archive, output_dir)
4067
- end
4068
- nil
4069
- end
4070
- private
4071
- def minitar_tgz(fn, files, opts)
4072
- require 'zlib'
4073
- fu_output_message "minitar #{fn}"
4074
- files = files.to_ary if files.respond_to? :to_ary
4075
- tgz = Zlib::GzipWriter.new(File.open(fn, 'wb'))
4076
- Rant::Archive::Minitar.pack(files, tgz, opts[:recurse])
4077
- nil
4078
- end
4079
- def minitar_unpack(archive, output_dir)
4080
- fu_output_message "unpacking #{archive} in #{output_dir}"
4081
- require 'zlib'
4082
- tgz = Zlib::GzipReader.new(File.open(archive, 'rb'))
4083
- Archive::Minitar.unpack(tgz, output_dir)
4084
- end
4085
- end # module Sys
4086
- end # module Rant
4087
-
4088
- $".concat(['rant/rantlib.rb', 'rant/init.rb', 'rant/rantvar.rb', 'rant/rantsys.rb', 'rant/import/filelist/core.rb', 'rant/node.rb', 'rant/import/nodes/default.rb', 'rant/coregen.rb', 'rant/import/c/dependencies.rb', 'rant/c/include.rb', 'rant/import/directedrule.rb', 'rant/import/clean.rb', 'rant/import/sys/tgz.rb', 'rant/archive/minitar.rb'])
4089
- Rant::CODE_IMPORTS.concat %w(c/dependencies directedrule nodes/default clean sys/tgz
4090
- )
4091
-
4092
- # Catch a `require "rant"', sad...
4093
- alias require_backup_by_rant require
4094
- def require libf
4095
- if libf == "rant"
4096
- # TODO: needs rework! look at lib/rant.rb
4097
- self.class.instance_eval { include Rant }
4098
- else
4099
- begin
4100
- require_backup_by_rant libf
4101
- rescue
4102
- raise $!, caller
4103
- end
4104
- end
4105
- end
4106
-
4107
- exit Rant.run