[Rake-devel] Rake Configuretask

Tilman Sauerbeck tilman at code-monkey.de
Mon May 23 14:16:19 EDT 2005


Hi guys,
I started work on a configuretask recently, which is to provide the
functionality of autoconf/mkmf, somewhat.

So far there is very little functionality, but before implementing more
tests etc I'd like to get some input from other Rake users.
What do you think about the API in general, any other ideas on the
design? :)

Regards,
Tilman

-- 
learn to quote: http://www.netmeister.org/news/learn2quote.html
-------------- next part --------------
require "rake/tasklib"
require "rake/clean"
require "yaml"
require "tempfile"

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 = nil

			if File.exist?(CACHE_FILE)
				File.open(CACHE_FILE, "r") { |f| r = YAML.load(f) }
			end

			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
			# stored_tests needs to be optional because of a bug in
			# Ruby 1.8's YAML implementation.
			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.to_str.strip.dup

				@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
					tf = Tempfile.open("configuretask")
					STDOUT.reopen(tf)
					STDERR.reopen(tf)

					begin
						exec(bin)
					rescue SystemCallError
						exit 0xb00bface
					end
				end

				Process.wait

				$?.exitstatus != 0xb00bface
			end
		end

		class FooConfigTest < Test
			def initialize(name, opts = {})
				super

				@result = {}
				@command = "#{name}-config"

				@on_checking << lambda { print "Checking for #{name}... " }
				@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?

				[:version, :cflags, :libs].each do |f|
					@result[f] = lookup_flags(f)
				end

				true
			end

			protected
			def lookup_flags(f)
				tmp = `#{@command} --#{f}`.strip
				$?.exitstatus.zero? ? tmp : nil
			end

			private
			def can_exec_command?
				can_exec_binary?(@command)
			end
		end

		class PkgConfigTest < FooConfigTest
			def initialize(name, opts = {})
				super

				@command = "pkg-config --silence-errors"
			end

			protected
			def lookup_flags(f)
				f = :modversion if f == :version

				tmp = `#{@command} --#{f} #{@name}`.strip
				$?.exitstatus.zero? ? tmp : nil
			end
		end
	end
end


More information about the Rake-devel mailing list