autoproj 1.10.2 → 1.11.0.b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +9 -0
- data/Rakefile +4 -0
- data/bin/aup +5 -1
- data/bin/autolocate +1 -82
- data/bin/autoproj +6 -56
- data/bin/autoproj-bootstrap +31 -3
- data/bin/autoproj-cache +3 -60
- data/bin/autoproj-clean +1 -1
- data/bin/autoproj-create-set +0 -0
- data/bin/autoproj-doc +1 -1
- data/bin/autoproj-envsh +0 -0
- data/bin/autoproj-list +1 -1
- data/bin/autoproj-locate +17 -16
- data/bin/autoproj-query +0 -0
- data/bin/autoproj-show +0 -0
- data/bin/autoproj-switch-config +23 -0
- data/bin/autoproj-test +2 -2
- data/bin/autoproj_bootstrap +354 -210
- data/bin/autoproj_bootstrap.in +8 -0
- data/bin/autoproj_stress_test +1 -1
- data/lib/autoproj.rb +7 -0
- data/lib/autoproj/autobuild.rb +14 -35
- data/lib/autoproj/build_option.rb +104 -0
- data/lib/autoproj/cmdline.rb +62 -355
- data/lib/autoproj/configuration.rb +161 -0
- data/lib/autoproj/gitorious.rb +31 -20
- data/lib/autoproj/installation_manifest.rb +7 -1
- data/lib/autoproj/manifest.rb +124 -342
- data/lib/autoproj/ops/build.rb +107 -0
- data/lib/autoproj/ops/cache.rb +82 -0
- data/lib/autoproj/ops/configuration.rb +302 -0
- data/lib/autoproj/ops/loader.rb +97 -0
- data/lib/autoproj/ops/main_config_switcher.rb +271 -0
- data/lib/autoproj/ops/tools.rb +54 -0
- data/lib/autoproj/options.rb +26 -178
- data/lib/autoproj/osdeps.rb +49 -9
- data/lib/autoproj/package_set.rb +168 -87
- data/lib/autoproj/system.rb +6 -23
- data/lib/autoproj/variable_expansion.rb +0 -1
- data/lib/autoproj/vcs_definition.rb +23 -0
- data/lib/autoproj/version.rb +1 -1
- metadata +17 -7
@@ -0,0 +1,97 @@
|
|
1
|
+
module Autoproj
|
2
|
+
module Ops
|
3
|
+
class Loader
|
4
|
+
# @return [Array<String>] information about what is being loaded
|
5
|
+
attr_reader :file_stack
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@file_stack = Array.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def in_package_set(pkg_set, path)
|
12
|
+
@file_stack.push([pkg_set, File.expand_path(path).gsub(/^#{Regexp.quote(Autoproj.root_dir)}\//, '')])
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
@file_stack.pop
|
16
|
+
end
|
17
|
+
|
18
|
+
def filter_load_exception(error, package_set, path)
|
19
|
+
raise error if Autoproj.verbose
|
20
|
+
rx_path = Regexp.quote(path)
|
21
|
+
if error_line = error.backtrace.find { |l| l =~ /#{rx_path}/ }
|
22
|
+
if line_number = Integer(/#{rx_path}:(\d+)/.match(error_line)[1])
|
23
|
+
line_number = "#{line_number}:"
|
24
|
+
end
|
25
|
+
|
26
|
+
if package_set.local?
|
27
|
+
raise ConfigError.new(path), "#{path}:#{line_number} #{error.message}", error.backtrace
|
28
|
+
else
|
29
|
+
raise ConfigError.new(path), "#{File.basename(path)}(package_set=#{package_set.name}):#{line_number} #{error.message}", error.backtrace
|
30
|
+
end
|
31
|
+
else
|
32
|
+
raise error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the information about the file that is currently being loaded
|
37
|
+
#
|
38
|
+
# The return value is [package_set, path], where +package_set+ is the
|
39
|
+
# PackageSet instance and +path+ is the path of the file w.r.t. the autoproj
|
40
|
+
# root directory
|
41
|
+
def current_file
|
42
|
+
if file = @file_stack.last
|
43
|
+
file
|
44
|
+
else raise ArgumentError, "not in a #in_package_set context"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# The PackageSet object representing the package set that is currently being
|
49
|
+
# loaded
|
50
|
+
def current_package_set
|
51
|
+
current_file.first
|
52
|
+
end
|
53
|
+
|
54
|
+
# Load a definition file from a package set
|
55
|
+
#
|
56
|
+
# If any error is detected, the backtrace will be filtered so that it is
|
57
|
+
# easier to understand by the user. Moreover, if +source+ is non-nil, the
|
58
|
+
# package set name will be mentionned.
|
59
|
+
#
|
60
|
+
# @param [PackageSet] pkg_set
|
61
|
+
# @param [Array<String>] path
|
62
|
+
def load(pkg_set, *path)
|
63
|
+
path = File.join(*path)
|
64
|
+
in_package_set(pkg_set, File.expand_path(path).gsub(/^#{Regexp.quote(Autoproj.root_dir)}\//, '')) do
|
65
|
+
begin
|
66
|
+
Kernel.load path
|
67
|
+
rescue Interrupt
|
68
|
+
raise
|
69
|
+
rescue ConfigError => e
|
70
|
+
raise
|
71
|
+
rescue Exception => e
|
72
|
+
filter_load_exception(e, pkg_set, path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Load a definition file from a package set if the file is present
|
78
|
+
#
|
79
|
+
# (see load)
|
80
|
+
def load_if_present(pkg_set, *path)
|
81
|
+
path = File.join(*path)
|
82
|
+
if File.file?(path)
|
83
|
+
load(pkg_set, *path)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Singleton object that maintains the loading state
|
89
|
+
#
|
90
|
+
# Note that it is here hopefully temporarily. All the Ops classes should
|
91
|
+
# have their loader object given at construction time
|
92
|
+
def self.loader
|
93
|
+
@loader ||= Loader.new
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,271 @@
|
|
1
|
+
module Autoproj
|
2
|
+
module Ops
|
3
|
+
# Operations that modify the source of the main configuration (bootstrap
|
4
|
+
# and switch-config)
|
5
|
+
class MainConfigSwitcher
|
6
|
+
attr_reader :root_dir
|
7
|
+
|
8
|
+
def initialize(root_dir)
|
9
|
+
@root_dir = root_dir
|
10
|
+
end
|
11
|
+
|
12
|
+
# Set of directory entries that are expected to be present in the
|
13
|
+
# directory in which the user bootstraps
|
14
|
+
#
|
15
|
+
# @see check_root_dir_empty
|
16
|
+
EXPECTED_ROOT_ENTRIES = [".", "..", "autoproj_bootstrap",
|
17
|
+
".gems", "bootstrap.sh", ENV_FILENAME].to_set
|
18
|
+
|
19
|
+
# Verifies that {#root_dir} contains only expected entries, to make
|
20
|
+
# sure that the user bootstraps into a new directory
|
21
|
+
#
|
22
|
+
# If the environment variable AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR
|
23
|
+
# is set to 1, the check is skipped
|
24
|
+
def check_root_dir_empty
|
25
|
+
return if ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] == '1'
|
26
|
+
|
27
|
+
require 'set'
|
28
|
+
curdir_entries = Dir.entries('.').to_set - EXPECTED_ROOT_ENTRIES
|
29
|
+
return if curdir_entries.empty?
|
30
|
+
|
31
|
+
while true
|
32
|
+
print "The current directory is not empty, continue bootstrapping anyway ? [yes] "
|
33
|
+
STDOUT.flush
|
34
|
+
answer = STDIN.readline.chomp
|
35
|
+
if answer == "no"
|
36
|
+
raise Interrupt, "Interrupted by user"
|
37
|
+
end
|
38
|
+
|
39
|
+
if answer == "" || answer == "yes"
|
40
|
+
# Set this environment variable since we might restart
|
41
|
+
# autoproj later on.
|
42
|
+
ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] = '1'
|
43
|
+
return
|
44
|
+
else
|
45
|
+
STDOUT.puts "invalid answer. Please answer 'yes' or 'no'"
|
46
|
+
STDOUT.flush
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Validates the environment variable AUTOPROJ_CURRENT_ROOT during a
|
52
|
+
# bootstrap
|
53
|
+
#
|
54
|
+
# AUTOPROJ_CURRENT_ROOT must be set to either the new root
|
55
|
+
# ({root_dir}) or a root that we are reusing
|
56
|
+
#
|
57
|
+
# @param [Array<String>] reuse set of autoproj roots that are being reused
|
58
|
+
# @raise ConfigError
|
59
|
+
def validate_autoproj_current_root(reuse)
|
60
|
+
if current_root = ENV['AUTOPROJ_CURRENT_ROOT']
|
61
|
+
# Allow having a current root only if it is being reused
|
62
|
+
if (current_root != root_dir) && !reuse.include?(current_root)
|
63
|
+
Autoproj.error "the env.sh from #{ENV['AUTOPROJ_CURRENT_ROOT']} seem to already be sourced"
|
64
|
+
Autoproj.error "start a new shell and try to bootstrap again"
|
65
|
+
Autoproj.error
|
66
|
+
Autoproj.error "you are allowed to boostrap from another autoproj installation"
|
67
|
+
Autoproj.error "only if you reuse it with the --reuse flag"
|
68
|
+
raise ConfigError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def handle_bootstrap_options(args)
|
74
|
+
reuse = []
|
75
|
+
parser = OptionParser.new do |opt|
|
76
|
+
opt.on '--reuse [DIR]', "reuse the given autoproj installation (can be given multiple times). If given without arguments, reuse the currently active install (#{ENV['AUTOPROJ_CURRENT_ROOT']})" do |path|
|
77
|
+
path ||= ENV['AUTOPROJ_CURRENT_ROOT']
|
78
|
+
|
79
|
+
path = File.expand_path(path)
|
80
|
+
if !File.directory?(path) || !File.directory?(File.join(path, 'autoproj'))
|
81
|
+
raise ConfigError.new, "#{path} does not look like an autoproj installation"
|
82
|
+
end
|
83
|
+
reuse << path
|
84
|
+
end
|
85
|
+
end
|
86
|
+
args = parser.parse(args)
|
87
|
+
return args, reuse
|
88
|
+
end
|
89
|
+
|
90
|
+
def bootstrap(*args)
|
91
|
+
if File.exists?(File.join(root_dir, 'autoproj', "manifest"))
|
92
|
+
raise ConfigError.new, "this installation is already bootstrapped. Remove the autoproj directory if it is not the case"
|
93
|
+
end
|
94
|
+
|
95
|
+
check_root_dir_empty
|
96
|
+
args, reuse = handle_bootstrap_options(args)
|
97
|
+
validate_autoproj_current_root(reuse)
|
98
|
+
|
99
|
+
Autoproj.root_dir = root_dir
|
100
|
+
Autobuild.prefix = Autoproj.build_dir
|
101
|
+
Autobuild.srcdir = Autoproj.root_dir
|
102
|
+
Autobuild.logdir = File.join(Autobuild.prefix, 'log')
|
103
|
+
|
104
|
+
Autoproj.manifest = Manifest.new
|
105
|
+
load_autoprojrc
|
106
|
+
Autoproj.prepare_environment
|
107
|
+
|
108
|
+
Autoproj::OSDependencies.define_osdeps_mode_option
|
109
|
+
osdeps = Autoproj::OSDependencies.load_default
|
110
|
+
if osdeps_forced_mode
|
111
|
+
osdeps.osdeps_mode = osdeps_forced_mode
|
112
|
+
end
|
113
|
+
osdeps.osdeps_mode
|
114
|
+
|
115
|
+
CmdLine.update_myself :force => true, :restart_on_update => false
|
116
|
+
Autoproj.change_option 'reused_autoproj_installations', reuse, true
|
117
|
+
Autoproj.export_env_sh
|
118
|
+
|
119
|
+
# If we are not getting the installation setup from a VCS, copy the template
|
120
|
+
# files
|
121
|
+
if args.empty? || args.size == 1
|
122
|
+
sample_dir = File.expand_path(File.join("..", "..", "samples"), File.dirname(__FILE__))
|
123
|
+
FileUtils.cp_r File.join(sample_dir, "autoproj"), "autoproj"
|
124
|
+
end
|
125
|
+
|
126
|
+
if args.size == 1 # the user asks us to download a manifest
|
127
|
+
manifest_url = args.first
|
128
|
+
Autoproj.message("autoproj: downloading manifest file #{manifest_url}", :bold)
|
129
|
+
manifest_data =
|
130
|
+
begin open(manifest_url) { |file| file.read }
|
131
|
+
rescue
|
132
|
+
# Delete the autoproj directory
|
133
|
+
FileUtils.rm_rf 'autoproj'
|
134
|
+
raise ConfigError.new, "cannot read file / URL #{manifest_url}, did you mean 'autoproj bootstrap VCSTYPE #{manifest_url}' ?"
|
135
|
+
end
|
136
|
+
|
137
|
+
File.open(File.join(Autoproj.config_dir, "manifest"), "w") do |io|
|
138
|
+
io.write(manifest_data)
|
139
|
+
end
|
140
|
+
|
141
|
+
elsif args.size >= 2 # is a VCS definition for the manifest itself ...
|
142
|
+
type, url, *options = *args
|
143
|
+
url = VCSDefinition.to_absolute_url(url, Dir.pwd)
|
144
|
+
do_switch_config(false, type, url, *options)
|
145
|
+
end
|
146
|
+
Autoproj.save_config
|
147
|
+
end
|
148
|
+
|
149
|
+
def switch_config(*args)
|
150
|
+
Autoproj.load_config
|
151
|
+
if Autoproj.has_config_key?('manifest_source')
|
152
|
+
vcs = VCSDefinition.from_raw(Autoproj.user_config('manifest_source'))
|
153
|
+
end
|
154
|
+
|
155
|
+
if args.first =~ /^(\w+)=/
|
156
|
+
# First argument is an option string, we are simply setting the
|
157
|
+
# options without changing the type/url
|
158
|
+
type, url = vcs.type, vcs.url
|
159
|
+
else
|
160
|
+
type, url = args.shift, args.shift
|
161
|
+
end
|
162
|
+
options = args
|
163
|
+
|
164
|
+
url = VCSDefinition.to_absolute_url(url)
|
165
|
+
|
166
|
+
if vcs && (vcs.type == type && vcs.url == url)
|
167
|
+
# Don't need to do much: simply change the options and save the config
|
168
|
+
# file, the VCS handler will take care of the actual switching
|
169
|
+
vcs_def = Autoproj.user_config('manifest_source')
|
170
|
+
options.each do |opt|
|
171
|
+
opt_name, opt_value = opt.split('=')
|
172
|
+
vcs_def[opt_name] = opt_value
|
173
|
+
end
|
174
|
+
# Validate the VCS definition, but save the hash as-is
|
175
|
+
VCSDefinition.from_raw(vcs_def)
|
176
|
+
Autoproj.change_option "manifest_source", vcs_def.dup, true
|
177
|
+
Autoproj.save_config
|
178
|
+
true
|
179
|
+
|
180
|
+
else
|
181
|
+
# We will have to delete the current autoproj directory. Ask the user.
|
182
|
+
opt = Autoproj::BuildOption.new("delete current config", "boolean",
|
183
|
+
Hash[:default => "false",
|
184
|
+
:doc => "delete the current configuration ? (required to switch)"], nil)
|
185
|
+
|
186
|
+
return if !opt.ask(nil)
|
187
|
+
|
188
|
+
Dir.chdir(Autoproj.root_dir) do
|
189
|
+
do_switch_config(true, type, url, *options)
|
190
|
+
end
|
191
|
+
false
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def do_switch_config(delete_current, type, url, *options)
|
196
|
+
vcs_def = Hash.new
|
197
|
+
vcs_def[:type] = type
|
198
|
+
vcs_def[:url] = VCSDefinition.to_absolute_url(url)
|
199
|
+
options.each do |opt|
|
200
|
+
name, value = opt.split("=")
|
201
|
+
if value =~ /^\d+$/
|
202
|
+
value = Integer(value)
|
203
|
+
end
|
204
|
+
|
205
|
+
vcs_def[name] = value
|
206
|
+
end
|
207
|
+
|
208
|
+
vcs = VCSDefinition.from_raw(vcs_def)
|
209
|
+
|
210
|
+
# Install the OS dependencies required for this VCS
|
211
|
+
Autoproj::OSDependencies.define_osdeps_mode_option
|
212
|
+
osdeps = Autoproj::OSDependencies.load_default
|
213
|
+
osdeps.osdeps_mode
|
214
|
+
osdeps.install([vcs.type])
|
215
|
+
|
216
|
+
# Now check out the actual configuration
|
217
|
+
config_dir = File.join(Dir.pwd, "autoproj")
|
218
|
+
if delete_current
|
219
|
+
# Find a backup name for it
|
220
|
+
backup_base_name = backup_name = "#{config_dir}.bak"
|
221
|
+
index = 0
|
222
|
+
while File.directory?(backup_name)
|
223
|
+
backup_name = "#{backup_base_name}-#{index}.bak"
|
224
|
+
index += 1
|
225
|
+
end
|
226
|
+
|
227
|
+
FileUtils.mv config_dir, backup_name
|
228
|
+
end
|
229
|
+
Ops::Configuration.update_configuration_repository(
|
230
|
+
vcs,
|
231
|
+
"autoproj main configuration",
|
232
|
+
config_dir)
|
233
|
+
|
234
|
+
# If the new tree has a configuration file, load it and set
|
235
|
+
# manifest_source
|
236
|
+
Autoproj.load_config
|
237
|
+
|
238
|
+
# And now save the options: note that we keep the current option set even
|
239
|
+
# though we switched configuration. This is not a problem as undefined
|
240
|
+
# options will not be reused
|
241
|
+
#
|
242
|
+
# TODO: cleanup the options to only keep the relevant ones
|
243
|
+
vcs_def = Hash['type' => type, 'url' => url]
|
244
|
+
options.each do |opt|
|
245
|
+
opt_name, opt_val = opt.split '='
|
246
|
+
vcs_def[opt_name] = opt_val
|
247
|
+
end
|
248
|
+
# Validate the option hash, just in case
|
249
|
+
VCSDefinition.from_raw(vcs_def)
|
250
|
+
# Save the new options
|
251
|
+
Autoproj.change_option "manifest_source", vcs_def.dup, true
|
252
|
+
Autoproj.save_config
|
253
|
+
|
254
|
+
rescue Exception => e
|
255
|
+
Autoproj.error "switching configuration failed: #{e.message}"
|
256
|
+
if backup_name
|
257
|
+
Autoproj.error "restoring old configuration"
|
258
|
+
FileUtils.rm_rf config_dir if config_dir
|
259
|
+
FileUtils.mv backup_name, config_dir
|
260
|
+
end
|
261
|
+
raise
|
262
|
+
ensure
|
263
|
+
if backup_name
|
264
|
+
FileUtils.rm_rf backup_name
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Autoproj
|
2
|
+
module Ops
|
3
|
+
module Tools
|
4
|
+
# Data structure used to use autobuild importers without a package, to
|
5
|
+
# import configuration data.
|
6
|
+
#
|
7
|
+
# It has to match the interface of Autobuild::Package that is relevant
|
8
|
+
# for importers
|
9
|
+
class FakePackage < Autobuild::Package
|
10
|
+
attr_reader :srcdir
|
11
|
+
attr_reader :importer
|
12
|
+
|
13
|
+
# Used by the autobuild importers
|
14
|
+
attr_accessor :updated
|
15
|
+
|
16
|
+
def autoproj_name
|
17
|
+
name
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(text_name, srcdir, importer = nil)
|
21
|
+
super(text_name)
|
22
|
+
@srcdir = srcdir
|
23
|
+
@importer = importer
|
24
|
+
@@packages.delete(text_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def import(only_local = false)
|
28
|
+
importer.import(self, only_local)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_stat(*args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates an autobuild package whose job is to allow the import of a
|
36
|
+
# specific repository into a given directory.
|
37
|
+
#
|
38
|
+
# +vcs+ is the VCSDefinition file describing the repository, +text_name+
|
39
|
+
# the name used when displaying the import progress, +pkg_name+ the
|
40
|
+
# internal name used to represent the package and +into+ the directory
|
41
|
+
# in which the package should be checked out.
|
42
|
+
def create_autobuild_package(vcs, text_name, into)
|
43
|
+
importer = vcs.create_autobuild_importer
|
44
|
+
FakePackage.new(text_name, into, importer)
|
45
|
+
|
46
|
+
rescue Autobuild::ConfigException => e
|
47
|
+
raise ConfigError.new, "cannot import #{text_name}: #{e.message}", e.backtrace
|
48
|
+
end
|
49
|
+
|
50
|
+
extend Tools
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
data/lib/autoproj/options.rb
CHANGED
@@ -1,203 +1,51 @@
|
|
1
1
|
module Autoproj
|
2
|
-
|
3
|
-
|
4
|
-
class << self
|
5
|
-
# Programatically overriden autoproj options
|
6
|
-
#
|
7
|
-
# @see override_option
|
8
|
-
attr_reader :option_overrides
|
9
|
-
end
|
10
|
-
@option_overrides = Hash.new
|
11
|
-
|
12
|
-
# Programatically override a user-selected option without changing the
|
13
|
-
# configuration file
|
2
|
+
# @deprecated use config.override instead
|
14
3
|
def self.override_option(option_name, value)
|
15
|
-
|
4
|
+
config.override(option_name, value)
|
16
5
|
end
|
17
|
-
|
18
|
-
class BuildOption
|
19
|
-
attr_reader :name
|
20
|
-
attr_reader :type
|
21
|
-
attr_reader :options
|
22
|
-
|
23
|
-
attr_reader :validator
|
24
|
-
|
25
|
-
TRUE_STRINGS = %w{on yes y true}
|
26
|
-
FALSE_STRINGS = %w{off no n false}
|
27
|
-
def initialize(name, type, options, validator)
|
28
|
-
@name, @type, @options = name.to_str, type.to_str, options.to_hash
|
29
|
-
@validator = validator.to_proc if validator
|
30
|
-
if !BuildOption.respond_to?("validate_#{type}")
|
31
|
-
raise ConfigError.new, "invalid option type #{type}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def short_doc
|
36
|
-
if short_doc = options[:short_doc]
|
37
|
-
short_doc
|
38
|
-
elsif doc = options[:doc]
|
39
|
-
if doc.respond_to?(:to_ary) then doc.first
|
40
|
-
else doc
|
41
|
-
end
|
42
|
-
else "#{name} (no documentation for this option)"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def doc
|
47
|
-
doc = (options[:doc] || "#{name} (no documentation for this option)")
|
48
|
-
if doc.respond_to?(:to_ary) # multi-line
|
49
|
-
first_line = doc[0]
|
50
|
-
remaining = doc[1..-1]
|
51
|
-
if remaining.empty?
|
52
|
-
first_line
|
53
|
-
else
|
54
|
-
remaining = remaining.join("\n").split("\n").join("\n ")
|
55
|
-
Autoproj.color(first_line, :bold) + "\n " + remaining
|
56
|
-
end
|
57
|
-
else
|
58
|
-
doc
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def ask(current_value, doc = nil)
|
63
|
-
default_value =
|
64
|
-
if !current_value.nil? then current_value.to_s
|
65
|
-
elsif options[:default] then options[:default].to_str
|
66
|
-
else ''
|
67
|
-
end
|
68
|
-
|
69
|
-
STDOUT.print " #{doc || self.doc} [#{default_value}] "
|
70
|
-
STDOUT.flush
|
71
|
-
answer = STDIN.readline.chomp
|
72
|
-
if answer == ''
|
73
|
-
answer = default_value
|
74
|
-
end
|
75
|
-
validate(answer)
|
76
|
-
|
77
|
-
rescue InputError => e
|
78
|
-
Autoproj.message("invalid value: #{e.message}", :red)
|
79
|
-
retry
|
80
|
-
end
|
81
|
-
|
82
|
-
def validate(value)
|
83
|
-
value = BuildOption.send("validate_#{type}", value, options)
|
84
|
-
if validator
|
85
|
-
value = validator[value]
|
86
|
-
end
|
87
|
-
value
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.validate_boolean(value, options)
|
91
|
-
if TRUE_STRINGS.include?(value.downcase)
|
92
|
-
true
|
93
|
-
elsif FALSE_STRINGS.include?(value.downcase)
|
94
|
-
false
|
95
|
-
else
|
96
|
-
raise InputError, "invalid boolean value '#{value}', accepted values are '#{TRUE_STRINGS.join(", ")}' for true, and '#{FALSE_STRINGS.join(", ")} for false"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.validate_string(value, options)
|
101
|
-
if possible_values = options[:possible_values]
|
102
|
-
if options[:lowercase]
|
103
|
-
value = value.downcase
|
104
|
-
elsif options[:uppercase]
|
105
|
-
value = value.upcase
|
106
|
-
end
|
107
|
-
|
108
|
-
if !possible_values.include?(value)
|
109
|
-
raise InputError, "invalid value '#{value}', accepted values are '#{possible_values.join("', '")}' (without the quotes)"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
value
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
@user_config = Hash.new
|
117
|
-
|
118
|
-
def self.option_set
|
119
|
-
@user_config.inject(Hash.new) do |h, (k, v)|
|
120
|
-
h[k] = v.first
|
121
|
-
h
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
6
|
+
# @deprecated use config.reset instead
|
125
7
|
def self.reset_option(key)
|
126
|
-
|
8
|
+
config.reset(key)
|
127
9
|
end
|
128
|
-
|
10
|
+
# @deprecated use config.set(key, value, user_validated) instead
|
129
11
|
def self.change_option(key, value, user_validated = false)
|
130
|
-
|
12
|
+
config.set(key, value, user_validated)
|
131
13
|
end
|
132
|
-
|
14
|
+
# @deprecated use config.validated_values instead
|
15
|
+
def self.option_set
|
16
|
+
config.validated_values
|
17
|
+
end
|
18
|
+
# @deprecated use config.get(key) instead
|
133
19
|
def self.user_config(key)
|
134
|
-
|
135
|
-
# All non-user options are always considered as "seen"
|
136
|
-
seen ||= !@declared_options.has_key?(key)
|
137
|
-
|
138
|
-
if value.nil? || (!seen && Autoproj.reconfigure?)
|
139
|
-
value = configure(key)
|
140
|
-
else
|
141
|
-
if !seen
|
142
|
-
doc = @declared_options[key].short_doc
|
143
|
-
if doc[-1, 1] != "?"
|
144
|
-
doc = "#{doc}:"
|
145
|
-
end
|
146
|
-
Autoproj.message " #{doc} #{value}"
|
147
|
-
@user_config[key] = [value, true]
|
148
|
-
end
|
149
|
-
value
|
150
|
-
end
|
20
|
+
config.get(key)
|
151
21
|
end
|
152
|
-
|
153
|
-
@declared_options = Hash.new
|
22
|
+
# @deprecated use config.declare(name, type, options, &validator) instead
|
154
23
|
def self.configuration_option(name, type, options, &validator)
|
155
|
-
|
24
|
+
config.declare(name, type, options, &validator)
|
156
25
|
end
|
157
|
-
|
26
|
+
# @deprecated use config.declared?(name, type, options, &validator) instead
|
158
27
|
def self.declared_option?(name)
|
159
|
-
|
28
|
+
config.declared?(name)
|
160
29
|
end
|
161
|
-
|
30
|
+
# @deprecated use config.configure(option_name) instead
|
162
31
|
def self.configure(option_name)
|
163
|
-
|
164
|
-
if current_value = @user_config[option_name]
|
165
|
-
current_value = current_value.first
|
166
|
-
end
|
167
|
-
value = opt.ask(current_value)
|
168
|
-
@user_config[option_name] = [value, true]
|
169
|
-
value
|
170
|
-
else
|
171
|
-
raise ConfigError.new, "undeclared option '#{option_name}'"
|
172
|
-
end
|
32
|
+
config.configure(option_name)
|
173
33
|
end
|
174
|
-
|
175
|
-
def self.
|
176
|
-
|
177
|
-
config = Hash.new
|
178
|
-
@user_config.each_key do |key|
|
179
|
-
config[key] = @user_config[key].first
|
180
|
-
end
|
181
|
-
|
182
|
-
io.write YAML.dump(config)
|
183
|
-
end
|
34
|
+
# @deprecated use config.has_value_for?(name)
|
35
|
+
def self.has_config_key?(name)
|
36
|
+
config.has_value_for?(name)
|
184
37
|
end
|
185
38
|
|
186
|
-
def self.
|
187
|
-
|
39
|
+
def self.save_config
|
40
|
+
config.save(File.join(Autoproj.config_dir, "config.yml"))
|
188
41
|
end
|
189
42
|
|
190
43
|
def self.load_config
|
44
|
+
@config ||= Configuration.new
|
45
|
+
|
191
46
|
config_file = File.join(Autoproj.config_dir, "config.yml")
|
192
47
|
if File.exists?(config_file)
|
193
|
-
config
|
194
|
-
if !config
|
195
|
-
return
|
196
|
-
end
|
197
|
-
|
198
|
-
config.each do |key, value|
|
199
|
-
@user_config[key] = [value, false]
|
200
|
-
end
|
48
|
+
config.load(config_file, reconfigure?)
|
201
49
|
end
|
202
50
|
end
|
203
51
|
|