blob: f5d4374d17d6b54582c4a27387193c14b9bb2f32 [file] [log] [blame]
load "#{MRUBY_ROOT}/tasks/mruby_build_gem.rake"
load "#{MRUBY_ROOT}/tasks/mruby_build_commands.rake"
module MRuby
class << self
def targets
@targets ||= {}
end
def each_target(&block)
return to_enum(:each_target) if block.nil?
@targets.each do |key, target|
target.instance_eval(&block)
end
end
end
class Toolchain
class << self
attr_accessor :toolchains
end
def initialize(name, &block)
@name, @initializer = name.to_s, block
MRuby::Toolchain.toolchains ||= {}
MRuby::Toolchain.toolchains[@name] = self
end
def setup(conf,params={})
conf.instance_exec(conf, params, &@initializer)
end
def self.load
Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file|
Kernel.load file
end
end
end
Toolchain.load
class Build
class << self
attr_accessor :current
end
include Rake::DSL
include LoadGems
attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
attr_reader :libmruby, :gems, :toolchains
attr_writer :enable_bintest, :enable_test
COMPILERS = %w(cc cxx objc asm)
COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
attr_block MRuby::Build::COMMANDS
Exts = Struct.new(:object, :executable, :library)
def initialize(name='host', build_dir=nil, &block)
@name = name.to_s
unless MRuby.targets[@name]
if ENV['OS'] == 'Windows_NT'
@exts = Exts.new('.o', '.exe', '.a')
else
@exts = Exts.new('.o', '', '.a')
end
build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build"
@file_separator = '/'
@build_dir = "#{build_dir}/#{@name}"
@gem_clone_dir = "#{build_dir}/mrbgems"
@cc = Command::Compiler.new(self, %w(.c))
@cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp))
@objc = Command::Compiler.new(self, %w(.m))
@asm = Command::Compiler.new(self, %w(.S .asm))
@linker = Command::Linker.new(self)
@archiver = Command::Archiver.new(self)
@yacc = Command::Yacc.new(self)
@gperf = Command::Gperf.new(self)
@git = Command::Git.new(self)
@mrbc = Command::Mrbc.new(self)
@bins = []
@gems, @libmruby = MRuby::Gem::List.new, []
@build_mrbtest_lib_only = false
@cxx_exception_enabled = false
@cxx_exception_disabled = false
@cxx_abi_enabled = false
@enable_bintest = false
@enable_test = false
@toolchains = []
MRuby.targets[@name] = self
end
MRuby::Build.current = MRuby.targets[@name]
MRuby.targets[@name].instance_eval(&block)
build_mrbc_exec if name == 'host'
build_mrbtest if test_enabled?
end
def enable_debug
compilers.each do |c|
c.defines += %w(MRB_DEBUG)
if toolchains.any? { |toolchain| toolchain == "gcc" }
c.flags += %w(-g3 -O0)
end
end
@mrbc.compile_options += ' -g'
end
def disable_cxx_exception
if @cxx_exception_enabled or @cxx_abi_enabled
raise "cxx_exception already enabled"
end
@cxx_exception_disabled = true
end
def enable_cxx_exception
return if @cxx_exception_enabled
return if @cxx_abi_enabled
if @cxx_exception_disabled
raise "cxx_exception disabled"
end
@cxx_exception_enabled = true
compilers.each { |c|
c.defines += %w(MRB_ENABLE_CXX_EXCEPTION)
c.flags << c.cxx_exception_flag
}
linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
end
def cxx_exception_enabled?
@cxx_exception_enabled
end
def cxx_abi_enabled?
@cxx_abi_enabled
end
def enable_cxx_abi
return if @cxx_abi_enabled
if @cxx_exception_enabled
raise "cxx_exception already enabled"
end
compilers.each { |c|
c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI)
c.flags << c.cxx_compile_flag
}
compilers.each { |c| c.flags << c.cxx_compile_flag }
linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
@cxx_abi_enabled = true
end
def compile_as_cxx src, cxx_src, obj = nil, includes = []
src = File.absolute_path src
cxx_src = File.absolute_path cxx_src
obj = objfile(cxx_src) if obj.nil?
file cxx_src => [src, __FILE__] do |t|
FileUtils.mkdir_p File.dirname t.name
IO.write t.name, <<EOS
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#ifndef MRB_ENABLE_CXX_ABI
extern "C" {
#endif
#include "#{src}"
#ifndef MRB_ENABLE_CXX_ABI
}
#endif
EOS
end
file obj => cxx_src do |t|
cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes
end
obj
end
def enable_bintest
@enable_bintest = true
end
def bintest_enabled?
@enable_bintest
end
def toolchain(name, params={})
tc = Toolchain.toolchains[name.to_s]
fail "Unknown #{name} toolchain" unless tc
tc.setup(self, params)
@toolchains.unshift name.to_s
end
def primary_toolchain
@toolchains.first
end
def root
MRUBY_ROOT
end
def enable_test
@enable_test = true
end
def test_enabled?
@enable_test
end
def build_mrbtest
gem :core => 'mruby-test'
end
def build_mrbc_exec
gem :core => 'mruby-bin-mrbc'
end
def mrbcfile
return @mrbcfile if @mrbcfile
mrbc_build = MRuby.targets['host']
gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' }
@mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc")
end
def compilers
COMPILERS.map do |c|
instance_variable_get("@#{c}")
end
end
def define_rules
compilers.each do |compiler|
if respond_to?(:enable_gems?) && enable_gems?
compiler.defines -= %w(DISABLE_GEMS)
else
compiler.defines += %w(DISABLE_GEMS)
end
compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..'))
end
end
def filename(name)
if name.is_a?(Array)
name.flatten.map { |n| filename(n) }
else
'"%s"' % name.gsub('/', file_separator)
end
end
def cygwin_filename(name)
if name.is_a?(Array)
name.flatten.map { |n| cygwin_filename(n) }
else
'"%s"' % `cygpath -w "#{filename(name)}"`.strip
end
end
def exefile(name)
if name.is_a?(Array)
name.flatten.map { |n| exefile(n) }
else
"#{name}#{exts.executable}"
end
end
def objfile(name)
if name.is_a?(Array)
name.flatten.map { |n| objfile(n) }
else
"#{name}#{exts.object}"
end
end
def libfile(name)
if name.is_a?(Array)
name.flatten.map { |n| libfile(n) }
else
"#{name}#{exts.library}"
end
end
def build_mrbtest_lib_only
@build_mrbtest_lib_only = true
end
def build_mrbtest_lib_only?
@build_mrbtest_lib_only
end
def run_test
puts ">>> Test #{name} <<<"
mrbtest = exefile("#{build_dir}/bin/mrbtest")
sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}"
puts
run_bintest if bintest_enabled?
end
def run_bintest
targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
targets << filename(".") if File.directory? "./bintest"
sh "ruby test/bintest.rb #{targets.join ' '}"
end
def print_build_summary
puts "================================================"
puts " Config Name: #{@name}"
puts " Output Directory: #{self.build_dir.relative_path}"
puts " Binaries: #{@bins.join(', ')}" unless @bins.empty?
unless @gems.empty?
puts " Included Gems:"
@gems.map do |gem|
gem_version = " - #{gem.version}" if gem.version != '0.0.0'
gem_summary = " - #{gem.summary}" if gem.summary
puts " #{gem.name}#{gem_version}#{gem_summary}"
puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty?
end
end
puts "================================================"
puts
end
end # Build
class CrossBuild < Build
attr_block %w(test_runner)
# cross compiling targets for building native extensions.
# host - arch of where the built binary will run
# build - arch of the machine building the binary
attr_accessor :host_target, :build_target
def initialize(name, build_dir=nil, &block)
@test_runner = Command::CrossTestRunner.new(self)
super
end
def mrbcfile
MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc")
end
def run_test
mrbtest = exefile("#{build_dir}/bin/mrbtest")
if (@test_runner.command == nil)
puts "You should run #{mrbtest} on target device."
puts
else
@test_runner.run(mrbtest)
end
end
end # CrossBuild
end # MRuby