rubu 0.0.1

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.
@@ -0,0 +1,114 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.7.6
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!top-level-namespace.html";
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Rubu.html" title="Rubu (module)">Rubu</a></span>
89
+
90
+
91
+
92
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="Array.html" title="Array (class)">Array</a></span>
93
+
94
+
95
+ </p>
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+ </div>
106
+
107
+ <div id="footer">
108
+ Generated on Fri Jun 29 10:04:07 2018 by
109
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
110
+ 0.8.7.6 (ruby-2.3.3).
111
+ </div>
112
+
113
+ </body>
114
+ </html>
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ IO.write( 'build/world.c', "\
4
+ #include <stdio.h>
5
+
6
+ void print_world( void )
7
+ {
8
+ printf( \"World\" );
9
+ }"
10
+ )
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'como'
4
+ include Como
5
+ require 'rubu'
6
+ include Rubu
7
+
8
+ require 'fileutils'
9
+
10
+
11
+ Spec.command( 'rubu_example', 'Tero Isannainen', '2018',
12
+ [
13
+ [ :opt_multi, 'conf', '-c', "Configuration option." ],
14
+ [ :switch, 'verbose', '-v', "Verbose." ],
15
+ [ :switch, 'serial', '-s', "Serial execution." ],
16
+ [ :default, nil, nil, "Flow(s) to execute." ],
17
+ ] )
18
+
19
+
20
+
21
+ # ------------------------------------------------------------
22
+ # Build specific definitions:
23
+
24
+ # Set flow related options.
25
+ Var[ :bin_dir ] = 'bin'
26
+ Var[ :source_dir ] = 'src'
27
+ Var[ :build_dir ] = 'build'
28
+ Var[ :exe_name ] = 'build/hello'
29
+
30
+ # Default for :fast option.
31
+ Var[ :fast ] = false
32
+
33
+ # Change Rubu to verbose mode.
34
+ if Opt['verbose'].given
35
+ Order[ :verbose ] = true
36
+ end
37
+
38
+ # Read setup files and apply Como command line arguments to Var space.
39
+ Flow.setup( :como => 'conf' )
40
+
41
+
42
+
43
+ # ------------------------------------------------------------
44
+ # Build Commands:
45
+
46
+ # Code generator with generated file dependency.
47
+ class GenWorld < MarkBuild
48
+ def build
49
+ shrun "bin/gen_world"
50
+ end
51
+ end
52
+
53
+
54
+ # Typical C compilation with GCC, from source to object.
55
+ class GccCompileFile < DateBuild
56
+
57
+ def setup
58
+ @cmd = "gcc -Wall -I #{Var[:source_dir]} -c #{source.rpath} -o #{target.rpath}"
59
+
60
+ # Compile with "-O2" if :fast is set.
61
+ if Var[ :fast ]
62
+ @cmd += " -O2"
63
+ else
64
+ @cmd += " -g"
65
+ end
66
+ end
67
+
68
+ def build
69
+ shrun @cmd
70
+ end
71
+ end
72
+
73
+
74
+ # GCC linking.
75
+ class GccLinkExe < DateBuild
76
+
77
+ def setup
78
+ @cmd = "gcc #{sources.rpath} -o #{target.rpath}"
79
+ end
80
+
81
+ def build
82
+ shrun @cmd
83
+ end
84
+ end
85
+
86
+
87
+ # Cleaning.
88
+ class CleanObjects < Build
89
+
90
+ def build
91
+ # Multiple jobs are grouped with walk (serial execution).
92
+ walk do
93
+ shdef "rm -f #{Info[ :gen_files ].rpath}"
94
+ shdef "rm -f #{Var[ :build_dir ]}/*.o"
95
+
96
+ # Use Ruby for deleting the exe as an example.
97
+ rbdef "rm -f #{Var[ :exe_name ]}" do
98
+ FileUtils.rm_f Var[ :exe_name ]
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+
105
+
106
+ # ------------------------------------------------------------
107
+ # Collect sources and create targets:
108
+
109
+ # Generator file and the target.
110
+ gen_file = Mark.path( "#{Var[:bin_dir]}/gen_world" )
111
+ gen_cee_file = Mark.path( "#{Var[:build_dir]}/world.c" )
112
+
113
+ # Collect "normal" C files.
114
+ cee_files = Mark.glob( "#{Var[:source_dir]}/*.c" )
115
+
116
+ # Create list of all C files.
117
+ all_cee_files = [ gen_cee_file ] + cee_files
118
+
119
+ # Convert C files to corresponding Object files.
120
+ obj_files = all_cee_files.peer( Var[ :build_dir ], '.o' )
121
+
122
+ # Specify executable file.
123
+ exe_file = Mark.path( Var[ :exe_name ] )
124
+
125
+
126
+ # Generate execution time information to Info. Used by CleanObjects.
127
+ Info[ :gen_files ] = [ gen_cee_file ]
128
+
129
+
130
+
131
+ # ------------------------------------------------------------
132
+ # Define flows:
133
+
134
+ # Serial flow for code generation.
135
+ Walk.form 'gen-cee' do
136
+ # Create GenWorld build and register it to 'gen-cee'.
137
+ GenWorld.use( gen_file, gen_cee_file )
138
+ end
139
+
140
+ # Parallel compilation of all C files.
141
+ Fork.form 'compile-cee' do
142
+ # Create set of GCC builds by joining 'all_cee_files' and
143
+ # 'obj_files' in pairs.
144
+ GccCompileFile.usezip( all_cee_files, obj_files )
145
+ end
146
+
147
+ # Default flow, i.e. generate, compile, link to executable.
148
+ Walk.form 'default' do
149
+ # Reference 'gen-cee' flow.
150
+ pick 'gen-cee'
151
+ # Reference 'compile-cee' flow.
152
+ pick 'compile-cee'
153
+ # Create GCC link build and register it.
154
+ GccLinkExe.use( obj_files, exe_file )
155
+ end
156
+
157
+ # Cleaner flow.
158
+ Walk.form 'clean' do
159
+ CleanObjects.use
160
+ end
161
+
162
+
163
+
164
+ # ------------------------------------------------------------
165
+ # Run selected flows:
166
+
167
+ flows = nil
168
+ if Opt[ nil ].given
169
+ flows = Opt[ nil ].value
170
+ else
171
+ flows = [ 'default' ]
172
+ end
173
+
174
+ if Opt[ 'serial' ].given
175
+ Order[ :serial ] = true
176
+ end
177
+
178
+ Flow.run( flows )
@@ -0,0 +1,26 @@
1
+ #!/bin/sh
2
+
3
+ echo "-- Starting with cleaning without any output from Build Program:"
4
+ bin/rubu_example clean
5
+
6
+ echo "-- The same with command outputs:"
7
+ bin/rubu_example -v clean
8
+
9
+ echo "-- Initial build with all files created:"
10
+ bin/rubu_example -v default
11
+
12
+ echo "-- Re-build using implicit \"default\" build, but no changes and thus no actions:"
13
+ bin/rubu_example -v
14
+
15
+ echo "-- Touch main source file, and re-building occurs (serial flow forced/selected):"
16
+ touch src/hello_world.c
17
+ bin/rubu_example -s -v
18
+
19
+ echo "-- Touch file generator script, but no actions since building is file context sensitive only:"
20
+ touch bin/gen_world
21
+ bin/rubu_example -v
22
+
23
+ echo "-- Complete re-build by combining clean and default build:"
24
+ bin/rubu_example -v clean default
25
+
26
+
@@ -0,0 +1,11 @@
1
+ #include <stdio.h>
2
+
3
+ void print_world( void );
4
+
5
+ int main( int argc, char** argv )
6
+ {
7
+ printf( "Hello " );
8
+ print_world();
9
+ printf( "!\n" );
10
+ return 0;
11
+ }
@@ -0,0 +1,790 @@
1
+ require 'open3'
2
+ require 'yaml'
3
+ require 'digest/md5'
4
+
5
+
6
+ module Rubu
7
+
8
+ class State
9
+
10
+ @@md5 = {}
11
+ @@state_file = ".rubu_state.yml"
12
+
13
+ def State.save
14
+ if @@md5.any?
15
+ IO.write( @@state_file, YAML.dump( @@md5 ) )
16
+ end
17
+ end
18
+
19
+ def State.load
20
+ if File.exist?( @@state_file )
21
+ @@md5 = YAML.load_file( @@state_file )
22
+ end
23
+ end
24
+
25
+ def State.md5_gen( file )
26
+ Digest::MD5.hexdigest( File.read( file ) )
27
+ end
28
+
29
+ def State.md5_check_and_update?( file )
30
+ if @@md5[ file ]
31
+ md5 = State.md5_gen( file )
32
+ if md5 != @@md5[ file ]
33
+ @@md5[ file ] = md5
34
+ return true
35
+ end
36
+ return false
37
+ else
38
+ @@md5[ file ] = State.md5_gen( file )
39
+ return true
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+
46
+ # Build activity.
47
+ class Action
48
+
49
+ @@host = []
50
+
51
+ # Status after execution.
52
+ attr_reader :status
53
+
54
+ # Storage for error message.
55
+ attr_reader :errmsg
56
+
57
+ # Command output.
58
+ attr_reader :output
59
+
60
+ # Sub actions.
61
+ attr_reader :subs
62
+
63
+ def initialize
64
+ @status = :success
65
+ @errmsg = nil
66
+ @output = nil
67
+
68
+ @subs = []
69
+ end
70
+
71
+ # Register Action to host (upper level).
72
+ def use
73
+ host.subs.push( self ) if host
74
+ self
75
+ end
76
+
77
+ # Take flow into use.
78
+ def pick( flow )
79
+ Flow[ flow ].use
80
+ end
81
+
82
+ # Push host.
83
+ def host_in
84
+ @@host.push self
85
+ end
86
+
87
+ # Pop host.
88
+ def host_out
89
+ @@host.pop
90
+ end
91
+
92
+ # Current host.
93
+ def host
94
+ @@host[-1]
95
+ end
96
+
97
+ # Report (and store) error.
98
+ def error( msg )
99
+ @errmsg = msg
100
+ STDERR.puts "Rubu Error: #{@errmsg}"
101
+ end
102
+
103
+ # Display command output.
104
+ def display( msg )
105
+ @output = msg
106
+ STDOUT.puts @output
107
+ end
108
+
109
+ end
110
+
111
+
112
+ # Shell based command.
113
+ class ShellCommand < Action
114
+
115
+ def initialize( cmd )
116
+ super()
117
+ @cmd = cmd
118
+ end
119
+
120
+ def run
121
+ begin
122
+ stdout, stderr, status = Open3.capture3( @cmd )
123
+
124
+ if Order[ :verbose ]
125
+ STDOUT.puts @cmd
126
+ end
127
+
128
+ if status.exitstatus == 0
129
+ @status = :success
130
+ else
131
+ @status = :error
132
+ error( stderr )
133
+ end
134
+
135
+ rescue
136
+
137
+ error( "Invalid command: \"#{@cmd}\"..." )
138
+ @status = :error
139
+
140
+ end
141
+
142
+ self
143
+ end
144
+ end
145
+
146
+
147
+ # Ruby based command.
148
+ class RubyCommand < Action
149
+
150
+ def initialize( desc = nil, &cmd )
151
+ super()
152
+ @desc = desc
153
+ @cmd = cmd
154
+ end
155
+
156
+ def run
157
+ begin
158
+ ret = instance_eval( &@cmd )
159
+ @status = :success
160
+ if @desc && Order[ :verbose ]
161
+ STDOUT.puts @desc
162
+ end
163
+ rescue => f
164
+ @status = :error
165
+ error( f.backtrace.join("\n") )
166
+ end
167
+
168
+ self
169
+ end
170
+ end
171
+
172
+
173
+ # Flow execution methods.
174
+ module FlowRun
175
+
176
+ def serial_run
177
+ @subs.each do |sub|
178
+ sub.run
179
+ if sub.status == :error
180
+ @status = :error
181
+ @errmsg = sub.errmsg
182
+ break
183
+ end
184
+ end
185
+
186
+ self
187
+ end
188
+
189
+ def parallel_run
190
+
191
+ if Order[ :serial ]
192
+
193
+ serial_run
194
+
195
+ else
196
+
197
+ if Order[ :parmax ] == 0 || @subs.length < Order[ :parmax ]
198
+
199
+ ths = []
200
+ @subs.each do |item|
201
+ ths.push(
202
+ Thread.new do
203
+ item.run
204
+ end
205
+ )
206
+ end
207
+ ths.each do |th|
208
+ th.join
209
+ end
210
+
211
+ else
212
+
213
+ cnt = @subs.length
214
+ done = 0
215
+ while done < cnt
216
+
217
+ incr = Order[ :parmax ]
218
+ if done >= cnt
219
+ incr = done - cnt
220
+ end
221
+
222
+ ths = []
223
+ incr.times do |i|
224
+ item = @subs[ done+i ]
225
+ ths.push(
226
+ Thread.new do
227
+ item.run
228
+ end
229
+ )
230
+ end
231
+ ths.each do |th|
232
+ th.join
233
+ end
234
+
235
+ done += incr
236
+ end
237
+
238
+ end
239
+
240
+ @subs.each do |item|
241
+ if item.status == :error
242
+ @status = :error
243
+ @errmsg = item.errmsg
244
+ break
245
+ end
246
+ end
247
+
248
+ self
249
+ end
250
+ end
251
+
252
+ end
253
+
254
+
255
+ # Source or Target file for build commands. Rubu handles only
256
+ # Marks, and hence bare file names are not usable.
257
+ class Mark
258
+
259
+ # Convert a list of file names to Marks.
260
+ def Mark.list( files )
261
+ unless files.kind_of? Array
262
+ files = [ files ]
263
+ end
264
+ files.map{ |file| Mark.path( file ) }
265
+ end
266
+
267
+ # Convert a list of file names matched with pattern to Marks.
268
+ def Mark.glob( pattern )
269
+ Mark.list( Dir.glob( pattern ) )
270
+ end
271
+
272
+ # Convert file path to Mark.
273
+ def Mark.path( file_path )
274
+ path = File.absolute_path( file_path )
275
+ dir = File.dirname( path ).split( '/' )
276
+ rdir = dir - Dir.pwd.split( '/' )
277
+ ext = File.extname( path )
278
+ base = File.basename( path, ext )
279
+ Mark.new( rdir, base, ext )
280
+ end
281
+
282
+
283
+ # Absolute directory.
284
+ attr_reader :dir
285
+
286
+ # Relative directory.
287
+ attr_reader :rdir
288
+
289
+ # Base name.
290
+ attr_reader :base
291
+
292
+ # File extension.
293
+ attr_reader :ext
294
+
295
+ # Skip file.
296
+ attr_accessor :skip
297
+
298
+
299
+ def initialize( rdir, base, ext )
300
+ if rdir.kind_of? Array
301
+ @dir = File.absolute_path( rdir.join( '/' ) ).split( '/' )
302
+ @rdir = rdir
303
+ else
304
+ @dir = File.absolute_path( rdir ).split( '/' )
305
+ @rdir = rdir.split( '/' )
306
+ end
307
+ @ext = ext
308
+ @base = base
309
+ @opt = {}
310
+ @skip = false
311
+ end
312
+
313
+ # Get options.
314
+ def []( key )
315
+ @opt[ key ]
316
+ end
317
+
318
+ # Set options.
319
+ def []=( key, val )
320
+ @opt[ key ] = val
321
+ end
322
+
323
+ # Set options.
324
+ def set_opt( key, val )
325
+ @opt[ key ] = val
326
+ self
327
+ end
328
+
329
+ # Return absolute path.
330
+ def path( ext = nil )
331
+ ext ||= @ext
332
+ "#{@dir.join('/')}/#{@base}#{ext}"
333
+ end
334
+
335
+ # Return relative path.
336
+ def rpath( ext = nil )
337
+ ext ||= @ext
338
+ "#{@rdir.join('/')}/#{@base}#{ext}"
339
+ end
340
+
341
+ # Return peer of Mark.
342
+ def peer( rdir, ext, base = nil )
343
+ base ||= @base
344
+ Mark.new( rdir, base, ext )
345
+ end
346
+
347
+ # Does Mark exist?
348
+ def exist?
349
+ File.exist?( path )
350
+ end
351
+
352
+ # Mark creation time.
353
+ def time
354
+ File.stat( path ).mtime
355
+ end
356
+
357
+ end
358
+
359
+
360
+ # Configuration space for Rubu.
361
+ class Order
362
+
363
+ @@order = {}
364
+
365
+ def Order.[]=( key, val )
366
+ @@order[ key ] = val
367
+ end
368
+
369
+ def Order.[]( key )
370
+ @@order[ key ]
371
+ end
372
+
373
+
374
+ # Order defaults:
375
+
376
+ # Force serial flow.
377
+ Order[ :serial ] = false
378
+
379
+ # Maximun parallel runs (0 for no limit).
380
+ Order[ :parmax ] = 0
381
+
382
+ # Verbose execution.
383
+ Order[ :verbose ] = false
384
+
385
+ end
386
+
387
+
388
+ # Option space for program.
389
+ class Var
390
+
391
+ @@var = {}
392
+
393
+ def Var.[]=( key, val )
394
+ @@var[ key ] = val
395
+ end
396
+
397
+ def Var.[]( key )
398
+ @@var[ key ]
399
+ end
400
+
401
+ end
402
+
403
+
404
+ # Information space for program.
405
+ class Info
406
+
407
+ @@info = {}
408
+
409
+ def Info.[]=( key, val )
410
+ @@info[ key ] = val
411
+ end
412
+
413
+ def Info.[]( key )
414
+ @@info[ key ]
415
+ end
416
+
417
+ end
418
+
419
+
420
+ # Build Action. Build Action takes one or more sources, and turns
421
+ # them into one or more targets.
422
+ class Build < Action
423
+
424
+ include FlowRun
425
+
426
+ # Create Action and register.
427
+ def self.use( sources = [], targets = [] )
428
+ self.new( sources, targets ).use
429
+ end
430
+
431
+ # Combine list of sources and targets to source/target pairs.
432
+ def self.zip( sources, targets )
433
+ sources.zip( targets ).map do |pair|
434
+ self.new( *pair )
435
+ end
436
+ end
437
+
438
+ # Combine list of sources and targets to source/target pairs
439
+ # and register.
440
+ def self.usezip( sources, targets )
441
+ sources.zip( targets ).map do |pair|
442
+ self.new( *pair ).use
443
+ end
444
+ end
445
+
446
+
447
+ attr_reader :sources
448
+ attr_reader :targets
449
+
450
+ def initialize( sources = [], targets = [] )
451
+ super()
452
+
453
+ unless sources.kind_of? Array
454
+ @sources = [ sources ]
455
+ else
456
+ @sources = sources
457
+ end
458
+
459
+ unless targets.kind_of? Array
460
+ @targets = [ targets ]
461
+ else
462
+ @targets = targets
463
+ end
464
+
465
+ setup
466
+ end
467
+
468
+
469
+ # Defined by users.
470
+ def setup
471
+ end
472
+
473
+
474
+ # Run Build Action and capture status.
475
+ def run
476
+ if update?
477
+ build
478
+ else
479
+ @status = :success
480
+ end
481
+ self
482
+ end
483
+
484
+
485
+ # Default update.
486
+ def update?
487
+ true
488
+ end
489
+
490
+
491
+ # Check for date (timestamp) based update needs.
492
+ def date_update?
493
+
494
+ # Check if targets are missing.
495
+ @targets.each do |target|
496
+ unless target.exist?
497
+ return true
498
+ end
499
+ end
500
+
501
+ # Check if source(s) are newer than target(s).
502
+
503
+ newest_source = Time.new( 0 )
504
+ @sources.each do |source|
505
+ if source.time > newest_source && !source.skip
506
+ newest_source = source.time
507
+ end
508
+ end
509
+
510
+ oldest_target = Time.now
511
+ @targets.each do |target|
512
+ if target.time < oldest_target
513
+ oldest_target = target.time
514
+ end
515
+ end
516
+
517
+ return newest_source > oldest_target
518
+ end
519
+
520
+
521
+ # Check for mark (checksum) based update needs.
522
+ def mark_update?
523
+
524
+ unless date_update?
525
+ return false
526
+ end
527
+
528
+ # Check if targets are missing.
529
+ unless target.exist?
530
+ return true
531
+ end
532
+
533
+ old_verbose = Order[ :verbose ]
534
+ Order[ :verbose ] = false
535
+ build
536
+ Order[ :verbose ] = old_verbose
537
+
538
+ unless target.exist?
539
+ error "file generation failure"
540
+ exit false
541
+ end
542
+
543
+ unless State.md5_check_and_update?( target.rpath )
544
+ target.skip = true
545
+ return false
546
+ end
547
+
548
+ true
549
+ end
550
+
551
+
552
+ # Main (first) source file.
553
+ def source
554
+ @sources[0]
555
+ end
556
+
557
+ # Main (first) target file.
558
+ def target
559
+ @targets[0]
560
+ end
561
+
562
+ # Define and run Shell command.
563
+ def shrun( cmd )
564
+ ShellCommand.new( cmd ).run
565
+ end
566
+
567
+ # Define and register Shell command.
568
+ def shdef( cmd )
569
+ sh = ShellCommand.new( cmd )
570
+ sh.use
571
+ end
572
+
573
+ # Define and run Ruby command.
574
+ def rbrun( desc = nil, &cmd )
575
+ RubyCommand.new( desc, &cmd ).run
576
+ end
577
+
578
+ # Define and register Ruby command.
579
+ def rbdef( desc = nil, &cmd )
580
+ rb = RubyCommand.new( desc, &cmd )
581
+ rb.use
582
+ end
583
+
584
+ # Execute commands (in block) in parallel.
585
+ def fork( &blk )
586
+ host_in
587
+ instance_eval &blk
588
+ host_out
589
+ parallel_run
590
+ end
591
+
592
+ # Execute commands (in block) in series.
593
+ def walk( &blk )
594
+ host_in
595
+ instance_eval &blk
596
+ host_out
597
+ serial_run
598
+ end
599
+ end
600
+
601
+
602
+ # Uncondition build.
603
+ class AlwaysBuild < Build
604
+ def update?
605
+ true
606
+ end
607
+ end
608
+
609
+
610
+ # Date based build.
611
+ class DateBuild < Build
612
+ def update?
613
+ date_update?
614
+ end
615
+ end
616
+
617
+
618
+ # Mark based build.
619
+ class MarkBuild < Build
620
+ def update?
621
+ mark_update?
622
+ end
623
+ end
624
+
625
+
626
+ # Action Flow with name. Action Flow is a collection of build
627
+ # steps.
628
+ class Flow < Action
629
+
630
+ include FlowRun
631
+
632
+ # Flow hash.
633
+ @@flows = {}
634
+
635
+
636
+ # Replacement for new method.
637
+ def self.form( name = nil, &blk )
638
+ self.new( name, &blk )
639
+ end
640
+
641
+
642
+ # Reference Flow by name.
643
+ def self.[]( name )
644
+ @@flows[ name ]
645
+ end
646
+
647
+
648
+ attr_reader :name
649
+
650
+ def initialize( name = nil, &blk )
651
+ super()
652
+ @name = name
653
+ host_in
654
+ instance_eval &blk
655
+ host_out
656
+ if @name
657
+ @@flows[ @name ] = self
658
+ end
659
+ end
660
+
661
+ # Default run style for Flow.
662
+ def run
663
+ serial_run
664
+ end
665
+
666
+ def Flow.load_setup( setup )
667
+
668
+ if File.exist? setup
669
+
670
+ conf = YAML.load_file( setup )
671
+
672
+ conf.each do |k,v|
673
+
674
+ scope = nil
675
+
676
+ case k
677
+ when :var; scope = Var
678
+ when :order; scope = Order
679
+ when :info; scope = Info
680
+ end
681
+
682
+ v.each do |k2,v2|
683
+ scope[ k2 ] = v2
684
+ end
685
+ end
686
+ end
687
+ end
688
+
689
+ # Apply configuration options if any.
690
+ def Flow.setup( spec )
691
+
692
+ load_setup( "#{ENV['HOME']}/.rubu.yml" )
693
+ load_setup( ENV['RUBU_CONF'] ) if ENV['RUBU_CONF']
694
+ load_setup( ".rubu.yml" )
695
+
696
+ State.load
697
+
698
+ # Apply options from Como.
699
+ if spec[ :como ]
700
+ como = spec[ :como ]
701
+ if Opt[ como ].given
702
+ Opt[ como ].value.each do |conf|
703
+ name, value = conf.split( '=' )
704
+ value = case value
705
+ when 'true'; true
706
+ when 'false'; false
707
+ else value
708
+ end
709
+ Var[ name.to_sym ] = value
710
+ end
711
+ end
712
+ end
713
+
714
+ end
715
+
716
+ # Run selected flow(s).
717
+ def Flow.run( flows )
718
+
719
+ flows.each do |name|
720
+
721
+ begin
722
+
723
+ ret = Flow[ name ].run
724
+ if ret.status == :error
725
+ STDERR.puts "Rubu FAILURE..."
726
+ exit false
727
+ end
728
+
729
+ rescue
730
+
731
+ STDERR.puts "Broken flow: \"#{name}\"..."
732
+ exit false
733
+
734
+ end
735
+
736
+ end
737
+
738
+ State.save
739
+
740
+ exit true
741
+ end
742
+
743
+ end
744
+
745
+
746
+ # Serial Flow.
747
+ class Walk < Flow; end
748
+
749
+
750
+ # Parallel Flow.
751
+ class Fork < Flow
752
+ def run
753
+ parallel_run
754
+ end
755
+ end
756
+
757
+ end
758
+
759
+
760
+ # Array class extension to support common Mark operations.
761
+ class Array
762
+
763
+ def use
764
+ self.each do |item|
765
+ item.use
766
+ end
767
+ end
768
+
769
+ def set_opt( key, val )
770
+ self.each do |item|
771
+ item.set_opt( key, val )
772
+ end
773
+ self
774
+ end
775
+
776
+ def peer( rdir, ext, base = nil )
777
+ self.map do |item|
778
+ item.peer( rdir, ext, base )
779
+ end
780
+ end
781
+
782
+ def path( joiner = ' ' )
783
+ self.map{ |item| item.path }.join( joiner )
784
+ end
785
+
786
+ def rpath( joiner = ' ' )
787
+ self.map{ |item| item.rpath }.join( joiner )
788
+ end
789
+
790
+ end