pkg
*.o
*.so
+.configure_state.yaml
-require "library"
require "rake/clean"
require "rake/testtask"
require "rake/rdoctask"
require "rake/contrib/compositepublisher"
require "rake/contrib/sshpublisher"
+require "rake/configuretask"
+require "rake/extensiontask"
+
PKG_NAME = "ruby-ecore"
-CLOBBER.include("src/*/*.{s,}o")
+modules = [:ecore, :ecore_evas, :ecore_job, :ecore_x, :ecore_con]
+module_objects = {
+ :ecore => [
+ :rb_ecore, :rb_animator, :rb_event_handler, :rb_fd_handler,
+ :rb_idle_enterer, :rb_idler, :rb_timer
+ ],
+ :ecore_evas => [
+ :rb_ecore_evas_main, :rb_ecore_evas,
+ :rb_buffer, :rb_fb, :rb_gl_x11,
+ :rb_software_x11, :rb_xrender_x11
+ ],
+ :ecore_job => [:rb_ecore_job, :rb_job],
+ :ecore_x => [:rb_ecore_x, :rb_window, :rb_cursor],
+ :ecore_con => [:rb_ecore_con, :rb_server]
+}
+
+module_tasks = modules.map { |mod| "ext_#{mod}".to_sym }
+
+task :default => module_tasks
+
+config = Rake::ConfigureTask.new do |t|
+ modules.each do |mod|
+ d = mod.to_s.sub(/ecore_/, "ecore-")
+ t.tests << Rake::ConfigureTask::
+ PkgConfigTest.new(d, :is_critical => true)
+ end
+end
-ext_libs = %w{ecore ecore_evas ecore_job ecore_x ecore_con}.map do |lib|
- Library.new(lib, ENV["ECORE_PREFIX"] || "/usr/local", "src/#{lib}")
+module_tasks.each do |mt|
+ task mt => ["pre_#{mt}".to_sym]
end
-task :default => ext_libs.map { |l| l.so }
+exts = {}
+
+modules.each_with_index do |mod, i|
+ name = module_tasks[i]
-ext_libs.each do |lib|
- file lib.so => lib.objects do |t|
- sh "cc #{lib.ldflags} #{t.prerequisites.join(" ")} -o #{t.name}"
+ exts[mod] = Rake::ExtensionTask.new name => module_objects[mod] do |t|
+ t.dir = "src/#{mod}"
+ t.lib_name = "#{t.dir}/#{mod}.so"
end
- lib.objects.each do |object|
- file object => object.sub(/\.[^.]+$/, ".c") do |t|
- sh "cc #{lib.cflags} #{t.prerequisites.first} " +
- "-c -o #{t.name}"
- end
+ task "pre_#{name}".to_sym => [:configure] do
+ d = mod.to_s.sub(/ecore_/, "ecore-")
+ exts[mod].link_libs << config[d].libs
+
+ cflags = [
+ exts[mod].env[:cflags],
+ config[d].cflags
+ ]
+
+ exts[mod].env.update(:cflags => cflags)
end
end
-task :install => ext_libs.map { |l| l.so } do |t|
+task :install => module_tasks do |t|
destdir = "#{ENV["DESTDIR"]}"
sitearchdir = ENV["RUBYARCHDIR"] || Config::CONFIG['sitearchdir']
ddir = destdir + sitearchdir
FileUtils::Verbose.mkdir_p(ddir) unless File.directory?(ddir)
- FileUtils::Verbose.install(t.prerequisites.to_a, ddir,
- :mode => 0755)
+
+ sos = exts.values.map { |e| e.lib_name }
+ FileUtils::Verbose.install(sos, ddir, :mode => 0755)
ddir = destdir + sitearchdir + "/ecore"
FileUtils::Verbose.mkdir_p(ddir) unless File.directory?(ddir)
+++ /dev/null
-require "rbconfig"
-
-class Library
- def initialize(name, prefix, dir, lib = name)
- @name = name
- @prefix = prefix
- @dir = dir
- @lib = lib
- end
-
- def ldflags
- s = "#{ENV["LDFLAGS"]} -L #{@prefix}/lib -L " +
- Config::CONFIG["libdir"] + " " +
- Config::CONFIG["LIBRUBYARG_SHARED"] +
- " -shared -Wl"
- s << " -l#{@lib}" unless @lib.nil?
- s
- end
-
- def cflags
- "#{ENV["CFLAGS"]} -I #{@prefix}/include " +
- "-I #{Config::CONFIG['archdir']} " +
- "-I #{Config::CONFIG['sitearchdir']} -fPIC"
- end
-
- def so
- "#{@dir}/#{@name}.so"
- end
-
- def objects
- Dir["#{@dir}/*.c"].map { |f| f.sub(/\.[^.]+$/, ".o") }
- end
-end
--- /dev/null
+#
+# 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
--- /dev/null
+require 'rake'
+require 'rake/clean'
+require 'rake/tasklib'
+
+module Rake
+
+ # Create a build task that will generate a Ruby extension (e.g. .so) from one or more
+ # C (.c) or C++ (.cc, .cpp, .cxx) files, and is intended as a replcaement for mkmf.
+ # It determines platform-specific settings (e.g. file extensions, compiler flags, etc.)
+ # from rbconfig (note: examples assume *nix file extensions).
+ #
+ # *Note*: Strings vs Symbols
+ # In places where filenames are expected (e.g. lib_name and objs), Strings are used
+ # as verbatim filenames, while, Symbols have the platform-dependant extension
+ # appended (e.g. '.so' for libraries and '.o' for objects). Also, only Symbols
+ # have #dir prepended to them.
+ #
+ # Example:
+ # desc "build sample extension"
+ # # build sample.so (from foo.{c,cc,cxx,cpp}, through foo.o)
+ # Rake::ExtensionTask.new :sample => :foo do |t|
+ # # all extension files under this directory
+ # t.dir = 'ext'
+ # # link libraries (libbar.so)
+ # t.link_libs << 'bar'
+ # end
+ #
+ # Author:: Steve Sloan (mailto:steve@finagle.org)
+ # Copyright:: Copyright (c) 2006 Steve Sloan
+ # License:: GPL
+
+ class ExtensionTask < Rake::TaskLib
+ # The name of the extension
+ attr_accessor :name
+
+ # The filename of the extension library file (e.g. 'extension.so')
+ attr_accessor :lib_name
+
+ # Object files to build and link into the extension.
+ attr_accessor :objs
+
+ # The directory where the extension files (source, output, and
+ # intermediate) are stored.
+ attr_accessor :dir
+
+ # Environment configuration -- i.e. CONFIG from rbconfig, with a few other
+ # settings, and converted to lowercase-symbols.
+ attr_accessor :env
+
+ # Additional link libraries
+ attr_accessor :link_libs
+
+ # Same arguments as Rake::define_task
+ def initialize( args, &blk )
+ @env = @@DefaultEnv.dup
+ @name, @objs = resolve_args(args)
+ set_defaults
+ yield self if block_given?
+ define_tasks
+ end
+
+ # Generate default values. This is called from initialize _before_ the
+ # yield block.
+ #
+ # Defaults:
+ # - lib_name: name.so
+ # - objs: name.o (<- name.{c,cxx,cpp,cc})
+ # - dir: .
+ # - link_libs: <none>
+ def set_defaults
+ @lib_name ||= name.to_sym
+ @objs ||= [name.to_sym]
+ @dir ||= '.'
+ @link_libs ||= []
+ end
+
+ # Defines the library task.
+ def define_tasks
+ output_objs = @objs.collect { |obj| filepath obj, :objext }
+ output_lib = filepath lib_name, :dlext
+
+ task name => output_lib
+
+ file output_lib => output_objs do |t|
+ sh_cmd :ldshared, :dldflags, :ldflags,
+ {'-L' => :libdirs}, '-o', output_lib,
+ output_objs.join(' '),
+ link_libs.join(' '),
+ :libs, :dldlibs, :librubyarg_shared
+ end
+
+ CLEAN.include output_objs
+ CLOBBER.include output_lib
+ define_rules
+ end
+
+ # Defines C and C++ source-to-object rules, using the source extensions from env.
+ def define_rules
+ for ext in env[:c_exts]
+ Rake::Task.create_rule '.'+env[:objext] => '.'+ext do |r|
+ sh_cmd :cc, :cflags, :cppflags, {'-D' => :defines}, {'-I' => :includedirs}, {'-I' => :topdir}, {'-I' => :sitearchdir},
+ '-c', '-o', r.name, r.sources
+ end
+ end
+
+ for ext in env[:cpp_exts]
+ Rake::Task.create_rule '.'+env[:objext] => '.'+ext do |r|
+ sh_cmd :cxx, :cxxflags, :cppflags, {'-D' => :defines}, {'-I' => :includedirs}, {'-I' => :topdir}, {'-I' => :sitearchdir},
+ '-o', r.name, '-c', r.sources
+ end
+ end
+ end
+
+ class << self
+ # The default environment for all extensions.
+ @@DefaultEnv = {}
+ def env
+ @@DefaultEnv
+ end
+ def env=(e)
+ @@DefaultEnv = e
+ end
+
+ Config::CONFIG.merge(ENV).each { |k, v| @@DefaultEnv[k.downcase.to_sym] = v }
+ @@DefaultEnv = {
+ :cxx => 'c++',
+ :cxxflags => '',
+ :c_exts => ['c'],
+ :cpp_exts => ['cc', 'cxx', 'cpp'],
+ :includedirs => [],
+ :libdirs => [],
+ }.update(@@DefaultEnv)
+ end
+
+ protected
+
+ # Handles convenience filenames:
+ # * f (String) => f
+ # * f (Symbol) => dir/f.ext
+ def filepath( f, ext )
+ ext = env[ext] if Symbol === ext
+ Symbol === f ? File.join( dir, "#{f}.#{ext}" ) : f
+ end
+
+ # Convenience function for cnstructing command lines for build tools.
+ def optify( *opts )
+ return optify(*opts.first) if opts.size == 1 and opts.first.kind_of? Array
+ opts.collect do |opt|
+ case opt
+ when String then opt
+ when Symbol then optify env[opt]
+ when Hash
+ opt.collect do |k, v|
+ v = env[v] if v.kind_of? Symbol
+ if v.kind_of? Array
+ optify v.collect { |w| k.to_s + w.to_s }
+ elsif v
+ k.to_s + v.to_s
+ end
+ end
+ else
+ opt.to_s
+ end
+ end.join(' ').squeeze(' ')
+ end
+
+ def sh_cmd( cmd, *opts )
+ sh optify( cmd, *opts )
+ end
+
+ # For some reason, Rake::TaskManager.resolve_args can't be found, so snarf it.
+ def resolve_args(args)
+ case args
+ when Hash
+ fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
+ fail "No Task Name Given" if args.size < 1
+ task_name = args.keys[0]
+ deps = args[task_name]
+ deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
+ else
+ task_name = args
+ deps = []
+ end
+ [task_name, deps]
+ end
+
+ end
+
+end