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 +81 -0
- data/README.zh_CN +203 -0
- data/README.zh_CN~ +197 -0
- data/README~ +79 -0
- data/example2/Rakefile.rb +167 -0
- data/lib/plugins/rbuild_export_c.rb +78 -0
- data/lib/plugins/rbuild_export_targets_list.rb +49 -0
- data/lib/rbuild.rb +848 -0
- data/lib/rbuild_menuconfig.rb +397 -0
- metadata +61 -0
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
|
+
|
data/lib/rbuild.rb
ADDED
@@ -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
|