rbuild 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README~ ADDED
@@ -0,0 +1,79 @@
1
+ RBuild - a KBuild like software configure/build system by Ruby DSL.
2
+ -------------------------------------------------------------------
3
+
4
+ -- Introduction --
5
+
6
+ Rbuild is a KBuild like configure/build system, like KBuild, RBuild provide a menu driven configure interface for target software. RBuild take 'RConfig' files as input, generate target software configuration files by RBuild plugins, for example, the 'export_c' plugin generate C/C++ header config.h and Makefiles.
7
+
8
+ A RConfig's syntax looks more like KConfig, plus lots of ruby flavors. Since RConfig file actually is a ruby script, it could be very flexiable and powerful.
9
+
10
+ -- RConfig --
11
+
12
+ All RConfig instructions falls into four catalogs:
13
+ - organizing: 'menu', 'group'
14
+ - configuration: 'choice', 'config'
15
+ - property: 'title', 'default', 'help', 'range', 'depends', 'select', 'unselect' and 'property'
16
+ - file inclusion: 'source'
17
+
18
+ A Simple RConfig example:
19
+
20
+ title "Welcom to my software config system !"
21
+ menu "Package config" do
22
+ config :PACKAGE_A => "enable package A"
23
+ group "Package A options" do
24
+ depends :PACKAGE_A
25
+ config :PACKAGE_A_FUN_A => "function A"
26
+ config :PACKAGE_A_FUN_B => "function B"
27
+ end
28
+ config :PACKAGE_B => "enable package B"
29
+ choice :PACKAGE_B_PARAM => "select package B param" do
30
+ depends :PACKAGE_B
31
+ default 200
32
+ range 100, 200, 300
33
+ end
34
+ end
35
+ menu "RBuild Configuration" do
36
+ group "RBuild System Options" do
37
+ choice :RBUILD_SYS_CONFIG_FILE => "RBuild Configuration File" do
38
+ property :no_export # do not export this, it's for rbuild system internal.
39
+ default 'rb.config'
40
+ end
41
+
42
+ choice :RBUILD_PLUGIN_EXP_C_HEADER_FILE => "RBuild Export File" do
43
+ property :no_export # do not export this, it's for rbuild system internal.
44
+ default 'config.h'
45
+ end
46
+ end
47
+ end
48
+
49
+
50
+ -- Install --
51
+
52
+ 1. Install Ruby interpreter (http://www.ruby-lang.org)
53
+ 2. Copy 'scripts' directory into your target software package
54
+ 3. Write your 'RConfig' files
55
+ 4. Create a 'Rakefile' as:
56
+ require 'scripts/rbuild'
57
+
58
+ task :menuconfig do
59
+ rconf = RBuild::RConfig.new 'RConfig'
60
+ rconf.menuconfig()
61
+ end
62
+ 5. at your target software package's root dir, run:
63
+ rake menuconfig
64
+
65
+
66
+ -- Project Home --
67
+
68
+ RBuild is hosted by SourceForge (http://www.sourceforge.net/projects/rbuild/)
69
+ RBuild's home page http://rbuild.sf.net/
70
+
71
+
72
+ -- LICENCE --
73
+
74
+ RBuild is released under GNU GPLv2.
75
+
76
+ Copy Right (c) 2008, Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
77
+
78
+
79
+
@@ -0,0 +1,167 @@
1
+ #
2
+ # arm-elf cross compiler toolchain building script,
3
+ # by Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
4
+ #
5
+ require 'rubygems'
6
+ begin
7
+ require 'rbuild'
8
+ rescue Exception
9
+ raise "\n\n**** Please install rbuild gem first ! ****\n\n"
10
+ end
11
+
12
+ $GCC_VER = nil
13
+ $BINUTILS_VER = nil
14
+ $NEWLIB_VER = nil
15
+
16
+ $GCC_DOWNLOAD_SITE = "ftp://ftp.gnu.org/pub/gnu/gcc"
17
+ $BINUTILS_DOWNLOAD_SITE = "ftp://ftp.gnu.org/pub/gnu/binutils"
18
+ $NEWLIB_DOWNLOAD_SITE = "ftp://sources.redhat.com/pub/newlib"
19
+
20
+ $CURDIR = File.expand_path(Dir.pwd)
21
+ $DOWNLOAD_DIR = "./dl"
22
+ $check_integrity = false
23
+ $target="arm-elf"
24
+ $prefix = "./#{$target}"
25
+
26
+ $with_newlib = nil
27
+ $add_gcc_options = "--with-stabs --disable-tls --disable-libssp --disable-libgomp --without-headers --disable-bootstrap"
28
+ $add_bin_options = "--disable-nls --enable-debug --with-gcc --with-gnu-as --with-gnu-ld --with-stabs"
29
+
30
+ def shell(cmd, desc = "")
31
+ unless system(cmd)
32
+ Dir.chdir $CURDIR
33
+ raise "Error when invoke: #{cmd}"
34
+ end
35
+ end
36
+
37
+ def check_integrity(f)
38
+ return unless $check_integrity
39
+ if File.exist?(f)
40
+ puts "Check #{f} ..."
41
+ if f =~ /\.bz2$/
42
+ unless system("bzip2 -t #{f}")
43
+ `rm -f #{f}`
44
+ end
45
+ elsif f =~ /\.gz$/
46
+ unless system("gzip -t #{f}")
47
+ `rm -f #{f}`
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ task :download => :prepare do
54
+ dl = File.expand_path($DOWNLOAD_DIR)
55
+ shell("mkdir -p #{dl}")
56
+ src = $CURDIR + '/src'
57
+ shell("mkdir -p #{src}")
58
+ Dir.chdir dl
59
+ f = "gcc-#{$GCC_VER}.tar.bz2"
60
+ check_integrity f
61
+ shell("wget #{$GCC_DOWNLOAD_SITE}/gcc-#{$GCC_VER}/#{f}") unless File.exist?(f)
62
+ Dir.chdir src
63
+ shell("tar -jxvf #{dl}/#{f}") unless File.exist?("gcc-#{$GCC_VER}")
64
+
65
+ Dir.chdir dl
66
+ f = "binutils-#{$BINUTILS_VER}.tar.gz"
67
+ check_integrity f
68
+ shell("wget #{$BINUTILS_DOWNLOAD_SITE}/#{f}") unless File.exist?(f)
69
+ Dir.chdir src
70
+ shell("tar -zxvf #{dl}/#{f}") unless File.exist?("binutils-#{$BINUTILS_VER}")
71
+
72
+ if $with_newlib
73
+ Dir.chdir dl
74
+ f = "newlib-#{$NEWLIB_VER}.tar.gz"
75
+ check_integrity f
76
+ shell("wget #{$NEWLIB_DOWNLOAD_SITE}/#{f}") unless File.exist?(f)
77
+ Dir.chdir src
78
+ shell("tar -zxvf #{dl}/#{f}") unless File.exist?("newlib-#{$NEWLIB_VER}")
79
+
80
+ shell("cp -a newlib-#{$NEWLIB_VER}/newlib gcc-#{$GCC_VER}/") unless File.exist?("gcc-#{$GCC_VER}/newlib")
81
+ shell("cp -a newlib-#{$NEWLIB_VER}/libgloss gcc-#{$GCC_VER}/") unless File.exist?("gcc-#{$GCC_VER}/libgloss")
82
+ end
83
+
84
+ Dir.chdir $CURDIR
85
+ end
86
+
87
+ task :menuconfig do
88
+ rconf = RBuild::RConfig.new 'RConfig'
89
+ rconf.merge!
90
+ rconf.menuconfig()
91
+ end
92
+
93
+ task :prepare do
94
+ rconf = RBuild::RConfig.new 'RConfig'
95
+ rconf.merge!
96
+ $GCC_VER = rconf.get_value(:GCC_VER)
97
+ $BINUTILS_VER = rconf.get_value(:BINUTILS_VER)
98
+ $NEWLIB_VER = rconf.get_value(:NEWLIB_VER)
99
+ $DOWNLOAD_DIR = File.expand_path(rconf.get_value(:DOWNLOAD_DIR))
100
+ $check_integrity = rconf.hit?(:CHECK_INTEGRITY)
101
+ $prefix = File.expand_path(rconf.get_value(:PREFIX))
102
+
103
+ $add_gcc_options += " --enable-languages=c"
104
+ $add_gcc_options += ",c++" if rconf.hit?(:ENABLE_CPP)
105
+
106
+ $add_gcc_options += " --prefix=#{$prefix} --target=#{$target}"
107
+ $add_bin_options += " --prefix=#{$prefix} --target=#{$target}"
108
+ if rconf.hit?(:SOFT_FLOAT)
109
+ $add_gcc_options += " --with-float=soft"
110
+ $add_bin_options += " --with-float=soft"
111
+ end
112
+ $add_gcc_options += " --enable-interwork" if rconf.hit?(:ARM_THUMB_INTERWORK)
113
+ $add_gcc_options += " --enable-multilib" if rconf.hit?(:ENABLE_MULTILIB)
114
+ $add_gcc_options += " --with-newlib" if rconf.hit?(:WITH_NEWLIB)
115
+ if rconf.hit?(:DISABLE_THREAD)
116
+ $add_gcc_options += " --disable-thread"
117
+ $add_bin_options += " --disable-thread"
118
+ end
119
+ end
120
+
121
+ task :diag => :prepare do
122
+ puts "gcc ver: #{$GCC_VER}"
123
+ puts "binutils ver: #{$BINUTILS_VER}"
124
+ puts "newlib ver: #{$NEWLIB_VER}"
125
+ puts "download dir: #{$DOWNLOAD_DIR}"
126
+ puts "install: #{$prefix}"
127
+ puts "gcc configure: #{$add_gcc_options}"
128
+ puts "binutils configure: #{$add_bin_options}"
129
+ end
130
+
131
+ task :all => :build
132
+
133
+ task :build => [:binutils, :gcc] do
134
+ puts "-------------------------------------"
135
+ puts "The new #{$target} toolchain is ready: "
136
+ puts " #{$prefix}"
137
+ puts "-------------------------------------"
138
+ end
139
+
140
+ task :gcc => [:binutils] do
141
+ shell "export PATH=#{$prefix}/bin:${PATH}"
142
+ Dir.chdir $CURDIR
143
+ shell "mkdir -p build/gcc"
144
+ Dir.chdir "build/gcc"
145
+ shell "#{$CURDIR}/src/gcc-#{$GCC_VER}/configure #{$gcc_add_cflags} -v 2>&1 | tee gcc_configure.log"
146
+ shell "make all 2>&1 | tee gcc_make.log"
147
+ shell "make install 2>&1 | tee gcc_install.log"
148
+ Dir.chdir $CURDIR
149
+ end
150
+
151
+ task :binutils => :download do
152
+ Dir.chdir $CURDIR
153
+ shell "mkdir -p #{$prefix}"
154
+ shell "mkdir -p build/binutils"
155
+ Dir.chdir "build/binutils"
156
+ shell "#{$CURDIR}/src/binutils-#{$BINUTILS_VER}/configure #{$add_bin_options} -v 2>&1 | tee binutils_configure.log"
157
+ shell "make all 2>&1 | tee binutils_make.log"
158
+ shell "make install 2>&1 | tee binutils_install.log"
159
+ Dir.chdir $CURDIR
160
+ end
161
+
162
+ task :clean do
163
+ Dir.chdir $CURDIR
164
+ shell "rm -Rf build/*"
165
+ end
166
+
167
+
@@ -0,0 +1,78 @@
1
+ #
2
+ # RBuild: a Linux KBuild like configure/build system by Ruby DSL.
3
+ #
4
+ # Copy right(C) 2008, Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
5
+ #
6
+ # Licence: GNU GPLv2
7
+ #
8
+ # http://rbuild.sourceforge.net/
9
+ #
10
+ #
11
+ # RBuild plugin for exporting configurations to C/C++ header.
12
+ #
13
+
14
+ module RBuild
15
+
16
+ class RConfig
17
+
18
+ public
19
+
20
+ # will be actived by :RBUILD_PLUGIN_EXP_C_HEADER
21
+ def exp_c_header_file(file)
22
+ headers = []
23
+ headers << "/* This file is created by RBuild - a KBuild like configure/build\n"
24
+ headers << " * system implemented by ruby DSL.\n"
25
+ headers << " * \n"
26
+ headers << " * http://rbuild.sourceforge.net/\n"
27
+ headers << " */\n"
28
+ headers << "\n"
29
+ headers << "#ifndef _RBUILD_CONFIG_H_\n"
30
+ headers << "#define _RBUILD_CONFIG_H_\n"
31
+ headers << "\n"
32
+ datas = []
33
+ @nodes.each do |node|
34
+ unless node[:no_export] || node_no?(node)
35
+ case node[:id]
36
+ when :config, :choice
37
+ s = "#define CONFIG_" + node[:key].to_s
38
+ value = get_node_value(node)
39
+ if value && (value.is_a?(String) || value.is_a?(Fixnum))
40
+ s += " (#{value.to_s})"
41
+ end
42
+ s += "\n"
43
+ unless node[:id] == :choice && value.nil?
44
+ datas << s
45
+ end
46
+ end
47
+ end
48
+ end
49
+ footers = []
50
+ footers << "\n"
51
+ footers << "#endif\n"
52
+ footers << "\n"
53
+
54
+ lines = []
55
+ if File.exist?(file)
56
+ File.open(file, "r") do |f|
57
+ while line = f.gets
58
+ if line =~ /#define\s*CONFIG_/
59
+ lines << line
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ if datas.sort == lines.sort
66
+ footer_msg "config file not changed, skip."
67
+ else
68
+ footer_msg "Export C header to file: '#{file}'"
69
+ File.open(file, "w") do |f|
70
+ headers.each do |line| f.write line end
71
+ datas.each do |line| f.write line end
72
+ footers.each do |line| f.write line end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,49 @@
1
+ #
2
+ # RBuild: a Linux KBuild like configure/build system by Ruby DSL.
3
+ #
4
+ # Copy right(C) 2008, Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
5
+ #
6
+ # Licence: GNU GPLv2
7
+ #
8
+ # http://rbuild.sourceforge.net/
9
+ #
10
+ #
11
+ # RBuild plugin for exporting targets list.
12
+ #
13
+
14
+ module RBuild
15
+
16
+ class RConfig
17
+
18
+ public
19
+
20
+ # will be actived by :RBUILD_PLUGIN_EXP_TARGETS_LIST
21
+ def exp_targets_list(file)
22
+ targets = get_targets()
23
+ old_targets = []
24
+
25
+ if File.exist?(file)
26
+ File.open(file, "rb") do |f|
27
+ while line = f.gets
28
+ t = line.chomp.strip
29
+ if t.size > 0
30
+ old_targets << t
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ if targets.sort == old_targets.sort
37
+ footer_msg "target file not changed, skip."
38
+ else
39
+ footer_msg "Export targets list to file: '#{file}'"
40
+ File.open(file, "wb") do |f|
41
+ targets.each do |t|
42
+ f.puts t
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,848 @@
1
+ #
2
+ # RBuild: a Linux KBuild like configure/build system by Ruby DSL.
3
+ #
4
+ # Copy right(C) 2008, Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
5
+ #
6
+ # Licence: GNU GPLv2
7
+ #
8
+ # http://rbuild.sourceforge.net/
9
+ #
10
+
11
+ # Reserved keys:
12
+ # :RBUILD_SYS_CONFIG_FILE, for load/save rbuild config file.
13
+ # :RBUILD_PLUGIN_XXXX, for rbuild plugins.
14
+
15
+ require 'fileutils'
16
+ require 'yaml'
17
+
18
+ require File.dirname(__FILE__) + '/rbuild_menuconfig'
19
+ Dir.glob File.dirname(__FILE__) + '/plugins/*.rb' do |plugin|
20
+ require File.dirname(__FILE__) + '/plugins/' + File.basename(plugin, '.rb')
21
+ end
22
+
23
+ require File.dirname(__FILE__) + '/rbuild_menuconfig'
24
+
25
+ module RBuild
26
+
27
+ DEFAULT_CONFIG_FILE = 'rb.config'
28
+ DEFAULT_LOG_FILE = 'rbuild.log'
29
+
30
+ class RConfig
31
+
32
+ include Menuconfig
33
+ #include Export_C_Header
34
+
35
+ def initialize(rconfig_file = nil)
36
+ @top_worker_path = File.expand_path(Dir.pwd)
37
+ log_to_file(nil) # just delete the log file
38
+ start_from(rconfig_file)
39
+ end
40
+
41
+ def start_from(rconfig_file)
42
+ @conf = {}
43
+ @current = {:id => :menu,
44
+ :key => :RBUILD_TOP_GLOBAL,
45
+ :title => "Welcom to RBuild Configuration System !",
46
+ :children => [],
47
+ :depends =>[],
48
+ }
49
+ @current[:parent] = @current[:key]
50
+ @conf[@current[:key]] = @current
51
+ @nodes = [@current]
52
+ @stack = []
53
+ @deferrers = {} # deferrer node setting value, |node, value|
54
+
55
+ @dirstack = []
56
+ @top_rconfig_path = @top_worker_path
57
+ @curpath = @top_worker_path
58
+ @sources = []
59
+
60
+ @targets = [] # target files
61
+ @targets_cache = {} # cache the targets
62
+ @target_deps = {} # target depend symbols
63
+ @target_flags = {} # target special flags
64
+
65
+ if rconfig_file
66
+ unless File.exist?(rconfig_file)
67
+ warning "RConfig file: #{abs_file_name(rconfig_file)} doesn't exist ?"
68
+ else
69
+ @top_rconfig_path = File.expand_path(File.dirname(rconfig_file))
70
+ exec_rconfig_file rconfig_file
71
+ end
72
+ end
73
+ @deferrers.each {|node, value| set_node_value(node, value) }
74
+ end
75
+
76
+ # merge config from config_file, if config_file is nil, use @conf[:RBUILD_SYS_CONFIG_LOAD_FILE]
77
+ # otherwise use DEFAULT_CONFIG_FILE
78
+ def merge!(config_file = nil)
79
+ cfg_file_node = @conf[:RBUILD_SYS_CONFIG_LOAD_FILE]
80
+ if cfg_file_node
81
+ config_file ||= get_node_value(cfg_file_node).to_s
82
+ end
83
+ config_file ||= RBuild::DEFAULT_CONFIG_FILE
84
+ return nil unless File.exist?(config_file)
85
+
86
+ cfg = YAML.load_file(config_file)
87
+ conf = cfg[:conf]
88
+ conf.each do |key, node|
89
+ n = @conf[key]
90
+ if n && n[:id] == node[:id] && (n[:id] == :config || n[:id] == :choice)
91
+ n[:hit] = node[:hit]
92
+ n[:value] = node[:value]
93
+ end
94
+ end
95
+ @conf
96
+ end
97
+
98
+ # ---- External APIs ----
99
+ def get_value(name)
100
+ node = @conf[name]
101
+ if node
102
+ get_node_value(node)
103
+ else
104
+ nil
105
+ end
106
+ end
107
+
108
+ # check the whether the node is hitted with a given name
109
+ def hit?(name)
110
+ node = @conf[name]
111
+ if node
112
+ node[:hit]
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+ # return children under name
119
+ def get_children(name)
120
+ node = @conf[name]
121
+ if node
122
+ node[:children]
123
+ else
124
+ []
125
+ end
126
+ end
127
+
128
+ # ---- end of External APIs ----
129
+
130
+ # turn file name with absolute path file name
131
+ def abs_file_name(name)
132
+ File.expand_path(File.dirname(name)) + '/' + File.basename(name)
133
+ end
134
+ private :abs_file_name
135
+
136
+ # return the 'top node'
137
+ def top_node
138
+ @conf[:RBUILD_TOP_GLOBAL]
139
+ end
140
+
141
+ # load config from file.
142
+ # if file is nil, use DEFAULT_CONFIG_FILE
143
+ def load_config(config_file = nil)
144
+ config_file ||= RBuild::DEFAULT_CONFIG_FILE
145
+ return unless File.exist?(config_file)
146
+
147
+ cfg = YAML.load_file(config_file)
148
+ @conf = cfg[:conf]
149
+ @current = top_node()
150
+ @nodes = []
151
+ @conf.each do |key, node|
152
+ @nodes << node
153
+ end
154
+ @stack = []
155
+ footer_msg "config loaded from: #{config_file}"
156
+ end
157
+
158
+ def get_node_value(node)
159
+ case node[:id]
160
+ when :config
161
+ if node[:range] && node[:range].is_a?(Array) && node[:range].size == 2
162
+ if node[:hit]
163
+ node[:range][1]
164
+ else
165
+ node[:range][0]
166
+ end
167
+ else
168
+ node[:hit] ? node[:value] : nil
169
+ end
170
+ when :choice
171
+ if node[:hit]
172
+ if node[:children].size > 0
173
+ value = nil
174
+ node[:children].each do |child|
175
+ if @conf[child][:hit]
176
+ #value = get_node_value(@conf[child])
177
+ value = child
178
+ break
179
+ end
180
+ end
181
+ value
182
+ else
183
+ node[:value]
184
+ end
185
+ else
186
+ nil
187
+ end
188
+ else
189
+ node[:title]
190
+ end
191
+ end
192
+ private :get_node_value
193
+
194
+ # save config to file.
195
+ # if file is nil, search the @conf[:RBUILD_SYS_CONFIG_SAVE_FILE], use [:value] as file name.
196
+ # otherwise, use DEFAULT_CONFIG_FILE
197
+ def save_config(config_file = nil)
198
+ cfg_file_node = @conf[:RBUILD_SYS_CONFIG_SAVE_FILE]
199
+ if cfg_file_node
200
+ config_file ||= get_node_value(cfg_file_node).to_s
201
+ end
202
+ config_file ||= RBuild::DEFAULT_CONFIG_FILE
203
+ File.open(config_file, "w") do |f|
204
+ cfg = {
205
+ :conf => @conf,
206
+ }
207
+ YAML.dump cfg, f
208
+ end
209
+ footer_msg "config saved to: #{config_file}"
210
+
211
+ end
212
+
213
+ # ----------- RBuild DSL APIs ---------
214
+ def menu(*args, &block)
215
+ key, desc, attr_cb = args_to_key_desc(args)
216
+ node = {:id => :menu, :key => key, :title => desc}
217
+ process_node(node, attr_cb, block)
218
+ end
219
+
220
+ def group(*args, &block)
221
+ key, desc, attr_cb = args_to_key_desc(args)
222
+ node = {:id => :group, :key => key, :title => desc}
223
+ process_node(node, attr_cb, block)
224
+ end
225
+
226
+ def choice(*args, &block)
227
+ key, desc, attr_cb = args_to_key_desc(args)
228
+ node = {:id => :choice, :key => key, :value => nil, :title => desc, :hit => false}
229
+ process_node(node, attr_cb, block)
230
+ end
231
+
232
+ def config(*args, &block)
233
+ key, desc, attr_cb = args_to_key_desc(args)
234
+ node = {:id => :config, :key => key, :title => desc, :hit => false, :value => nil }
235
+ process_node(node, attr_cb, block)
236
+ end
237
+
238
+ def depends(*keys)
239
+ keys.each {|key| @current[:depends] << key}
240
+ end
241
+
242
+ def select(*keys)
243
+ keys.each {|key| @current[:selects] << key}
244
+ end
245
+
246
+ def unselect(*keys)
247
+ keys.each {|key| @current[:unselects] << key}
248
+ end
249
+
250
+ def help(desc)
251
+ @current[:help] = desc
252
+ end
253
+
254
+ def title(desc)
255
+ @current[:title] = desc
256
+ end
257
+
258
+ def default(value)
259
+ @deferrers[@current] = value
260
+ end
261
+
262
+ def no_export
263
+ @current[:no_export] = true
264
+ end
265
+
266
+ def hidden
267
+ @current[:hidden] = true
268
+ end
269
+
270
+ def hex
271
+ @current[:hex] = true
272
+ end
273
+
274
+ def string
275
+ @current[:string] = true
276
+ end
277
+
278
+ def bool
279
+ @current[:bool] = true
280
+ end
281
+
282
+ def digi
283
+ @current[:digi] = true
284
+ end
285
+
286
+ # set :choice or :config node value range
287
+ # range can be:
288
+ # - Range, in this case, Range type would be Fixnum
289
+ # - Array, in this case, range type is defined by Array elements
290
+ # - Hash{value1 => desc1, value2 => desc2, ...}, in this case, range is multiple choice
291
+ # - Array of Hash{value => desc}, use this to appoint the order. in this case, range is multiple choices
292
+ def range(*arg)
293
+ return if arg.size == 0
294
+ if arg.size == 1
295
+ if arg[0].is_a?(Range) || arg[0].is_a?(Hash) || arg[0].is_a?(Array)
296
+ @current[:range] = arg[0]
297
+ else
298
+ error "Invalid range setting for '#{@current[:title]}'"
299
+ end
300
+ else
301
+ @current[:range] = arg # range is Array
302
+ end
303
+ end
304
+
305
+ # load other 'RConfig' file from dest
306
+ # dest can be:
307
+ # path_to_next_rconfig/RConfig
308
+ # or:
309
+ # */RConfig ==> search any sub folders
310
+ # or:
311
+ # **/RConfig ==> reclusivly search any sub folders
312
+ def source(*args)
313
+ args.each do |dest|
314
+ Dir.glob(dest).each do |fn|
315
+ exec_rconfig_file(fn)
316
+ end
317
+ end
318
+ end
319
+
320
+ # arg could be:
321
+ # - Symbols only:
322
+ # property :no_export, :hidden, :string
323
+ # - Hash:
324
+ # property :title => "Hello", :help => "this is a test"
325
+ # - Symbols + Hash (only last one can be Hash)
326
+ # property :no_export, :hidden, :string, :title => "Hello", :help => "this is a test"
327
+ def property(*arg)
328
+ arg.each do |a|
329
+ if a.is_a?(Symbol)
330
+ invoke_dsl a
331
+ elsif a.is_a?(Hash)
332
+ a.each do |key, value|
333
+ invoke_dsl key, value
334
+ end
335
+ else
336
+ warning "unsupported property type ! (of \"#{a}\", on \"#{@current[:title]}\")"
337
+ end
338
+ end
339
+ end
340
+
341
+ # collect target files to be compiled ...
342
+ def target(*arg)
343
+ arg.each do |a|
344
+ if a.is_a?(String)
345
+ target_add a
346
+ elsif a.is_a?(Hash)
347
+ a.each do |depend, target|
348
+ if target.is_a?(String)
349
+ target_add target, depend
350
+ elsif target.is_a?(Array)
351
+ target.each do |t|
352
+ target_add t, depend if t.is_a?(String)
353
+ end
354
+ end
355
+ end
356
+ else
357
+ warning "unsupported target type ! (of \"#{a}\", on \"#{@current[:title]}\")"
358
+ end
359
+ end
360
+ end
361
+
362
+ def get_targets()
363
+ targets = []
364
+ @targets.each do |t|
365
+ targets << t if target_dep_ok?(t)
366
+ end
367
+ targets
368
+ end
369
+
370
+ # ----------------------------------------------------------------------------------------------
371
+
372
+
373
+ # search plugin config keys from @conf, if found, call plugin.
374
+ # the plugin name is part of config key: RBUILD_PLUGIN_XXX
375
+ # if file is provided, use file as file name, otherwise use
376
+ # the value of @conf[:RBUILD_PLUGIN_XXX]
377
+ def export(file = nil)
378
+ @conf.each do |key, node|
379
+ if node[:no_export] && key.to_s =~ /RBUILD_PLUGIN_(.*)/
380
+ plugin = $1.downcase
381
+ @dirstack << @curpath
382
+ @curpath = @top_rconfig_path
383
+ Dir.chdir @curpath
384
+ if self.methods.include?(plugin)
385
+ self.send(plugin, file || get_node_value(node).to_s)
386
+ else
387
+ warning "plugin \"#{plugin}\" not installed ?"
388
+ end
389
+ Dir.chdir @dirstack.pop
390
+ end
391
+ end
392
+ end
393
+
394
+ private
395
+
396
+ def anonymous_key()
397
+ @anonymous_idx ||= 0
398
+ key = "ANONYMOUS_#{@anonymous_idx}".to_sym
399
+ @anonymous_idx += 1
400
+ key
401
+ end
402
+
403
+ def target_dep_ok?(t)
404
+ return true unless @target_deps[t]
405
+ @target_deps[t].each do |dep|
406
+ node = @conf[dep]
407
+ return false unless dep_node_ok?(node)
408
+ end
409
+ true
410
+ end
411
+
412
+ # check node's dep depandancy, and node itself.
413
+ def dep_node_ok?(dep_node, exceptions = [])
414
+ # no such node ? dep fail !
415
+ return false unless dep_node
416
+ return true if exceptions.include?(dep_node[:key])
417
+
418
+ # check my depends first
419
+ dep_node[:depends].each do |dep|
420
+ next if exceptions.include?(dep)
421
+ return false unless dep_node_ok?(@conf[dep], exceptions)
422
+ end
423
+
424
+ case dep_node[:id]
425
+ when :menu, :group
426
+ return true # for :menu, :group, or :choice, if depends are ok, I'm ok.
427
+ when :config, :choice
428
+ return node_no?(dep_node) ? false : true
429
+ end
430
+ false # unknown type ? false !
431
+ end
432
+
433
+ # check node's dep depandancy only.
434
+ def node_dep_ok?(node, exceptions = [])
435
+ # check my depends first
436
+ node[:depends].each do |dep|
437
+ return false unless dep_node_ok?(@conf[dep], exceptions)
438
+ end
439
+ return true
440
+ end
441
+
442
+ def target_add(target, depend = nil)
443
+ Dir.glob(@curpath + '/' + target) do |t|
444
+ unless @targets_cache[t]
445
+ @targets << t
446
+ @targets_cache[t] = true
447
+ @target_deps[t] = []
448
+ end
449
+
450
+ case @current[:id]
451
+ when :choice, :config
452
+ @target_deps[t] << @current[:key] unless @target_deps[t].include?(@current[:key])
453
+ end
454
+
455
+ if depend
456
+ if depend.is_a?(Array)
457
+ depend.each {|d| @target_deps[t] << d unless @target_deps[t].include?(t) }
458
+ else
459
+ @target_deps[t] << depend unless @target_deps[t].include?(depend)
460
+ end
461
+ end
462
+ end
463
+ end
464
+
465
+ def invoke_dsl(name, param = nil)
466
+ name_save = name
467
+ name = name.to_s
468
+ if param
469
+ if self.respond_to?(name)
470
+ if param.is_a?(Array)
471
+ self.send(name, *param)
472
+ else
473
+ self.send(name, param)
474
+ end
475
+ else
476
+ @current[name_save] = param
477
+ end
478
+ else
479
+ if self.respond_to?(name)
480
+ self.send(name)
481
+ else
482
+ @current[name_save] = true
483
+ end
484
+ end
485
+ end
486
+
487
+ def exec_rconfig_file(fn)
488
+ full = File.expand_path(fn)
489
+ unless @sources.include?(full)
490
+ @sources << full
491
+ File.open(fn) do |f|
492
+ @dirstack.push @curpath
493
+ @curpath = File.expand_path(File.dirname(fn))
494
+ Dir.chdir(@curpath)
495
+ begin
496
+ eval f.read
497
+ rescue Exception => e
498
+ error "RConfig file error? #{e.to_s}"
499
+ error "from file: #{abs_file_name(@curpath + '/' + File.basename(fn))}"
500
+ # error "Backtrace: #{e.backtrace.join("\n")}"
501
+ end
502
+ @curpath = @dirstack.pop
503
+ Dir.chdir(@curpath)
504
+ end
505
+ end
506
+ end
507
+
508
+ # conver the args to key,desc pair
509
+ # so you can pass parameters like:
510
+ # :KEY => "desc"
511
+ # or :KEY
512
+ # or "desc"
513
+ # to menu/choice/config/group
514
+ def args_to_key_desc(args)
515
+ attr_cb = nil
516
+ if args.size == 0 # example, group do ... end
517
+ key = anonymous_key()
518
+ desc = ""
519
+ elsif args.size == 1
520
+ if args[0].is_a?(Hash)
521
+ # example, choice :CPU=> "choice CPU", :no_export do ... end
522
+ key = args[0].keys[0]
523
+ desc = args[0][key]
524
+ elsif args[0].is_a?(Symbol)
525
+ # example, config :ENABLE_XXX do ... end
526
+ key = args[0]
527
+ desc = key.to_s
528
+ elsif args[0].is_a?(String)
529
+ # example, menu "Config AAA function" do ... end
530
+ key = anonymous_key()
531
+ desc = args[0].to_s
532
+ else
533
+ warning "Invalid parameters on \"#{@current[:title]}\""
534
+ end
535
+ elsif args.size > 1
536
+ # for args.size > 1, the first arg MUST always the symbol!
537
+ unless args[0].is_a?(Symbol)
538
+ warning "Invalid parameters on \"#{@current[:title]}\""
539
+ else
540
+ key = args[0]
541
+ desc = key.to_s
542
+ attr_cb = proc do
543
+ args.each do |a|
544
+ next if a == args[0]
545
+ if a.is_a?(Hash)
546
+ # example, config :MODULE_A, :no_export, :title => "enable module a", :default => true
547
+ a.each do |name, param|
548
+ invoke_dsl name, param
549
+ end
550
+ else
551
+ invoke_dsl a
552
+ end
553
+ end
554
+ end
555
+ end
556
+ end
557
+ return key, desc, attr_cb
558
+ end
559
+
560
+ # process node's select/unselect instruction
561
+ def process_sel_unsel(node)
562
+ node[:selects].each {|sel| do_select(node, sel)}
563
+ node[:unselects].each {|sel| do_unselect(node, sel)}
564
+ end
565
+
566
+ # search in node's ancestors.
567
+ # if there is a ancestor has 'key', return ancestor, otherwire reutrn nil
568
+ def search_ancestor(node, key)
569
+ worker = node
570
+ while worker && worker != top_node()
571
+ if key.is_a?(Array)
572
+ return worker if key.include?(worker[:key])
573
+ else
574
+ return worker if key == worker[:key]
575
+ end
576
+ worker = @conf[worker[:parent]]
577
+ end
578
+ return nil
579
+ end
580
+
581
+ # node: just for reference.
582
+ # key: the node to be selected !
583
+ def do_select(node, key)
584
+ if @conf[key].nil?
585
+ warning "Can't not select \"#{key}\" from \"#{node[:title]}\", no such node ?"
586
+ else
587
+ # search up to the top, to prevent select/unselect ancestors
588
+ set_node_yes(@conf[key]) unless search_ancestor(node, key)
589
+ end
590
+ end
591
+
592
+ # node: just for reference.
593
+ # key: the node to be unselected !
594
+ def do_unselect(node, key)
595
+ if @conf[key].nil?
596
+ warning "Can't not unselect \"#{key}\" from \"#{node[:title]}\", no such node ?"
597
+ else
598
+ # search up to the top, to prevent select/unselect ancestors
599
+ set_node_no(@conf[key]) unless search_ancestor(node, key)
600
+ end
601
+ end
602
+
603
+ def log_to_file(desc)
604
+ log_file = @top_worker_path + '/' + DEFAULT_LOG_FILE
605
+ if desc
606
+ File.open(log_file, "a+") do |f|
607
+ f.puts desc
608
+ end
609
+ else
610
+ FileUtils.rm_f(log_file) if File.exist?(log_file)
611
+ end
612
+ end
613
+
614
+ # log warning message
615
+ def warning(desc)
616
+ log_to_file "Warning: " + desc
617
+ end
618
+
619
+ # log error message
620
+ def error(desc)
621
+ @have_error = true
622
+ @errmsg ||= ""
623
+ @errmsg += "*** " + desc + "\n"
624
+ log_to_file "Error: " + desc
625
+ end
626
+
627
+ def have_error?
628
+ @have_error
629
+ end
630
+
631
+ # process current DSL calling
632
+ def process_node(node, arrt_cb, block)
633
+ old = @conf[node[:key]]
634
+ if old # node exist ?
635
+ @stack.push @current
636
+ @current = old
637
+ arrt_cb.call if arrt_cb
638
+ block.call if block
639
+ @current = @stack.pop
640
+ else
641
+ @nodes << node
642
+ node[:children] ||= []
643
+ node[:depends] ||= []
644
+ node[:selects] ||= []
645
+ node[:unselects] ||= []
646
+ node[:depends] << @current[:key] if @current[:key]
647
+ @current[:children] << node[:key]
648
+ node[:parent] = @current[:key]
649
+ @stack.push @current
650
+ @conf[node[:key]] = node
651
+ @current = node
652
+ arrt_cb.call if arrt_cb
653
+ block.call if block
654
+ @current = @stack.pop
655
+ end
656
+ end
657
+
658
+ def windows?
659
+ RUBY_PLATFORM =~ /win/
660
+ end
661
+
662
+ # set node's value, the value must not be 'false' or 'nil'
663
+ # if you want to unselect a choice, use 'set_node_no' instead
664
+ def set_node_value(node, value)
665
+ if value
666
+ node[:hit] = true
667
+ if node[:id] == :choice && node[:value] != value
668
+ process_sel_unsel(node)
669
+ if node[:children].size > 0
670
+ # for :choice which have children, the value is the child's key.
671
+ node[:children].each do |child|
672
+ if child == value
673
+ node[:value] = child
674
+ set_node_yes(@conf[child])
675
+ else
676
+ set_node_no(@conf[child])
677
+ end
678
+ end
679
+ else
680
+ # for :choice which don't have children, the value is the value :)
681
+ node[:value] = value
682
+ end
683
+ end
684
+
685
+ parent = @conf[node[:parent]]
686
+ if parent[:id] == :choice && parent[:value] != node[:key]
687
+ set_node_value(parent, node[:key])
688
+ end
689
+ end
690
+ end
691
+
692
+ # search ancestor which :id is included in ids.
693
+ def search_ancestor_with_ids(node, ids)
694
+ parent = @conf[node[:parent]]
695
+ return nil if parent == top_node()
696
+ return parent if ids.include?(parent[:id])
697
+ return search_ancestor_with_ids(parent, ids)
698
+ end
699
+
700
+ # set the node's value to {yes}
701
+ def set_node_yes(node)
702
+ if node_no?(node)
703
+ if node[:id] == :config
704
+ node[:hit] = true
705
+ process_sel_unsel(node)
706
+ parent = search_ancestor_with_ids(node, [:choice, :config])
707
+ if parent
708
+ if parent[:id] == :choice
709
+ set_node_value(parent, node[:key])
710
+ elsif parent[:id] == :config
711
+ set_node_yes(parent) if node_no?(parent)
712
+ end
713
+ end
714
+ else
715
+ error "Bug! why call 'set_node_yes' on a non-config node (#{node[:title]}) ?"
716
+ end
717
+ end
718
+ end
719
+
720
+ # set the node's value to {no}
721
+ def set_node_no(node)
722
+ return unless node
723
+ if node[:id] == :menu || node[:id] == :group
724
+ node[:children].each do |child|
725
+ set_node_no(@conf[child])
726
+ end
727
+ else
728
+ if node_yes?(node)
729
+ node[:hit] = nil
730
+ if node[:id] == :config
731
+ # set parent to "no" if have a :choice parent.
732
+ parent = search_ancestor_with_ids(node, [:choice])
733
+ if parent && parent[:value] == node[:key]
734
+ set_node_no(parent)
735
+ end
736
+ # set children to "no"
737
+ node[:children].each do |child|
738
+ set_node_no(@conf[child])
739
+ end
740
+ elsif node[:id] == :choice
741
+ node[:value] = nil
742
+ node[:children].each do |child|
743
+ set_node_no(@conf[child])
744
+ end
745
+ else
746
+ end
747
+ end
748
+ end
749
+ end
750
+
751
+ # node's value is {yes} ?
752
+ def node_yes?(node)
753
+ node[:hit] ? true : false
754
+ end
755
+
756
+ # node's value is {no} ?
757
+ def node_no?(node)
758
+ node[:hit] ? false : true
759
+ end
760
+
761
+ # toggle node's value
762
+ # if the node's value is {yes} or {value}, change to {no}
763
+ # if the node's value is {no}, change to {yes}
764
+ def toggle_node(node)
765
+ if node_yes?(node)
766
+ set_node_no(node)
767
+ else
768
+ set_node_yes(node)
769
+ end
770
+ end
771
+
772
+ # get the viewable nodes under 'parent'
773
+ # will append to the 'list_nodes' if given
774
+ def get_list_nodes(parent, list_nodes = [], level = 1)
775
+ children = parent[:children]
776
+ if children
777
+ children.each do |child|
778
+ node = @conf[child]
779
+ unless node.nil? || node[:hidden]
780
+ if parent[:id] == :choice
781
+ if node_dep_ok?(node, [node[:key], parent[:key]])
782
+ list_nodes << {:node => node, :level => level}
783
+ end
784
+ else
785
+ if node_dep_ok?(node)
786
+ if node[:id] == :group || node[:id] == :config
787
+ list_nodes << {:node => node, :level => level}
788
+ get_list_nodes(node, list_nodes, level + 1) if node[:children].size > 0
789
+ else
790
+ list_nodes << {:node => node, :level => level}
791
+ end
792
+ end
793
+ end # end of if partne[:id] == :choice
794
+ end # end of child.nil? || child[:hidden]
795
+ end # end of children.each
796
+ end
797
+ list_nodes
798
+ end
799
+
800
+ # check whether there are any conflit/invalid default setting.
801
+ def check_defaults
802
+ @nodes.each do |node|
803
+ if node[:id] == :choice
804
+ if node[:value] && node[:children].size > 0
805
+ children = node[:children]
806
+ found = nil
807
+ children.each do |child|
808
+ if child == node[:value]
809
+ found = @conf[child]
810
+ break
811
+ end
812
+ end
813
+ if found
814
+ children.each do |child|
815
+ @conf[child][:value] = false
816
+ end
817
+ found[:value] = true
818
+ else
819
+ warning "'#{node[:title]}' default value \"#{node[:value]}\" is invalid !"
820
+ end
821
+ end
822
+ end
823
+ end
824
+ end
825
+
826
+ def footer_msg(msg = nil)
827
+ @footer_msg = msg if msg
828
+ @footer_msg
829
+ end
830
+
831
+ def footer_add(msg)
832
+ @footer_msg ||= ""
833
+ @footer_msg += ("\n" + msg)
834
+ end
835
+
836
+ def footer_clear()
837
+ @footer_msg = nil
838
+ end
839
+ end
840
+
841
+ end
842
+
843
+ if __FILE__ == $0
844
+ Dir.chdir File.expand_path(File.dirname(__FILE__))
845
+ rconf = RBuild::RConfig.new('../example/RConfig')
846
+ rconf.merge!
847
+ rconf.menuconfig()
848
+ end