Use a configure task and extension tasks to build ruby-ecore.
[ruby-ecore.git] / rake / configuretask.rb
diff --git a/rake/configuretask.rb b/rake/configuretask.rb
new file mode 100644 (file)
index 0000000..b673c84
--- /dev/null
@@ -0,0 +1,296 @@
+#
+# 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 =<<EOF
+int main () {
+       void *foo = (void *) #{func};
+       foo = (void *) 0;
+       return 0;
+}
+EOF
+
+                               header + body
+                       end
+               end
+       end
+end