Skip to content

Instantly share code, notes, and snippets.

@doudou
Last active August 29, 2015 14:01
Show Gist options
  • Save doudou/65726d99a0198776f3c4 to your computer and use it in GitHub Desktop.
Save doudou/65726d99a0198776f3c4 to your computer and use it in GitHub Desktop.
#! /usr/bin/ruby
if RUBY_VERSION < "1.9.2"
STDERR.puts "autoproj requires Ruby >= 1.9.2"
exit 1
end
if defined? Encoding # This is a 1.9-only thing
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
end
if ENV['AUTOPROJ_CURRENT_ROOT'] && ENV['AUTOPROJ_CURRENT_ROOT'] != Dir.pwd
STDERR.puts "the env.sh from #{ENV['AUTOPROJ_CURRENT_ROOT']} seem to already be sourced"
STDERR.puts "start a new shell and try to bootstrap again"
exit 1
end
require 'rbconfig'
module Autobuild
@windows = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw|[Ww]indows)!
def self.windows?
@windows
end
@macos = RbConfig::CONFIG["host_os"] =~ %r!([Dd]arwin)!
def self.macos?
@macos
end
end
require 'set'
curdir_entries = Dir.entries('.').to_set - [".", "..", "autoproj_bootstrap", ".gems", 'env.sh'].to_set
if !curdir_entries.empty? && ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] != '1'
while true
print "The current directory is not empty, continue bootstrapping anyway ? [yes] "
STDOUT.flush
answer = STDIN.readline.chomp
if answer == "no"
exit
elsif answer == "" || answer == "yes"
# Set the environment variable since we might restart the
# autoproj_bootstrap script and -- anyway -- will run "autoproj
# bootstrap" later on
break
else
STDOUT.puts "invalid answer. Please answer 'yes' or 'no'"
STDOUT.flush
end
end
end
# Environment is clean, so just mark it as so unconditionally
ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] = '1'
gem_home = ENV['AUTOPROJ_GEM_HOME'] || File.join(Dir.pwd, '.gems')
gem_path = ([gem_home] + Gem.default_path).join(":")
Gem.paths = Hash['GEM_HOME' => gem_home, 'GEM_PATH' => gem_path]
ENV['GEM_HOME'] = gem_home
ENV['GEM_PATH'] = gem_path
ENV['PATH'] = "#{ENV['GEM_HOME']}/bin:#{ENV['PATH']}"
require 'yaml'
require 'set'
module Autoproj
class ConfigError < RuntimeError; end
class << self
attr_reader :verbose
end
def self.color(string, *args)
string
end
def self.warn(str, *args)
STDERR.puts "WARN #{str}"
end
def self.message(str)
STDERR.puts " #{str}"
end
end
module Autobuild
def self.do_update
true
end
def self.message(str)
STDERR.puts " #{str}"
end
def self.progress(key, str)
STDERR.puts " #{str}"
end
def self.progress_done(key)
end
def self.message(str)
STDERR.puts " #{str}"
end
class << self
attr_reader :programs
end
@programs = Hash.new
def self.tool(name)
# Let the ability to set programs[name] to nil to make sure we don't use
# that program. This is used later on in this file to make sure we
# aren't using the wrong rubygems binary
if programs.has_key?(name)
programs[name]
else
name
end
end
module Subprocess
def self.run(name, phase, *cmd)
output = `#{cmd.join(" ")}`
if $?.exitstatus != 0
STDERR.puts "ERROR: failed to run #{cmd.join(" ")}"
STDERR.puts "ERROR: command output is: #{output}"
exit 1
end
end
end
end
require 'tempfile'
require 'json'
module Autoproj
# Module that contains the package manager implementations for the
# OSDependencies class
module PackageManagers
# Base class for all package managers. Subclasses must add the
# #install(packages) method and may add the
# #filter_uptodate_packages(packages) method
#
# Package managers must be registered in PACKAGE_HANDLERS and
# (if applicable) OS_PACKAGE_HANDLERS.
class Manager
# @return [Array<String>] the various names this package manager is
# known about
attr_reader :names
attr_writer :enabled
def enabled?; !!@enabled end
attr_writer :silent
def silent?; !!@silent end
# Create a package manager registered with various names
#
# @param [Array<String>] names the package manager names. It MUST be
# different from the OS names that autoproj uses. See the comment
# for OS_PACKAGE_HANDLERS for an explanation
def initialize(names = [])
@names = names.dup
@enabled = true
@silent = true
end
# The primary name for this package manager
def name
names.first
end
# Overload to perform initialization of environment variables in
# order to have a properly functioning package manager
#
# This is e.g. needed for python pip or rubygems
def self.initialize_environment
end
end
# Dummy package manager used for unknown OSes. It simply displays a
# message to the user when packages are needed
class UnknownOSManager < Manager
def initialize
super(['unknown'])
@installed_osdeps = Set.new
end
def osdeps_interaction_unknown_os(osdeps)
puts <<-EOMSG
#{Autoproj.color("The build process requires some other software packages to be installed on our operating system", :bold)}
#{Autoproj.color("If they are already installed, simply ignore this message", :red)}
#{osdeps.to_a.sort.join("\n ")}
EOMSG
print Autoproj.color("Press ENTER to continue", :bold)
STDOUT.flush
STDIN.readline
puts
nil
end
def install(osdeps)
if silent?
return false
else
osdeps = osdeps.to_set
osdeps -= @installed_osdeps
if !osdeps.empty?
result = osdeps_interaction_unknown_os(osdeps)
end
@installed_osdeps |= osdeps
return result
end
end
end
# Base class for all package managers that simply require the call of a
# shell script to install packages (e.g. yum, apt, ...)
class ShellScriptManager < Manager
def self.execute(script, with_locking, with_root)
if with_locking
File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
begin
while !lock_io.flock(File::LOCK_EX | File::LOCK_NB)
Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
sleep 5
end
return execute(script, false,with_root)
ensure
lock_io.flock(File::LOCK_UN)
end
end
end
sudo = Autobuild.tool_in_path('sudo')
Tempfile.open('osdeps_sh') do |io|
io.puts "#! /bin/bash"
io.puts GAIN_ROOT_ACCESS % [sudo] if with_root
io.write script
io.flush
Autobuild::Subprocess.run 'autoproj', 'osdeps', '/bin/bash', io.path
end
end
GAIN_ROOT_ACCESS = <<-EOSCRIPT
# Gain root access using sudo
if test `id -u` != "0"; then
exec %s /bin/bash $0 "$@"
fi
EOSCRIPT
# Overrides the {#needs_locking?} flag
attr_writer :needs_locking
# Whether two autoproj instances can run this package manager at the
# same time
#
# This declares if this package manager cannot be used concurrently.
# If it is the case, autoproj will ensure that there is no two
# autoproj instances running this package manager at the same time
#
# @return [Boolean]
# @see needs_locking=
def needs_locking?; !!@needs_locking end
# Overrides the {#needs_root?} flag
attr_writer :needs_root
# Whether this package manager needs root access.
#
# This declares if the command line(s) for this package manager
# should be started as root. Root access is provided using sudo
#
# @return [Boolean]
# @see needs_root=
def needs_root?; !!@needs_root end
# Command line used by autoproj to install packages
#
# Since it is to be used for automated install by autoproj, it
# should not require any interaction with the user. When generating
# the command line, the %s slot is replaced by the quoted package
# name(s).
#
# @return [String] a command line pattern that allows to install
# packages without user interaction. It is used when a package
# should be installed by autoproj automatically
attr_reader :auto_install_cmd
# Command line displayed to the user to install packages
#
# When generating the command line, the %s slot is replaced by the
# quoted package name(s).
#
# @return [String] a command line pattern that allows to install
# packages with user interaction. It is displayed to the
# user when it chose to not let autoproj install packages for this
# package manager automatically
attr_reader :user_install_cmd
# @param [Array<String>] names the package managers names, see
# {#names}
# @param [Boolean] needs_locking whether this package manager can be
# started by two separate autoproj instances at the same time. See
# {#needs_locking?}
# @param [String] user_install_cmd the user-visible command line. See
# {#user_install_cmd}
# @param [String] auto_install_cmd the command line used by autoproj
# itself, see {#auto_install_cmd}.
# @param [Boolean] needs_root if the command lines should be started
# as root or not. See {#needs_root?}
def initialize(names, needs_locking, user_install_cmd, auto_install_cmd,needs_root=true)
super(names)
@needs_locking, @user_install_cmd, @auto_install_cmd,@needs_root =
needs_locking, user_install_cmd, auto_install_cmd, needs_root
end
# Generate the shell script that would allow the user to install
# the given packages
#
# @param [Array<String>] os_packages the name of the packages to be
# installed
# @option options [String] :user_install_cmd (#user_install_cmd) the
# command-line pattern that should be used to generate the script.
# If given, it overrides the default value stored in
# {#user_install_cmd]
def generate_user_os_script(os_packages, options = Hash.new)
user_install_cmd = options[:user_install_cmd] || self.user_install_cmd
if user_install_cmd
(user_install_cmd % [os_packages.join("' '")])
else generate_auto_os_script(os_packages)
end
end
# Generate the shell script that should be executed by autoproj to
# install the given packages
#
# @param [Array<String>] os_packages the name of the packages to be
# installed
# @option options [String] :auto_install_cmd (#auto_install_cmd) the
# command-line pattern that should be used to generate the script.
# If given, it overrides the default value stored in
# {#auto_install_cmd]
def generate_auto_os_script(os_packages, options = Hash.new)
auto_install_cmd = options[:auto_install_cmd] || self.auto_install_cmd
(auto_install_cmd % [os_packages.join("' '")])
end
# Handles interaction with the user
#
# This method will verify whether the user required autoproj to
# install packages from this package manager automatically. It
# displays a relevant message if it is not the case.
#
# @return [Boolean] true if the packages should be installed
# automatically, false otherwise
def osdeps_interaction(os_packages, shell_script)
if OSDependencies.force_osdeps
return true
elsif enabled?
return true
elsif silent?
return false
end
# We're asked to not install the OS packages but to display them
# anyway, do so now
puts <<-EOMSG
#{Autoproj.color("The build process and/or the packages require some other software to be installed", :bold)}
#{Autoproj.color("and you required autoproj to not install them itself", :bold)}
#{Autoproj.color("\nIf these packages are already installed, simply ignore this message\n", :red) if !respond_to?(:filter_uptodate_packages)}
The following packages are available as OS dependencies, i.e. as prebuilt
packages provided by your distribution / operating system. You will have to
install them manually if they are not already installed
#{os_packages.sort.join("\n ")}
the following command line(s) can be run as root to install them:
#{shell_script.split("\n").join("\n| ")}
EOMSG
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
STDOUT.flush
STDIN.readline
puts
false
end
# Install packages using this package manager
#
# @param [Array<String>] packages the name of the packages that
# should be installed
# @option options [String] :user_install_cmd (#user_install_cmd) the
# command line that should be displayed to the user to install said
# packages. See the option in {#generate_user_os_script}
# @option options [String] :auto_install_cmd (#auto_install_cmd) the
# command line that should be used by autoproj to install said
# packages. See the option in {#generate_auto_os_script}
# @return [Boolean] true if packages got installed, false otherwise
def install(packages, options = Hash.new)
handled_os = OSDependencies.supported_operating_system?
if handled_os
shell_script = generate_auto_os_script(packages, options)
user_shell_script = generate_user_os_script(packages, options)
end
if osdeps_interaction(packages, user_shell_script)
Autoproj.message " installing OS packages: #{packages.sort.join(", ")}"
if Autoproj.verbose
Autoproj.message "Generating installation script for non-ruby OS dependencies"
Autoproj.message shell_script
end
ShellScriptManager.execute(shell_script, needs_locking?,needs_root?)
return true
end
false
end
end
# Package manager interface for systems that use port (i.e. MacPorts/Darwin) as
# their package manager
class PortManager < ShellScriptManager
def initialize
super(['macports'], true,
"port install '%s'",
"port install '%s'")
end
end
# Package manager interface for Mac OS using homebrew as
# its package manager
class HomebrewManager < ShellScriptManager
def initialize
super(['brew'], true,
"brew install '%s'",
"brew install '%s'",
false)
end
def filter_uptodate_packages(packages)
# TODO there might be duplicates in packages which should be fixed
# somewhere else
packages = packages.uniq
result = `brew info --json=v1 '#{packages.join("' '")}'`
result = begin
result = JSON.parse(result)
if packages.size == 1
[result]
else
result
end
rescue JSON::ParserError
if result && !result.empty?
Autoproj.warn "Error while parsing result of brew info --json=v1"
else
# one of the packages is unknown fallback to install all
# packaes which will complain about it
end
return packages
end
# fall back if something else went wrong
if packages.size != result.size
Autoproj.warn "brew info returns less or more packages when requested. Falling back to install all packages"
return packages
end
new_packages = []
result.each do |pkg|
new_packages << pkg["name"] if pkg["installed"].empty?
end
new_packages
end
end
# Package manager interface for systems that use pacman (i.e. arch) as
# their package manager
class PacmanManager < ShellScriptManager
def initialize
super(['pacman'], true,
"pacman -Sy --needed '%s'",
"pacman -Sy --needed --noconfirm '%s'")
end
end
# Package manager interface for systems that use emerge (i.e. gentoo) as
# their package manager
class EmergeManager < ShellScriptManager
def initialize
super(['emerge'], true,
"emerge '%s'",
"emerge --noreplace '%s'")
end
end
#Package manger for OpenSuse and Suse (untested)
class ZypperManager < ShellScriptManager
def initialize
super(['zypper'], true,
"zypper install '%s'",
"zypper -n install '%s'")
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 => "zypper --non-interactive install --type pattern '%s'",
:user_install_cmd => "zypper install --type pattern '%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 yum
class YumManager < ShellScriptManager
def initialize
super(['yum'], true,
"yum install '%s'",
"yum install -y '%s'")
end
def filter_uptodate_packages(packages)
result = `LANG=C rpm -q --queryformat "%{NAME}\n" '#{packages.join("' '")}'`
installed_packages = []
new_packages = []
result.split("\n").each_with_index do |line, index|
line = line.strip
if line =~ /package (.*) is not installed/
package_name = $1
if !packages.include?(package_name) # something is wrong, fallback to installing everything
return packages
end
new_packages << package_name
else
package_name = line.strip
if !packages.include?(package_name) # something is wrong, fallback to installing everything
return packages
end
installed_packages << package_name
end
end
new_packages
end
end
# Package manager interface for systems that use APT and dpkg for
# package management
class AptDpkgManager < ShellScriptManager
attr_accessor :status_file
def initialize(status_file = "/var/lib/dpkg/status")
@status_file = status_file
super(['apt-dpkg'], true,
"apt-get install '%s'",
"export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'")
end
# On a dpkg-enabled system, checks if the provided package is installed
# and returns true if it is the case
def installed?(package_name)
if !@installed_packages
@installed_packages = Set.new
dpkg_status = File.readlines(status_file)
dpkg_status << ""
current_packages = []
is_installed = false
dpkg_status.each do |line|
line = line.chomp
line = line.encode( "UTF-8", "binary", :invalid => :replace, :undef => :replace)
if line == ""
if is_installed
current_packages.each do |pkg|
@installed_packages << pkg
end
is_installed = false
end
current_packages.clear
elsif line =~ /Package: (.*)$/
current_packages << $1
elsif line =~ /Provides: (.*)$/
current_packages.concat($1.split(',').map(&:strip))
elsif line == "Status: install ok installed"
is_installed = true
end
end
end
if package_name =~ /^(\w[a-z0-9+-.]+)/
@installed_packages.include?($1)
else
Autoproj.warn "#{package_name} is not a valid Debian package name"
false
end
end
def install(packages)
if super
# Invalidate caching of installed packages, as we just
# installed new packages !
@installed_packages = nil
end
end
def filter_uptodate_packages(packages)
packages.find_all do |package_name|
!installed?(package_name)
end
end
end
# Package manager interface for the RubyGems system
class GemManager < Manager
class << self
attr_accessor :with_prerelease
attr_accessor :with_doc
end
@with_prerelease = false
@with_doc = false
# Filters all paths that come from other autoproj installations out
# of GEM_PATH
def self.initialize_environment
Autobuild::ORIGINAL_ENV['GEM_PATH'] =
(ENV['GEM_PATH'] || "").split(File::PATH_SEPARATOR).find_all do |p|
!Autoproj.in_autoproj_installation?(p)
end.join(File::PATH_SEPARATOR)
Autobuild.env_inherit 'GEM_PATH'
Autobuild.env_init_from_env 'GEM_PATH'
orig_gem_path = Autobuild::ORIGINAL_ENV['GEM_PATH'].split(File::PATH_SEPARATOR)
Autobuild::SYSTEM_ENV['GEM_PATH'] = Gem.default_path
Autobuild::ORIGINAL_ENV['GEM_PATH'] = orig_gem_path.join(File::PATH_SEPARATOR)
Autoproj.manifest.each_reused_autoproj_installation do |p|
p_gems = File.join(p, '.gems')
if File.directory?(p_gems)
Autobuild.env_add_path 'GEM_PATH', p_gems
Autobuild.env_add_path 'PATH', File.join(p_gems, 'bin')
end
end
Autobuild.env_add_path 'GEM_PATH', gem_home
Autobuild.env_set 'GEM_HOME', gem_home
Autobuild.env_add_path 'PATH', "#{gem_home}/bin"
# Now, reset the directories in our own RubyGems instance
Gem.paths = ENV
end
# Return the directory in which RubyGems package should be installed
def self.gem_home
ENV['AUTOPROJ_GEM_HOME'] || File.join(Autoproj.root_dir, ".gems")
end
# Returns the set of default options that are added to gem
#
# By default, we add --no-user-install to un-break distributions
# like Arch that set --user-install by default (thus disabling the
# role of GEM_HOME)
def self.default_install_options
@default_install_options ||= ['--no-user-install', '--no-format-executable']
end
def initialize
super(['gem'])
@installed_gems = Set.new
end
# Used to override the Gem::SpecFetcher object used by this gem
# manager. Useful mainly for testing
attr_writer :gem_fetcher
# The set of gems installed during this autoproj session
attr_reader :installed_gems
def gem_fetcher
if !@gem_fetcher
Autoproj.message " looking for RubyGems updates"
@gem_fetcher = Gem::SpecFetcher.fetcher
end
@gem_fetcher
end
def guess_gem_program
if Autobuild.programs['gem']
return Autobuild.programs['gem']
end
ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
if ruby_bin =~ /^ruby(.+)$/
Autobuild.programs['gem'] = "gem#{$1}"
else
Autobuild.programs['gem'] = "gem"
end
end
def reinstall
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'
end
def install(gems)
guess_gem_program
base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem'), 'install', *GemManager.default_install_options]
if !GemManager.with_doc
base_cmdline << '--no-rdoc' << '--no-ri'
end
if GemManager.with_prerelease
base_cmdline << "--prerelease"
end
with_version, without_version = gems.partition { |name, v| v }
cmdlines = []
if !without_version.empty?
cmdlines << (base_cmdline + without_version.flatten)
end
with_version.each do |name, v|
cmdlines << base_cmdline + [name, "-v", v]
end
if gems_interaction(gems, cmdlines)
Autoproj.message " installing/updating RubyGems dependencies: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
cmdlines.each do |c|
Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
end
gems.each do |name, v|
installed_gems << name
end
did_something = true
end
end
# Returns the set of RubyGem packages in +packages+ that are not already
# installed, or that can be upgraded
def filter_uptodate_packages(gems)
# Don't install gems that are already there ...
gems = gems.dup
gems.delete_if do |name, version|
next(true) if installed_gems.include?(name)
version_requirements = Gem::Requirement.new(version || '>= 0')
installed =
if Gem::Specification.respond_to?(:find_by_name)
begin
[Gem::Specification.find_by_name(name, version_requirements)]
rescue Gem::LoadError
[]
end
else
Gem.source_index.find_name(name, version_requirements)
end
if !installed.empty? && Autobuild.do_update
# Look if we can update the package ...
dep = Gem::Dependency.new(name, version_requirements)
available =
if gem_fetcher.respond_to?(:find_matching)
non_prerelease = gem_fetcher.find_matching(dep, true, true).map(&:first)
if GemManager.with_prerelease
prerelease = gem_fetcher.find_matching(dep, false, true, true).map(&:first)
else prerelease = Array.new
end
(non_prerelease + prerelease).
map { |n, v, _| [n, v] }
else # Post RubyGems-2.0
type = if GemManager.with_prerelease then :complete
else :released
end
gem_fetcher.detect(type) do |tuple|
tuple.name == name && dep.match?(tuple)
end.map { |tuple, _| [tuple.name, tuple.version] }
end
installed_version = installed.map(&:version).max
available_version = available.map { |_, v| v }.max
if !available_version
if version
raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
else
raise ConfigError.new, "cannot find any gem with the name '#{name}'"
end
end
needs_update = (available_version > installed_version)
!needs_update
else
!installed.empty?
end
end
gems
end
def parse_package_entry(entry)
if entry =~ /^([^><=~]*)([><=~]+.*)$/
[$1.strip, $2.strip]
else
[entry]
end
end
def gems_interaction(gems, cmdlines)
if OSDependencies.force_osdeps
return true
elsif enabled?
return true
elsif silent?
return false
end
# We're not supposed to install rubygem packages but silent is not
# set, so display information about them anyway
puts <<-EOMSG
#{Autoproj.color("The build process and/or the packages require some Ruby Gems to be installed", :bold)}
#{Autoproj.color("and you required autoproj to not do it itself", :bold)}
You can use the --all or --ruby options to autoproj osdeps to install these
packages anyway, and/or change to the osdeps handling mode by running an
autoproj operation with the --reconfigure option as for instance
autoproj build --reconfigure
The following command line can be used to install them manually
#{cmdlines.map { |c| c.join(" ") }.join("\n ")}
Autoproj expects these Gems to be installed in #{GemManager.gem_home} This can
be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually
EOMSG
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
STDOUT.flush
STDIN.readline
puts
false
end
end
# Using pip to install python packages
class PipManager < Manager
attr_reader :installed_gems
def self.initialize_environment
Autoproj.env_set 'PYTHONUSERBASE', pip_home
end
# Return the directory where python packages are installed to.
# The actual path is pip_home/lib/pythonx.y/site-packages.
def self.pip_home
ENV['AUTOPROJ_PYTHONUSERBASE'] || File.join(Autoproj.root_dir,".pip")
end
def initialize
super(['pip'])
@installed_pips = Set.new
end
def guess_pip_program
if Autobuild.programs['pip']
return Autobuild.programs['pip']
end
Autobuild.programs['pip'] = "pip"
end
def install(pips)
guess_pip_program
if pips.is_a?(String)
pips = [pips]
end
base_cmdline = [Autobuild.tool('pip'), 'install','--user']
cmdlines = [base_cmdline + pips]
if pips_interaction(pips, cmdlines)
Autoproj.message " installing/updating Python dependencies: "+
"#{pips.sort.join(", ")}"
cmdlines.each do |c|
Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
end
pips.each do |p|
@installed_pips << p
end
end
end
def pips_interaction(pips, cmdlines)
if OSDependencies.force_osdeps
return true
elsif enabled?
return true
elsif silent?
return false
end
# We're not supposed to install rubygem packages but silent is not
# set, so display information about them anyway
puts <<-EOMSG
#{Autoproj.color("The build process and/or the packages require some Python packages to be installed", :bold)}
#{Autoproj.color("and you required autoproj to not do it itself", :bold)}
The following command line can be used to install them manually
#{cmdlines.map { |c| c.join(" ") }.join("\n ")}
Autoproj expects these Python packages to be installed in #{PipManager.pip_home} This can
be overridden by setting the AUTOPROJ_PYTHONUSERBASE environment variable manually
EOMSG
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
STDOUT.flush
STDIN.readline
puts
false
end
end
end
# Manager for packages provided by external package managers
class OSDependencies
class << self
# When requested to load a file called X.Y, the osdeps code will
# also look for files called X-suffix.Y, where 'suffix' is an
# element in +osdeps_suffixes+
#
# A usage of this functionality is to make loading conditional to
# the available version of certain tools, namely Ruby. Autoproj for
# instance adds ruby18 when started on Ruby 1.8 and ruby19 when
# started on Ruby 1.9
attr_reader :suffixes
end
@suffixes = []
def self.load(file)
if !File.file?(file)
raise ArgumentError, "no such file or directory #{file}"
end
candidates = [file]
candidates.concat(suffixes.map { |s| "#{file}-#{s}" })
error_t = if defined? Psych::SyntaxError then [ArgumentError, Psych::SyntaxError]
else ArgumentError
end
result = OSDependencies.new
candidates.each do |file|
next if !File.file?(file)
file = File.expand_path(file)
begin
data = YAML.load(File.read(file)) || Hash.new
verify_definitions(data)
rescue *error_t => e
raise ConfigError.new, "error in #{file}: #{e.message}", e.backtrace
end
result.merge(OSDependencies.new(data, file))
end
result
end
class << self
attr_reader :aliases
attr_accessor :force_osdeps
end
@aliases = Hash.new
attr_writer :silent
def silent?; @silent end
def self.alias(old_name, new_name)
@aliases[new_name] = old_name
end
def self.ruby_version_keyword
"ruby#{RUBY_VERSION.split('.')[0, 2].join("")}"
end
def self.autodetect_ruby_program
ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
ruby_bindir = RbConfig::CONFIG['bindir']
ruby_executable = File.join(ruby_bindir, ruby)
Autobuild.programs['ruby'] = ruby_executable
ruby_executable
end
def self.autodetect_ruby
self.alias(ruby_version_keyword, "ruby")
end
self.suffixes << ruby_version_keyword
autodetect_ruby
AUTOPROJ_OSDEPS = File.join(File.expand_path(File.dirname(__FILE__)), 'default.osdeps')
def self.load_default
file = ENV['AUTOPROJ_DEFAULT_OSDEPS'] || AUTOPROJ_OSDEPS
if !File.file?(file)
Autoproj.warn "#{file} (from AUTOPROJ_DEFAULT_OSDEPS) is not a file, falling back to #{AUTOPROJ_OSDEPS}"
file = AUTOPROJ_OSDEPS
end
OSDependencies.load(file)
end
PACKAGE_HANDLERS = [PackageManagers::AptDpkgManager,
PackageManagers::GemManager,
PackageManagers::EmergeManager,
PackageManagers::PacmanManager,
PackageManagers::HomebrewManager,
PackageManagers::YumManager,
PackageManagers::PortManager,
PackageManagers::ZypperManager,
PackageManagers::PipManager]
# Mapping from OS name to package manager name
#
# Package handlers and OSes MUST have different names. The former are
# used to resolve packages and the latter to resolve OSes in the osdeps.
# Since one can force the use of a package manager in any OS by adding a
# package manager entry, as e.g.
#
# ubuntu:
# homebrew: package
#
# we need to be able to separate between OS and package manager names.
OS_PACKAGE_HANDLERS = {
'debian' => 'apt-dpkg',
'gentoo' => 'emerge',
'arch' => 'pacman',
'fedora' => 'yum',
'macos-port' => 'macports',
'macos-brew' => 'brew',
'opensuse' => 'zypper'
}
# The information contained in the OSdeps files, as a hash
attr_reader :definitions
# All the information contained in all the OSdeps files, as a mapping
# from the OSdeps package name to [osdeps_file, definition] pairs
attr_reader :all_definitions
# The information as to from which osdeps file the current package
# information in +definitions+ originates. It is a mapping from the
# package name to the osdeps file' full path
attr_reader :sources
# Use to override the autodetected OS-specific package handler
attr_writer :os_package_handler
# Returns the package manager object for the current OS
def os_package_handler
if @os_package_handler.nil?
os_names, _ = OSDependencies.operating_system
if os_names && (key = os_names.find { |name| OS_PACKAGE_HANDLERS[name] })
@os_package_handler = package_handlers[OS_PACKAGE_HANDLERS[key]]
if !@os_package_handler
raise ArgumentError, "found #{OS_PACKAGE_HANDLERS[name]} as the required package handler for #{os_names.join(", ")}, but it is not registered"
end
else
@os_package_handler = PackageManagers::UnknownOSManager.new
end
end
return @os_package_handler
end
# Returns the set of package managers
def package_handlers
if !@package_handlers
@package_handlers = Hash.new
PACKAGE_HANDLERS.each do |klass|
obj = klass.new
obj.names.each do |n|
@package_handlers[n] = obj
end
end
end
@package_handlers
end
# The Gem::SpecFetcher object that should be used to query RubyGems, and
# install RubyGems packages
def initialize(defs = Hash.new, file = nil)
@definitions = defs.to_hash
@all_definitions = Hash.new { |h, k| h[k] = Array.new }
@sources = Hash.new
@installed_packages = Set.new
if file
defs.each_key do |package_name|
sources[package_name] = file
all_definitions[package_name] << [[file], defs[package_name]]
end
end
@silent = true
@filter_uptodate_packages = true
end
# Returns the full path to the osdeps file from which the package
# definition for +package_name+ has been taken
def source_of(package_name)
sources[package_name]
end
# Merges the osdeps information of +info+ into +self+. If packages are
# defined in both OSDependencies objects, the information in +info+
# takes precedence
def merge(info)
root_dir = nil
@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}")
end
v2
end
@sources = sources.merge(info.sources)
@all_definitions = all_definitions.merge(info.all_definitions) do |package_name, all_defs, new_all_defs|
all_defs = all_defs.dup
new_all_defs = new_all_defs.dup
new_all_defs.delete_if do |files, data|
if entry = all_defs.find { |_, d| d == data }
entry[0] |= files
end
end
all_defs.concat(new_all_defs)
end
end
# Perform some sanity checks on the given osdeps definitions
def self.verify_definitions(hash, path = [])
hash.each do |key, value|
if value && !key.kind_of?(String)
raise ArgumentError, "invalid osdeps definition: found an #{key.class} as a key in #{path.join("/")}. Don't forget to put quotes around numbers"
elsif !value && (key.kind_of?(Hash) || key.kind_of?(Array))
verify_definitions(key)
end
next if !value
if value.kind_of?(Array) || value.kind_of?(Hash)
verify_definitions(value, (path + [key]))
else
if !value.kind_of?(String)
raise ArgumentError, "invalid osdeps definition: found an #{value.class} as a value in #{path.join("/")}. Don't forget to put quotes around numbers"
end
end
end
end
# Returns true if it is possible to install packages for the operating
# system on which we are installed
def self.supported_operating_system?
if @supported_operating_system.nil?
os_names, _ = operating_system
@supported_operating_system =
if !os_names then false
else
os_names.any? { |os_name| OS_PACKAGE_HANDLERS.has_key?(os_name) }
end
end
return @supported_operating_system
end
# Used mainly during testing to bypass the operating system
# autodetection
def self.operating_system=(values)
@supported_operating_system = nil
@operating_system = values
end
def self.guess_operating_system
if File.exists?('/etc/debian_version')
versions = [File.read('/etc/debian_version').strip]
if versions.first =~ /sid/
versions = ["unstable", "sid"]
end
[['debian'], versions]
elsif File.exists?('/etc/redhat-release')
release_string = File.read('/etc/redhat-release').strip
release_string =~ /(.*) release ([\d.]+)/
name = $1.downcase
version = $2
if name =~ /Red Hat Entreprise/
name = 'rhel'
end
[[name], [version]]
elsif File.exists?('/etc/gentoo-release')
release_string = File.read('/etc/gentoo-release').strip
release_string =~ /^.*([^\s]+)$/
version = $1
[['gentoo'], [version]]
elsif File.exists?('/etc/arch-release')
[['arch'], []]
elsif Autobuild.macos?
version=`sw_vers | head -2 | tail -1`.split(":")[1]
manager =
if ENV['AUTOPROJ_MACOSX_PACKAGE_MANAGER']
ENV['AUTOPROJ_MACOSX_PACKAGE_MANAGER']
else 'macos-brew'
end
if !OS_PACKAGE_HANDLERS.include?(manager)
known_managers = OS_PACKAGE_HANDLERS.keys.grep(/^macos/)
raise ArgumentError, "#{manager} is not a known MacOSX package manager. Known package managers are #{known_managers.join(", ")}"
end
managers =
if manager == 'macos-port'
[manager, 'port']
else [manager]
end
[[*managers, 'darwin'], [version.strip]]
elsif Autobuild.windows?
[['windows'], []]
elsif File.exists?('/etc/SuSE-release')
version = File.read('/etc/SuSE-release').strip
version =~/.*VERSION\s+=\s+([^\s]+)/
version = $1
[['opensuse'], [version.strip]]
end
end
def self.ensure_derivatives_refer_to_their_parents(names)
names = names.dup
version_files = Hash[
'/etc/debian_version' => 'debian',
'/etc/redhat-release' => 'fedora',
'/etc/gentoo-release' => 'gentoo',
'/etc/arch-release' => 'arch',
'/etc/SuSE-release' => 'opensuse']
version_files.each do |file, name|
if File.exists?(file) && !names.include?(name)
names << name
end
end
names
end
def self.normalize_os_representation(names, versions)
# Normalize the names to lowercase
names = names.map(&:downcase)
versions = versions.map(&:downcase)
if !versions.include?('default')
versions += ['default']
end
return names, versions
end
# Autodetects the operating system name and version
#
# +osname+ is the operating system name, all in lowercase (e.g. ubuntu,
# arch, gentoo, debian)
#
# +versions+ is a set of names that describe the OS version. It includes
# both the version number (as a string) and/or the codename if there is
# one.
#
# Examples: ['debian', ['sid', 'unstable']] or ['ubuntu', ['lucid lynx', '10.04']]
def self.operating_system(options = Hash.new)
# Validate the options. We check on the availability of
# validate_options as to not break autoproj_bootstrap (in which
# validate_options is not available)
options =
if Kernel.respond_to?(:validate_options)
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']
@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
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) }
@operating_system = os
return os
end
end
@operating_system = nil # Invalid OS format in the configuration file
end
Autobuild.progress :operating_system_autodetection, "autodetecting the operating system"
names, versions = os_from_os_release
# Don't use the os-release information on Debian, since they
# refuse to put enough information to detect 'unstable'
# reliably. So, we use the heuristic method for it
if !names || names[0] == 'debian'
names, versions = guess_operating_system
end
return if !names
names = ensure_derivatives_refer_to_their_parents(names)
names, versions = normalize_os_representation(names, versions)
@operating_system = [names, versions]
Autoproj.change_option('operating_system', @operating_system, true)
Autobuild.progress :operating_system_autodetection, "operating system: #{(names - ['default']).join(",")} - #{(versions - ['default']).join(",")}"
@operating_system
ensure
Autobuild.progress_done :operating_system_autodetection
end
def self.os_from_os_release(filename = '/etc/os-release')
return if !File.exists?(filename)
fields = Hash.new
File.readlines(filename).each do |line|
line = line.strip
if line.strip =~ /^(\w+)=(?:["'])?([^"']+)(?:["'])?$/
fields[$1] = $2
elsif !line.empty?
Autoproj.warn "could not parse line '#{line.inspect}' in /etc/os-release"
end
end
names = []
versions = []
names << fields['ID'] << fields['ID_LIKE']
versions << fields['VERSION_ID']
version = fields['VERSION'] || ''
versions.concat(version.gsub(/[^\w.]/, ' ').split(' '))
return names.compact.uniq, versions.compact.uniq
end
def self.os_from_lsb
if !Autobuild.find_in_path('lsb_release')
return
end
distributor = `lsb_release -i -s`
distributor = distributor.strip.downcase
codename = `lsb_release -c -s`.strip.downcase
version = `lsb_release -r -s`.strip.downcase
return [distributor, [codename, version]]
end
# Return the list of packages that should be installed for +name+
#
# The following two simple return values are possible:
#
# nil:: +name+ has no definition
# []:: +name+ has no definition on this OS and/or for this specific OS
# version
#
# In all other cases, the method returns an array of triples:
#
# [package_handler, status, package_list]
#
# where status is FOUND_PACKAGES if +package_list+ is the list of
# packages that should be installed with +package_handler+ for +name+,
# and FOUND_NONEXISTENT if the nonexistent keyword is used for this OS
# name and version. The package list might be empty even if status ==
# FOUND_PACKAGES, for instance if the ignore keyword is used.
def resolve_package(name)
while OSDependencies.aliases.has_key?(name)
name = OSDependencies.aliases[name]
end
os_names, os_versions = OSDependencies.operating_system
os_names = os_names.dup
os_names << 'default'
dep_def = definitions[name]
if !dep_def
return nil
end
# Partition the found definition in all entries that are interesting
# for us: toplevel os-independent package managers, os-dependent
# package managers and os-independent package managers selected by
# OS or version
if !os_names
os_names = ['default']
os_versions = ['default']
end
package_handler_names = package_handlers.keys
result = []
found, pkg = partition_osdep_entry(name, dep_def, nil, (package_handler_names - os_package_handler.names), os_names, os_versions)
if found
result << [os_package_handler, found, pkg]
end
# NOTE: package_handlers might contain the same handler multiple
# times (when a package manager has multiple names). That's why we
# do a to_set.each
package_handlers.each_value.to_set.each do |handler|
found, pkg = partition_osdep_entry(name, dep_def, handler.names, [], os_names, os_versions)
if found
result << [handler, found, pkg]
end
end
# Recursive resolutions
found, pkg = partition_osdep_entry(name, dep_def, ['osdep'], [], os_names, os_versions)
if found
pkg.each do |pkg_name|
result.concat(resolve_package(pkg_name))
end
end
result.map do |handler, status, entries|
if handler.respond_to?(:parse_package_entry)
[handler, status, entries.map { |s| handler.parse_package_entry(s) }]
else
[handler, status, entries]
end
end
end
# Value returned by #resolve_package and #partition_osdep_entry in
# the status field. See the documentation of these methods for more
# information
FOUND_PACKAGES = 0
# Value returned by #resolve_package and #partition_osdep_entry in
# the status field. See the documentation of these methods for more
# information
FOUND_NONEXISTENT = 1
# Helper method that parses the osdep definition to split between the
# parts needed for this OS and specific package handlers.
#
# +osdep_name+ is the name of the osdep. It is used to resolve explicit
# mentions of a package handler, i.e. so that:
#
# pkg: gem
#
# is resolved as the 'pkg' package to be installed by the 'gem' handler
#
# +dep_def+ is the content to parse. It can be a string, array or hash
#
# +handler_names+ is a list of entries that we are looking for. If it is
# not nil, only entries that explicitely refer to +handler_names+ will
# be browsed, i.e. in:
#
# pkg:
# - test: 1
# - [a, list, of, packages]
#
# partition_osdep_entry('osdep_name', data, ['test'], [])
#
# will ignore the toplevel list of packages, while
#
# partition_osdep_entry('osdep_name', data, nil, [])
#
# will return it.
#
# +excluded+ is a list of branches that should be ignored during
# parsing. It is used to e.g. ignore 'gem' when browsing for the main OS
# package list. For instance, in
#
# pkg:
# - test
# - [a, list, of, packages]
#
# partition_osdep_entry('osdep_name', data, nil, ['test'])
#
# the returned value will only include the list of packages (and not
# 'test')
#
# The rest of the arguments are array of strings that contain list of
# keys to browse for (usually, the OS names and version)
#
# The return value is either nil if no packages were found, or a pair
# [status, package_list] where status is FOUND_NONEXISTENT if the
# nonexistent keyword was found, and FOUND_PACKAGES if either packages
# or the ignore keyword were found.
#
def partition_osdep_entry(osdep_name, dep_def, handler_names, excluded, *keys)
keys, *additional_keys = *keys
keys ||= []
found = false
nonexistent = false
result = []
found_keys = Hash.new
Array(dep_def).each do |names, values|
if !values
# Raw array of packages. Possible only if we are not at toplevel
# (i.e. if we already have a handler)
if names == 'ignore'
found = true if !handler_names
elsif names == 'nonexistent'
nonexistent = true if !handler_names
elsif !handler_names && names.kind_of?(Array)
result.concat(result)
found = true
elsif names.respond_to?(:to_str)
if excluded.include?(names)
elsif handler_names && handler_names.include?(names)
result << osdep_name
found = true
elsif !handler_names
result << names
found = true
end
elsif names.respond_to?(:to_hash)
rec_found, rec_result = partition_osdep_entry(osdep_name, names, handler_names, excluded, keys, *additional_keys)
if rec_found == FOUND_NONEXISTENT then nonexistent = true
elsif rec_found == FOUND_PACKAGES then found = true
end
result.concat(rec_result)
end
else
if names.respond_to?(:to_str) # names could be an array already
names = names.split(',')
end
if handler_names
if matching_name = handler_names.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
rec_found, rec_result = partition_osdep_entry(osdep_name, values, nil, excluded, keys, *additional_keys)
if rec_found == FOUND_NONEXISTENT then nonexistent = true
elsif rec_found == FOUND_PACKAGES then found = true
end
result.concat(rec_result)
end
end
matching_name = keys.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
if matching_name
rec_found, rec_result = partition_osdep_entry(osdep_name, values, handler_names, excluded, *additional_keys)
# We only consider the first highest-priority entry,
# regardless of whether it has some packages for us or
# not
idx = keys.index(matching_name)
if !rec_found
if !found_keys.has_key?(idx)
found_keys[idx] = nil
end
else
found_keys[idx] ||= [0, []]
found_keys[idx][0] += rec_found
found_keys[idx][1].concat(rec_result)
end
end
end
end
first_entry = found_keys.keys.sort.first
found_keys = found_keys[first_entry]
if found_keys
if found_keys[0] > 0
nonexistent = true
else
found = true
end
result.concat(found_keys[1])
end
found =
if nonexistent then FOUND_NONEXISTENT
elsif found then FOUND_PACKAGES
else false
end
return found, result
end
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
def resolve_os_dependencies(dependencies)
all_packages = []
dependencies.each do |name|
result = resolve_package(name)
if !result
raise MissingOSDep.new, "there is no osdeps definition for #{name}"
end
if result.empty?
if OSDependencies.supported_operating_system?
os_names, os_versions = OSDependencies.operating_system
raise MissingOSDep.new, "there is an osdeps definition for #{name}, but not for this operating system and version (resp. #{os_names.join(", ")} and #{os_versions.join(", ")})"
end
result = [[os_package_handler, FOUND_PACKAGES, [name]]]
end
result.each do |handler, status, packages|
if status == FOUND_NONEXISTENT
raise MissingOSDep.new, "there is an osdep definition for #{name}, and it explicitely states that this package does not exist on your OS"
end
if entry = all_packages.find { |h, _| h == handler }
entry[1].concat(packages)
else
all_packages << [handler, packages]
end
end
end
all_packages.delete_if do |handler, pkg|
pkg.empty?
end
return all_packages
end
# Returns true if +name+ is an acceptable OS package for this OS and
# version
def has?(name)
status = availability_of(name)
status == AVAILABLE || status == IGNORE
end
# Value returned by #availability_of if the required package has no
# definition
NO_PACKAGE = 0
# Value returned by #availability_of if the required package has
# definitions, but not for this OS name or version
WRONG_OS = 1
# Value returned by #availability_of if the required package has
# definitions, but the local OS is unknown
UNKNOWN_OS = 2
# Value returned by #availability_of if the required package has
# definitions, but the nonexistent keyword was used for this OS
NONEXISTENT = 3
# Value returned by #availability_of if the required package is
# available
AVAILABLE = 4
# Value returned by #availability_of if the required package is
# available, but no package needs to be installed to have it
IGNORE = 5
# If +name+ is an osdeps that is available for this operating system,
# returns AVAILABLE. Otherwise, returns one of:
#
# NO_PACKAGE:: the package has no definitions
# WRONG_OS:: the package has a definition, but not for this OS
# UNKNOWN_OS:: the package has a definition, but the local OS is unknown
# NONEXISTENT:: the package has a definition, but the 'nonexistent'
# keyword was found for this OS
# AVAILABLE:: the package is available for this OS
# IGNORE:: the package is available for this OS, but no packages need to
# be installed for it
def availability_of(name)
resolved = resolve_package(name)
if !resolved
return NO_PACKAGE
end
if resolved.empty?
if !OSDependencies.operating_system
return UNKNOWN_OS
elsif !OSDependencies.supported_operating_system?
return AVAILABLE
else return WRONG_OS
end
end
resolved = resolved.delete_if { |_, status, list| status == FOUND_PACKAGES && list.empty? }
failed = resolved.find_all do |handler, status, list|
status == FOUND_NONEXISTENT
end
if failed.empty?
if resolved.empty?
return IGNORE
else
return AVAILABLE
end
else
return NONEXISTENT
end
end
HANDLE_ALL = 'all'
HANDLE_RUBY = 'ruby'
HANDLE_OS = 'os'
HANDLE_NONE = 'none'
def self.osdeps_mode_option_unsupported_os
long_doc =<<-EOT
The software packages that autoproj will have to build may require other
prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
packages, packages from your operating system/distribution, ...). Autoproj is
usually able to install those automatically, but unfortunately your operating
system is not (yet) supported by autoproj's osdeps mechanism, it can only offer
you some limited support.
Some package handlers are cross-platform, and are therefore supported. However,
you will have to install the kind of OS dependencies (so-called OS packages)
This option is meant to allow you to control autoproj's behaviour while handling
OS dependencies.
* if you say "all", all OS-independent packages are going to be installed.
* if you say "gem", the RubyGem packages will be installed.
* if you say "pip", the Pythin PIP packages will be installed.
* if you say "none", autoproj will not do anything related to the OS
dependencies.
As any configuration value, the mode can be changed anytime by calling
autoproj reconfigure
Finally, the "autoproj osdeps" command will give you the necessary information
about the OS packages that you will need to install manually.
So, what do you want ? (all, none or a comma-separated list of: gem pip)
EOT
message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: gem pip) ?", long_doc.strip ]
Autoproj.configuration_option 'osdeps_mode', 'string',
:default => 'ruby',
:doc => message,
:lowercase => true
end
def self.osdeps_mode_option_supported_os
long_doc =<<-EOT
The software packages that autoproj will have to build may require other
prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
packages, packages from your operating system/distribution, ...). Autoproj
is able to install those automatically for you.
Advanced users may want to control this behaviour. Additionally, the
installation of some packages require administration rights, which you may
not have. This option is meant to allow you to control autoproj's behaviour
while handling OS dependencies.
* if you say "all", it will install all packages automatically.
This requires root access thru 'sudo'
* if you say "pip", only the Ruby packages will be installed.
Installing these packages does not require root access.
* if you say "gem", only the Ruby packages will be installed.
Installing these packages does not require root access.
* if you say "os", only the OS-provided packages will be installed.
Installing these packages requires root access.
* if you say "none", autoproj will not do anything related to the
OS dependencies.
Finally, you can provide a comma-separated list of pip gem and os.
As any configuration value, the mode can be changed anytime by calling
autoproj reconfigure
Finally, the "autoproj osdeps" command will give you the necessary information
about the OS packages that you will need to install manually.
So, what do you want ? (all, none or a comma-separated list of: os gem pip)
EOT
message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: os gem pip) ?", long_doc.strip ]
Autoproj.configuration_option 'osdeps_mode', 'string',
:default => 'all',
:doc => message,
:lowercase => true
end
def self.define_osdeps_mode_option
if supported_operating_system?
osdeps_mode_option_supported_os
else
osdeps_mode_option_unsupported_os
end
end
def self.osdeps_mode_string_to_value(string)
string = string.to_s.downcase.split(',')
modes = []
string.map do |str|
case str
when 'all' then modes.concat(['os', 'gem', 'pip'])
when 'ruby' then modes << 'gem'
when 'gem' then modes << 'gem'
when 'pip' then modes << 'pip'
when 'os' then modes << 'os'
when 'none' then
else raise ArgumentError, "#{str} is not a known package handler"
end
end
modes
end
# If set to true (the default), #install will try to remove the list of
# already uptodate packages from the installed packages. Set to false to
# install all packages regardless of their status
attr_writer :filter_uptodate_packages
# If set to true (the default), #install will try to remove the list of
# already uptodate packages from the installed packages. Use
# #filter_uptodate_packages= to set it to false to install all packages
# regardless of their status
def filter_uptodate_packages?
!!@filter_uptodate_packages
end
# Override the osdeps mode
def osdeps_mode=(value)
@osdeps_mode = OSDependencies.osdeps_mode_string_to_value(value)
end
# Returns the osdeps mode chosen by the user
def osdeps_mode
# This has two uses. It caches the value extracted from the
# AUTOPROJ_OSDEPS_MODE and/or configuration file. Moreover, it
# allows to override the osdeps mode by using
# OSDependencies#osdeps_mode=
if @osdeps_mode
return @osdeps_mode
end
@osdeps_mode = OSDependencies.osdeps_mode
end
def self.osdeps_mode
while true
mode =
if !Autoproj.has_config_key?('osdeps_mode') &&
mode_name = ENV['AUTOPROJ_OSDEPS_MODE']
begin OSDependencies.osdeps_mode_string_to_value(mode_name)
rescue ArgumentError
Autoproj.warn "invalid osdeps mode given through AUTOPROJ_OSDEPS_MODE (#{mode})"
nil
end
else
mode_name = Autoproj.user_config('osdeps_mode')
begin OSDependencies.osdeps_mode_string_to_value(mode_name)
rescue ArgumentError
Autoproj.warn "invalid osdeps mode stored in configuration file"
nil
end
end
if mode
@osdeps_mode = mode
Autoproj.change_option('osdeps_mode', mode_name, true)
return mode
end
# Invalid configuration values. Retry
Autoproj.reset_option('osdeps_mode')
ENV['AUTOPROJ_OSDEPS_MODE'] = nil
end
end
# The set of packages that have already been installed
attr_reader :installed_packages
# Set up the registered package handlers according to the specified osdeps mode
#
# It enables/disables package handlers based on either the value
# returned by {#osdeps_mode} or the value passed as option (the latter
# takes precedence). Moreover, sets the handler's silent flag using
# {#silent?}
#
# @option options [Array<String>] the package handlers that should be
# enabled. The default value is returned by {#osdeps_mode}
# @return [Array<PackageManagers::Manager>] the set of enabled package
# managers
def setup_package_handlers(options = Hash.new)
options =
if Kernel.respond_to?(:validate_options)
Kernel.validate_options options, :osdeps_mode => osdeps_mode
else
options = options.dup
options[:osdeps_mode] ||= osdeps_mode
options
end
os_package_handler.enabled = false
package_handlers.each_value do |handler|
handler.enabled = false
end
options[:osdeps_mode].each do |m|
if m == 'os'
os_package_handler.enabled = true
elsif pkg = package_handlers[m]
pkg.enabled = true
else
Autoproj.warn "osdep handler #{m.inspect} has no handler, available handlers are #{package_handlers.keys.map(&:inspect).sort.join(", ")}"
end
end
os_package_handler.silent = self.silent?
package_handlers.each_value do |v|
v.silent = self.silent?
end
enabled_handlers = []
if os_package_handler.enabled?
enabled_handlers << os_package_handler
end
package_handlers.each_value do |v|
if v.enabled?
enabled_handlers << v
end
end
enabled_handlers
end
# Requests the installation of the given set of packages
def install(packages, options = Hash.new)
# Remove the set of packages that have already been installed
packages = packages.to_set - installed_packages
return false if packages.empty?
setup_package_handlers(options)
packages = resolve_os_dependencies(packages)
packages = packages.map do |handler, list|
if filter_uptodate_packages? && handler.respond_to?(:filter_uptodate_packages)
list = handler.filter_uptodate_packages(list)
end
if !list.empty?
[handler, list]
end
end.compact
return false if packages.empty?
# Install OS packages first, as the other package handlers might
# depend on OS packages
os_packages, other_packages = packages.partition { |handler, list| handler == os_package_handler }
[os_packages, other_packages].each do |packages|
packages.each do |handler, list|
handler.install(list)
@installed_packages |= list.to_set
end
end
true
end
end
end
module Autobuild
class << self
# Configure the programs used by different packages
attr_reader :programs
# A cache of entries in programs to their resolved full path
#
# @return [{String=>[String,String,String]}] the triplet (full path,
# tool name, value of ENV['PATH']). The last two values are used to
# invalidate the cache when needed
#
# @see tool_in_path
attr_reader :programs_in_path
# Get a given program, using its name as default value. For
# instance
# tool('automake')
# will return 'automake' unless the autobuild script defined
# another automake program in Autobuild.programs by doing
# Autobuild.programs['automake'] = 'automake1.9'
def tool(name)
programs[name.to_sym] || programs[name.to_s] || name.to_s
end
def find_in_path(file)
path = ENV['PATH'].split(File::PATH_SEPARATOR).
find { |dir| File.exists?(File.join(dir, file)) }
if path
return File.join(path, file)
end
end
# Resolves the absolute path to a given tool
def tool_in_path(name)
path, path_name, path_env = programs_in_path[name]
current = tool(name)
if path_env != ENV['PATH'] || path_name != current
# Delete the current entry given that it is invalid
programs_in_path.delete(name)
if current[0, 1] == "/"
# This is already a full path
path = current
else
path = find_in_path(current)
end
if !path
raise ArgumentError, "tool #{name}, set to #{current}, can not be found in PATH=#{path_env}"
end
# Verify that the new value is a file and is executable
if !File.file?(path)
raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not a file"
elsif !File.executable?(path)
raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not executable"
end
programs_in_path[name] = [path, current, ENV['PATH']]
end
return path
end
end
@programs = Hash.new
@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
def self.override_option(option_name, value)
@option_overrides[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
def self.reset_option(key)
@user_config.delete(key)
end
def self.change_option(key, value, user_validated = false)
@user_config[key] = [value, user_validated]
end
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
end
@declared_options = Hash.new
def self.configuration_option(name, type, options, &validator)
@declared_options[name] = BuildOption.new(name, type, options, validator)
end
def self.declared_option?(name)
@declared_options.has_key?(name)
end
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
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
end
def self.has_config_key?(name)
@user_config.has_key?(name)
end
def self.load_config
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
end
end
class << self
attr_accessor :reconfigure
end
def self.reconfigure?; @reconfigure end
end
module Autoproj
class UserError < RuntimeError; end
# OS-independent creation of symbolic links. Note that on windows, it only
# works for directories
def self.create_symlink(from, to)
if Autobuild.windows?
Dir.create_junction(to, from)
else
FileUtils.ln_sf from, to
end
end
# Returns true if +path+ is part of an autoproj installation
def self.in_autoproj_installation?(path)
root_dir(File.expand_path(path))
true
rescue UserError
false
end
# Forcefully sets the root directory
#
# This is mostly useful during bootstrapping (i.e. when the search would
# fail)
def self.root_dir=(dir)
@root_dir = dir
end
# Returns the root directory of the current autoproj installation.
#
# If the current directory is not in an autoproj installation,
# raises UserError.
def self.root_dir(dir = Dir.pwd)
if @root_dir
return @root_dir
end
root_dir_rx =
if Autobuild.windows? then /^[a-zA-Z]:\\\\$/
else /^\/$/
end
while root_dir_rx !~ dir && !File.directory?(File.join(dir, "autoproj"))
dir = File.dirname(dir)
end
if root_dir_rx =~ dir
raise UserError, "not in a Autoproj installation"
end
#Preventing backslashed in path, that might be confusing on some path compares
if Autobuild.windows?
dir = dir.gsub(/\\/,'/')
end
dir
end
# Returns the configuration directory for this autoproj installation.
#
# If the current directory is not in an autoproj installation,
# raises UserError.
def self.config_dir
File.join(root_dir, "autoproj")
end
# @deprecated use Autobuild.find_in_path instead
#
# Warning: the autobuild method returns nil (instead of raising) if the
# argument cannot be found
def self.find_in_path(name)
if path = Autobuild.find_in_path(name)
return path
else raise ArgumentError, "cannot find #{name} in PATH (#{ENV['PATH']})"
end
end
class << self
# The directory in which packages will be installed.
#
# If it is a relative path, it is relative to the root dir of the
# installation.
#
# The default is "install"
attr_reader :prefix
# Change the value of 'prefix'
def prefix=(new_path)
@prefix = new_path
Autoproj.change_option('prefix', new_path, true)
end
end
@prefix = "install"
# Returns the build directory (prefix) for this autoproj installation.
#
# If the current directory is not in an autoproj installation, raises
# UserError.
def self.build_dir
File.expand_path(Autoproj.prefix, root_dir)
end
# Returns the path to the provided configuration file.
#
# If the current directory is not in an autoproj installation, raises
# UserError.
def self.config_file(file)
File.join(config_dir, file)
end
# Run the provided command as user
def self.run_as_user(*args)
if !system(*args)
raise "failed to run #{args.join(" ")}"
end
end
# Run the provided command as root, using sudo to gain root access
def self.run_as_root(*args)
if !system(Autobuild.tool_in_path('sudo'), *args)
raise "failed to run #{args.join(" ")} as root"
end
end
# Return the directory in which remote package set definition should be
# checked out
def self.remotes_dir
File.join(root_dir, ".remotes")
end
def self.env_inherit(*names)
Autobuild.env_inherit(*names)
end
# @deprecated use isolate_environment instead
def self.set_initial_env
isolate_environment
end
# Initializes the environment variables to a "sane default"
#
# Use this in autoproj/init.rb to make sure that the environment will not
# get polluted during the build.
def self.isolate_environment
Autobuild.env_inherit = false
Autobuild.env_push_path 'PATH', "/usr/local/bin", "/usr/bin", "/bin"
end
def self.prepare_environment
# Set up some important autobuild parameters
env_inherit 'PATH', 'PKG_CONFIG_PATH', 'RUBYLIB', \
'LD_LIBRARY_PATH', 'CMAKE_PREFIX_PATH', 'PYTHONPATH'
env_set 'AUTOPROJ_CURRENT_ROOT', Autoproj.root_dir
env_set 'RUBYOPT', "-rubygems"
Autoproj::OSDependencies::PACKAGE_HANDLERS.each do |pkg_mng|
pkg_mng.initialize_environment
end
end
class << self
attr_writer :shell_helpers
def shell_helpers?; !!@shell_helpers end
end
@shell_helpers = true
# Create the env.sh script in +subdir+. In general, +subdir+ should be nil.
def self.export_env_sh(subdir = nil)
# Make sure that we have as much environment as possible
Autoproj::CmdLine.update_environment
filename = if subdir
File.join(Autoproj.root_dir, subdir, ENV_FILENAME)
else
File.join(Autoproj.root_dir, ENV_FILENAME)
end
shell_dir = File.expand_path(File.join("..", "..", "shell"), File.dirname(__FILE__))
if Autoproj.shell_helpers? && shell = ENV['SHELL']
shell_kind = File.basename(shell)
if shell_kind =~ /^\w+$/
shell_file = File.join(shell_dir, "autoproj_#{shell_kind}")
if File.exists?(shell_file)
Autoproj.message
Autoproj.message "autodetected the shell to be #{shell_kind}, sourcing autoproj shell helpers"
Autoproj.message "add \"Autoproj.shell_helpers = false\" in autoproj/init.rb to disable"
Autobuild.env_source_after(shell_file)
end
end
end
File.open(filename, "w") do |io|
if Autobuild.env_inherit
io.write <<-EOF
if test -n "$AUTOPROJ_CURRENT_ROOT" && test "$AUTOPROJ_CURRENT_ROOT" != "#{Autoproj.root_dir}"; then
echo "the env.sh from $AUTOPROJ_CURRENT_ROOT is already loaded. Start a new shell before sourcing this one"
return
fi
EOF
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.
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
end
# Same as #load, but runs only if the file exists.
def self.load_if_present(package_set, *path)
path = File.join(*path)
if File.file?(path)
self.load(package_set, *path)
end
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 = [])
Find.find(File.expand_path(dir)) do |name|
next unless name =~ /\.so$/
next if exclude_paths.find { |p| name =~ p }
output = `ldd -r #{name} 2>&1`
if output =~ /undefined symbol/
Autoproj.message(" WARN: #{name} has undefined symbols", :magenta)
end
end
end
end
# Override Autoproj.root_dir
module Autoproj
def self.root_dir
@root_dir
end
@root_dir = Dir.pwd
end
DEFS = <<EODEFS
---
none: ignore
ruby19:
debian:
- ruby1.9.1
- ruby1.9.1-dev
- rubygems1.9.1
- rake
- rubygems-integration
ubuntu:
'12.04':
- ruby1.9.1
- ruby1.9.1-dev
- rubygems1.9.1
- ri1.9.1
- libopenssl-ruby1.9.1
- rake
default:
- ruby1.9.1
- ruby1.9.1-dev
- rubygems1.9.1
- ri1.9.1
- libopenssl-ruby1.9.1
- rake
- rubygems-integration
gentoo:
- dev-lang/ruby:1.9
- rake
fedora:
'17':
- ruby
- rubygems
macos-port:
- ruby19
- rake
macos-brew:
- gem: rake
opensuse: ruby19-devel
default: ignore
ruby20:
debian:
- ruby2.0
- ruby2.0-dev
- rake
- rubygems-integration
ubuntu:
13.10,14.04:
- ruby2.0
- ruby2.0-dev
- rake
- rubygems-integration
fedora:
'20':
- ruby
- ruby-devel
- rubygem-rake
opensuse: ruby20-devel
macos-brew:
- gem: rake
default: ignore
ruby21:
debian:
- ruby2.1
- ruby2.1-dev
- rake
- rubygems-integration
fedora: ruby-devel
macos-brew:
- gem: rake
default,ubuntu: ignore
build-essential:
debian,ubuntu: build-essential
gentoo: ignore
arch: base-devel
fedora:
- gcc-c++
- make
- glibc-devel
darwin: ignore
opensuse:
- ! '@devel_C_C++'
- gcc-c++
autobuild:
- gem: autobuild
- osdep: readline
autoproj:
- gem: autoproj
- osdep: readline
readline:
debian,ubuntu: libreadline-dev
fedora: readline-devel
opensuse: readline-devel
arch: core/readline
macos-brew: readline
default: ignore
git:
debian:
lenny: git
default: git-core
ubuntu: git-core
gentoo: dev-vcs/git
arch: git
fedora: git
macos-port: git-core
macos-brew: git
opensuse: git
hg:
debian,ubuntu: mercurial
gentoo: dev-vcs/mercurial
arch: mercurial
fedora: mercurial
darwin: mercurial
opensuse: mercurial
svn:
debian,ubuntu: subversion
gentoo: dev-util/subversion
arch: subversion
fedora: subversion
darwin: subversion
opensuse: subversion
cmake:
debian,ubuntu: cmake
gentoo: dev-util/cmake
arch: cmake
fedora: cmake
darwin: cmake
opensuse: cmake
autotools:
debian,ubuntu:
- automake1.9
- autoconf
gentoo:
- sys-devel/automake:1.9
- sys-devel/autoconf
arch:
- automake
- autoconf
fedora:
- automake
- autoconf
darwin:
- automake
- autoconf
opensuse:
- automake
- autoconf
archive:
debian,ubuntu:
- tar
- unzip
gentoo:
- app-arch/tar
- app-arch/unzip
arch:
- tar
- unzip
fedora:
- tar
- unzip
macos-port:
- gnutar
- unzip
macos-brew:
- gnu-tar
opensuse:
- tar
- unzip
cvs:
debian,ubuntu: cvs
fedora: cvs
darwin: cvs
arch: cvs
opensuse: cvs
pip:
debian,ubuntu: python-pip
arch: python2-pip
opensuse: python-pip
fedora: python-pip
EODEFS
Autoproj::OSDependencies.define_osdeps_mode_option
osdeps_mode = Autoproj::OSDependencies.osdeps_mode.join(",")
ENV['AUTOPROJ_OSDEPS_MODE'] = osdeps_mode
# First thing we do is install a proper ruby environment. We make sure that we
# aren't installing any gems for now (as we need to choose the right gem
# binary) by setting Autobuild.programs['gem'] to nil
Autobuild.programs['gem'] = nil
Autoproj::OSDependencies.autodetect_ruby
Autoproj::OSDependencies.autodetect_ruby_program
osdeps_management =
if ENV['AUTOPROJ_DEFAULT_OSDEPS']
Autoproj::OSDependencies.load(ENV['AUTOPROJ_DEFAULT_OSDEPS'])
else
Autoproj::OSDependencies.new(YAML.load(DEFS))
end
osdeps_management.silent = false
begin
STDERR.puts "autoproj: installing a proper Ruby environment (this can take a long time)"
osdeps_management.install(['ruby'])
rescue Autoproj::ConfigError => e
STDERR.puts "failed: #{e.message}"
exit(1)
end
# Now try to find out the name of the gem binary
PACKAGES = []
STDERR.puts "autoproj: installing autoproj and its dependencies (this can take a long time)"
# First install the dependencies of autoproj, as we don't want them to be
# affected by the prerelease flag
begin
if !PACKAGES.empty?
osdeps_management.install(PACKAGES)
end
rescue Autoproj::ConfigError => e
STDERR.puts "failed: #{e.message}"
exit(1)
end
File.open('env.sh', 'w') do |io|
io.write <<-EOSHELL
export RUBYOPT=-rubygems
export GEM_PATH=#{gem_path}
export GEM_HOME=#{gem_home}
export PATH=$GEM_HOME/bin:$PATH
EOSHELL
end
# If the user specifies "dev" on the command line, install the prerelease
# version of autoproj. If it is "localdev", expect him to install autoproj and
# run autoproj bootstrap manually.
if ARGV.first != "localdev"
if ARGV.first == "dev"
ENV['AUTOPROJ_USE_PRERELEASE'] = '1'
Autoproj::PackageManagers::GemManager.with_prerelease = true
ARGV.shift
end
begin
osdeps_management.install(['build-essential'])
osdeps_management.install(['autobuild'])
osdeps_management.install(['autoproj'])
rescue Autoproj::ConfigError => e
STDERR.puts "failed: #{e.message}"
exit(1)
end
Autoproj::PackageManagers::GemManager.with_prerelease = false
if !system('autoproj', 'bootstrap', *ARGV)
STDERR.puts "ERROR: failed to run autoproj bootstrap #{ARGV.join(", ")}"
exit 1
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment