rbuild 0.1.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.
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