Fixed some C/C++ compiler errors due to stricter checks.
[rubinius.git] / configure.rb
blob07333a2ae77409723c58c6ade2aaa29edc673d45
1 #!/usr/bin/env ruby
3 require './rakelib/configure'
4 require './rakelib/release'
5 require './rakelib/build_signature'
6 require 'rbconfig'
7 require 'tempfile'
8 require 'fileutils'
9 require 'stringio'
10 require 'date'
11 require 'digest/md5'
12 require 'digest/sha2'
13 require 'net/http'
15 module Rubinius
16   BUILD_CONFIG = {}
17 end
19 root = File.expand_path File.dirname(__FILE__)
21 require File.join(root, "core", "options")
23 class Configure
25   # Default settings only. All code that may depend on user-selected options
26   # must run after options are processed.
27   def initialize(root)
28     @log = Logger.new "configure.log"
30     @command_line = ARGV.dup
31     @log.log "Command line: #{@command_line.join(" ").inspect}"
33     @features = {}
34     @defines = []
35     @config = File.join(root, "build/config/config.rb")
37     # Platform settings
38     @host = `sh -c ./build/bin/config.guess`.chomp
39     @cpu = nil
40     @vendor = nil
41     @os = nil
42     @windows = nil
43     @darwin = nil
44     @bsd = nil
45     @linux = nil
46     @little_endian = false
47     @sizeof = {}
48     @gc_stack_check = false
49     @log_concurrent_update = false
50     @raise_concurrent_update = false
52     # Build tools
53     @cc = nil
54     @cxx = nil
55     @make = nil
56     @rake = nil
57     @tar = nil
58     @bzip = nil
59     @perl = nil
60     @gem = nil
62     # LLVM settings
63     @llvm_path              = nil
64     @llvm_system_name       = get_system_name
65     @llvm_configure         = nil
66     @llvm_version           = nil
67     @llvm_api_version       = nil
68     @llvm_shared            = false
69     @llvm_shared_objs       = nil
70     @llvm_cxxflags          = ""
71     @llvm_ldflags           = ""
73     # System settings
74     @libc         = nil
75     @x86_64       = false
76     @aarch64      = false
77     @dtrace       = false
78     @dtrace_const = false
79     @have_lchmod  = false
80     @have_lchown  = false
81     @have_mkfifo  = false
82     @debug_build  = false
83     @include_dirs = []
84     @lib_dirs     = []
86     # File system paths
87     @sourcedir    = root
88     @prefixdir    = nil
89     @bindir       = nil
90     @appdir       = nil
91     @libdir       = nil
92     @encdir       = nil
93     @runtimedir   = nil
94     @codedbdir    = nil
95     @codetoolsdir = nil
96     @stdlibdir    = nil
97     @coredir      = nil
98     @sitedir      = nil
99     @archdir      = nil
100     @vendordir    = nil
101     @mandir       = nil
102     @gemsdir      = nil
103     @includedir   = nil
105     @build_libdir = nil
106     @builddir     = nil
107     @scriptdir    = nil
108     @capi_includedir  = "#{@sourcedir}/machine/include/capi"
110     @bootstrap_gems_dir = nil
112     @vm_release_h = File.join(root, "/machine/release.h")
114     @preserve_prefix = false
116     @program_name = "rbx"
117     @bin_links = ["rbx", "ruby", "rake", "gem", "irb", "rdoc", "ri", "erb"]
118     @use_bin_links = true
120     # List of all gems to pre-install.
121     @gems_list = File.join(root, "gems_list.txt")
122     @gem_files = File.readlines(@gems_list).map { |x| x.chomp }
123     @gem_names = @gem_files.map { |x| /(.*)-\d+\.\d+(\.\d+)?\.gem$/.match(x)[1] }
125     @installed_gems = [
126       "bundler-1.16.1.gem",
127       "minitest-5.11.1.gem",
128       "racc-1.4.14.gem",
129       "rake-12.3.0.gem",
130       "rdoc-5.1.0.gem",
131       "rb-readline-0.5.5.gem",
132       "test-unit-3.2.7.gem",
133      ]
135     # Default cache directory, can be overwritten by the user.
136     @gems_cache = File.expand_path "../build/libraries/cache", __FILE__
138     # Vendored library settings
139     @build_libdir = File.join(root, "/build/libraries")
141     # Ruby compatibility version
142     @ruby_version = "10.0"
143     @ruby_libversion = @ruby_version.split(/\./)[0..1].join.to_i
145     @build_bin = "#{@sourcedir}/build/bin"
147     # Configure settings
148     @release_build = !in_git?
149   end
151   # Set up system commands to run in cmd.exe on Windows. Either Windows
152   # or MRI on Windows has issues with subprocesses where the invocation
153   # of the subprocess will return before the subprocess has finished.
154   # This manifests in configure when uncompressing LLVM source returns
155   # but attempting to move the directory fails sporadically with an access
156   # exception. Adding the, essentially no-op, 'sleep 0' resolves this.
157   def msys_system(cmd)
158     old_system %[cmd.exe /C "#{cmd} && sleep 0"]
159   end
161   def msys_backquote(cmd)
162     old_backquote %[cmd.exe /C "#{cmd}"]
163   end
165   def expand(path)
166     File.expand_path(path)
167   end
169   def expand_install_dir(dir)
170     dir = expand dir
171     if !@preserve_prefix and File.directory?(dir) and dir !~ /(rubinius|rbx).*\/?$/
172       original = dir
173       dir += "/rubinius/#{@libversion}"
174       @log.write "The directory #{original} already exists, installing to #{dir}"
175     end
176     dir
177   end
179   def set_host
180     /([^-]+)-([^-]+)-(.*)/ =~ @host
181     @cpu, @vendor, @os = $1, $2, $3
183     # TODO: For better cross-compiling support, it may be necessary to
184     # use the feature facility to check for a define in the compiler.
185     @windows = (@host =~ /mingw|mswin/) != nil
186     @darwin  = (@host =~ /darwin/) != nil
187     @bsd     = (@host =~ /bsd/) != nil
188     @linux   = (@host =~ /linux/) != nil
189   end
191   def set_system_commands
192     # Set up system commands to run in cmd.exe on Windows.
193     if @windows
194       alias :old_system    :system
195       alias :old_backquote :`
196       alias :system        :msys_system
197       alias :`             :msys_backquote
198     end
199   end
201   def set_filesystem_paths
202     @prefixdir = @prefixdir ? expand_install_dir(@prefixdir) : @sourcedir
204     if @appdir
205       dir = expand_install_dir @appdir
207       @libdir     = dir + "/library"
208       @runtimedir = dir + "/runtime"
209       @codedbdir  = dir + "/codedb"
210       @coredir    = dir + "/core"
211       @sitedir    = dir + "/site"
212       @archdir    = dir + "/site/#{@cpu}-#{@os}"
213       @encdir     = dir + "/site/#{@cpu}-#{@os}/encoding/converter"
214       @vendordir  = dir + "/vendor"
215     end
217     @bindir       = @prefixdir + "/bin" unless @bindir
218     @libdir       = @prefixdir + "/library" unless @libdir
219     @runtimedir   = @prefixdir + "/runtime" unless @runtimedir
220     @codedbdir    = @prefixdir + "/codedb" unless @codedbdir
221     @coredir      = @prefixdir + "/core" unless @coredir
222     @sitedir      = @prefixdir + "/site" unless @sitedir
223     @archdir      = @prefixdir + "/site/#{@cpu}-#{@os}" unless @archdir
224     @encdir       = @prefixdir + "/site/#{@cpu}-#{@os}/encoding/converter" unless @encdir
225     @vendordir    = @prefixdir + "/vendor" unless @vendordir
226     @mandir       = @prefixdir + "/man" unless @mandir
227     @gemsdir      = @prefixdir + "/gems" unless @gemsdir
228     @includedir   = @prefixdir + "/machine/include/capi" unless @includedir
231     dirs = [@bindir, @libdir, @runtimedir, @codedbdir, @coredir, @sitedir,
232             @archdir, @vendordir, @mandir, @gemsdir, @includedir, @encdir]
234     parts = dirs.map { |d| d.split "/" }
236     i = 0
237     total = parts[0].size
238     prefix = []
240     while i < total
241       part = parts[0][i]
242       break unless parts.all? { |p| p[i] == part }
243       prefix << part
244       i += 1
245     end
247     @prefixdir = prefix.join "/"
248     size = @prefixdir.size
250     dirs.each { |d| d.replace d[size..-1] }
252     @scriptdir = "#{@sourcedir}/build/scripts"
254     @builddir = "#{@sourcedir}/build/rubinius" unless @builddir
256     stat = File.stat @builddir if File.exist? @builddir
258     if stat and stat.owned? and not @builddir.empty? and @builddir != "/"
259       FileUtils.rm_r @builddir
260     end
262     if @preserve_prefix
263       @builddir = File.expand_path "#{@builddir}/#{@prefixdir}"
264     end
266     FileUtils.mkdir_p @builddir
268     @bootstrap_gems_dir ||= "#{@sourcedir}/build/libraries/gems"
269     @codetoolsdir = "#{@sourcedir}/build/codetools"
270     @stdlibdir = "#{@sourcedir}/build/stdlib"
271   end
273   def add_opt_dir(dir)
274     @include_dirs << "#{dir}/include"
275     @lib_dirs     << "#{dir}/lib" << "#{dir}/lib64"
276   end
278   def options
279     @options = Rubinius::Options.new "Usage: configure [options]", 30
280     o = @options
281     o.left_align
283     o.doc " Configure settings"
285     o.on "--log-file", "NAME", "Write log to file NAME" do |name|
286       old_log = @log.path
287       @log = Logger.new name, false
288       @log.replace old_log
289     end
291     o.on "--make", "NAME", "Use NAME as 'make' during build" do |name|
292       @make = name
293     end
295     o.on "--rake", "NAME", "Use NAME as 'rake' during build" do |name|
296       @rake = name
297     end
299     o.on "--tar", "NAME", "Use NAME as 'tar'" do |name|
300       @tar = name
301     end
303     o.on "--bzip", "NAME", "Use NAME as 'bzip'" do |name|
304       @bzip = name
305     end
307     o.on "--perl", "NAME", "Use NAME as 'perl' during build" do |name|
308       @perl = name
309     end
311     o.on "--gem", "NAME", "Use NAME as 'gem' during build" do |name|
312       @gem = name
313     end
315     o.on "--debug-build", "Disable C++ optimizations and retain debugging symbols" do
316       @debug_build = true
317     end
319     o.on "--sanitize", "SANITIZER", "Enable the Clang sanitizer: 'memory', 'address', 'undefined'" do |sanitizer|
320       if ["address", "memory", "undefined"].include?(sanitizer)
321         @debug_build = true
322         (@system_cxxflags ||= "") << " -fsanitize=#{sanitizer}  -fno-omit-frame-pointer -fno-optimize-sibling-calls "
323         @system_cxxflags << " -fsanitize-address-use-after-scope " if sanitizer == "address"
324         @system_cxxflags << " -fsanitize-memory-track-origins " if sanitizer == "memory"
326         (@system_ldflags ||= "") << " -g -fsanitize=#{sanitizer} "
327       end
328     end
330     o.on "--release-build", "Build from local files instead of accessing the network" do
331       @release_build = true
332     end
334     o.on "--no-release-build", "Build from the network instead of local files" do
335       @release_build = false
336     end
338     o.on "--gc-stack-check", "Emit stack trace for all threads on pause for GC" do
339       @gc_stack_check = true
340     end
342     o.on "--log-concurrent-update", "Log when T2 updates an object created by T1" do
343       @log_concurrent_update = true
344     end
346     o.on "--raise-concurrent-update", "Raise a RuntimeError when T2 updates an object created by T1" do
347       @raise_concurrent_update = true
348     end
350     o.doc "\n Compiler settings"
352     o.on "--cc", "COMPILER", "Compiler to use for C code (eg clang)" do |cc|
353       @cc = cc
354     end
356     o.on "--cxx", "COMPILER", "Compiler to use for C++ code (eg clang++)" do |cxx|
357       @cxx = cxx
358     end
360     o.doc "\n LLVM settings"
362     o.on "--system-name", "NAME", "Name of OS (eg fedora-8, ubuntu-10.04)" do |name|
363       @llvm_system_name = name
364     end
366     o.on "--llvm-path", "PATH", "File system path to the directory containing LLVM" do |dir|
367       @llvm_path = dir.dup
368     end
370     o.on "--llvm-config", "PROGRAM", "File system path to the llvm-config program" do |program|
371       @llvm_configure = program
372     end
374     o.on "--llvm-shared", "Link to shared LLVM library" do
375       @llvm_shared = true
376     end
378     o.doc "\n System settings"
380     o.on "--with-include-dir", "DIR", "Add DIR to the default include search paths" do |dir|
381       dir.split(File::PATH_SEPARATOR).each do |d|
382         @include_dirs << d
383       end
384     end
386     o.on "--with-lib-dir", "DIR", "Add DIR to the default library search paths" do |dir|
387       dir.split(File::PATH_SEPARATOR).each do |d|
388         @lib_dirs << d
389       end
390     end
392     o.on "--with-opt-dir", "DIR", "Add DIR/include and DIR/lib to include and library search paths" do |dir|
393       dir.split(File::PATH_SEPARATOR).each do |d|
394         add_opt_dir(d)
395       end
396     end
398     o.on "--libc", "NAME", "Use NAME as the libc for FFI" do |name|
399       @libc = name
400     end
402     o.on "--host", "HOST", "Override guessed platform with HOST specification" do |host|
403       @log.write "------------------------------------------------------"
404       @log.write "\nChanging the platform specification can cause Rubinius"
405       @log.write "to malfunction. The current platform specification is:"
406       @log.write "\n#{@host}"
407       @log.write "\n------------------------------------------------------"
409       @host = host
410     end
412     o.doc "\n Program names"
414     o.on "--program-name", "NAME", "Build Rubinius executable as NAME" do |name|
415       @program_name = name
416     end
418     o.on "--bin-link", "NAME", "Create NAME as binary symlink to program name" do |name|
419       @bin_links << name
420     end
422     o.on "--no-bin-links", "Do not create any symlinks to program name" do
423       @use_bin_links = false
424     end
426     o.doc "\n File system paths for installing Rubinius"
428     o.on "-P", "--prefix", "PATH", "Install Rubinius in subdirectories of PATH" do |dir|
429       warn_prefix dir
430       @prefixdir = dir.dup
431     end
433     o.on "-B", "--bindir", "PATH", "Install Rubinius executable in PATH" do |dir|
434       @bindir = expand dir
435     end
437     o.on "-I", "--includedir", "PATH", "Install Rubinius C-API include files in PATH" do |dir|
438       @includedir = expand dir
439     end
441     o.on "-A", "--appdir", "PATH", "Install Ruby runtime and libraries in PATH" do |dir|
442       @appdir = dir.dup
443     end
445     o.on "-L", "--libdir", "PATH", "Install Rubinius shared library in PATH" do |dir|
446       @libdir = dir.dup
447     end
449     o.on "-M", "--mandir", "PATH", "Install man pages in PATH" do |dir|
450       @mandir = expand dir
451     end
453     o.on "-G", "--gemsdir", "PATH", "Install gems in PATH" do |dir|
454       @gemsdir = expand dir
455     end
457     o.on "--gems-cache", "PATH", "Cache Gems in PATH during compilation" do |dir|
458       @gems_cache = expand dir
459     end
461     o.on "--sitedir", "PATH", "Install site-specific Ruby code in PATH" do |dir|
462       @sitedir = expand dir
463     end
465     o.on "--archdir", "PATH", "Install arch-specific native extensions in PATH" do |dir|
466       @archdir = expand dir
467     end
469     o.on "--vendordir", "PATH", "Install vendor-specific Ruby code in PATH" do |dir|
470       @vendordir = expand dir
471     end
473     o.on "--preserve-prefix", "Use the configure prefix for staging Rubinius to install" do
474       @preserve_prefix = true
475     end
477     o.on "--stagingdir", "PATH", "Use PATH to build and prepare all files for install" do |dir|
478       @builddir = expand dir
479     end
481     o.doc "\n Optional features"
483     feature "execinfo", true
484     feature "vendor-zlib", false
485     feature "vendor-libsodium", true
486     feature "alloc-tracking", false
487     feature "dtrace", false
488     feature "rpath", false
490     o.doc "\n Help!"
492     o.on "--show", "Print the current configuration and exit" do
493       print_debug
494       exit 0
495     end
497     o.on "-V", "--verbose", "Print additional info" do
498       @verbose = true
499     end
501     o.help
503     o.doc ""
505   end
507   def feature(name, default_value=true)
508     @features[name] = ConfigurationToggle.new default_value
510     @options.on "--with-#{name}", "Enable #{name}" do
511       @features[name].configured = true
512     end
514     @options.on "--without-#{name}", "Disable #{name}" do
515       @features[name].configured = false
516     end
517   end
519   def parse(ary)
520     @options.parse ary
521   end
523   def md5_checksum(md5_path, full_path)
524     return Digest::MD5.file(full_path).hexdigest == File.read(md5_path).strip.split(" ").first
525   end
527   def download(url, full_path, follows=0)
528     begin
529       dir = File.dirname full_path
530       Dir.mkdir dir unless File.exist? dir
532       uri = url.kind_of?(URI) ? url : URI(url)
534       if ENV['http_proxy']
535         protocol, userinfo, p_host, p_port  = URI::split(ENV['http_proxy'])
536         p_user, p_pass = userinfo.split(/:/) if userinfo
537         http = Net::HTTP.new(uri.host, uri.port, p_host, p_port, p_user, p_pass)
538       else
539         http = Net::HTTP.new(uri.host, uri.port)
540       end
541       http.use_ssl = true if uri.scheme == 'https'
542       request = Net::HTTP::Get.new(uri.request_uri)
544       http.request(request) do |res|
545         case res
546         when Net::HTTPNotFound
547           @log.write "      #{url} not found."
548           return false
549         when Net::HTTPMovedPermanently,
550              Net::HTTPFound,
551              Net::HTTPSeeOther,
552              Net::HTTPTemporaryRedirect
553           if follows > 3
554             @log.write "      ERROR: too many redirects: #{url}"
555             return false
556           end
558           return download URI.parse(res['Location']), full_path, follows + 1
559         when Net::HTTPClientError
560           @log.write "      ERROR: #{res.inspect}"
561           return false
562         end
564         size = 0
565         total = res.header['Content-Length'].to_i
567         @log.write "    Downloading #{File.basename(full_path)}..."
568         File.open full_path, "wb" do |f|
569           res.read_body do |chunk|
570             f << chunk
571             size += chunk.size
572             print "\r      [ %d%% (%d of %d) ]" % [(size * 100) / total, size, total]
573           end
574         end
575         @log.write ": done!"
576       end
577     rescue Interrupt
578       File.unlink full_path if File.exist?(full_path)
579       raise
580     rescue StandardError => e
581       File.unlink full_path if File.exist?(full_path)
582       @log.write " ERROR: #{e.message}"
583       return false
584     end
586     return true
587   end
589   def setup_llvm
590     @log.print "  Checking for 'llvm-config': "
592     config = @llvm_configure
593     if !config
594       which = ENV['PATH'].split(":").find do |path|
595         File.exist? File.join(path, "llvm-config")
596       end
597       if which
598         config = File.join(which, "llvm-config")
599       elsif @darwin
600         if macports?
601           config = macports_llvm_config
602         else
603           out = brew "--prefix llvm"
604           config = "#{out}/bin/llvm-config" if $?.success?
605         end
606       end
607     end
609     if config
610       config_cmd = llvm_config_cmd config
611       begin
612         version = `#{config_cmd} --version`.strip
614         # Ruby 1.8 returns an empty string
615         failed = true if version.empty?
616       rescue Errno::ENOENT
617         # Ruby 1.9 raises this error
618         failed = true
619       end
621       unless failed
622         parts = version.sub(/svn$/, "").split(".").map { |i| i.to_i }
623         api_version = ("%d%02d" % parts[0..1]).to_i
624         if api_version < 306
625           @log.write "only LLVM 3.6+ is supported"
626         else
627           @log.write "found! (version #{version} - api: #{api_version})"
628           @llvm = :config
629           @llvm_configure = config_cmd
630           @llvm_version = version
631           @llvm_api_version = api_version
633           check_llvm_flags
635           if @llvm_shared
636             setup_llvm_shared
637           end
639           return true
640         end
641       else
642         @log.write "executing #{config_cmd.inspect} failed"
643       end
644     else
645       @log.write "not found"
646     end
648     failure "ABORT: unable to set up LLVM"
649   end
651   def setup_llvm_shared
652     @log.print "  Checking for LLVM shared libs: "
654     src = <<-EOP
655 #include <llvm/IR/LLVMContext.h>
656 using namespace llvm;
657 int main() { LLVMContext &Context = getGlobalContext(); }
658     EOP
660     common_args = "`#{@llvm_configure} --cppflags` #{@llvm_cxxflags} #{@llvm_ldflags}".strip.split(/\s+/)
661     shared_configs = {
662       "libLLVM-#{@llvm_version}"  => ["-lLLVM-#{@llvm_version}"],
663       "#{@llvm_configure} --libs" => `#{@llvm_configure} --libs`.strip.split(/\s+/)
664     }
666     shared_configs.each do |desc, objs|
667       status = check_program(false, *(common_args + objs)) do |f|
668         f.puts src
669         @log.log src
670       end
672       if status == 0
673         @log.write "found! (using #{desc})"
674         @llvm_shared_objs = objs
675         return true
676       end
677     end
679     @log.write "not found"
680     false
681   end
683   def check_llvm_flags
684     flags = '--ldflags'
686     # Starting with LLVM 3.5 the --system-libs option is required in order to
687     # link against libraries such as zlib. Prior to 3.5 this was handled by
688     # --ldflags.
689     flags << ' --system-libs'
691     # Generate the actual flags. For whatever reason llvm-config also includes
692     # newlines in the output, so lets get rid of those while we're at it.
693     @llvm_ldflags = `#{@llvm_configure} #{flags}`.strip.gsub("\n", ' ')
694   end
696   def env(which)
697     ENV[which] || ""
698   end
700   def default_link_libs
701     libs = []
702     unless @host =~ /haiku/
703       libs << "m"
704     end
705     libs
706   end
708   def failure(message=nil)
709     @log.error message if message
711     STDERR.puts "\nRunning 'configure' failed. Please check configure.log for more details."
712     exit 1
713   end
715   def supported_compiler(name)
716     failure <<-EOM
717 Unable to find #{name} compiler. Support for compilers other than #{name}
718 compiler was deprecated 1 Jun 2016 and has been removed. If your platform does
719 not support #{name} compiler, please email contact@rubinius.com
720     EOM
721   end
723   def default_cc
724     return 'clang' if `clang --version > /dev/null 2>&1` && $?.success?
725     supported_compiler "clang C"
726   end
728   def default_cxx
729     return 'clang++' if `clang++ --version > /dev/null 2>&1` && $?.success?
730     supported_compiler "clang++ C++"
731   end
733   def check_tools
734     @cc ||= ENV['CC'] || default_cc
735     @cxx ||= ENV['CXX'] || default_cxx
737     check_tool_version @cc, '-dumpversion', [4, 1]
738     check_tool_version @cxx, '-dumpversion', [4, 1]
740     supported_compiler "clang C" unless @cc =~ /clang|ccc-analyzer/
741     supported_compiler "clang++ C++" unless @cxx =~ /clang\+\+|c\+\+\-analyzer/
743     if File.exist? @build_bin
744       if !File.directory? @build_bin
745         fail "#{@build_bin} already exists and is not a directory"
746       end
747     else
748       FileUtils.mkdir_p @build_bin
749     end
751     if @cc != "cc"
752       cc = "#{@build_bin}/cc"
753       File.symlink `which #{@cc}`.chomp, cc unless File.exist? cc
754     end
756     if @cxx != "c++"
757       cxx = "#{@build_bin}/c++"
758       File.symlink `which #{@cxx}`.chomp, cxx unless File.exist? cxx
759     end
761     @make ||= ENV['MAKE'] || 'make'
762     @rake ||= ENV['RAKE'] || 'rake'
763     @tar ||= ENV['TAR'] || (@windows ? 'bsdtar' : 'tar')
764     @bzip ||= ENV['BZIP'] || 'bzip2'
765     @perl ||= ENV['PERL'] || 'perl'
766     @gem ||= ENV['GEM'] || 'gem'
768     @gcc_major = `#{@cc} -dumpversion`.strip.split(".")[0,2].join(".")
769     if @host == "i686-pc-linux-gnu" || @host == "x86_64-unknown-linux-gnu"
770       @llvm_generic_prebuilt  = "llvm-#{@llvm_version}-#{@host}-#{@gcc_major}.tar.bz2"
771     else
772       @llvm_generic_prebuilt  = "llvm-#{@llvm_version}-#{@host}.tar.bz2"
773     end
775     @system_cflags    ||= ""
776     (@system_cxxflags ||= "") << "-std=c++14 "
777     @system_cppflags  ||= ""
778     @system_incflags  ||= ""
779     @system_ldflags   ||= ""
781     @user_cflags =   ENV['CFLAGS']
782     @user_cxxflags = ENV['CXXFLAGS']
783     @user_cppflags = ENV['CPPFLAGS']
784     @user_incflags = ENV['INCFLAGS']
785     @user_ldflags =  ENV['LDFLAGS']
787     setup_platform
788   end
790   def setup_platform
791     @ldsharedxx = "#{@cxx} -shared"
792     @ldshared   = "#{@cc} -shared"
794     @include_dirs.each do |d|
795       @system_incflags << "-I#{d} "
796     end
797     @lib_dirs.each do |d|
798       @system_ldflags << "-L#{d} "
799     end
801     case RUBY_PLATFORM
802     when /mswin/i, /mingw/i, /bccwin32/i
803       # TODO: discovery helpers
804       #check_heads(%w[windows.h winsock.h], true)
805       #check_libs(%w[kernel32 rpcrt4 gdi32], true)
807       unless RUBY_PLATFORM =~ /mingw/
808         @system_cflags << "-EHs -GR"
809       end
810       @system_ldflags << "-lws2_32"
811       @features["rpath"].configured = false
812     when /solaris/i
813       # GNU CHAIN only supported
814       @ldsharedxx = "#{@cxx} -shared -G -fPIC -lstdc++"
815       @ldshared   = "#{@cc} -shared -G -fPIC"
816       @system_cflags << "-fPIC -Wno-strict-aliasing"
817       @system_ldflags << "-lsocket -lnsl -fPIC"
818       @features["rpath"].configured = false
819       @make = "gmake"
820     when /freebsd/i
821       @ldsharedxx = "#{@cxx} -shared -fPIC"
822       @ldshared   = "#{@cc} -shared -fPIC"
823       @system_cflags << "-fPIC"
824       @system_ldflags << "-lcrypt -pthread -rdynamic"
825       @make = "gmake"
826     when /openbsd/i
827       # OpenBSD branch contributed by Guillaume Sellier.
829       # on Unix we need a g++ link, not gcc. On OpenBSD, linking against
830       # libstdc++ have to be explicitly done for shared libs
831       @ldsharedxx = "#{@cxx} -shared -lstdc++ -fPIC"
832       @ldshared   = "#{@cc} -shared -fPIC"
833       @system_cflags << "-fPIC"
834       @system_ldflags << "-pthread -rdynamic -Wl,--export-dynamic"
835       @make = "gmake"
836     when /netbsd/i
837       @ldsharedxx = "#{@cxx} -shared -lstdc++ -fPIC"
838       @ldshared   = "#{@cc} -shared -fPIC"
839       @system_cflags << "-fPIC"
840       @system_ldflags << "-lcrypt -pthread -rdynamic -Wl,--export-dynamic"
841       @make = "gmake"
842     when /darwin/i
843       # on Unix we need a g++ link, not gcc.
844       # Ff line contributed by Daniel Harple.
845       @ldsharedxx = "#{@cxx} -bundle -undefined suppress -flat_namespace"
846       @ldshared   = "#{@cc} -bundle -undefined suppress -flat_namespace"
847       @system_cflags << "-fPIC -D_DARWIN_USE_64_BIT_INODE"
848       @features["rpath"].configured = false
849     when /haiku/i
850       @system_cflags << "-fPIC"
851       @system_ldflags << "-ldl -lnetwork"
852       @features["rpath"].configured = false
853     when /aix/i
854       @ldsharedxx = "#{@cxx} -shared -Wl,-G -Wl,-brtl"
855       @ldshared   = "#{@cc} -shared -Wl,-G -Wl,-brtl"
856       @features["rpath"].configured = false
857     when /linux/i
858       @system_cflags << "-fPIC"
859       @system_ldflags << "-Wl,--export-dynamic -lrt -lcrypt -ldl -lpthread"
860     else
861       # on Unix we need a g++ link, not gcc.
862       @system_cflags << "-fPIC"
863       @system_ldflags << "-ldl -lpthread"
864     end
866     if @features["rpath"].value
867       @lib_dirs.each do |d|
868         @system_ldflags << " -Wl,-rpath=#{d}"
869       end
870     end
871   end
873   def check_program(run=true, *arguments)
874     begin
875       basename = "rbx-configure-test"
876       source   = basename + ".cpp"
877       File.open source, "wb" do |f|
878         yield f
879       end
881       File.open source, "rb" do |f|
882         @log.log f.read
883       end
885       libs = default_link_libs.map { |l| "-l#{l}" }.join(" ")
886       args = arguments.join(" ")
888       cmd = "#{@cxx} #{@user_cppflags} #{@user_cflags} #{@user_cxxflags} #{@user_incflags} #{@user_ldflags} -o #{basename} #{source} #{@system_cppflags} #{@system_cflags} #{@system_cxxflags} #{@system_incflags} #{@system_ldflags} #{libs} #{args} >>#{@log.path} 2>&1"
889       @log.log cmd
890       system cmd
891       return $?.exitstatus unless run
893       unless $?.exitstatus == 0
894         @log.log msg = "Compiling configure test program failed."
895         raise RuntimeError, msg
896       end
898       system expand("./#{basename}")
899       return $?.exitstatus
900     rescue => e
901       @log.log "Error in check_program: #{e.class} #{e.message}\n  #{e.backtrace.join("\n  ")}"
902       raise e
903     ensure
904       FileUtils.rm_r(Dir["#{basename}*"])
905     end
906   end
908   def write_have_defines(f)
909     f.puts
910     @defines.each { |d| f.puts "#define #{d.ljust(20)} 1" }
911   end
913   def write_have_sizeof_defines(f)
914     f.puts
915     @sizeof.keys.sort.each { |k| f.puts "#define HAVE_#{k}".ljust(30) + "1" }
916   end
918   def write_sizeof_defines(f)
919     f.puts
920     @sizeof.keys.sort.each { |k| f.puts "#define SIZEOF_#{k}".ljust(30) + @sizeof[k].to_s }
921   end
923   def sizeof_typename(type)
924     if type =~ /(\*+)$/
925       name = "#{type[0...-$1.size]}#{"p" * $1.size}"
926     else
927       name = type
928     end
929     name.gsub(/\W/, "_").upcase
930   end
932   def sizeof(type)
933     @sizeof[sizeof_typename(type)] or failure("Unknown type: '#{type}'.")
934   end
936   def assert_sizeof
937     @log.print "Checking sizeof(intptr_t) == sizeof(int64_t): "
939     status = check_program do |f|
940       src = <<-EOP
941 #include <stdint.h>
943 int main(int argc, char* argv[]) {
944   return sizeof(intptr_t) == sizeof(int64_t);
946       EOP
947       f.puts src
948       @log.log src
949     end
951     if status == 1
952       @log.write "yes"
953     else
954       @log.write "no"
955       failure "\nRubinius requires that sizeof(intptr_t) == sizeof(int64_t)"
956     end
957   end
959   def detect_sizeof(type, includes=[])
960     @log.print "Checking sizeof(#{type}): "
962     size = check_program do |f|
963       src = includes.map { |include| "#include <#{include}>\n" }.join
964       src += <<-EOP
965 #include <stddef.h>
966 #include <stdint.h>
968 int main() { return sizeof(#{type}); }
969       EOP
970       f.puts src
971       @log.log src
972     end
974     @sizeof[sizeof_typename(type)] = size
976     @log.write "#{size} bytes"
977   end
979   def detect_endian
980     @log.print "Checking platform endianness: "
982     status = check_program do |f|
983       src = "int main() { int one = 1; return (*((char*)&one)) == 1 ? 0 : 1; }"
984       f.puts src
985       @log.log src
986     end
988     @little_endian = (status == 0)
989     @log.write @little_endian ? "little endian" : "big endian"
990   end
992   def detect_tr1
993     @log.print "Checking for tr1: "
995     status = check_program(false) do |f|
996       src = <<-EOP
997 #include <tr1/unordered_map>
999 typedef std::tr1::unordered_map<int, void*> X;
1001 int main() { X x; return 0; }
1002       EOP
1003       f.puts src
1004       @log.log src
1005     end
1007     @tr1 = (status == 0)
1008     @log.write @tr1 ? "found" : "not found"
1009   end
1011   def detect_tr1_hash
1012     @log.print "Checking for tr1/hash definition: "
1014     status = check_program(false) do |f|
1015       src = <<-EOP
1016 #include <stdint.h>
1017 #include <tr1/unordered_map>
1019 typedef std::tr1::unordered_map<uint64_t, void*> X;
1021 int main() { X x; return 0; }
1022       EOP
1023       f.puts src
1024       @log.log src
1025     end
1027     @tr1_hash = (status == 0)
1028     @log.write @tr1_hash ? "found" : "not found"
1029   end
1031   def detect_x86
1032     print "Checking for x86_64: "
1034     status = check_program do |f|
1035       src = <<-EOP
1036 int main() {
1037 #if defined(__x86_64) || defined(__x86_64__)
1038 return 1;
1039 #else
1040 return 0;
1041 #endif
1043       EOP
1045       f.puts src
1046       @log.log src
1047     end
1048     @x86_64 = (status == 1)
1050     puts @x86_64 ? "yes" : "no"
1051   end
1053   def detect_aarch64
1054     print "Checking for aarch64: "
1056     status = check_program do |f|
1057       src = <<-EOP
1058 int main() {
1059 #if defined(__ARM_ARCH_ISA_A64) 
1060 return 1;
1061 #else
1062 return 0;
1063 #endif
1065       EOP
1067       f.puts src
1068       @log.log src
1069     end
1070     @aarch64 = (status == 1)
1072     puts @aarch64 ? "yes" : "no"
1073   end
1075   def detect_curses
1076     @log.print "Checking curses library: "
1078     src = <<-EOP
1079 #include <curses.h>
1080 #include <term.h>
1082 int main() { return tgetnum(""); }
1083     EOP
1085     ["-lcurses", "-lncurses", "-ltermcap"].each do |lib|
1086       status = check_program(false, lib) do |f|
1087         f.puts src
1088         @log.log src
1089       end
1091       if status == 0
1092         @curses = lib
1093         break
1094       end
1095     end
1097     if @curses
1098       @log.write(@curses)
1099     end
1100   end
1102   def detect_build_dirs
1103     ["/usr/local", "/opt/local", "/usr/pkg"].each do |dir|
1104       add_opt_dir(dir)
1105     end
1107     @include_dirs = @include_dirs.select {|p| File.directory? p }
1108     @lib_dirs = @lib_dirs.select {|p| File.directory? p }
1109   end
1111   def has_struct_member(struct, member, includes = [])
1112     compile_check "struct #{struct} has member #{member}" do |src|
1113       includes.each do |i|
1114         src.puts "#include <#{i}>"
1115       end
1117       src.puts "int main() { struct #{struct} st; st.#{member}; }"
1118     end
1119   end
1121   def has_global(name, includes=[])
1122     compile_check "global '#{name}'" do |src|
1123       includes.each do |i|
1124         src.puts "#include <#{i}>"
1125       end
1126       src.puts "int main() { #{name}; }"
1127     end
1128   end
1130   def has_header(name)
1131     compile_check "header '#{name}'" do |src|
1132       # Some headers have an implicit dependency on stdio.h. For example,
1133       # readline/readline.h requires it but doesn't actually include it for
1134       # you. Because there could be an infinite amount of headers that require
1135       # stdio.h we'll just always include it.
1136       src.puts "#include <stdio.h>"
1138       src.puts "#include <#{name}>"
1139       src.puts "int main() {return 0;}"
1140     end
1141   end
1143   def has_function(name, includes=[], defines = [])
1144     compile_check "function '#{name}'", defines do |src|
1145       includes.each do |i|
1146         src.puts "#include <#{i}>"
1147       end
1148       src.puts "int main() { void* ptr = (void *) &#{name}; }"
1149     end
1150   end
1152   def has_library(name, function, libraries, includes=[])
1153     @log.print "Checking for library: #{name}: "
1155     args = libraries.map { |l| "-l#{l}" }
1157     status = check_program(true, *args) do |src|
1158       includes.each do |i|
1159         src.puts "#include <#{i}>"
1160       end
1161       src.puts "int main() { void* ptr = (void*)(&#{function}); return 0; }"
1162     end
1164     success = status == 0
1165     @log.write(success ? "found!" : "not found!")
1166     success
1167   end
1169   def has_dtrace
1170     @log.print "Checking for dtrace: "
1172     begin
1173       basename = "rbx-configure-dtrace-test"
1174       source   = basename + ".d"
1175       output   = basename + ".h"
1177       File.open source, "wb" do |f|
1178         f.write "provider conftest{ probe m__entry(const char*); };"
1179       end
1181       cmd = "dtrace -h -o #{output} -s #{source}"
1182       @log.log cmd
1183       system cmd
1185       @dtrace = $?.exitstatus == 0
1186       @dtrace_const = !!File.read(output).index("const") if @dtrace
1188       @log.write(@dtrace ? "yes" : "no")
1190       @dtrace
1191     ensure
1192       File.delete(*Dir["#{basename}*"])
1193     end
1194   end
1196   def compile_check(logpart, defines = [], &block)
1197     @log.print "Checking for #{logpart}: "
1199     source = StringIO.new
1200     yield source
1201     file = Tempfile.new("rbx-test")
1203     source.rewind
1204     string = source.read
1206     file.puts string
1207     file.close
1209     @log.log string
1211     cmd = "#{@cxx} -S -o - -x c++ #{defines.join(" ")} #{@user_cppflags} #{@user_incflags} #{@user_cxxflags} #{@user_cflags} #{@user_ldflags} #{@system_cppflags} #{@system_incflags} #{@system_cxxflags} #{@system_cflags} #{@system_ldflags} #{file.path} >>#{@log.path} 2>&1"
1212     @log.log cmd
1213     system cmd
1215     status = ($?.exitstatus == 0)
1216     file.unlink
1218     @log.write(status ? "found!" : "not found")
1219     status
1220   end
1222   def enable_features
1223     if @features["vendor-zlib"].value
1224       # Our vendored zlib uses long as the crc_table type
1225       # If we update vendored zlib in the future, we have to
1226       # review this and make sure we update it properly to
1227       # match the newer version which like will have uint32_t
1228       # as the type.
1229       @include_dirs << "#{@build_libdir}/zlib"
1230       @lib_dirs     << "#{@build_libdir}/zlib"
1231     end
1233     if @features["vendor-libsodium"].value
1234       @include_dirs << "#{@build_libdir}/libsodium/src/libsodium/include"
1235       @lib_dirs     << "#{@build_libdir}/libsodium/src/libsodium/.libs/"
1236     end
1237   end
1239   def detect_features
1240     # Default on *BSD is no execinfo
1241     if @bsd and @features["execinfo"].configured.nil?
1242       @features["execinfo"].configured = false
1243     end
1245     if @features["execinfo"].value and has_function("backtrace", ["execinfo.h"])
1246       @defines << "HAS_EXECINFO"
1247     end
1249     if @features["alloc-tracking"].value
1250       @defines << "RBX_ALLOC_TRACKING"
1251     end
1253     if @features["dtrace"].value and has_dtrace
1254       @defines << "HAVE_DTRACE"
1255     end
1257     # Default on Windows is vendor-zlib
1258     if @windows and @features["vendor-zlib"].configured.nil?
1259       @features["vendor-zlib"].configured = true
1260     end
1262     @defines << "HAVE_SPT_REUSEARGV" if @linux || @darwin || @bsd
1263   end
1265   def detect_functions
1266     if has_function("clock_gettime", ["time.h"])
1267       @defines << "HAVE_CLOCK_GETTIME"
1268     end
1270     if has_function("nl_langinfo", ["langinfo.h"])
1271       @defines << "HAVE_NL_LANGINFO"
1272     end
1274     if has_function("setproctitle", ["sys/types.h", "unistd.h"])
1275       @defines << "HAVE_SETPROCTITLE"
1276     end
1278     if has_function("posix_fadvise", ["fcntl.h"])
1279       @defines << "HAVE_POSIX_FADVISE"
1280     end
1282     if has_function("strnlen", ["string.h"])
1283       @defines << "HAVE_STRNLEN"
1284     end
1286     if has_function("kqueue", ["sys/types.h", "sys/event.h", "sys/time.h"])
1287       @defines << "HAVE_KQUEUE"
1288     end
1290     if has_function("timerfd_create", ["sys/timerfd.h"])
1291       @defines << "HAVE_TIMERFD"
1292     end
1294     if has_function("inotify_init", ["sys/inotify.h"])
1295       @defines << "HAVE_INOTIFY"
1296     end
1298     if has_function("gettid", ["unistd.d", "sys/types.h"])
1299       @defines << "HAVE_GETTID"
1300     end
1302     if has_struct_member("stat", "st_atim", ["sys/stat.h"])
1303       @defines << "HAVE_STRUCT_STAT_ST_ATIM"
1304     end
1306     if has_struct_member("stat", "st_atimespec", ["sys/stat.h"])
1307       @defines << "HAVE_STRUCT_STAT_ST_ATIMESPEC"
1308     end
1310     if has_struct_member("stat", "st_atimensec", ["sys/stat.h"])
1311       @defines << "HAVE_STRUCT_STAT_ST_ATIMENSEC"
1312     end
1314     if has_struct_member("stat", "st_mtim", ["sys/stat.h"])
1315       @defines << "HAVE_STRUCT_STAT_ST_MTIM"
1316     end
1318     if has_struct_member("stat", "st_mtimespec", ["sys/stat.h"])
1319       @defines << "HAVE_STRUCT_STAT_ST_MTIMESPEC"
1320     end
1322     if has_struct_member("stat", "st_mtimensec", ["sys/stat.h"])
1323       @defines << "HAVE_STRUCT_STAT_ST_MTIMENSEC"
1324     end
1326     if has_struct_member("stat", "st_ctim", ["sys/stat.h"])
1327       @defines << "HAVE_STRUCT_STAT_ST_CTIM"
1328     end
1330     if has_struct_member("stat", "st_ctimespec", ["sys/stat.h"])
1331       @defines << "HAVE_STRUCT_STAT_ST_CTIMESPEC"
1332     end
1334     if has_struct_member("stat", "st_ctimensec", ["sys/stat.h"])
1335       @defines << "HAVE_STRUCT_STAT_ST_CTIMENSEC"
1336     end
1338     if has_struct_member("stat", "st_birthtimespec", ["sys/stat.h"])
1339       @defines << "HAVE_ST_BIRTHTIME"
1340     end
1342     # glibc has useless lchmod() so we don't try to use lchmod() on linux
1343     if !@linux and has_function("lchmod", ["sys/stat.h", "unistd.h"])
1344       @have_lchmod = true
1345     end
1347     if has_function("lchown", ["sys/stat.h", "unistd.h"])
1348       @have_lchown = true
1349     end
1351     if has_function("mkfifo", ["sys/stat.h", "sys/types.h"])
1352       @have_mkfifo = true
1353     end
1354   end
1356   def detect_structures
1357     if has_struct_member("tm", "tm_gmtoff", ["time.h"])
1358       @defines << "HAVE_TM_GMTOFF"
1359     end
1361     if has_struct_member("tm", "tm_zone", ["time.h"])
1362       @defines << "HAVE_TM_ZONE"
1363     end
1364   end
1366   def detect_globals
1367     if has_global("timezone", ["time.h"])
1368       @defines << "HAVE_TIMEZONE"
1369     end
1371     if has_global("tzname", ["time.h"])
1372       @defines << "HAVE_TZNAME"
1373     end
1375     if has_global("daylight", ["time.h"])
1376       @defines << "HAVE_DAYLIGHT"
1377     end
1378   end
1380   def detect_headers
1381     unless @features["vendor-zlib"].value
1382       unless has_header("zlib.h")
1383         failure "zlib.h is required"
1384       end
1385     end
1387     unless @features["vendor-libsodium"].value
1388       unless has_header("sodium.h")
1389         failure "sodium.h is required"
1390       end
1391     end
1393     unless has_header("openssl/ssl.h")
1394       failure "openssl/ssl.h is required"
1395     end
1397     if has_header("alloca.h")
1398       @defines << "HAVE_ALLOCA_H"
1399     end
1401     if has_header("string.h")
1402       @defines << "HAVE_STRING_H"
1403     end
1405     if has_header("sys/time.h")
1406       @defines << "HAVE_SYS_TIME_H"
1407     end
1409     if has_header("sys/times.h")
1410       @defines << "HAVE_SYS_TIMES_H"
1411     end
1413     if has_header("sys/types.h")
1414       @defines << "HAVE_SYS_TYPES_H"
1415     end
1417     if has_header("unistd.h")
1418       @defines << "HAVE_UNISTD_H"
1419     end
1421     if has_header("stdarg.h")
1422       @defines << "HAVE_STDARG_H"
1423     end
1425     if has_header("sys/pstat.h")
1426       @defines << "HAVE_SYS_PSTAT_H"
1427     end
1429     if has_header("valgrind/valgrind.h")
1430       @defines << "HAVE_VALGRIND_H"
1431     end
1432   end
1434   def strerror_r_returns_char_pointer
1435     status = check_program(false) do |src|
1436       src.puts "#include <string.h>"
1437       src.puts "int main() { char buf[1024]; static_cast<char*>(strerror_r(42, buf, 1024)); }"
1438     end
1439     status == 0
1440   end
1442   def detect_strerror
1443     @log.print "Checking if function 'strerror_r' returns char*: "
1444     if strerror_r_returns_char_pointer
1445       @defines << "STRERROR_R_CHAR_P"
1446       @log.write "yes"
1447     else
1448       @log.write "no"
1449     end
1450   end
1452   def detect_atomic
1453     @log.print "Checking -latomic: "
1455     saved_ldflags = @system_ldflags.dup
1456     @system_ldflags << " -latomic "
1458     begin
1459       status = check_program() do |src|
1460         src.puts <<-EOP
1461 #include <stdio.h>
1462 #include <atomic>
1464 int main(int argc, char* argv[]) {
1465   std::atomic<int> i;
1466   printf("%d", (int)i);
1467   return 0;
1469         EOP
1470       end
1471     rescue
1472       status = nil
1473     end
1475     if status == 0
1476       @log.write "yes"
1477     else
1478       @log.write "no"
1479       @system_ldflags = saved_ldflags
1480     end
1481   end
1483   def warn_prefix(dir)
1484     delimiter = "-------------------------%s-----------------------"
1486     if File.file? dir
1487       @log.write delimiter % " ERROR "
1488       @log.write "The specified prefix '#{dir}' is a regular file."
1489       @log.write "Remove the file or specify a different prefix."
1490       @log.write delimiter % "-------"
1491       exit 1
1492     elsif File.directory? dir
1493       @log.write delimiter % " WARNING "
1494       @log.write "The specified prefix '#{dir}' already exists."
1495       @log.write "Installing Rubinius into an existing directory may"
1496       @log.write "overwrite existing unrelated files or cause conflicts"
1497       @log.write "between different versions of Rubinius files."
1498       @log.write delimiter % "---------"
1499       sleep 2
1500     end
1501   end
1503   def process
1504     set_system_commands
1506     enable_features
1507     detect_build_dirs
1509     setup_llvm
1511     @log.write ""
1513     assert_sizeof
1515     detect_sizeof("short")
1516     detect_sizeof("int")
1517     detect_sizeof("void*")
1518     detect_sizeof("intptr_t")
1519     detect_sizeof("uintptr_t")
1520     detect_sizeof("size_t")
1521     detect_sizeof("long")
1522     detect_sizeof("long long")
1523     detect_sizeof("float")
1524     detect_sizeof("double")
1525     detect_sizeof("off_t", ["unistd.h"])
1526     detect_sizeof("time_t", ["time.h"])
1528     detect_libc_name
1530     detect_endian
1531     detect_tr1
1532     detect_tr1_hash
1533     detect_x86
1534     detect_aarch64
1535     detect_features
1536     detect_functions
1537     detect_structures
1538     detect_globals
1539     detect_headers
1540     detect_curses
1541     detect_strerror
1542     detect_atomic
1543  end
1545   # Checks whether the given config file is a Perl script by checking its first
1546   # line for a Perl hashbang.
1547   def llvm_config_cmd(config)
1548     begin
1549       File.open(config, "r") do |f|
1550         first_line = f.readline
1551         if first_line =~ /^#! ?\/usr(\/local)?\/bin\/(env )?perl/
1552           "#{@perl} #{config}"
1553         else
1554           config
1555         end
1556       end
1557     rescue Errno::ENOENT, ArgumentError
1558       # The file doesn't exist (ENOENT) or it's a binary file (ArgumentError).
1559       config
1560     end
1561   end
1563   def get_system_name
1564     return unless @os =~ /linux/
1565     return unless File.exist? "/etc/issue"
1567     data = IO.readlines("/etc/issue").first
1568     data =~ /([^ ]+)[^\d\.]*([\d\.]*)/
1570     name = $1.downcase
1571     version = $2
1573     if name == "debian" and File.exist? "/etc/debian_version"
1574       version = IO.read("/etc/debian_version").split.first.gsub(/\W/, "-")
1575     end
1577     return "#{name}-#{version}"
1578   end
1580   def check_tool_version(tool_name, opts, version, regexp=/(?=\d)(\d+).(\d+).?(\d+)?/)
1581     @log.print "Checking #{tool_name}:"
1583     output = `#{tool_name} #{opts}`
1585     if $?.exitstatus == 0
1586       v = output.scan(regexp)[0].map { |x| x.to_i }
1587       unless (v <=> version) >= 0
1588         failure " Expected #{tool_name} version >= #{version.join('.')}, found #{v.join('.')}"
1589       end
1590       @log.write " found"
1591     else
1592       failure "#{tool_name} not found."
1593     end
1594   end
1596   def detect_libc_name
1597     return if @libc
1599     @log.print "Checking for libc version: "
1601     case
1602     when @windows
1603       @libc = "msvcrt.dll"
1604     when @darwin
1605       @libc = "libc.dylib"
1606     else
1607       begin
1608         exe = ENV["SHELL"] || "/bin/sh"
1609         ldd_output = `ldd #{exe}`
1611         @libc = ldd_output[/libc\.so\.[0-9]+/]
1612       rescue
1613         # Don't abort if the command is not found
1614       end
1616       unless $?.success? and @libc
1617         failure "libc not found. Use the --libc configure option."
1618       end
1619     end
1621     @log.write "#{@libc} found!"
1622   end
1624   def write_configure_files
1625     @log.write "\nWriting configuration files..."
1627     @bin_links.delete @program_name
1629     config_settings = {
1630       :config_file        => @config,
1631       :command_line       => @command_line,
1632       :build_make         => @make,
1633       :build_rake         => @rake,
1634       :build_perl         => @perl,
1635       :llvm_path          => @llvm_path,
1636       :llvm_system_name   => @llvm_system_name,
1637       :llvm_configure     => @llvm_configure,
1638       :llvm_version       => @llvm_version,
1639       :llvm_api_version   => @llvm_api_version,
1640       :llvm_shared        => @llvm_shared,
1641       :llvm_shared_objs   => @llvm_shared_objs,
1642       :llvm_cxxflags      => @llvm_cxxflags,
1643       :llvm_ldflags       => @llvm_ldflags,
1644       :cc                 => @cc,
1645       :cxx                => @cxx,
1646       :make               => @make,
1647       :rake               => @rake,
1648       :tar                => @tar,
1649       :bzip               => @bzip,
1650       :perl               => @perl,
1651       :gem                => @gem,
1652       :ldshared           => @ldshared,
1653       :ldsharedxx         => @ldsharedxx,
1654       :gcc_major          => @gcc_major,
1655       :user_cflags        => "#{@user_cflags}",
1656       :user_cxxflags      => "#{@user_cxxflags}",
1657       :user_cppflags      => "#{@user_cppflags}",
1658       :user_incflags      => "#{@user_incflags}",
1659       :user_ldflags       => "#{@user_ldflags}",
1660       :system_cflags      => "#{@system_cflags}",
1661       :system_cxxflags    => "#{@system_cxxflags}",
1662       :system_cppflags    => "#{@system_cppflags}",
1663       :system_incflags    => "#{@system_incflags}",
1664       :system_ldflags     => "#{@system_ldflags}",
1665       :include_dirs       => @include_dirs,
1666       :lib_dirs           => @lib_dirs,
1667       :defines            => @defines,
1668       :curses             => @curses,
1669       :host               => @host,
1670       :cpu                => @cpu,
1671       :vendor             => @vendor,
1672       :os                 => @os,
1673       :little_endian      => @little_endian,
1674       :sizeof_short       => sizeof("short"),
1675       :sizeof_int         => sizeof("int"),
1676       :sizeof_void_ptr    => sizeof("void*"),
1677       :sizeof_intptr_t    => sizeof("intptr_t"),
1678       :sizeof_uintptr_t   => sizeof("uintptr_t"),
1679       :sizeof_size_t      => sizeof("size_t"),
1680       :sizeof_long        => sizeof("long"),
1681       :sizeof_long_long   => sizeof("long long"),
1682       :sizeof_float       => sizeof("float"),
1683       :sizeof_double      => sizeof("double"),
1684       :sizeof_off_t       => sizeof("off_t"),
1685       :sizeof_time_t      => sizeof("time_t"),
1686       :x86_64             => @x86_64,
1687       :aarch64            => @aarch64,
1688       :dtrace             => @dtrace,
1689       :dtrace_const       => @dtrace_const,
1690       :debug_build        => @debug_build,
1691       :sourcedir          => @sourcedir,
1692       :builddir           => @builddir,
1693       :scriptdir          => @scriptdir,
1694       :bootstrap_gems_dir => @bootstrap_gems_dir,
1695       :capi_includedir    => @capi_includedir,
1696       :build_libdir       => @build_libdir,
1697       :build_exe          => "#{@builddir}#{@bindir}/#{@program_name}",
1698       :build_bin          => @build_bin,
1699       :prefixdir          => @prefixdir,
1700       :bindir             => @bindir,
1701       :libdir             => @libdir,
1702       :encdir             => @encdir,
1703       :runtimedir         => @runtimedir,
1704       :codedbdir          => @codedbdir,
1705       :codetoolsdir       => @codetoolsdir,
1706       :stdlibdir          => @stdlibdir,
1707       :coredir            => @coredir,
1708       :sitedir            => @sitedir,
1709       :archdir            => @archdir,
1710       :vendordir          => @vendordir,
1711       :includedir         => @includedir,
1712       :mandir             => @mandir,
1713       :gemsdir            => @gemsdir,
1714       :gems_cache         => @gems_cache,
1715       :gems_list          => @gems_list,
1716       :gem_files          => @gem_files,
1717       :installed_gems     => @installed_gems,
1718       :program_name       => @program_name,
1719       :bin_links          => @bin_links,
1720       :use_bin_links      => @use_bin_links,
1721       :rpath              => @features["rpath"].value,
1722       :windows            => @windows,
1723       :darwin             => @darwin,
1724       :bsd                => @bsd,
1725       :linux              => @linux,
1726       :vendor_zlib        => @features["vendor-zlib"].value,
1727       :vendor_libsodium   => @features["vendor-libsodium"].value,
1728       :vm_release_h       => @vm_release_h,
1729       :ruby_version       => @ruby_version,
1730       :ruby_libversion    => @ruby_libversion,
1731     }
1733     write_config_rb @config, config_settings
1735     FileUtils.cp @config, "#{@sourcedir}/core/build_config.rb"
1737     # Write the config file used to build the C++ VM.
1738     Dir.mkdir "machine/gen" unless File.directory? "machine/gen"
1740     vm_paths_h = "machine/paths.h"
1741     File.open vm_paths_h, "wb" do |f|
1742       f.puts <<-EOF
1743 #ifndef RBX_PATHS_H
1744 #define RBX_PATHS_H
1746 #define RBX_PREFIX_PATH      "#{@prefixdir}"
1747 #define RBX_BIN_PATH         "#{@bindir}"
1748 #define RBX_GEMS_PATH        "#{@gemsdir}"
1749 #define RBX_RUNTIME_PATH     "#{@runtimedir}"
1750 #define RBX_CODEDB_PATH      "#{@codedbdir}"
1751 #define RBX_KERNEL_PATH      "#{@coredir}"
1752 #define RBX_CORE_PATH        "#{@coredir}"
1753 #define RBX_LIB_PATH         "#{@libdir}"
1754 #define RBX_ENC_PATH         "#{@encdir}"
1755 #define RBX_HDR_PATH         "#{@includedir}"
1756 #define RBX_SITE_PATH        "#{@sitedir}"
1757 #define RBX_ARCH_PATH        "#{@archdir}"
1758 #define RBX_VENDOR_PATH      "#{@vendordir}"
1760 #endif
1761       EOF
1762     end
1764     vm_config_h = "machine/config.h"
1765     File.open vm_config_h, "wb" do |f|
1766       f.puts <<-EOC
1767 #ifndef RBX_CONFIG
1768 #define RBX_CONFIG
1770 #define RBX_PROGRAM_NAME     "#{@program_name}"
1771 #define RBX_HOST             "#{@host}"
1772 #define RBX_CPU              "#{@cpu}"
1773 #define RBX_VENDOR           "#{@vendor}"
1774 #define RBX_OS               "#{@os}"
1775 #define RBX_RUBY_LIB_VERSION #{@ruby_libversion}
1776 #define RBX_LDSHARED         "#{@ldshared}"
1777 #define RBX_LDSHAREDXX       "#{@ldsharedxx}"
1778 #define RBX_SIZEOF_LONG      #{sizeof("long")}
1779 #define RBX_LLVM_API_VER     #{@llvm_api_version}
1780 #define RBX_LIBC             "#{@libc}"
1781 #define RBX_HAVE_LCHMOD      #{@have_lchmod}
1782 #define RBX_HAVE_LCHOWN      #{@have_lchown}
1783 #define RBX_HAVE_MKFIFO      #{@have_mkfifo}
1784 #define RBX_DEBUG_BUILD      #{@debug_build.inspect}
1785       EOC
1787       if @llvm_version
1788         f.puts "#define RBX_LLVM_VERSION     #{@llvm_version.inspect}"
1789       end
1791       if @little_endian
1792         f.puts "#define RBX_LITTLE_ENDIAN    1"
1793       end
1795       if @tr1
1796         f.puts "#define RBX_HAVE_TR1         1"
1797       end
1799       if @tr1_hash
1800         f.puts "#define RBX_HAVE_TR1_HASH    1"
1801       end
1803       if @gc_stack_check
1804         f.puts "#define RBX_GC_STACK_CHECK   1"
1805       end
1807       if @log_concurrent_update
1808         f.puts "#define RBX_LOG_CONCURRENT_UPDATE 1"
1809       end
1811       if @raise_concurrent_update
1812         f.puts "#define RBX_RAISE_CONCURRENT_UPDATE 1"
1813       end
1815       [:windows, :darwin, :bsd, :linux].each do |platform|
1816         if instance_variable_get(:"@#{platform}")
1817           f.puts "#define RBX_#{platform.to_s.upcase}           1"
1818         end
1819       end
1821       f.puts "#define RBX_DTRACE_CONST     #{@dtrace_const ? "const" : ""}"
1823       write_have_defines f
1825       f.puts <<-EOC
1827 #include "detection.hpp"
1829 #define RBX_STRERROR_BUFSIZE 256
1831 // strerror_r has different signatures on GNU and XSI.
1832 // - The GNU version returns a pointer to a string, which may be the one passed
1833 //   to the function as 'buf', or some immutable static string, in which case
1834 //   'buf' is unused.
1835 // - The XSI version always stores the error message in 'buf' and returns 0 on
1836 //   success.
1837 // This macro makes sure that the error message is returned either way.
1838 #ifdef STRERROR_R_CHAR_P
1839 #define RBX_STRERROR(errno, buf, size) strerror_r(errno, buf, size)
1840 #else
1841 #define RBX_STRERROR(errno, buf, size) (strerror_r(errno, buf, size), buf)
1842 #endif
1844 // Enable this define for some minimal GC debugging
1845 // #define RBX_GC_DEBUG
1847 // Enable for GC stress. This only ensures that the interrupts
1848 // for a GC are set. Use RBX_GC_STRESS_YOUNG and / or RBX_GC_STRESS_MATURE
1849 // to run either the young or mature gen on each possibility
1850 // #define RBX_GC_STRESS
1852 // When stress testing is enabled, forces a young collection every time it
1853 // is possible. This can be useful to flush out bugs because of moving objects.
1854 // #define RBX_GC_STRESS_YOUNG
1856 // When stress testing is enabled, forces a mature collection every time it
1857 // is possible. This can be useful to flush out bugs with reachability etc.
1858 // #define RBX_GC_STRESS_MATURE
1860 #endif
1861       EOC
1862     end
1864     # Write the config file for vendor/oniguruma.
1865     File.open "#{@build_libdir}/oniguruma/config.h", "wb" do |f|
1866       f.puts <<-EOC
1867 /* This file is generated by the Rubinius build system. Your edits
1868  * will be lost. See the configure script.
1869  */
1870       EOC
1872       write_have_defines f
1873       write_have_sizeof_defines f
1874       write_sizeof_defines f
1875     end
1877     # Write release header file.
1878     write_release @vm_release_h
1880     # Write the rubinius-specific C-API config headers.
1881     vm_capi_header_gen = "#{@capi_includedir}/gen"
1882     FileUtils.mkdir_p vm_capi_header_gen
1883     FileUtils.cp vm_config_h, "#{vm_capi_header_gen}/rbx_config.h"
1884     FileUtils.cp @vm_release_h, "#{vm_capi_header_gen}/rbx_release.h"
1886     # Write the config file used in the C-API.
1887     capi_config_h = "#{@capi_includedir}/ruby/config.h"
1888     FileUtils.mkdir_p File.dirname(capi_config_h)
1889     File.open capi_config_h, "wb" do |f|
1890       f.puts <<-EOC
1891 /* This file is generated by the build system. Your edits
1892  * will be lost. See the configure script.
1893  */
1895 #ifndef NORETURN
1896 #define NORETURN(x) __attribute__ ((noreturn)) x
1897 #endif
1899 #ifndef UNREACHABLE
1900 #define UNREACHABLE __builtin_unreachable()
1901 #endif
1903       EOC
1905       write_have_defines f
1906       write_have_sizeof_defines f
1907       write_sizeof_defines f
1909       if @windows
1910         f.puts "#define RBX_WINDOWS 1"
1911       end
1912     end
1913   end
1915   def print_debug
1916     puts "\nUsing the following configuration to build"
1917     puts "------------------------------------------"
1918     cat("build/config/config.rb")
1919     puts "\nSetting the following defines for the VM"
1920     puts "----------------------------------------"
1921     cat("machine/config.h")
1922   end
1924   def cat(file)
1925     puts IO.read(relative_file(file))
1926   end
1928   def relative_file(name)
1929     File.expand_path("../#{name}", __FILE__)
1930   end
1932   def check_force_clean
1933     unless verify_build_signature
1934       @log.write "\nDetected old configuration settings, forcing a clean build"
1935       system("#{build_ruby} -S #{@rake} clean")
1936     end
1937   end
1939   def fetch_gems
1940     @log.write "\nFetching gems..."
1941     failed = false
1943     Dir.chdir @gems_cache do
1944       @gem_files.each do |gem|
1945         next if File.exist? gem
1947         failed = true unless download "https://rubygems.org/gems/#{gem}", "./#{gem}"
1948       end
1949     end
1951     failure "Unable to download required gems." if failed
1952   end
1954   def verify_gems
1955     @log.write "\nVerifying gems..."
1957     failed = false
1959     @gem_files.each do |gem_name|
1960       unless File.file? "#{@gems_cache}/#{gem_name}"
1961         @log.write "unable to find gem #{gem_name}"
1962         failed = true
1963       end
1964     end
1966     failure "Unable to find required gems." if failed
1967   end
1969   def clean_gems(dir, gems)
1970     unpacked = Dir["#{dir}/*"]
1972     # Remove unpacked gems not specified by these configure settings
1973     unpacked.each do |dir|
1974       d = File.basename dir
1975       unless gems.find { |x| d =~ /^#{x}/ } and
1976              @gem_files.find { |x| d =~ /^#{x[0..-5]}/ }
1977         FileUtils.rm_rf dir
1978       end
1979     end
1980   end
1982   def unpack_gems(source, destination, list)
1983     FileUtils.mkdir_p destination unless File.directory? destination
1985     Dir.chdir destination do
1986       list.each do |name|
1987         gem_name = @gem_files.find { |x| x =~ /^#{name}/ }
1988         failure "Unable to find gem to unpack: #{name}" unless gem_name
1990         next if @installed_gems.include? gem_name
1992         unless File.directory? gem_name[0..-5]
1993           system("#{@gem} unpack #{source}/#{gem_name}")
1995           unless $?.exitstatus == 0
1996             failure "Unable to unpack bootstrap gems."
1997           end
1998         end
1999       end
2000     end
2001   end
2003   def setup_gems
2004     @log.write "\nSetting up gems..."
2006     # Remove unpacked gems not specified by these configure settings
2007     clean_gems @bootstrap_gems_dir, @gem_names
2009     # Unpack gems not found for these configure settings
2010     unpack_gems @gems_cache, @bootstrap_gems_dir, @gem_files
2011   end
2013   def setup_codedb
2014     @log.write "\nSetting up CodeDB..."
2016     dir = "#{@builddir}#{@codedbdir}"
2017     codedb_cache = "#{dir}/cache"
2019     unless File.file? codedb_cache
2020       url = "https://rubinius-binaries-rubinius-com.s3.amazonaws.com/codedb/"
2021       cache = "rubinius-codedb-cache"
2022       cache_bzip = "#{cache}.bz2"
2023       cache_digest = "#{cache_bzip}.sha512"
2025       unless File.file? cache_bzip
2026         download "#{url}#{cache_bzip}", cache_bzip
2027       end
2029       unless File.file? cache_digest
2030         download "#{url}#{cache_digest}", cache_digest
2031       end
2033       if Digest::SHA512.file(cache_bzip).hexdigest !=
2034           File.read(cache_digest).strip.split(" ").first
2035         failure "CodeDB cache SHA does not match"
2036       end
2037     end
2038   end
2040   def setup_stdlib
2041     @log.write "\nSetting up stdlib..."
2043     stdlib_cache = "rubinius-stdlib-cache"
2044     cache_bzip = "#{stdlib_cache}.bz2"
2045     cache_digest = "#{cache_bzip}.sha512"
2047     unless File.file? cache_bzip
2048       url = "https://rubinius-binaries-rubinius-com.s3.amazonaws.com/stdlib/"
2050       unless File.file? cache_bzip
2051         download "#{url}#{cache_bzip}", cache_bzip
2052       end
2054       unless File.file? cache_digest
2055         download "#{url}#{cache_digest}", cache_digest
2056       end
2058       if Digest::SHA512.file(cache_bzip).hexdigest !=
2059           File.read(cache_digest).strip.split(" ").first
2060         failure "Stdlib cache SHA does not match"
2061       end
2062     end
2063   end
2065   def setup_codetools
2066     @log.write "\nSetting up codetools..."
2068     codetools_cache = "rubinius-codetools-cache"
2069     cache_bzip = "#{codetools_cache}.bz2"
2070     cache_digest = "#{cache_bzip}.sha512"
2072     unless File.file? cache_bzip
2073       url = "https://rubinius-binaries-rubinius-com.s3.amazonaws.com/codetools/"
2075       unless File.file? cache_bzip
2076         download "#{url}#{cache_bzip}", cache_bzip
2077       end
2079       unless File.file? cache_digest
2080         download "#{url}#{cache_digest}", cache_digest
2081       end
2083       if Digest::SHA512.file(cache_bzip).hexdigest !=
2084           File.read(cache_digest).strip.split(" ").first
2085         failure "Codetools cache SHA does not match"
2086       end
2087     end
2088   end
2090   # Create directories that don't have to be created by the end user
2091   # themselves.
2092   def create_directories
2093     FileUtils.mkdir_p @gems_cache
2094   end
2096   def run
2097     unless ENV["RBX_SUPRESS_DEPRECATION"]
2098       @log.deprecated "\n\n'configure' is deprecated and will be removed in the future.\n" \
2099         "Use 'build.sh' to configure, build, package, and install Rubinius.\n\n\n"
2100     end
2102     options
2103     set_host
2104     parse ARGV
2105     detect_homebrew_openssl_lib
2106     create_directories
2107     check_tools
2108     check_force_clean
2110     set_filesystem_paths
2112     process
2114     unless sizeof("long") == 8
2115       failure "Support for non-64bit platforms was deprecated 1 Jun 2016 and has now been removed. If non-64bit support is a critical feature for your application, please email contact@rubinius.com"
2116     end
2118     if @release_build
2119       verify_gems
2120     else
2121       fetch_gems
2122     end
2123     setup_gems
2124     setup_codedb
2125     setup_codetools
2126     setup_stdlib
2127     write_configure_files
2128     write_build_signature
2130     return if @release_config
2132     print_debug if @verbose
2134     if @llvm_source_build
2135       files = prebuilt_files.map { |f| File.basename f, ".tar.bz2" }.join("\n  ")
2137       @log.write <<-EOM
2139 ------------------------------------------------------------------
2140 Unable to find an existing binary build of LLVM for your platform.
2142 Please notify the Rubinius team at the #rubinius channel on
2143 irc.freenode.net and provide the following system information:
2145   prebuilts:
2147   #{files}
2148 ------------------------------------------------------------------
2149       EOM
2150     end
2152     unless @builddir
2153       build_msg = <<-EOM
2154 Rubinius (#{release_revision.last[0, 8]}) has been configured.
2156 Run 'rake' to build and test Rubinius.
2157       EOM
2158     else
2159       build_msg = <<-EOM
2160 Rubinius (#{release_revision.last[0, 8]}) has been configured for the following paths:
2162 prefix:     #{@prefixdir}
2163 bin:        #{@prefixdir}#{@bindir}
2164 lib:        #{@prefixdir}#{@libdir}
2165 include:    #{@prefixdir}#{@includedir}
2166 codedb:     #{@prefixdir}#{@codedbdir}
2167 site:       #{@prefixdir}#{@sitedir}
2168 arch:       #{@prefixdir}#{@archdir}
2169 vendor:     #{@prefixdir}#{@vendordir}
2170 man:        #{@prefixdir}#{@mandir}
2171 gems:       #{@prefixdir}#{@gemsdir}
2172 gems cache: #{@gems_cache}
2174 Run 'rake' to build, test and install Rubinius.
2175       EOM
2176     end
2178     links = (@bin_links + [@program_name]).uniq
2180     @log.write <<-EOM
2181 ------------------------------------------------------------------
2183 #{build_msg}
2184 After building, you may add
2186 '#{@prefixdir}#{@bindir}'
2188 to your PATH or run commands directly from that directory.
2190 Available commands are:
2192   #{links.join(", ")}
2194 ------------------------------------------------------------------
2195     EOM
2196   end
2199   # Configuration item that has both a default and a configured value
2200   class ConfigurationToggle
2201     attr_reader :default, :configured
2203     def initialize(default_value)
2204       @default = !!default_value
2205       @configured = nil
2206     end
2208     def configured=(value)
2209       @configured = !!value
2210     end
2212     def value
2213       unless @configured.nil?
2214         @configured
2215       else
2216         @default
2217       end
2218     end
2219   end
2221   # Handles user output and logging while running configure.
2222   class Logger
2223     attr_reader :path
2225     # Creates an instance of Logger writing to +file+.
2226     def initialize(file, init=true)
2227       @path = File.expand_path("../#{file}", __FILE__)
2228       if init
2229         File.open(@path, "wb") { }
2230         log "Configuring Rubinius..."
2231       end
2232     end
2234     # Copies the contents of +other+ into this logger's file.
2235     def replace(other)
2236       output do |f|
2237         f.puts File.read(other)
2238       end
2239     end
2241     # Writes +message+ to the logging file but not to the screen.
2242     def log(message, error=false)
2243       output do |f|
2244         stamp = "#{timestamp}#{'*** ERROR' if error}"
2245         if multiline?(message)
2246           f.puts "#{stamp} ---"
2247           f.puts message
2248           f.puts "---"
2249         else
2250           f.puts "#{stamp} #{message}"
2251         end
2252       end
2253     end
2255     # Writes a normal message to STDOUT and logs to the file.
2256     def write(message)
2257       log message
2258       STDOUT.puts message
2259     end
2261     # Writes a normal message to STDOUT with #print and logs to file.
2262     def print(message)
2263       log message
2264       STDOUT.print message
2265     end
2267     # Writes an error message to STDERR and logs to the file with
2268     # error decorations. This should only be used for errors that
2269     # affect configure itself.
2270     def error(message)
2271       log message, true
2272       STDERR.puts message
2273     end
2275     DEPRECATION_HEADER =
2276       "------------------------------ Deprecation notice ------------------------------"
2277     DEPRECATION_FOOTER =
2278       "--------------------------------------------------------------------------------"
2280     def deprecated(message)
2281       log DEPRECATION_HEADER, true
2282       log message, true
2283       log DEPRECATION_FOOTER, true
2285       STDERR.puts DEPRECATION_HEADER
2286       STDERR.puts message
2287       STDERR.puts DEPRECATION_FOOTER
2288     end
2290     # Yields an IO for writing log messages.
2291     def output
2292       File.open @path, "a" do |f|
2293         yield f
2294       end
2295     end
2297     # Returns a formatted times suitable for logging.
2298     def timestamp
2299       Time.now.strftime "[%Y-%m-%d %H:%M:%S]"
2300     end
2302     # Returns true if the message has more than one line.
2303     def multiline?(message)
2304       message.index("\n") != nil
2305     end
2306   end
2308   # Returns true if the *port* command is in the PATH and identifies
2309   # itself with "MacPorts" when run interactively.
2310   def macports?
2311     `echo quit | port 2>&-`.start_with? 'MacPorts'
2312   end
2314   # Query MacPorts for the path to the latest installed version of
2315   # llvm-config that is within the range of supported LLVM versions.
2316   def macports_llvm_config
2317     supported_versions = (3.6..3.9)
2318     installed_ports    = `port installed | egrep -o 'llvm-[^ ]+'`.split
2319     latest_usable_port = installed_ports.sort.select do |fname|
2320                            version = fname.match(/-\K.*/)[0].to_f
2321                            supported_versions.include? version
2322                          end.last
2323     avail_binaries     = `port contents #{latest_usable_port} |
2324                           fgrep llvm-config`.split
2325     avail_binaries.reject { |fname| fname.include? 'libexec' }.last
2326   end
2328   def brew(args)
2329     `brew #{args}`.chomp
2330   end
2332   # Returns true if the *brew* command is in the PATH and identifies
2333   # itself with "Homebrew" when run interactively with -v argument.
2334   def homebrew?
2335     brew("-v 2>&1").start_with? 'Homebrew'
2336   end
2338   # Check if latest version of openssl is installed; if so, add to include
2339   # and libs
2340   def detect_homebrew_openssl_lib
2341     if @darwin && homebrew? && (brew("list").split("\n").include? "openssl")
2342       prefix = brew("--prefix openssl")
2344       ENV["OPENSSL_DIR"] = prefix unless ENV["OPENSSL_DIR"]
2345       add_opt_dir prefix
2346     end
2347   end
2351 STDOUT.sync = true
2352 Configure.new(root).run