bin/autoproj_bootstrap in autoproj-1.10.2 vs bin/autoproj_bootstrap in autoproj-1.11.0.b1
- old
+ new
@@ -123,10 +123,283 @@
end
end
end
end
+module Autoproj
+ class InputError < RuntimeError; end
+
+ # Definition of an autoproj option as defined by
+ # {Configuration#configuration_option}
+ class BuildOption
+ attr_reader :name
+ attr_reader :type
+ attr_reader :options
+
+ attr_reader :validator
+
+ TRUE_STRINGS = %w{on yes y true}
+ FALSE_STRINGS = %w{off no n false}
+ def initialize(name, type, options, validator)
+ @name, @type, @options = name.to_str, type.to_str, options.to_hash
+ @validator = validator.to_proc if validator
+ if !BuildOption.respond_to?("validate_#{type}")
+ raise ConfigError.new, "invalid option type #{type}"
+ end
+ end
+
+ def short_doc
+ if short_doc = options[:short_doc]
+ short_doc
+ elsif doc = options[:doc]
+ if doc.respond_to?(:to_ary) then doc.first
+ else doc
+ end
+ else "#{name} (no documentation for this option)"
+ end
+ end
+
+ def doc
+ doc = (options[:doc] || "#{name} (no documentation for this option)")
+ if doc.respond_to?(:to_ary) # multi-line
+ first_line = doc[0]
+ remaining = doc[1..-1]
+ if remaining.empty?
+ first_line
+ else
+ remaining = remaining.join("\n").split("\n").join("\n ")
+ Autoproj.color(first_line, :bold) + "\n " + remaining
+ end
+ else
+ doc
+ end
+ end
+
+ def ask(current_value, doc = nil)
+ default_value =
+ if !current_value.nil? then current_value.to_s
+ elsif options[:default] then options[:default].to_str
+ else ''
+ end
+
+ STDOUT.print " #{doc || self.doc} [#{default_value}] "
+ STDOUT.flush
+ answer = STDIN.readline.chomp
+ if answer == ''
+ answer = default_value
+ end
+ validate(answer)
+
+ rescue InputError => e
+ Autoproj.message("invalid value: #{e.message}", :red)
+ retry
+ end
+
+ def validate(value)
+ value = BuildOption.send("validate_#{type}", value, options)
+ if validator
+ value = validator[value]
+ end
+ value
+ end
+
+ def self.validate_boolean(value, options)
+ if TRUE_STRINGS.include?(value.downcase)
+ true
+ elsif FALSE_STRINGS.include?(value.downcase)
+ false
+ else
+ raise InputError, "invalid boolean value '#{value}', accepted values are '#{TRUE_STRINGS.join(", ")}' for true, and '#{FALSE_STRINGS.join(", ")} for false"
+ end
+ end
+
+ def self.validate_string(value, options)
+ if possible_values = options[:possible_values]
+ if options[:lowercase]
+ value = value.downcase
+ elsif options[:uppercase]
+ value = value.upcase
+ end
+
+ if !possible_values.include?(value)
+ raise InputError, "invalid value '#{value}', accepted values are '#{possible_values.join("', '")}' (without the quotes)"
+ end
+ end
+ value
+ end
+ end
+end
+
+
+module Autoproj
+ # Class that does the handling of configuration options as well as
+ # loading/saving on disk
+ class Configuration
+ # Set of currently known options
+ #
+ # These are the values that are going to be saved on disk. Use
+ # {override} to change a value without changing the saved configuration
+ # file.
+ attr_reader :config
+ # Set of overriden option values that won't get written to file
+ attr_reader :overrides
+ # Set of options that have been declared with {declare}
+ attr_reader :declared_options
+ # The options that have already been shown to the user
+ attr_reader :displayed_options
+
+ def initialize
+ @config = Hash.new
+ @overrides = Hash.new
+ @declared_options = Hash.new
+ @displayed_options = Hash.new
+ end
+
+ # Deletes the current value for an option
+ #
+ # The user will be asked for a new value next time the option is needed
+ #
+ # @param [String] the option name
+ # @return the deleted value
+ def reset(name)
+ config.delete(name)
+ end
+
+ # Sets a configuration option
+ #
+ # @param [String] key the option name
+ # @param [Object] value the option value
+ # @param [Boolean] user_validated if true, autoproj will not ask the
+ # user about this value next time it is needed. Otherwise, it will be
+ # asked about it, the new value being used as default
+ def set(key, value, user_validated = false)
+ config[key] = [value, user_validated]
+ end
+
+ # Override a known option value
+ #
+ # The new value will not be saved to disk, unlike with {set}
+ def override(option_name, value)
+ overrides[option_name] = value
+ end
+
+ # Tests whether a value is set for the given option name
+ #
+ # @return [Boolean]
+ def has_value_for?(name)
+ config.has_key?(name) || overrides.has_key?(name)
+ end
+
+ # Get the value for a given option
+ def get(key)
+ if overrides.has_key?(key)
+ return overrides[key]
+ end
+
+ value, validated = config[key]
+ if value.nil? || (declared?(key) && !validated)
+ value = configure(key)
+ else
+ if declared?(key) && (displayed_options[key] != value)
+ doc = declared_options[key].short_doc
+ if doc[-1, 1] != "?"
+ doc = "#{doc}:"
+ end
+ Autoproj.message " #{doc} #{value}"
+ displayed_options[key] = value
+ end
+ value
+ end
+ end
+
+ # Returns the option's name-value pairs for the options that do not
+ # require user input
+ def validated_values
+ config.inject(Hash.new) do |h, (k, v)|
+ h[k] =
+ if overrides.has_key?(k) then overrides[k]
+ elsif v.last || !declared?(k) then v.first
+ end
+ h
+ end
+ end
+
+ # Declare an option
+ #
+ # This declares a given option, thus allowing to ask the user about it
+ #
+ # @param [String] name the option name
+ # @param [String] type the option type (can be 'boolean' or 'string')
+ # @option options [String] :short_doc the one-line documentation string
+ # that is displayed when the user does not have to be queried. It
+ # defaults to the first line of :doc if not given
+ # @option options [String] :doc the full option documentation. It is
+ # displayed to the user when he is explicitly asked about the option's
+ # value
+ # @option options [Object] :default the default value this option should
+ # take
+ # @option options [Array] :possible_values list of possible values (only
+ # if the option type is 'string')
+ # @option options [Boolean] :lowercase (false) whether the user's input
+ # should be converted to lowercase before it gets validated / saved.
+ # @option options [Boolean] :uppercase (false) whether the user's input
+ # should be converted to uppercase before it gets validated / saved.
+ def declare(name, type, options, &validator)
+ declared_options[name] = BuildOption.new(name, type, options, validator)
+ end
+
+ # Checks if an option exists
+ # @return [Boolean]
+ def declared?(name)
+ declared_options.has_key?(name)
+ end
+
+ # Configures a given option by asking the user about its desired value
+ #
+ # @return [Object] the new option value
+ # @raise ConfigError if the option is not declared
+ def configure(option_name)
+ if opt = declared_options[option_name]
+ if current_value = config[option_name]
+ current_value = current_value.first
+ end
+ value = opt.ask(current_value)
+ config[option_name] = [value, true]
+ displayed_options[option_name] = value
+ value
+ else
+ raise ConfigError.new, "undeclared option '#{option_name}'"
+ end
+ end
+
+ def load(path, reconfigure = false)
+ if h = YAML.load(File.read(path))
+ h.each do |key, value|
+ set(key, value, !reconfigure)
+ end
+ end
+ end
+
+ def save(path)
+ File.open(path, "w") do |io|
+ h = Hash.new
+ config.each do |key, value|
+ h[key] = value.first
+ end
+
+ io.write YAML.dump(h)
+ end
+ end
+ end
+end
+
+module Autoproj
+ def self.config
+ @config ||= Configuration.new
+ end
+end
+
require 'tempfile'
require 'json'
module Autoproj
# Module that contains the package manager implementations for the
# OSDependencies class
@@ -545,10 +818,29 @@
installed_packages << package_name
end
end
new_packages
end
+
+ def install(packages)
+ patterns, packages = packages.partition { |pkg| pkg =~ /^@/ }
+ patterns = patterns.map { |str| str[1..-1] }
+ result = false
+ if !patterns.empty?
+ result |= super(patterns,
+ :auto_install_cmd => "yum groupinstall -y '%s'",
+ :user_install_cmd => "yum groupinstall '%s'")
+ end
+ if !packages.empty?
+ result |= super(packages)
+ end
+ if result
+ # Invalidate caching of installed packages, as we just
+ # installed new packages !
+ @installed_packages = nil
+ end
+ end
end
# Package manager interface for systems that use APT and dpkg for
# package management
class AptDpkgManager < ShellScriptManager
@@ -699,10 +991,11 @@
Autobuild.programs['gem'] = "gem"
end
end
def reinstall
+ Autoproj.message "reinstalling all RubyGems"
base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem')]
Autobuild::Subprocess.run 'autoproj', 'osdeps', 'reinstall', *base_cmdline,
'clean'
Autobuild::Subprocess.run 'autoproj', 'osdeps', 'reinstall', *base_cmdline,
'pristine', '--all', '--extensions'
@@ -736,11 +1029,11 @@
Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
end
gems.each do |name, v|
installed_gems << name
end
- did_something = true
+ true
end
end
# Returns the set of RubyGem packages in +packages+ that are not already
# installed, or that can be upgraded
@@ -1123,11 +1416,24 @@
@definitions = definitions.merge(info.definitions) do |h, v1, v2|
if v1 != v2
root_dir ||= "#{Autoproj.root_dir}/"
old = source_of(h).gsub(root_dir, '')
new = info.source_of(h).gsub(root_dir, '')
- Autoproj.warn("osdeps definition for #{h}, previously defined in #{old} overridden by #{new}")
+
+ # Warn if the new osdep definition resolves to a different
+ # set of packages than the old one
+ old_resolved = resolve_package(h).inject(Hash.new) do |osdep_h, (handler, status, list)|
+ osdep_h[handler.name] = [status, list]
+ h
+ end
+ new_resolved = info.resolve_package(h).inject(Hash.new) do |osdep_h, (handler, status, list)|
+ osdep_h[handler.name] = [status, list]
+ h
+ end
+ if old_resolved != new_resolved
+ Autoproj.warn("osdeps definition for #{h}, previously defined in #{old} overridden by #{new}")
+ end
end
v2
end
@sources = sources.merge(info.sources)
@all_definitions = all_definitions.merge(info.all_definitions) do |package_name, all_defs, new_all_defs|
@@ -1279,22 +1585,25 @@
Kernel.validate_options options, :force => false
else
options.dup
end
- if options[:force]
- @operating_system = nil
- elsif !@operating_system.nil? # @operating_system can be set to false to simulate an unknown OS
- return @operating_system
- elsif user_os = ENV['AUTOPROJ_OS']
+ if user_os = ENV['AUTOPROJ_OS']
@operating_system =
if user_os.empty? then false
else
names, versions = user_os.split(':')
normalize_os_representation(names.split(','), versions.split(','))
end
return @operating_system
+ end
+
+
+ if options[:force]
+ @operating_system = nil
+ elsif !@operating_system.nil? # @operating_system can be set to false to simulate an unknown OS
+ return @operating_system
elsif Autoproj.has_config_key?('operating_system')
os = Autoproj.user_config('operating_system')
if os.respond_to?(:to_ary)
if os[0].respond_to?(:to_ary) && os[0].all? { |s| s.respond_to?(:to_str) } &&
os[1].respond_to?(:to_ary) && os[1].all? { |s| s.respond_to?(:to_str) }
@@ -1586,12 +1895,16 @@
class MissingOSDep < ConfigError; end
# Resolves the given OS dependencies into the actual packages that need
# to be installed on this particular OS.
#
- # Raises ConfigError if some packages can't be found or if the
- # nonexistent keyword was found for some of them
+ # @param [Array<String>] dependencies the list of osdep names that should be resolved
+ # @return [Array<#install,Array<String>>] the set of packages, grouped
+ # by the package handlers that should be used to install them
+ #
+ # @raise MissingOSDep if some packages can't be found or if the
+ # nonexistent keyword was found for some of them
def resolve_os_dependencies(dependencies)
all_packages = []
dependencies.each do |name|
result = resolve_package(name)
if !result
@@ -2017,209 +2330,57 @@
@programs_in_path = Hash.new
end
module Autoproj
- class InputError < RuntimeError; end
-
- class << self
- # Programatically overriden autoproj options
- #
- # @see override_option
- attr_reader :option_overrides
- end
- @option_overrides = Hash.new
-
- # Programatically override a user-selected option without changing the
- # configuration file
+ # @deprecated use config.override instead
def self.override_option(option_name, value)
- @option_overrides[option_name] = value
+ config.override(option_name, value)
end
-
- class BuildOption
- attr_reader :name
- attr_reader :type
- attr_reader :options
-
- attr_reader :validator
-
- TRUE_STRINGS = %w{on yes y true}
- FALSE_STRINGS = %w{off no n false}
- def initialize(name, type, options, validator)
- @name, @type, @options = name.to_str, type.to_str, options.to_hash
- @validator = validator.to_proc if validator
- if !BuildOption.respond_to?("validate_#{type}")
- raise ConfigError.new, "invalid option type #{type}"
- end
- end
-
- def short_doc
- if short_doc = options[:short_doc]
- short_doc
- elsif doc = options[:doc]
- if doc.respond_to?(:to_ary) then doc.first
- else doc
- end
- else "#{name} (no documentation for this option)"
- end
- end
-
- def doc
- doc = (options[:doc] || "#{name} (no documentation for this option)")
- if doc.respond_to?(:to_ary) # multi-line
- first_line = doc[0]
- remaining = doc[1..-1]
- if remaining.empty?
- first_line
- else
- remaining = remaining.join("\n").split("\n").join("\n ")
- Autoproj.color(first_line, :bold) + "\n " + remaining
- end
- else
- doc
- end
- end
-
- def ask(current_value, doc = nil)
- default_value =
- if !current_value.nil? then current_value.to_s
- elsif options[:default] then options[:default].to_str
- else ''
- end
-
- STDOUT.print " #{doc || self.doc} [#{default_value}] "
- STDOUT.flush
- answer = STDIN.readline.chomp
- if answer == ''
- answer = default_value
- end
- validate(answer)
-
- rescue InputError => e
- Autoproj.message("invalid value: #{e.message}", :red)
- retry
- end
-
- def validate(value)
- value = BuildOption.send("validate_#{type}", value, options)
- if validator
- value = validator[value]
- end
- value
- end
-
- def self.validate_boolean(value, options)
- if TRUE_STRINGS.include?(value.downcase)
- true
- elsif FALSE_STRINGS.include?(value.downcase)
- false
- else
- raise InputError, "invalid boolean value '#{value}', accepted values are '#{TRUE_STRINGS.join(", ")}' for true, and '#{FALSE_STRINGS.join(", ")} for false"
- end
- end
-
- def self.validate_string(value, options)
- if possible_values = options[:possible_values]
- if options[:lowercase]
- value = value.downcase
- elsif options[:uppercase]
- value = value.upcase
- end
-
- if !possible_values.include?(value)
- raise InputError, "invalid value '#{value}', accepted values are '#{possible_values.join("', '")}' (without the quotes)"
- end
- end
- value
- end
- end
-
- @user_config = Hash.new
-
- def self.option_set
- @user_config.inject(Hash.new) do |h, (k, v)|
- h[k] = v.first
- h
- end
- end
-
+ # @deprecated use config.reset instead
def self.reset_option(key)
- @user_config.delete(key)
+ config.reset(key)
end
-
+ # @deprecated use config.set(key, value, user_validated) instead
def self.change_option(key, value, user_validated = false)
- @user_config[key] = [value, user_validated]
+ config.set(key, value, user_validated)
end
-
+ # @deprecated use config.validated_values instead
+ def self.option_set
+ config.validated_values
+ end
+ # @deprecated use config.get(key) instead
def self.user_config(key)
- value, seen = @user_config[key]
- # All non-user options are always considered as "seen"
- seen ||= !@declared_options.has_key?(key)
-
- if value.nil? || (!seen && Autoproj.reconfigure?)
- value = configure(key)
- else
- if !seen
- doc = @declared_options[key].short_doc
- if doc[-1, 1] != "?"
- doc = "#{doc}:"
- end
- Autoproj.message " #{doc} #{value}"
- @user_config[key] = [value, true]
- end
- value
- end
+ config.get(key)
end
-
- @declared_options = Hash.new
+ # @deprecated use config.declare(name, type, options, &validator) instead
def self.configuration_option(name, type, options, &validator)
- @declared_options[name] = BuildOption.new(name, type, options, validator)
+ config.declare(name, type, options, &validator)
end
-
+ # @deprecated use config.declared?(name, type, options, &validator) instead
def self.declared_option?(name)
- @declared_options.has_key?(name)
+ config.declared?(name)
end
-
+ # @deprecated use config.configure(option_name) instead
def self.configure(option_name)
- if opt = @declared_options[option_name]
- if current_value = @user_config[option_name]
- current_value = current_value.first
- end
- value = opt.ask(current_value)
- @user_config[option_name] = [value, true]
- value
- else
- raise ConfigError.new, "undeclared option '#{option_name}'"
- end
+ config.configure(option_name)
end
+ # @deprecated use config.has_value_for?(name)
+ def self.has_config_key?(name)
+ config.has_value_for?(name)
+ end
def self.save_config
- File.open(File.join(Autoproj.config_dir, "config.yml"), "w") do |io|
- config = Hash.new
- @user_config.each_key do |key|
- config[key] = @user_config[key].first
- end
-
- io.write YAML.dump(config)
- end
+ config.save(File.join(Autoproj.config_dir, "config.yml"))
end
- def self.has_config_key?(name)
- @user_config.has_key?(name)
- end
-
def self.load_config
+ @config ||= Configuration.new
+
config_file = File.join(Autoproj.config_dir, "config.yml")
if File.exists?(config_file)
- config = YAML.load(File.read(config_file))
- if !config
- return
- end
-
- config.each do |key, value|
- @user_config[key] = [value, false]
- end
+ config.load(config_file, reconfigure?)
end
end
class << self
attr_accessor :reconfigure
@@ -2430,36 +2591,19 @@
end
Autobuild.export_env_sh(io)
end
end
- # Load a definition file given at +path+. +source+ is the package set from
- # which the file is taken.
- #
- # If any error is detected, the backtrace will be filtered so that it is
- # easier to understand by the user. Moreover, if +source+ is non-nil, the
- # package set name will be mentionned.
+ # @deprecated use Ops.loader.load or add a proper Loader object to your
+ # class
def self.load(package_set, *path)
- path = File.join(*path)
- in_package_set(package_set, File.expand_path(path).gsub(/^#{Regexp.quote(Autoproj.root_dir)}\//, '')) do
- begin
- Kernel.load path
- rescue Interrupt
- raise
- rescue ConfigError => e
- raise
- rescue Exception => e
- filter_load_exception(e, package_set, path)
- end
- end
+ Ops.loader.load(package_set, *path)
end
- # Same as #load, but runs only if the file exists.
+ # @deprecated use Ops.loader.load_if_present or add a proper Loader object
+ # to your class
def self.load_if_present(package_set, *path)
- path = File.join(*path)
- if File.file?(path)
- self.load(package_set, *path)
- end
+ Ops.loader.load_if_present(package_set, *path)
end
# Look into +dir+, searching for shared libraries. For each library, display
# a warning message if this library has undefined symbols.
def self.validate_solib_dependencies(dir, exclude_paths = [])