Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
executable file 1999 lines (1626 sloc) 53.723 kb
#!/usr/bin/env ruby
require 'rubygems'
require 'bundler/setup'
require './rakelib/configure'
require './rakelib/release'
require './rakelib/build_signature'
require 'rbconfig'
require 'tempfile'
require 'fileutils'
require 'stringio'
require 'date'
require 'digest/md5'
require 'net/http'
require 'redcard'
module Rubinius
BUILD_CONFIG = {}
end
root = File.expand_path File.dirname(__FILE__)
require File.join(root, "kernel", "delta", "options")
class Configure
# Default settings only. All code that may depend on user-selected options
# must run after options are processed.
def initialize(root)
@log = Logger.new "configure.log"
@command_line = ARGV.dup
@log.log "Command line: #{@command_line.join(" ").inspect}"
@features = {}
@defines = []
@config = File.join(root, "config.rb")
# Platform settings
@host = `sh -c ./rakelib/config.guess`.chomp
@cpu = nil
@vendor = nil
@os = nil
@windows = nil
@darwin = nil
@bsd = nil
@linux = nil
@little_endian = false
@sizeof = {}
# Build tools
@cc = nil
@cxx = nil
@make = nil
@rake = nil
@tar = nil
@perl = nil
@gem = nil
# LLVM settings
@llvm_enabled = true
@llvm_path = nil
@llvm_system_name = get_system_name
@llvm_configure = nil
@llvm_version = nil
@llvm_api_version = nil
@llvm_shared = false
@llvm_shared_objs = nil
@llvm_cxxflags = ""
@llvm_ldflags = ""
# System settings
@libc = nil
@x86_32 = false
@x86_64 = false
@fibers = false
@dtrace = false
@dtrace_const = false
@have_lchmod = false
@have_lchown = false
@debug_build = false
@include_dirs = []
@lib_dirs = []
# File system paths
@sourcedir = root
@prefixdir = nil
@bindir = nil
@appdir = nil
@libdir = nil
@encdir = nil
@runtimedir = nil
@kerneldir = nil
@sitedir = nil
@vendordir = nil
@mandir = nil
@gemsdir = nil
@includedir = nil
@stagingdir = nil
@build_prefix = nil
@capi_includedir = "#{@sourcedir}/vm/include/capi"
@runtime_gems_dir = nil
@bootstrap_gems_dir = nil
@vm_release_h = File.join(root, "/vm/gen/release.h")
@preserve_prefix = false
@program_name = "rbx"
@bin_links = ["rbx", "ruby", "rake", "gem", "irb", "rdoc", "ri", "erb"]
@use_bin_links = true
# Gems
#
# Rubinius build tools installed in the runtime dir
@runtime_gems = [
"rubinius-ast",
"rubinius-compiler",
"rubinius-melbourne",
"rubinius-processor",
"rubinius-toolset",
]
# Standard library gems to bootstrap rubygems and Bundler
@bootstrap_gems = [
"ffi2-generators",
"rubysl-etc",
"rubysl-fileutils",
"rubysl-mkmf",
"rubysl-shellwords",
"rubysl-date",
"rubysl-delegate",
"rubysl-digest",
"rubysl-etc",
"rubysl-fcntl",
"rubysl-fileutils",
"rubysl-monitor",
"rubysl-openssl",
"rubysl-optparse",
"rubysl-stringio",
"rubysl-strscan",
"rubysl-tempfile",
"rubysl-thread",
"rubysl-tmpdir",
"rubysl-uri",
"rubysl-yaml",
"rubysl-zlib",
"psych",
]
# List of all gems to pre-install.
@gems_list = File.join(root, "gems_list.txt")
@gem_files = File.readlines(@gems_list).map { |x| x.chomp }
# Default cache directory, can be overwritten by the user.
@gems_cache = File.expand_path "../vendor/cache", __FILE__
# Vendored library settings
@vendored_libdir = File.join(root, "/vendor")
# Ruby compatibility version
@ruby_version = "2.1.0"
@ruby_libversion = @ruby_version.split(/\./)[0..1].join.to_i
# Configure settings
@release_build = !git_directory
end
# Set up system commands to run in cmd.exe on Windows. Either Windows
# or MRI on Windows has issues with subprocesses where the invocation
# of the subprocess will return before the subprocess has finished.
# This manifests in configure when uncompressing LLVM source returns
# but attempting to move the directory fails sporadically with an access
# exception. Adding the, essentially no-op, 'sleep 0' resolves this.
def msys_system(cmd)
old_system %[cmd.exe /C "#{cmd} && sleep 0"]
end
def msys_backquote(cmd)
old_backquote %[cmd.exe /C "#{cmd}"]
end
def expand(path)
File.expand_path(path)
end
def expand_install_dir(dir)
dir = expand dir
if !@preserve_prefix and File.directory?(dir) and dir !~ /(rubinius|rbx).*\/?$/
original = dir
dir += "/rubinius/#{@libversion}"
@log.write "The directory #{original} already exists, installing to #{dir}"
end
dir
end
def set_host
/([^-]+)-([^-]+)-(.*)/ =~ @host
@cpu, @vendor, @os = $1, $2, $3
# TODO: For better cross-compiling support, it may be necessary to
# use the feature facility to check for a define in the compiler.
@windows = (@host =~ /mingw|mswin/) != nil
@darwin = (@host =~ /darwin/) != nil
@bsd = (@host =~ /bsd/) != nil
@linux = (@host =~ /linux/) != nil
end
def set_system_commands
# Set up system commands to run in cmd.exe on Windows.
if @windows
alias :old_system :system
alias :old_backquote :`
alias :system :msys_system
alias :` :msys_backquote
end
end
def set_filesystem_paths
@prefixdir = @prefixdir ? expand_install_dir(@prefixdir) : @sourcedir
if @appdir
dir = expand_install_dir @appdir
@libdir = dir + "/library"
@runtimedir = dir + "/runtime"
@kerneldir = dir + "/kernel"
@sitedir = dir + "/site"
@vendordir = dir + "/vendor"
end
@bindir = @prefixdir + "/bin" unless @bindir
@libdir = @prefixdir + "/library" unless @libdir
@runtimedir = @prefixdir + "/runtime" unless @runtimedir
@kerneldir = @prefixdir + "/kernel" unless @kerneldir
@sitedir = @prefixdir + "/site" unless @sitedir
@vendordir = @prefixdir + "/vendor" unless @vendordir
@mandir = @prefixdir + "/man" unless @mandir
@gemsdir = @prefixdir + "/gems" unless @gemsdir
@includedir = @prefixdir + "/vm/include/capi" unless @includedir
@encdir = @libdir + "/encoding/converter"
dirs = [@bindir, @libdir, @runtimedir, @kerneldir, @sitedir, @vendordir,
@mandir, @gemsdir, @includedir, @encdir]
parts = dirs.map { |d| d.split "/" }
i = 0
total = parts[0].size
prefix = []
while i < total
part = parts[0][i]
break unless parts.all? { |p| p[i] == part }
prefix << part
i += 1
end
@prefixdir = prefix.join "/"
size = @prefixdir.size
dirs.each { |d| d.replace d[size..-1] }
unless @prefixdir == @sourcedir
@stagingdir ||= "#{@sourcedir}/staging"
FileUtils.rm_rf @stagingdir
@stagingdir = File.join @stagingdir, @prefixdir if @preserve_prefix
FileUtils.mkdir_p @stagingdir
end
@build_prefix = @stagingdir || @sourcedir
@bootstrap_gems_dir ||= "#{@sourcedir}/bootstrap/gems"
@runtime_gems_dir ||= "#{@build_prefix}#{@runtimedir}/gems"
end
def options
@options = Rubinius::Options.new "Usage: configure [options]", 30
o = @options
o.left_align
o.doc " Configure settings"
o.on "--log-file", "NAME", "Write log to file NAME" do |name|
old_log = @log.path
@log = Logger.new name, false
@log.replace old_log
end
o.on "--make", "NAME", "Use NAME as 'make' during build" do |name|
@make = name
end
o.on "--rake", "NAME", "Use NAME as 'rake' during build" do |name|
@rake = name
end
o.on "--tar", "NAME", "Use NAME as 'tar'" do |name|
@tar = name
end
o.on "--perl", "NAME", "Use NAME as 'perl' during build" do |name|
@perl = name
end
o.on "--gem", "NAME", "Use NAME as 'gem' during build" do |name|
@gem = name
end
o.on "--debug-build", "Disable C++ optimizations and retain debugging symbols" do
@debug_build = true
end
o.on "--release-build", "Build from local files instead of accessing the network" do
@release_build = true
end
o.doc "\n Compiler settings"
o.on "--cc", "COMPILER", "Compiler to use for C code (eg gcc, clang)" do |cc|
@cc = cc
end
o.on "--cxx", "COMPILER", "Compiler to use for C++ code (eg g++, clang++)" do |cxx|
@cxx = cxx
end
o.doc "\n LLVM settings"
o.on "--disable-llvm", "Don't build with LLVM" do
@llvm_enabled = false
end
o.on "--system-name", "NAME", "Name of OS (eg fedora-8, ubuntu-10.04)" do |name|
@llvm_system_name = name
end
o.on "--llvm-path", "PATH", "File system path to the directory containing LLVM" do |dir|
@llvm_path = dir.dup
end
o.on "--llvm-config", "PROGRAM", "File system path to the llvm-config program" do |program|
@llvm_configure = program
end
o.on "--llvm-shared", "Link to shared LLVM library" do
@llvm_shared = true
end
o.doc "\n System settings"
o.on "--with-include-dir", "DIR", "Add DIR to the default include search paths" do |dir|
dir.split(File::PATH_SEPARATOR).each do |d|
@include_dirs << d
end
end
o.on "--with-lib-dir", "DIR", "Add DIR to the default library search paths" do |dir|
dir.split(File::PATH_SEPARATOR).each do |d|
@lib_dirs << d
end
end
o.on "--with-opt-dir", "DIR", "Add DIR/include and DIR/lib to include and library search paths" do |dir|
dir.split(File::PATH_SEPARATOR).each do |d|
@include_dirs << "#{d}/include"
@lib_dirs << "#{d}/lib" << "#{d}/lib64"
end
end
o.on "--libc", "NAME", "Use NAME as the libc for FFI" do |name|
@libc = name
end
o.on "--host", "HOST", "Override guessed platform with HOST specification" do |host|
@log.write "------------------------------------------------------"
@log.write "\nChanging the platform specification can cause Rubinius"
@log.write "to malfunction. The current platform specification is:"
@log.write "\n#{@host}"
@log.write "\n------------------------------------------------------"
@host = host
end
o.doc "\n Program names"
o.on "--program-name", "NAME", "Build Rubinius executable as NAME" do |name|
@program_name = name
end
o.on "--bin-link", "NAME", "Create NAME as binary symlink to program name" do |name|
@bin_links << name
end
o.on "--no-bin-links", "Do not create any symlinks to program name" do
@use_bin_links = false
end
o.doc "\n File system paths for installing Rubinius"
o.on "-P", "--prefix", "PATH", "Install Rubinius in subdirectories of PATH" do |dir|
warn_prefix dir
@prefixdir = dir.dup
end
o.on "-B", "--bindir", "PATH", "Install Rubinius executable in PATH" do |dir|
@bindir = expand dir
end
o.on "-I", "--includedir", "PATH", "Install Rubinius C-API include files in PATH" do |dir|
@includedir = expand dir
end
o.on "-A", "--appdir", "PATH", "Install Ruby runtime and libraries in PATH" do |dir|
@appdir = dir.dup
end
o.on "-L", "--libdir", "PATH", "Install Rubinius shared library in PATH" do |dir|
@libdir = dir.dup
end
o.on "-M", "--mandir", "PATH", "Install man pages in PATH" do |dir|
@mandir = expand dir
end
o.on "-G", "--gemsdir", "PATH", "Install gems in PATH" do |dir|
@gemsdir = expand dir
end
o.on "--gems-cache", "PATH", "Cache Gems in PATH during compilation" do |dir|
@gems_cache = expand dir
end
o.on "--sitedir", "PATH", "Install site-specific Ruby code in PATH" do |dir|
@sitedir = expand dir
end
o.on "--vendordir", "PATH", "Install vendor-specific Ruby code in PATH" do |dir|
@vendordir = expand dir
end
o.on "--preserve-prefix", "Use the configure prefix for staging Rubinius to install" do
@preserve_prefix = true
end
o.on "--stagingdir", "PATH", "Use PATH to build and prepare all files for install" do |dir|
@stagingdir = expand dir
end
o.doc "\n Optional features"
feature "execinfo", true
feature "vendor-zlib", false
feature "alloc-tracking", false
feature "fibers", true
feature "dtrace", false
feature "rpath", true
o.doc "\n Help!"
o.on "--show", "Print the current configuration and exit" do
print_debug
exit 0
end
o.on "-V", "--verbose", "Print additional info" do
@verbose = true
end
o.help
o.doc ""
end
def feature(name, default_value=true)
@features[name] = ConfigurationToggle.new default_value
@options.on "--with-#{name}", "Enable #{name}" do
@features[name].configured = true
end
@options.on "--without-#{name}", "Disable #{name}" do
@features[name].configured = false
end
end
def parse(ary)
@options.parse ary
end
def md5_checksum(md5_path, full_path)
return Digest::MD5.file(full_path).hexdigest == File.read(md5_path).strip.split(" ").first
end
def download(url, full_path, follows=0)
begin
dir = File.dirname full_path
Dir.mkdir dir unless File.exist? dir
uri = url.kind_of?(URI) ? url : URI(url)
if ENV['http_proxy']
protocol, userinfo, p_host, p_port = URI::split(ENV['http_proxy'])
p_user, p_pass = userinfo.split(/:/) if userinfo
http = Net::HTTP.new(uri.host, uri.port, p_host, p_port, p_user, p_pass)
else
http = Net::HTTP.new(uri.host, uri.port)
end
http.use_ssl = true if uri.scheme == 'https'
request = Net::HTTP::Get.new(uri.request_uri)
http.request(request) do |res|
case res
when Net::HTTPNotFound
@log.write " #{url} not found."
return false
when Net::HTTPMovedPermanently,
Net::HTTPFound,
Net::HTTPSeeOther,
Net::HTTPTemporaryRedirect
if follows > 3
@log.write " ERROR: too many redirects: #{url}"
return false
end
return download URI.parse(res['Location']), full_path, follows + 1
when Net::HTTPClientError
@log.write " ERROR: #{res.inspect}"
return false
end
size = 0
total = res.header['Content-Length'].to_i
@log.write " Downloading #{File.basename(full_path)}..."
File.open full_path, "wb" do |f|
res.read_body do |chunk|
f << chunk
size += chunk.size
print "\r [ %d%% (%d of %d) ]" % [(size * 100) / total, size, total]
end
end
@log.write ": done!"
end
rescue Interrupt
File.unlink full_path if File.exist?(full_path)
raise
rescue StandardError => e
File.unlink full_path if File.exist?(full_path)
@log.write " ERROR: #{e.message}"
return false
end
return true
end
def setup_llvm
unless @llvm_enabled
@log.write "WARNING: LLVM disabled."
return false
end
@log.print " Checking for 'llvm-config': "
config = @llvm_configure
if !config
which = ENV['PATH'].split(":").find do |path|
File.exist? File.join(path, "llvm-config")
end
if which
config = File.join(which, "llvm-config")
elsif @darwin
if macports?
config = macports_llvm_config
else
out = `brew list llvm | grep '/llvm-config$'`
config = out.chomp if $?.success?
end
end
end
if config
config_cmd = llvm_config_cmd config
begin
version = `#{config_cmd} --version`.strip
# Ruby 1.8 returns an empty string
failed = true if version.empty?
rescue Errno::ENOENT
# Ruby 1.9 raises this error
failed = true
end
unless failed
parts = version.sub(/svn$/, "").split(".").map { |i| i.to_i }
api_version = ("%d%02d" % parts[0..1]).to_i
if api_version < 300 or api_version > 305
@log.write "only LLVM 3.0-3.5 is supported"
else
@log.write "found! (version #{version} - api: #{api_version})"
@llvm = :config
@llvm_configure = config_cmd
@llvm_version = version
@llvm_api_version = api_version
check_llvm_flags
if @llvm_shared
setup_llvm_shared
end
return true
end
else
@log.write "executing #{config_cmd.inspect} failed"
end
else
@log.write "not found"
end
failure "ABORT: unable to set up LLVM"
end
def setup_llvm_shared
@log.print " Checking for LLVM shared libs: "
src = <<-EOP
#include <llvm/IR/LLVMContext.h>
using namespace llvm;
int main() { LLVMContext &Context = getGlobalContext(); }
EOP
common_args = "`#{@llvm_configure} --cppflags` #{@llvm_cxxflags} #{@llvm_ldflags}".strip.split(/\s+/)
shared_configs = {
"libLLVM-#{@llvm_version}" => ["-lLLVM-#{@llvm_version}"],
"#{@llvm_configure} --libs" => `#{@llvm_configure} --libs`.strip.split(/\s+/)
}
shared_configs.each do |desc, objs|
status = check_program(false, *(common_args + objs)) do |f|
f.puts src
@log.log src
end
if status == 0
@log.write "found! (using #{desc})"
@llvm_shared_objs = objs
return true
end
end
@log.write "not found"
false
end
def check_llvm_flags
flags = '--ldflags'
if @llvm_api_version >= 305
@llvm_cxxflags << " -std=c++11"
# Starting with LLVM 3.5 the --system-libs option is required in order to
# link against libraries such as zlib. Prior to 3.5 this was handled by
# --ldflags.
flags << ' --system-libs'
end
# Generate the actual flags. For whatever reason llvm-config also includes
# newlines in the output, so lets get rid of those while we're at it.
@llvm_ldflags = `#{@llvm_configure} #{flags}`.strip.gsub("\n", ' ')
end
def env(which)
ENV[which] || ""
end
def default_link_libs
libs = []
unless @host =~ /haiku/
libs << "m"
end
libs
end
def failure(message=nil)
@log.error message if message
STDERR.puts "\nRunning 'configure' failed. Please check configure.log for more details."
exit 1
end
def default_cc
return 'clang' if `clang --version > /dev/null 2>&1` && $?.success?
return 'gcc'
end
def default_cxx
return 'clang++' if `clang++ --version > /dev/null 2>&1` && $?.success?
return 'g++'
end
def check_tools
@cc ||= ENV['CC'] || default_cc
@cxx ||= ENV['CXX'] || default_cxx
check_tool_version @cc, '-dumpversion', [4, 1]
check_tool_version @cxx, '-dumpversion', [4, 1]
@make ||= ENV['MAKE'] || 'make'
@rake ||= ENV['RAKE'] || 'rake'
@tar ||= ENV['TAR'] || (@windows ? 'bsdtar' : 'tar')
@perl ||= ENV['PERL'] || 'perl'
@gem ||= ENV['GEM'] || 'gem'
@gcc_major = `#{@cc} -dumpversion`.strip.split(".")[0,2].join(".")
if @host == "i686-pc-linux-gnu" || @host == "x86_64-unknown-linux-gnu"
@llvm_generic_prebuilt = "llvm-#{@llvm_version}-#{@host}-#{@gcc_major}.tar.bz2"
else
@llvm_generic_prebuilt = "llvm-#{@llvm_version}-#{@host}.tar.bz2"
end
@system_cflags = ""
@system_cxxflags = ""
@system_cppflags = ""
@system_ldflags = ""
@user_cflags = ENV['CFLAGS']
@user_cxxflags = ENV['CXXFLAGS']
@user_cppflags = ENV['CPPFLAGS']
@user_ldflags = ENV['LDFLAGS']
setup_platform
end
def setup_platform
@ldsharedxx = "#{@cxx} -shared"
@ldshared = "#{@cc} -shared"
@include_dirs.each do |d|
@system_cflags << "-I#{d} "
end
@lib_dirs.each do |d|
@system_ldflags << "-L#{d} "
end
case RUBY_PLATFORM
when /mswin/i, /mingw/i, /bccwin32/i
# TODO: discovery helpers
#check_heads(%w[windows.h winsock.h], true)
#check_libs(%w[kernel32 rpcrt4 gdi32], true)
unless RUBY_PLATFORM =~ /mingw/
@system_cflags << "-EHs -GR"
end
@system_ldflags << "-lws2_32"
@features["rpath"].configured = false
when /solaris/i
# GNU CHAIN only supported
@ldsharedxx = "#{@cxx} -shared -G -fPIC -lstdc++"
@ldshared = "#{@cc} -shared -G -fPIC"
@system_cflags << "-fPIC -Wno-strict-aliasing"
@system_ldflags << "-lsocket -lnsl -fPIC"
@features["rpath"].configured = false
@make = "gmake"
when /freebsd/i
@ldsharedxx = "#{@cxx} -shared -fPIC"
@ldshared = "#{@cc} -shared -fPIC"
@system_cflags << "-fPIC"
@system_ldflags << "-lcrypt -pthread -rdynamic"
@make = "gmake"
when /openbsd/i
# OpenBSD branch contributed by Guillaume Sellier.
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against
# libstdc++ have to be explicitly done for shared libs
@ldsharedxx = "#{@cxx} -shared -lstdc++ -fPIC"
@ldshared = "#{@cc} -shared -fPIC"
@system_cflags << "-fPIC"
@system_ldflags << "-pthread -rdynamic -Wl,--export-dynamic"
@make = "gmake"
when /netbsd/i
@ldsharedxx = "#{@cxx} -shared -lstdc++ -fPIC"
@ldshared = "#{@cc} -shared -fPIC"
@system_cflags << "-fPIC"
@system_ldflags << "-lcrypt -pthread -rdynamic -Wl,--export-dynamic"
@make = "gmake"
when /darwin/i
# on Unix we need a g++ link, not gcc.
# Ff line contributed by Daniel Harple.
@ldsharedxx = "#{@cxx} -bundle -undefined suppress -flat_namespace"
@ldshared = "#{@cc} -bundle -undefined suppress -flat_namespace"
@system_cflags << "-fPIC -D_DARWIN_USE_64_BIT_INODE"
@features["rpath"].configured = false
when /haiku/i
@system_cflags << "-fPIC"
@system_ldflags << "-ldl -lnetwork"
@features["rpath"].configured = false
when /aix/i
@ldsharedxx = "#{@cxx} -shared -Wl,-G -Wl,-brtl"
@ldshared = "#{@cc} -shared -Wl,-G -Wl,-brtl"
@features["rpath"].configured = false
when /linux/i
@system_cflags << "-fPIC"
@system_ldflags << "-Wl,--export-dynamic -lrt -lcrypt -ldl -lpthread"
else
# on Unix we need a g++ link, not gcc.
@system_cflags << "-fPIC"
@system_ldflags << "-ldl -lpthread"
end
if @features["rpath"].value
@lib_dirs.each do |d|
@system_ldflags << " -Wl,-rpath=#{d}"
end
end
end
def check_program(run=true, *arguments)
begin
basename = "rbx-configure-test"
source = basename + ".cpp"
File.open source, "wb" do |f|
yield f
end
File.open source, "rb" do |f|
@log.log f.read
end
libs = default_link_libs.map { |l| "-l#{l}" }.join(" ")
args = arguments.join(" ")
cmd = "#{@cxx} #{@user_cppflags} #{@user_cflags} #{@user_cxxflags} #{@user_ldflags} -o #{basename} #{source} #{@system_cppflags} #{@system_cflags} #{@system_cxxflags} #{@system_ldflags} #{libs} #{args} >>#{@log.path} 2>&1"
@log.log cmd
system cmd
return $?.exitstatus unless run
unless $?.exitstatus == 0
failure "Compiling configure test program failed."
end
system expand("./#{basename}")
return $?.exitstatus
rescue => e
@log.log "Error in check_program: #{e.class} #{e.message}\n #{e.backtrace.join("\n ")}"
raise e
ensure
FileUtils.rm_r(Dir["#{basename}*"])
end
end
def write_have_defines(f)
f.puts
@defines.each { |d| f.puts "#define #{d.ljust(20)} 1" }
end
def write_have_sizeof_defines(f)
f.puts
@sizeof.keys.sort.each { |k| f.puts "#define HAVE_#{k}".ljust(30) + "1" }
end
def write_sizeof_defines(f)
f.puts
@sizeof.keys.sort.each { |k| f.puts "#define SIZEOF_#{k}".ljust(30) + @sizeof[k].to_s }
end
def sizeof_typename(type)
if type =~ /(\*+)$/
name = "#{type[0...-$1.size]}#{"p" * $1.size}"
else
name = type
end
name.gsub(/\W/, "_").upcase
end
def sizeof(type)
@sizeof[sizeof_typename(type)] or failure("Unknown type: '#{type}'.")
end
def detect_sizeof(type, includes=[])
@log.print "Checking sizeof(#{type}): "
size = check_program do |f|
src = includes.map { |include| "#include <#{include}>" }.join("\n")
src += <<-EOP
#include <stddef.h>
int main() { return sizeof(#{type}); }
EOP
f.puts src
@log.log src
end
@sizeof[sizeof_typename(type)] = size
@log.write "#{size} bytes"
end
def detect_endian
@log.print "Checking platform endianness: "
status = check_program do |f|
src = "int main() { int one = 1; return (*((char*)&one)) == 1 ? 0 : 1; }"
f.puts src
@log.log src
end
@little_endian = (status == 0)
@log.write @little_endian ? "little endian" : "big endian"
end
def detect_tr1
@log.print "Checking for tr1: "
status = check_program(false) do |f|
src = <<-EOP
#include <tr1/unordered_map>
typedef std::tr1::unordered_map<int, void*> X;
int main() { X x; return 0; }
EOP
f.puts src
@log.log src
end
@tr1 = (status == 0)
@log.write @tr1 ? "found" : "not found"
end
def detect_tr1_hash
@log.print "Checking for tr1/hash definition: "
status = check_program(false) do |f|
src = <<-EOP
#include <stdint.h>
#include <tr1/unordered_map>
typedef std::tr1::unordered_map<uint64_t, void*> X;
int main() { X x; return 0; }
EOP
f.puts src
@log.log src
end
@tr1_hash = (status == 0)
@log.write @tr1_hash ? "found" : "not found"
end
def detect_x86
print "Checking for x86_32: "
if sizeof("long") == 4
status = check_program do |f|
src = <<-EOP
int main() {
#if defined(i386) || defined(__i386__) || defined(__i386)
return 1;
#else
return 0;
#endif
}
EOP
f.puts src
@log.log src
end
@x86_32 = (status == 1)
end
puts @x86_32 ? "yes" : "no"
return if @x86_32
print "Checking for x86_64: "
status = check_program do |f|
src = <<-EOP
int main() {
#if defined(__x86_64) || defined(__x86_64__)
return 1;
#else
return 0;
#endif
}
EOP
f.puts src
@log.log src
end
@x86_64 = (status == 1)
puts @x86_64 ? "yes" : "no"
end
def detect_curses
@log.print "Checking curses library: "
src = <<-EOP
#include <curses.h>
#include <term.h>
int main() { return tgetnum(""); }
EOP
["-lcurses", "-lncurses", "-ltermcap"].each do |lib|
status = check_program(false, lib) do |f|
f.puts src
@log.log src
end
if status == 0
@curses = lib
break
end
end
if @curses
@log.write(@curses)
end
end
def detect_build_dirs
["/usr/local", "/opt/local", "/usr/pkg"].each do |dir|
@include_dirs << "#{dir}/include"
@lib_dirs << "#{dir}/lib" << "#{dir}/lib64"
end
@include_dirs = @include_dirs.select {|p| File.directory? p }
@lib_dirs = @lib_dirs.select {|p| File.directory? p }
end
def has_struct_member(struct, member, includes = [])
compile_check "struct #{struct} has member #{member}" do |src|
includes.each do |i|
src.puts "#include <#{i}>"
end
src.puts "int main() { struct #{struct} st; st.#{member}; }"
end
end
def has_global(name, includes=[])
compile_check "global '#{name}'" do |src|
includes.each do |i|
src.puts "#include <#{i}>"
end
src.puts "int main() { #{name}; }"
end
end
def has_header(name)
compile_check "header '#{name}'" do |src|
# Some headers have an implicit dependency on stdio.h. For example,
# readline/readline.h requires it but doesn't actually include it for
# you. Because there could be an infinite amount of headers that require
# stdio.h we'll just always include it.
src.puts "#include <stdio.h>"
src.puts "#include <#{name}>"
src.puts "int main() {return 0;}"
end
end
def has_function(name, includes=[], defines = [])
compile_check "function '#{name}'", defines do |src|
includes.each do |i|
src.puts "#include <#{i}>"
end
src.puts "int main() { void* ptr = (void *) &#{name}; }"
end
end
def has_library(name, function, libraries, includes=[])
@log.print "Checking for library: #{name}: "
args = libraries.map { |l| "-l#{l}" }
status = check_program(true, *args) do |src|
includes.each do |i|
src.puts "#include <#{i}>"
end
src.puts "int main() { void* ptr = (void*)(&#{function}); return 0; }"
end
success = status == 0
@log.write(success ? "found!" : "not found!")
success
end
def has_dtrace
@log.print "Checking for dtrace: "
begin
basename = "rbx-configure-dtrace-test"
source = basename + ".d"
output = basename + ".h"
File.open source, "wb" do |f|
f.write "provider conftest{ probe m__entry(const char*); };"
end
cmd = "dtrace -h -o #{output} -s #{source}"
@log.log cmd
system cmd
@dtrace = $?.exitstatus == 0
@dtrace_const = !!File.read(output).index("const") if @dtrace
@log.write(@dtrace ? "yes" : "no")
@dtrace
ensure
File.delete(*Dir["#{basename}*"])
end
end
def compile_check(logpart, defines = [], &block)
@log.print "Checking for #{logpart}: "
source = StringIO.new
yield source
file = Tempfile.new("rbx-test")
source.rewind
string = source.read
file.puts string
file.close
@log.log string
cmd = "#{@cxx} -S -o - -x c++ #{defines.join(" ")} #{@user_cppflags} #{@user_cxxflags} #{@user_cflags} #{@user_ldflags} #{@system_cppflags} #{@system_cxxflags} #{@system_cflags} #{@system_ldflags} #{file.path} >>#{@log.path} 2>&1"
@log.log cmd
system cmd
status = ($?.exitstatus == 0)
file.unlink
@log.write(status ? "found!" : "not found")
status
end
def enable_features
if @features["vendor-zlib"].value
# Our vendored zlib uses long as the crc_table type
# If we update vendored zlib in the future, we have to
# review this and make sure we update it properly to
# match the newer version which like will have uint32_t
# as the type.
@include_dirs << "#{@vendored_libdir}/zlib"
@lib_dirs << "#{@vendored_libdir}/zlib"
end
end
def detect_features
# Default on *BSD is no execinfo
if RUBY_PLATFORM =~ /bsd/i and @features["execinfo"].configured.nil?
@features["execinfo"].configured = false
end
if @features["execinfo"].value and has_function("backtrace", ["execinfo.h"])
@defines << "HAS_EXECINFO"
end
if @features["alloc-tracking"].value
@defines << "RBX_ALLOC_TRACKING"
end
if @features["fibers"].value
@fibers = true if @x86_32 or @x86_64
end
if @features["dtrace"].value and has_dtrace
@defines << "HAVE_DTRACE"
end
# Default on Windows is vendor-zlib
if @windows and @features["vendor-zlib"].configured.nil?
@features["vendor-zlib"].configured = true
end
@defines << "HAVE_SPT_REUSEARGV" if @linux || @darwin || @bsd
end
def detect_functions
if has_function("clock_gettime", ["time.h"])
@defines << "HAVE_CLOCK_GETTIME"
end
if has_function("nl_langinfo", ["langinfo.h"])
@defines << "HAVE_NL_LANGINFO"
end
if has_function("setproctitle", ["sys/types.h", "unistd.h"])
@defines << "HAVE_SETPROCTITLE"
end
if has_function("posix_fadvise", ["fcntl.h"])
@defines << "HAVE_POSIX_FADVISE"
end
if has_function("strnlen", ["string.h"])
@defines << "HAVE_STRNLEN"
end
if has_function("kqueue", ["sys/types.h", "sys/event.h", "sys/time.h"])
@defines << "HAVE_KQUEUE"
end
if has_function("timerfd_create", ["sys/timerfd.h"])
@defines << "HAVE_TIMERFD"
end
if has_function("inotify_init", ["sys/inotify.h"])
@defines << "HAVE_INOTIFY"
end
# glibc has useless lchmod() so we don't try to use lchmod() on linux
if !@linux and has_function("lchmod", ["sys/stat.h", "unistd.h"])
@have_lchmod = true
end
if has_function("lchown", ["sys/stat.h", "unistd.h"])
@have_lchown = true
end
end
def detect_structures
if has_struct_member("tm", "tm_gmtoff", ["time.h"])
@defines << "HAVE_TM_GMTOFF"
end
if has_struct_member("tm", "tm_zone", ["time.h"])
@defines << "HAVE_TM_ZONE"
end
end
def detect_globals
if has_global("timezone", ["time.h"])
@defines << "HAVE_TIMEZONE"
end
if has_global("tzname", ["time.h"])
@defines << "HAVE_TZNAME"
end
if has_global("daylight", ["time.h"])
@defines << "HAVE_DAYLIGHT"
end
end
def detect_headers
unless @features["vendor-zlib"].value
unless has_header("zlib.h")
failure "zlib.h is required"
end
end
unless has_header("openssl/ssl.h")
failure "openssl/ssl.h is required"
end
if has_header("alloca.h")
@defines << "HAVE_ALLOCA_H"
end
if has_header("string.h")
@defines << "HAVE_STRING_H"
end
if has_header("sys/time.h")
@defines << "HAVE_SYS_TIME_H"
end
if has_header("sys/times.h")
@defines << "HAVE_SYS_TIMES_H"
end
if has_header("sys/types.h")
@defines << "HAVE_SYS_TYPES_H"
end
if has_header("unistd.h")
@defines << "HAVE_UNISTD_H"
end
if has_header("stdarg.h")
@defines << "HAVE_STDARG_H"
end
if has_header("sys/pstat.h")
@defines << "HAVE_SYS_PSTAT_H"
end
if has_header("valgrind/valgrind.h")
@defines << "HAVE_VALGRIND_H"
end
end
def strerror_r_returns_char_pointer
status = check_program(false) do |src|
src.puts "#include <string.h>"
src.puts "int main() { char buf[1024]; static_cast<char*>(strerror_r(42, buf, 1024)); }"
end
status == 0
end
def detect_strerror
@log.print "Checking if function 'strerror_r' returns char*: "
if strerror_r_returns_char_pointer
@defines << "STRERROR_R_CHAR_P"
@log.write "yes"
else
@log.write "no"
end
end
def warn_prefix(dir)
delimiter = "-------------------------%s-----------------------"
if File.file? dir
@log.write delimiter % " ERROR "
@log.write "The specified prefix '#{dir}' is a regular file."
@log.write "Remove the file or specify a different prefix."
@log.write delimiter % "-------"
exit 1
elsif File.directory? dir
@log.write delimiter % " WARNING "
@log.write "The specified prefix '#{dir}' already exists."
@log.write "Installing Rubinius into an existing directory may"
@log.write "overwrite existing unrelated files or cause conflicts"
@log.write "between different versions of Rubinius files."
@log.write delimiter % "---------"
sleep 2
end
end
def process
set_host
set_system_commands
enable_features
detect_build_dirs
setup_llvm
@log.write ""
detect_sizeof("short")
detect_sizeof("int")
detect_sizeof("void*")
detect_sizeof("size_t")
detect_sizeof("long")
detect_sizeof("long long")
detect_sizeof("float")
detect_sizeof("double")
detect_sizeof("off_t", ["unistd.h"])
detect_sizeof("time_t", ["time.h"])
detect_libc_name
detect_endian
detect_tr1
detect_tr1_hash
detect_x86
detect_features
detect_functions
detect_structures
detect_globals
detect_headers
detect_curses
detect_strerror
end
# Checks whether the given config file is a Perl script by checking its first
# line for a Perl hashbang.
def llvm_config_cmd(config)
begin
File.open(config, "r") do |f|
first_line = f.readline
if first_line =~ /^#! ?\/usr(\/local)?\/bin\/(env )?perl/
"#{@perl} #{config}"
else
config
end
end
rescue Errno::ENOENT, ArgumentError
# The file doesn't exist (ENOENT) or it's a binary file (ArgumentError).
config
end
end
def get_system_name
return unless @os =~ /linux/
return unless File.exist? "/etc/issue"
data = IO.readlines("/etc/issue").first
data =~ /([^ ]+)[^\d\.]*([\d\.]*)/
name = $1.downcase
version = $2
if name == "debian" and File.exist? "/etc/debian_version"
version = IO.read("/etc/debian_version").split.first.gsub(/\W/, "-")
end
return "#{name}-#{version}"
end
def check_tool_version(tool_name, opts, version, regexp=/(?=\d)(\d+).(\d+).?(\d+)?/)
@log.print "Checking #{tool_name}:"
output = `#{tool_name} #{opts}`
if $?.exitstatus == 0
v = output.scan(regexp)[0].map { |x| x.to_i }
unless (v <=> version) >= 0
failure " Expected #{tool_name} version >= #{version.join('.')}, found #{v.join('.')}"
end
@log.write " found"
else
failure "#{tool_name} not found."
end
end
def detect_libc_name
return if @libc
@log.print "Checking for libc version: "
case
when @windows
@libc = "msvcrt.dll"
when @darwin
@libc = "libc.dylib"
else
begin
exe = ENV["SHELL"] || "/bin/sh"
ldd_output = `ldd #{exe}`
@libc = ldd_output[/libc\.so\.[0-9]+/]
rescue
# Don't abort if the command is not found
end
unless $?.success? and @libc
failure "libc not found. Use the --libc configure option."
end
end
@log.write "#{@libc} found!"
end
def write_configure_files
@log.write "\nWriting configuration files..."
unless @llvm_enabled
@llvm_configure = ""
end
@bin_links.delete @program_name
config_settings = {
:config_file => @config,
:command_line => @command_line,
:build_make => @make,
:build_rake => @rake,
:build_perl => @perl,
:llvm_enabled => @llvm_enabled,
:llvm_path => @llvm_path,
:llvm_system_name => @llvm_system_name,
:llvm_configure => @llvm_configure,
:llvm_version => @llvm_version,
:llvm_api_version => @llvm_api_version,
:llvm_shared => @llvm_shared,
:llvm_shared_objs => @llvm_shared_objs,
:llvm_cxxflags => @llvm_cxxflags,
:llvm_ldflags => @llvm_ldflags,
:cc => @cc,
:cxx => @cxx,
:ldshared => @ldshared,
:ldsharedxx => @ldsharedxx,
:gcc_major => @gcc_major,
:user_cflags => "#{@user_cflags}",
:user_cxxflags => "#{@user_cxxflags}",
:user_cppflags => "#{@user_cppflags}",
:user_ldflags => "#{@user_ldflags}",
:system_cflags => "#{@system_cflags}",
:system_cxxflags => "#{@system_cxxflags}",
:system_cppflags => "#{@system_cppflags}",
:system_ldflags => "#{@system_ldflags}",
:include_dirs => @include_dirs,
:lib_dirs => @lib_dirs,
:defines => @defines,
:curses => @curses,
:host => @host,
:cpu => @cpu,
:vendor => @vendor,
:os => @os,
:little_endian => @little_endian,
:sizeof_long => sizeof("long"),
:x86_32 => @x86_32,
:x86_64 => @x86_64,
:dtrace => @dtrace,
:dtrace_const => @dtrace_const,
:fibers => @fibers,
:debug_build => @debug_build,
:sourcedir => @sourcedir,
:stagingdir => @stagingdir,
:build_prefix => @build_prefix,
:runtime_gems_dir => @runtime_gems_dir,
:bootstrap_gems_dir => @bootstrap_gems_dir,
:capi_includedir => @capi_includedir,
:build_exe => "#{@build_prefix}#{@bindir}/#{@program_name}",
:prefixdir => @prefixdir,
:bindir => @bindir,
:libdir => @libdir,
:encdir => @encdir,
:runtimedir => @runtimedir,
:kerneldir => @kerneldir,
:sitedir => @sitedir,
:vendordir => @vendordir,
:includedir => @includedir,
:mandir => @mandir,
:gemsdir => @gemsdir,
:gems_cache => @gems_cache,
:gems_list => @gems_list,
:gem_files => @gem_files,
:program_name => @program_name,
:bin_links => @bin_links,
:use_bin_links => @use_bin_links,
:rpath => @features["rpath"].value,
:windows => @windows,
:darwin => @darwin,
:bsd => @bsd,
:linux => @linux,
:vendor_zlib => @features["vendor-zlib"].value,
:vm_release_h => @vm_release_h,
:bootstrap_gems => @bootstrap_gems,
:runtime_gems => @runtime_gems,
:ruby_version => @ruby_version,
:ruby_libversion => @ruby_libversion,
}
write_config_rb @config, config_settings
lib_rubinius_dir = "#{@sourcedir}/library/rubinius"
FileUtils.mkdir_p lib_rubinius_dir unless File.directory? lib_rubinius_dir
FileUtils.cp @config, "#{lib_rubinius_dir}/build_config.rb"
# Write the config file used to build the C++ VM.
Dir.mkdir "vm/gen" unless File.directory? "vm/gen"
vm_paths_h = "vm/gen/paths.h"
File.open vm_paths_h, "wb" do |f|
f.puts <<-EOF
#define RBX_PREFIX_PATH "#{@prefixdir}"
#define RBX_BIN_PATH "#{@bindir}"
#define RBX_GEMS_PATH "#{@gemsdir}"
#define RBX_RUNTIME_PATH "#{@runtimedir}"
#define RBX_KERNEL_PATH "#{@kerneldir}"
#define RBX_LIB_PATH "#{@libdir}"
#define RBX_ENC_PATH "#{@encdir}"
#define RBX_HDR_PATH "#{@includedir}"
#define RBX_SITE_PATH "#{@sitedir}"
#define RBX_VENDOR_PATH "#{@vendordir}"
EOF
end
vm_config_h = "vm/gen/config.h"
File.open vm_config_h, "wb" do |f|
f.puts <<-EOC
#define RBX_PROGRAM_NAME "#{@program_name}"
#define RBX_HOST "#{@host}"
#define RBX_CPU "#{@cpu}"
#define RBX_VENDOR "#{@vendor}"
#define RBX_OS "#{@os}"
#define RBX_RUBY_LIB_VERSION #{@ruby_libversion}
#define RBX_LDSHARED "#{@ldshared}"
#define RBX_LDSHAREDXX "#{@ldsharedxx}"
#define RBX_SIZEOF_LONG #{sizeof("long")}
#define RBX_LLVM_API_VER #{@llvm_api_version}
#define RBX_LIBC "#{@libc}"
#define RBX_HAVE_LCHMOD #{@have_lchmod}
#define RBX_HAVE_LCHOWN #{@have_lchown}
#define RBX_DEBUG_BUILD #{@debug_build.inspect}
EOC
if @llvm_version
f.puts "#define RBX_LLVM_VERSION #{@llvm_version.inspect}"
end
if @little_endian
f.puts "#define RBX_LITTLE_ENDIAN 1"
end
if @tr1
f.puts "#define RBX_HAVE_TR1 1"
end
if @tr1_hash
f.puts "#define RBX_HAVE_TR1_HASH 1"
end
[:windows, :darwin, :bsd, :linux].each do |platform|
if instance_variable_get(:"@#{platform}")
f.puts "#define RBX_#{platform.to_s.upcase} 1"
end
end
if @fibers
f.puts "#define RBX_FIBER_ENABLED 1"
end
f.puts "#define RBX_DTRACE_CONST #{@dtrace_const ? "const" : ""}"
write_have_defines f
end
# Write the config file for vendor/oniguruma.
File.open "vendor/oniguruma/config.h", "wb" do |f|
f.puts <<-EOC
/* This file is generated by the Rubinius build system. Your edits
* will be lost. See the configure script.
*/
EOC
write_have_defines f
write_have_sizeof_defines f
write_sizeof_defines f
end
# Write release header file.
write_release @vm_release_h
# Write the rubinius-specific C-API config headers.
vm_capi_header_gen = "#{@capi_includedir}/gen"
FileUtils.mkdir_p vm_capi_header_gen
FileUtils.cp vm_config_h, "#{vm_capi_header_gen}/rbx_config.h"
FileUtils.cp @vm_release_h, "#{vm_capi_header_gen}/rbx_release.h"
# Write the config file used in the C-API.
capi_config_h = "#{@capi_includedir}/ruby/config.h"
FileUtils.mkdir_p File.dirname(capi_config_h)
File.open capi_config_h, "wb" do |f|
f.puts <<-EOC
/* This file is generated by the build system. Your edits
* will be lost. See the configure script.
*/
#ifndef NORETURN
#define NORETURN(x) __attribute__ ((noreturn)) x
#endif
#ifndef UNREACHABLE
#define UNREACHABLE __builtin_unreachable()
#endif
EOC
write_have_defines f
write_have_sizeof_defines f
write_sizeof_defines f
if @windows
f.puts "#define RBX_WINDOWS 1"
end
end
# Create C-API header for vm/test
File.open "vm/test/ruby.h", "w" do |f|
f.puts %[#include "#{@capi_includedir}/ruby.h"]
end
end
def print_debug
puts "\nUsing the following configuration to build"
puts "------------------------------------------"
cat("config.rb")
puts "\nSetting the following defines for the VM"
puts "----------------------------------------"
cat("vm/gen/config.h")
end
def cat(file)
puts IO.read(relative_file(file))
end
def relative_file(name)
File.expand_path("../#{name}", __FILE__)
end
def check_force_clean
unless verify_build_signature
@log.write "\nDetected old configuration settings, forcing a clean build"
system("#{build_ruby} -S #{@rake} clean")
end
end
def fetch_gems
@log.write "\nFetching gems..."
failed = false
Dir.chdir @gems_cache do
@gem_files.each do |gem|
next if File.exist? gem
failed = true unless download "https://rubygems.org/gems/#{gem}", "./#{gem}"
end
end
failure "Unable to download required gems." if failed
end
def verify_gems
@log.write "\nVerifying gems..."
failed = false
@gem_files.each do |gem_name|
unless File.file? "#{@gems_cache}/#{gem_name}"
@log.write "unable to find gem #{gem_name}"
failed = true
end
end
failure "Unable to find required gems." if failed
end
def clean_gems(dir, gems)
unpacked = Dir["#{dir}/*"]
# Remove unpacked gems not specified by these configure settings
unpacked.each do |dir|
d = File.basename dir
unless gems.find { |x| d =~ /^#{x}/ } and
@gem_files.find { |x| d =~ /^#{x[0..-5]}/ }
FileUtils.rm_rf dir
end
end
end
def unpack_gems(source, destination, list)
FileUtils.mkdir_p destination unless File.directory? destination
Dir.chdir destination do
list.each do |name|
gem_name = @gem_files.find { |x| x =~ /^#{name}/ }
failure "Unable to find gem to unpack: #{name}" unless gem_name
unless File.directory? gem_name[0..-5]
system("#{@gem} unpack #{source}/#{gem_name}")
unless $?.exitstatus == 0
failure "Unable to unpack bootstrap gems."
end
end
end
end
end
def setup_gems
@log.write "\nSetting up gems..."
# Remove unpacked gems not specified by these configure settings
clean_gems @runtime_gems_dir, @runtime_gems
clean_gems @bootstrap_gems_dir, @bootstrap_gems
# Unpack gems not found for these configure settings
unpack_gems @gems_cache, @runtime_gems_dir, @runtime_gems
unpack_gems @gems_cache, @bootstrap_gems_dir, @bootstrap_gems
end
# Create directories that don't have to be created by the end user
# themselves.
def create_directories
FileUtils.mkdir_p @gems_cache
end
def run
unless RedCard.check :ruby, :rubinius
failure "Sorry, building Rubinius requires MRI or Rubinius."
end
unless RedCard.check "1.8.7"
@log.write "Ruby version 1.8.7+ is required to build Rubinius."
@log.write "Your version may work but is unsupported."
end
options
parse ARGV
create_directories
check_tools
check_force_clean
set_filesystem_paths
process
if @release_build
verify_gems
else
fetch_gems
end
setup_gems
write_configure_files
write_build_signature
return if @release_config
print_debug if @verbose
if @llvm_source_build
files = prebuilt_files.map { |f| File.basename f, ".tar.bz2" }.join("\n ")
@log.write <<-EOM
------------------------------------------------------------------
Unable to find an existing binary build of LLVM for your platform.
Please notify the Rubinius team at the #rubinius channel on
irc.freenode.net and provide the following system information:
prebuilts:
#{files}
------------------------------------------------------------------
EOM
end
unless @stagingdir
build_msg = <<-EOM
Rubinius (#{release_revision.last[0, 8]}) has been configured.
Run 'rake' to build and test Rubinius.
EOM
else
build_msg = <<-EOM
Rubinius (#{release_revision.last[0, 8]}) has been configured for the following paths:
prefix: #{@prefixdir}
bin: #{@prefixdir}#{@bindir}
lib: #{@prefixdir}#{@libdir}
include: #{@prefixdir}#{@includedir}
runtime: #{@prefixdir}#{@runtimedir}
kernel: #{@prefixdir}#{@kerneldir}
site: #{@prefixdir}#{@sitedir}
vendor: #{@prefixdir}#{@vendordir}
man: #{@prefixdir}#{@mandir}
gems: #{@prefixdir}#{@gemsdir}
gems cache: #{@gems_cache}
Run 'rake' to build, test and install Rubinius.
EOM
end
links = (@bin_links + [@program_name]).uniq
@log.write <<-EOM
------------------------------------------------------------------
#{build_msg}
After building, you may add
'#{@prefixdir}#{@bindir}'
to your PATH or run commands directly from that directory.
Available commands are:
#{links.join(", ")}
------------------------------------------------------------------
EOM
end
# Configuration item that has both a default and a configured value
class ConfigurationToggle
attr_reader :default, :configured
def initialize(default_value)
@default = !!default_value
@configured = nil
end
def configured=(value)
@configured = !!value
end
def value
unless @configured.nil?
@configured
else
@default
end
end
end
# Handles user output and logging while running configure.
class Logger
attr_reader :path
# Creates an instance of Logger writing to +file+.
def initialize(file, init=true)
@path = File.expand_path("../#{file}", __FILE__)
if init
File.open(@path, "wb") { }
log "Configuring Rubinius..."
end
end
# Copies the contents of +other+ into this logger's file.
def replace(other)
output do |f|
f.puts File.read(other)
end
end
# Writes +message+ to the logging file but not to the screen.
def log(message, error=false)
output do |f|
stamp = "#{timestamp}#{'*** ERROR' if error}"
if multiline?(message)
f.puts "#{stamp} ---"
f.puts message
f.puts "---"
else
f.puts "#{stamp} #{message}"
end
end
end
# Writes a normal message to STDOUT and logs to the file.
def write(message)
log message
STDOUT.puts message
end
# Writes a normal message to STDOUT with #print and logs to file.
def print(message)
log message
STDOUT.print message
end
# Writes an error message to STDERR and logs to the file with
# error decorations. This should only be used for errors that
# affect configure itself.
def error(message)
log message, true
STDERR.puts message
end
# Yields an IO for writing log messages.
def output
File.open @path, "a" do |f|
yield f
end
end
# Returns a formatted times suitable for logging.
def timestamp
Time.now.strftime "[%Y-%m-%d %H:%M:%S]"
end
# Returns true if the message has more than one line.
def multiline?(message)
message.index("\n") != nil
end
end
# Returns true if the *port* command is in the PATH and identifies
# itself with "MacPorts" when run interactively.
def macports?
`echo quit | port 2>&-`.start_with? 'MacPorts'
end
# Query MacPorts for the path to the latest installed version of
# llvm-config that is within the range of supported LLVM versions.
def macports_llvm_config
supported_versions = (3.0 .. 3.5)
installed_ports = `port installed | egrep -o 'llvm-[^ ]+'`.split
latest_usable_port = installed_ports.sort.select do |fname|
version = fname.match(/-\K.*/)[0].to_f
supported_versions.include? version
end.last
avail_binaries = `port contents #{latest_usable_port} |
fgrep llvm-config`.split
avail_binaries.reject { |fname| fname.include? 'libexec' }.last
end
end
STDOUT.sync = true
Configure.new(root).run
Jump to Line
Something went wrong with that request. Please try again.