trema 0.3.21 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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