# # Copyright (c) 2005, 2006 Tilman Sauerbeck (tilman at code-monkey de) # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require "rake/tasklib" require "rake/clean" require "yaml" require "fileutils" module Rake class ConfigureTask < TaskLib CACHE_FILE = ".configure_state.yaml" attr_reader :tests def initialize # :yield: self @tests = TestList.new(load_tests || []) yield self if block_given? define end # returns the test with the specified name def [](name) @tests.find { |t| t.name == name } end def method_missing(m) self[m.to_s] end private def load_tests r = YAML.load(File.read(CACHE_FILE)) rescue nil r.is_a?(TestList) ? r : nil end def define desc "Remove configure results" task :clobber_configure do FileUtils::Verbose.rm_f(CACHE_FILE) end task :clobber => :clobber_configure desc "Configure this package" task :configure => [CACHE_FILE] file CACHE_FILE do @tests.each do |t| t.on_checking.reverse_each { |b| b.call } if t.invoke t.on_success.reverse_each { |b| b.call } else t.on_failure.reverse_each { |b| b.call } end end # store the test results in CACHE_FILE File.open(CACHE_FILE, "w") { |f| YAML.dump(@tests, f) } end end class TestList < Array def initialize(stored_tests) @stored_tests = stored_tests end def <<(arg) assign_result(arg) super end def push(*args) args.each { |a| assign_result(a) } super end def unshift(arg) assign_result(arg) super end private def assign_result(test) st = @stored_tests.find { |st| st.name == test.name } test.result = st.result unless st.nil? end end class Test attr_reader :name, :on_checking, :on_success, :on_failure attr_accessor :result def initialize(name, opts = {}) # :yield: self @name = name @opts = opts @result = nil @on_checking = [] @on_success = [] @on_failure = [] if opts[:is_critical] @on_failure << lambda { raise } end yield self if block_given? end def to_yaml_properties ["@name", "@result"] end def invoke end protected def can_exec_binary?(bin) fork do STDOUT.reopen("/dev/null") STDERR.reopen("/dev/null") begin exec(bin) rescue SystemCallError exit 255 end end Process.wait $?.exitstatus != 255 end end class FooConfigTest < Test def initialize(name, opts = {}) super @result = {} @command = "#{name}-config" @on_checking << lambda do print "checking for #{name}... " STDOUT.flush end @on_success << lambda { puts "yes (#{version})" } @on_failure << lambda { puts "no" } end def method_missing(m) @result[m] end def invoke return false unless can_exec_command? begin [:version, :cflags, :libs].each do |f| @result[f] = lookup_flags(f) end rescue Exception @result.clear end !@result.empty? end protected def lookup_flags(f) tmp = `#{@command} --#{f}`.strip raise unless $?.exitstatus.zero? tmp end private def can_exec_command? can_exec_binary?(@command) end end class PkgConfigTest < FooConfigTest def initialize(name, opts = {}) super @command = "pkg-config" end protected def lookup_flags(f) f = :modversion if f == :version tmp = `#{@command} --silence-errors --#{f} #{@name}`. strip.tr("\n", "/") raise unless $?.exitstatus.zero? tmp end end class CompileTest < Test TMP_FILE = ".compile_test" def CompileTest.cflags @@cflags end def CompileTest.cflags=(f) @@cflags = f end def initialize(name, code, opts = {}) super(name, opts) @code = code end def invoke @result = false cc = ENV["CC"] || "cc" flags = (ENV["CFLAGS"] || "").dup flags << " -I" + Config::CONFIG["archdir"] unless @opts[:try_link] flags << " -c" end File.open(TMP_FILE + ".c", "w") do |f| f << @code << "\n" end `#{cc} #{flags} #{TMP_FILE}.c -o #{TMP_FILE}.o > /dev/null 2>&1` @result = $?.exitstatus.zero? ensure FileUtils.rm_f("#{TMP_FILE}.c") FileUtils.rm_f("#{TMP_FILE}.o") end end class HaveFuncTest < CompileTest def initialize(name, includes = [], opts = {}) super(name, assemble_code(name, includes), opts) @on_checking << lambda do print "checking for #{name}... " STDOUT.flush end @on_success << lambda { puts "yes" } @on_failure << lambda { puts "no" } end private def assemble_code(func, includes) header = includes.inject("") do |a, h| a << "#include <#{h}>\n" end body =<