2 # Copyright (c) 2005, 2006 Tilman Sauerbeck (tilman at code-monkey de)
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
12 # The above copyright notice and this permission notice shall be
13 # included in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 require "rake/tasklib"
29 class ConfigureTask < TaskLib
30 CACHE_FILE = ".configure_state.yaml"
34 def initialize # :yield: self
35 @tests = TestList.new(load_tests || [])
37 yield self if block_given?
42 # returns the test with the specified name
44 @tests.find { |t| t.name == name }
53 r = YAML.load(File.read(CACHE_FILE)) rescue nil
55 r.is_a?(TestList) ? r : nil
59 desc "Remove configure results"
60 task :clobber_configure do
61 FileUtils::Verbose.rm_f(CACHE_FILE)
64 task :clobber => :clobber_configure
66 desc "Configure this package"
67 task :configure => [CACHE_FILE]
71 t.on_checking.reverse_each { |b| b.call }
74 t.on_success.reverse_each { |b| b.call }
76 t.on_failure.reverse_each { |b| b.call }
80 # store the test results in CACHE_FILE
81 File.open(CACHE_FILE, "w") { |f| YAML.dump(@tests, f) }
85 class TestList < Array
86 def initialize(stored_tests)
87 @stored_tests = stored_tests
96 args.each { |a| assign_result(a) }
106 def assign_result(test)
107 st = @stored_tests.find { |st| st.name == test.name }
108 test.result = st.result unless st.nil?
113 attr_reader :name, :on_checking, :on_success, :on_failure
114 attr_accessor :result
116 def initialize(name, opts = {}) # :yield: self
125 if opts[:is_critical]
126 @on_failure << lambda { raise }
129 yield self if block_given?
132 def to_yaml_properties
140 def can_exec_binary?(bin)
142 STDOUT.reopen("/dev/null")
143 STDERR.reopen("/dev/null")
147 rescue SystemCallError
158 class FooConfigTest < Test
159 def initialize(name, opts = {})
163 @command = "#{name}-config"
165 @on_checking << lambda do
166 print "checking for #{name}... "
170 @on_success << lambda { puts "yes (#{version})" }
171 @on_failure << lambda { puts "no" }
174 def method_missing(m)
179 return false unless can_exec_command?
182 [:version, :cflags, :libs].each do |f|
183 @result[f] = lookup_flags(f)
194 tmp = `#{@command} --#{f}`.strip
196 raise unless $?.exitstatus.zero?
201 def can_exec_command?
202 can_exec_binary?(@command)
206 class PkgConfigTest < FooConfigTest
207 def initialize(name, opts = {})
210 @command = "pkg-config"
215 f = :modversion if f == :version
217 tmp = `#{@command} --silence-errors --#{f} #{@name}`.
220 raise unless $?.exitstatus.zero?
225 class CompileTest < Test
226 TMP_FILE = ".compile_test"
228 def CompileTest.cflags
232 def CompileTest.cflags=(f)
236 def initialize(name, code, opts = {})
245 cc = ENV["CC"] || "cc"
246 flags = (ENV["CFLAGS"] || "").dup
247 flags << " -I" + Config::CONFIG["archdir"]
249 unless @opts[:try_link]
253 File.open(TMP_FILE + ".c", "w") do |f|
257 `#{cc} #{flags} #{TMP_FILE}.c -o #{TMP_FILE}.o > /dev/null 2>&1`
258 @result = $?.exitstatus.zero?
260 FileUtils.rm_f("#{TMP_FILE}.c")
261 FileUtils.rm_f("#{TMP_FILE}.o")
265 class HaveFuncTest < CompileTest
266 def initialize(name, includes = [], opts = {})
267 super(name, assemble_code(name, includes), opts)
269 @on_checking << lambda do
270 print "checking for #{name}... "
274 @on_success << lambda { puts "yes" }
275 @on_failure << lambda { puts "no" }
279 def assemble_code(func, includes)
280 header = includes.inject("") do |a, h|
281 a << "#include <#{h}>\n"
286 void *foo = (void *) #{func};