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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +63 -0
- data/doc/Array.html +422 -0
- data/doc/Rubu.html +201 -0
- data/doc/Rubu/Action.html +1002 -0
- data/doc/Rubu/AlwaysBuild.html +275 -0
- data/doc/Rubu/Build.html +1663 -0
- data/doc/Rubu/DateBuild.html +275 -0
- data/doc/Rubu/Flow.html +893 -0
- data/doc/Rubu/FlowRun.html +377 -0
- data/doc/Rubu/Fork.html +253 -0
- data/doc/Rubu/Info.html +264 -0
- data/doc/Rubu/Mark.html +1375 -0
- data/doc/Rubu/MarkBuild.html +275 -0
- data/doc/Rubu/Order.html +264 -0
- data/doc/Rubu/RubyCommand.html +315 -0
- data/doc/Rubu/ShellCommand.html +333 -0
- data/doc/Rubu/State.html +412 -0
- data/doc/Rubu/Var.html +264 -0
- data/doc/Rubu/Walk.html +184 -0
- data/doc/_index.html +313 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.CHANGELOG.html +79 -0
- data/doc/file.README.html +133 -0
- data/doc/file_list.html +63 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +133 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +549 -0
- data/doc/top-level-namespace.html +114 -0
- data/example/bin/gen_world +10 -0
- data/example/bin/rubu_example +178 -0
- data/example/runme +26 -0
- data/example/src/hello_world.c +11 -0
- data/lib/rubu.rb +790 -0
- data/lib/version.rb +6 -0
- metadata +92 -0
@@ -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
|
+
— 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> »
|
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,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 )
|
data/example/runme
ADDED
@@ -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
|
+
|
data/lib/rubu.rb
ADDED
@@ -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
|