Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 723 lines (575 sloc) 18.3 KB
#!/usr/bin/env ruby
require 'rbconfig'
require 'tempfile'
require 'fileutils'
root = File.expand_path File.dirname(__FILE__)
require File.join(root, "kernel", "delta", "options")
class Configure
def initialize(root)
@defines = []
@config = File.join(root, "config.rb")
# TODO: conditionalize for Windows
@host = `./rakelib/config.guess`.chomp
/([^-]+)-([^-]+)-(.*)/ =~ @host
@cpu, @vendor, @os = $1, $2, $3
@little_endian = false
@sizeof_long = 0
# Compiler to use
@compiler = ENV['CC'] || 'gcc'
# LLVM settings
@use_llvm = true
@llvm = :no
@llvm_path = nil
@llvm_configure = nil
@llvm_skip_system = false
@llvm_skip_prebuilt = false
@llvm_prebuilt_name = nil
@llvm_system_name = get_system_name
@llvm_source = "llvm-2.6-source.tar.bz2"
@llvm_asset_path = "http://asset.rubini.us/prebuilt"
@llvm_generic_prebuilt = "llvm-#{@host}.tar.bz2"
@llvm_parent_path = File.join(root, "vm", "external_libs")
@llvm_default = File.join(@llvm_parent_path, "llvm")
@llvm_prebuilt_path = File.join(@llvm_parent_path, "prebuilt")
@llvm_include_path = File.join(@llvm_default, "include")
# File system paths
@bindir = root + "/bin"
@includedir = root + "/vm/capi/include"
@libdir = root
@runtime = @libdir + "/runtime"
@lib_path = @libdir + "/lib"
@ext_path = @libdir + "/lib/ext"
@mandir = root + "/man"
@gemsdir = root + "/gems"
# Some simple defaults for when running directly out of the build dir
@sitedir = @lib_path + "/site"
@vendordir = @lib_path + "/vendor"
@program_name = "rbx"
# Essential settings (modify these for creating releases)
@libversion = "1.1"
@version = "#{@libversion}.0"
@release_date = "2010-09-23"
@config_version = 15
if !ENV['RELEASE'] and File.directory?(root + "/.git")
@buildrev = `git rev-list --all | head -n1`.chomp
else
@buildrev = "release"
end
# TODO: add conditionals for platforms
if RbConfig::CONFIG["build_os"] =~ /darwin/
@ldshared = "cc -dynamic -bundle -undefined suppress -flat_namespace"
else
@ldshared = "cc -shared"
end
end
def expand(path)
File.expand_path(path)
end
def options
o = Rubinius::Options.new "Usage: configure [options]", 30
o.left_align
o.doc " Compiler settings"
o.on "--cc", "COMPILER", "Compiler to use (eg gcc, clang)" do |cc|
@compiler = cc
end
o.on "--disable-llvm", "Don't build with LLVM" do
@use_llvm = false
end
o.on "--enable-llvm", "Enable llvm (default)" do
@use_llvm = true
end
o.on "--skip-system", "Don't consider a system LLVM installation" do
@llvm_skip_system = true
end
o.on "--skip-prebuilt", "Don't try to use a prebuilt version of LLVM" do
@llvm_skip_prebuilt = true
end
o.on "--system-name", "NAME", "Name of OS (eg fedora-8, ubuntu-10.04)" do |name|
@llvm_system_name = name
end
o.on "--prebuilt-name", "NAME", "Full name of LLVM prebuilt archive" do |name|
@llvm_prebuilt_name = name
end
o.on "--llvm-path", "PATH", "File system path to the directory containing LLVM" do |dir|
@llvm_path = dir
end
o.on "--update-prebuilt", "Update prebuilt LLVM packages from the internet" do
update_prebuilt @llvm_generic_prebuilt, true
end
o.doc "\n File system paths for installing Rubinius"
o.on "-P", "--prefix", "PATH", "Install Rubinius in subdirectories of PATH" do |dir|
dir = File.expand_path dir
if !ENV['RELEASE'] and File.directory? dir and dir !~ /(rubinius|rbx)\/?$/
old = dir
dir += "/rubinius/#{@libversion}"
puts "The directory #{old} already exists, installing to #{dir}"
end
@bindir = dir + "/bin"
@includedir = dir + "/include"
@libdir = dir
@runtime = @libdir + "/runtime"
@lib_path = @libdir + "/lib"
@ext_path = @libdir + "/lib/ext"
@mandir = dir + "/man"
@gemsdir = dir + "/gems"
@sitedir = dir + "/site"
@vendordir = dir + "/vendor"
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 "-L", "--libdir", "PATH", "Install Ruby library in PATH" do |dir|
@libdir = expand(dir) + "/rubinius/#{@libversion}"
@runtime = @libdir + "/runtime"
@lib_path = @libdir + "/lib"
@ext_path = @libdir + "/lib/ext"
@sitedir = @libdir + "/site"
@vendordir = @libdir + "/vendor"
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 "--sitedir", "PATH", "Where site specific ruby code goes" do |dir|
@sitedir = expand dir
end
o.on "--vendordir", "PATH", "Where vendor specific ruby code goes" do |dir|
@vendordir = expand dir
end
o.doc "\n Optional features"
@options = o
@features = {}
feature "execinfo"
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, on_by_default=true)
@features[name] = on_by_default
@options.on "--with-#{name}", "Enable #{name}" do
@features[name] = true
end
@options.on "--without-#{name}", "Disable #{name}" do
@features[name] = false
end
end
def parse(ary)
@options.parse ary
end
require 'digest/md5'
def md5_checksum(md5_path, full_path)
md5 = Digest::MD5.new
File.open full_path do |file|
until file.eof?
md5 << file.read(1024)
end
end
return md5.hexdigest == File.read(md5_path).split(" ").first
end
require 'net/http'
def download(url, full_path)
begin
if ENV['http_proxy']
protocol, userinfo, host, port = URI::split(ENV['http_proxy'])
proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
http = Net::HTTP::Proxy(host, port, proxy_user, proxy_pass)
else
http = Net::HTTP
end
http.get_response(URI(url)) do |res|
return false if res.is_a?(Net::HTTPClientError)
size, total = 0, res.header['Content-Length'].to_i
File.open full_path, "w" 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
puts ": done!"
end
rescue Exception => e
File.unlink full_path if File.exists?(full_path)
puts " ERROR"
return false
end
return true
end
# Downloads a pre-built LLVM library for a platform if the file exists. If
# an MD5 checksum file exists for the library, the checksum of the library
# is compared and the update fails if it does not match. If no MD5 checksum
# file exists, the library is used without check.
def update_prebuilt(file, warn)
full_path = File.join @llvm_prebuilt_path, file
md5_path = "#{full_path}.md5"
dir = File.dirname full_path
Dir.mkdir dir unless File.exists? dir
url = File.join @llvm_asset_path, file
unless File.exists? full_path
download url, full_path
unless File.exists? full_path
puts "ERROR. No #{file} available on server." if warn
return false
end
end
md5_url = "#{url}.md5"
download md5_url, md5_path
if File.exists? md5_path
unless md5_checksum md5_path, full_path
puts "ERROR. #{file} was corrupted or MD5 checksum is outdated."
return false
else
puts " MD5 checksum for prebuilt LLVM verified."
end
else
puts " No MD5 checksum for #{file} available on server."
puts " Using LLVM library without checksum validation."
end
puts " Prebuilt packages updated."
end
def setup_source
url = File.join @llvm_asset_path, @llvm_source
path = File.join @llvm_prebuilt_path, @llvm_source
puts " Downloading #{url}..."
return false unless download(url, path)
if File.exists?(path)
print " Unpacking prebuilt LLVM source: "
system "cd #{@llvm_parent_path}; tar xjf #{path}"
puts "done!"
if File.exists?(@llvm_include_path)
puts " Code appears to be proper svn tree."
else
puts " Code doesn't appear to be proper LLVM tree!"
return false
end
@llvm = :svn
return true
end
end
def prebuilt_files
files = [@llvm_generic_prebuilt]
# If we have a system name, try to find a prebuilt specifically
# for this system first.
if @llvm_system_name
files.unshift "llvm-#{@host}-#{@llvm_system_name}.tar.bz2"
end
# If the user specified a name, try that before anything.
files.unshift @llvm_prebuilt_name if @llvm_prebuilt_name
files
end
def setup_prebuilt
puts " Checking for prebuilt LLVM package..."
prebuilt_files.each do |file|
path = File.join @llvm_prebuilt_path, file
update_prebuilt file, false unless File.exists?(path)
if File.exists?(path)
print " Unpacking prebuilt LLVM: #{file}: "
system "cd #{@llvm_parent_path}; mkdir llvm; cd llvm; tar xjf #{path}"
puts "done!"
@llvm = :prebuilt
return true
end
end
puts " Unable to download any LLVM prebuilt"
return false
end
def setup_path
print "Validating '#{@llvm_path}': "
if File.directory? @llvm_path
["Release", "Debug"].each do |which|
sub = File.join(@llvm_path, which, "bin")
if File.directory? sub
puts "Ok! Using #{which}"
@llvm_configure = File.join(@llvm_path, which, "bin", "llvm-config")
@llvm = :config
return true
end
end
puts "ERROR. Doesn't appear to be built already!"
return false
end
puts "ERROR. Path doesn't exist."
return false
end
def setup_auto
print " Checking for existing LLVM tree: "
if File.directory?(@llvm_default)
puts "found!"
if File.exists?(File.join(@llvm_default, "Makefile.common"))
@llvm = :svn
else
@llvm = :prebuilt
end
return
else
puts "not found."
end
# If they explicitly said where LLVM is, use that and fail hard.
if @llvm_path
unless setup_path
puts "ABORT: Path '#{@llvm_path}' not a proper LLVM path"
exit 1
end
return
end
return if !@llvm_skip_system && setup_config
return if !@llvm_skip_prebuilt && setup_prebuilt
return if setup_source
puts "WARNING: Unable to configure for LLVM, disabling support."
@use_llvm = false
end
def setup_config
print " Checking for 'llvm-config': "
which = ENV['PATH'].split(":").find do |path|
File.exists? File.join(path, "llvm-config")
end
if which
config = File.join(which, "llvm-config")
version = `#{config} --version`.strip
parts = version.sub(/svn$/, "").split(".").map { |i| i.to_i }
# 2.6svn is an unknown beast, don't trust it.
if version == "2.6svn" or parts[0] < 2 or parts[1] < 6
puts "too old of a version"
elsif parts[0] != 2 or parts[1] != 6
puts "only 2.6 is supported"
else
puts "found! (version #{version})"
@llvm_configure = config
@llvm = :config
return true
end
else
puts "not found"
end
false
end
def c_includes
str = []
if File.exists? "/usr/local/include"
str << "-I/usr/local/include"
end
if File.exists? "/opt/local/include"
str << "-I/opt/local/include"
end
return str.join(" ")
end
def env(which)
ENV[which] || ""
end
def check_program(run=true)
begin
basename = "rbx-configure-test"
source = basename + ".cpp"
File.open source, "w" do |f|
yield f
end
`#{@compiler} #{env('CFLAGS')} -lstdc++ -lm -o #{basename} #{source} 2>&1`
return $?.exitstatus unless run
unless $?.exitstatus == 0
STDERR.puts "compiling configure test program failed"
exit 1
end
`./#{basename}`
return $?.exitstatus
ensure
File.delete *Dir["#{basename}*"]
end
end
def detect_sizeof_long
print "Checking sizeof(long): "
@sizeof_long = check_program do |f|
f.puts "int main() { return sizeof(long); }"
end
puts "#{@sizeof_long} bytes"
end
def detect_endian
print "Checking platform endianness: "
status = check_program do |f|
f.puts "int main() { int one = 1; return (*((char*)&one)) == 1 ? 0 : 1; }"
end
@little_endian = (status == 0)
puts @little_endian ? "little endian" : "big endian"
end
def detect_tr1_hash
print "Checking tr1/hash definition: "
status = check_program(false) do |f|
f.puts <<-EOP
#include <stdint.h>
#include <tr1/unordered_map>
typedef std::tr1::unordered_map<uint64_t, void*> X;
int main() { X x; return 0; }
EOP
end
@tr1_hash = (status == 0)
puts @tr1_hash ? "found" : "not found"
end
def has_function(name, includes=[])
print "Checking for function '#{name}': "
tf = Tempfile.new("rbx-test")
includes.each do |i|
tf.puts "#include <#{i}>"
end
tf.puts "int main() { void* ptr = &#{name}; }"
tf.close
`#{@compiler} -S -o - -x c #{c_includes} #{env('CFLAGS')} #{tf.path} 2>&1`
status = ($?.exitstatus == 0)
tf.unlink
if status
puts "found!"
else
puts "not found."
end
return status
end
def detect_features
if @features["execinfo"] and has_function("backtrace", ["execinfo.h"])
@defines << "HAS_EXECINFO"
end
end
def process
if @use_llvm
puts "Configuring LLVM..."
setup_auto
else
print "WARNING: LLVM disabled."
end
puts
detect_sizeof_long
detect_endian
detect_tr1_hash
detect_features
end
def which_ruby
if Object.const_defined?(:RUBY_ENGINE)
@which_ruby = RUBY_ENGINE.to_sym
else
@which_ruby = :ruby
end
end
# Records the full path to the ruby executable that runs this configure
# script. That path will be made available to the rest of the build system
# so the same version of ruby is invoked as needed.
def build_ruby
bin = RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"]
bin << (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
File.join(RbConfig::CONFIG['bindir'], bin)
end
def get_system_name
return unless @os =~ /linux/
return unless File.exists? "/etc/issue"
data = IO.readlines("/etc/issue").first
data =~ /([^ ]+)[^\d\.]*([\d\.]*)/
name = $1.downcase
version = $2
if name == "debian" and File.exists? "/etc/debian_version"
version = IO.read("/etc/debian_version").split.first.gsub(/\W/, "-")
end
return "#{name}-#{version}"
end
def write_config
unless @use_llvm
@llvm = :no
@llvm_configure = ""
end
File.open @config, "w" do |f|
f.puts <<-EOC
module Rubinius
BUILD_CONFIG = {
:which_ruby => :#{which_ruby},
:build_ruby => "#{build_ruby}",
:llvm => :#{@llvm},
:llvm_configure => "#{@llvm_configure}",
:compiler => "#{@compiler}",
:defines => #{@defines.inspect},
:host => "#{@host}",
:cpu => "#{@cpu}",
:vendor => "#{@vendor}",
:os => "#{@os}",
:little_endian => #{@little_endian},
:sizeof_long => #{@sizeof_long},
:bindir => "#{@bindir}",
:libdir => "#{@libdir}",
:runtime => "#{@runtime}",
:lib_path => "#{@lib_path}",
:ext_path => "#{@ext_path}",
:includedir => "#{@includedir}",
:mandir => "#{@mandir}",
:gemsdir => "#{@gemsdir}",
:sitedir => "#{@sitedir}",
:vendordir => "#{@vendordir}",
:program_name => "#{@program_name}",
:version => "#{@version}",
:libversion => "#{@libversion}",
:release_date => "#{@release_date}",
:config_version => #{@config_version}
}
end
EOC
end
Dir.mkdir "lib/rubinius" unless File.directory? "lib/rubinius"
FileUtils.cp @config, "lib/rubinius/build_config.rb"
Dir.mkdir "vm/gen" unless File.directory? "vm/gen"
File.open "vm/gen/config.h", "w" do |f|
f.puts <<-EOC
#define RBX_HOST "#{@host}"
#define RBX_CPU "#{@cpu}"
#define RBX_VENDOR "#{@vendor}"
#define RBX_OS "#{@os}"
#define RBX_BIN_PATH "#{@bindir}"
#define RBX_GEMS_PATH "#{@gemsdir}"
#define RBX_RUNTIME "#{@runtime}"
#define RBX_LIB_PATH "#{@lib_path}"
#define RBX_EXT_PATH "#{@ext_path}"
#define RBX_HDR_PATH "#{@includedir}"
#define RBX_SITE_PATH "#{@sitedir}"
#define RBX_VENDOR_PATH "#{@vendordir}"
#define RBX_VERSION "#{@version}"
#define RBX_LIB_VERSION "#{@libversion}"
#define RBX_BUILD_REV "#{@buildrev}"
#define RBX_LDSHARED "#{@ldshared}"
#define RBX_RELEASE_DATE "#{@release_date}"
#define RBX_SIZEOF_LONG #{@sizeof_long}
EOC
if @little_endian
f.puts "#define RBX_LITTLE_ENDIAN 1"
end
if @tr1_hash
f.puts "#define RBX_HAVE_TR1_HASH 1"
end
end
end
def print_debug
puts "\nUsing the following configuration to build"
puts "------------------------------------------"
system "cat config.rb"
puts "\nSetting the following defines for the VM"
puts "----------------------------------------"
system "cat vm/gen/config.h"
end
def run
unless which_ruby == :ruby or which_ruby == :rbx
STDERR.puts "Sorry, building Rubinius requires MRI or Rubinius"
exit 1
end
options
parse ARGV
process
write_config
print_debug if @verbose
puts "\nConfigured. Run 'rake' to build and run VM tests and rubyspecs"
end
end
STDOUT.sync = true
Configure.new(root).run