1 require "mruby/build/load_gems"
2 require "mruby/build/command"
10 def each_target(&block)
11 return to_enum(:each_target) if block.nil?
12 @targets.each do |key, target|
13 target.instance_eval(&block)
20 attr_accessor :toolchains
23 def initialize(name, &block)
24 @name, @initializer = name.to_s, block
25 MRuby::Toolchain.toolchains ||= {}
26 MRuby::Toolchain.toolchains[@name] = self
29 def setup(conf,params={})
30 conf.instance_exec(conf, params, &@initializer)
34 Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file|
43 attr_accessor :current
47 attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
48 attr_reader :libmruby_objs, :gems, :toolchains
49 attr_writer :enable_bintest, :enable_test
51 alias libmruby libmruby_objs
53 COMPILERS = %w(cc cxx objc asm)
54 COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
55 attr_block MRuby::Build::COMMANDS
57 Exts = Struct.new(:object, :executable, :library)
59 def initialize(name='host', build_dir=nil, &block)
62 unless MRuby.targets[@name]
63 if ENV['OS'] == 'Windows_NT'
64 @exts = Exts.new('.o', '.exe', '.a')
66 @exts = Exts.new('.o', '', '.a')
69 build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build"
72 @build_dir = "#{build_dir}/#{@name}"
73 @gem_clone_dir = "#{build_dir}/mrbgems"
74 @cc = Command::Compiler.new(self, %w(.c))
75 @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp))
76 @objc = Command::Compiler.new(self, %w(.m))
77 @asm = Command::Compiler.new(self, %w(.S .asm))
78 @linker = Command::Linker.new(self)
79 @archiver = Command::Archiver.new(self)
80 @yacc = Command::Yacc.new(self)
81 @gperf = Command::Gperf.new(self)
82 @git = Command::Git.new(self)
83 @mrbc = Command::Mrbc.new(self)
86 @gems, @libmruby_objs = MRuby::Gem::List.new, []
87 @build_mrbtest_lib_only = false
88 @cxx_exception_enabled = false
89 @cxx_exception_disabled = false
90 @cxx_abi_enabled = false
91 @enable_bintest = false
95 MRuby.targets[@name] = self
98 MRuby::Build.current = MRuby.targets[@name]
99 MRuby.targets[@name].instance_eval(&block)
101 build_mrbc_exec if name == 'host'
102 build_mrbtest if test_enabled?
110 compilers.each do |c|
111 c.defines += %w(MRB_DEBUG)
112 if toolchains.any? { |toolchain| toolchain == "gcc" }
113 c.flags += %w(-g3 -O0)
116 @mrbc.compile_options += ' -g'
121 def disable_cxx_exception
122 if @cxx_exception_enabled or @cxx_abi_enabled
123 raise "cxx_exception already enabled"
125 @cxx_exception_disabled = true
128 def enable_cxx_exception
129 return if @cxx_exception_enabled
130 return if @cxx_abi_enabled
131 if @cxx_exception_disabled
132 raise "cxx_exception disabled"
134 @cxx_exception_enabled = true
136 c.defines += %w(MRB_ENABLE_CXX_EXCEPTION)
137 c.flags << c.cxx_exception_flag
139 linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
142 def cxx_exception_enabled?
143 @cxx_exception_enabled
151 return if @cxx_abi_enabled
152 if @cxx_exception_enabled
153 raise "cxx_exception already enabled"
156 c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI)
157 c.flags << c.cxx_compile_flag
159 compilers.each { |c| c.flags << c.cxx_compile_flag }
160 linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
161 @cxx_abi_enabled = true
164 def compile_as_cxx src, cxx_src, obj = nil, includes = []
165 obj = objfile(cxx_src) if obj.nil?
167 file cxx_src => [src, __FILE__] do |t|
168 FileUtils.mkdir_p File.dirname t.name
169 IO.write t.name, <<EOS
170 #define __STDC_CONSTANT_MACROS
171 #define __STDC_LIMIT_MACROS
173 #ifndef MRB_ENABLE_CXX_ABI
176 #include "#{File.absolute_path src}"
177 #ifndef MRB_ENABLE_CXX_ABI
183 file obj => cxx_src do |t|
184 cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes
191 @enable_bintest = true
198 def toolchain(name, params={})
199 tc = Toolchain.toolchains[name.to_s]
200 fail "Unknown #{name} toolchain" unless tc
201 tc.setup(self, params)
202 @toolchains.unshift name.to_s
205 def primary_toolchain
222 gem :core => 'mruby-test'
226 gem :core => 'mruby-bin-mrbc'
230 return @mrbcfile if @mrbcfile
232 mrbc_build = MRuby.targets['host']
233 gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' }
234 @mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc")
239 instance_variable_get("@#{c}")
244 compilers.each do |compiler|
245 if respond_to?(:enable_gems?) && enable_gems?
246 compiler.defines -= %w(DISABLE_GEMS)
248 compiler.defines += %w(DISABLE_GEMS)
250 compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
256 name.flatten.map { |n| filename(n) }
258 '"%s"' % name.gsub('/', file_separator)
262 def cygwin_filename(name)
264 name.flatten.map { |n| cygwin_filename(n) }
266 '"%s"' % `cygpath -w "#{filename(name)}"`.strip
272 name.flatten.map { |n| exefile(n) }
273 elsif File.extname(name).empty?
274 "#{name}#{exts.executable}"
276 # `name` sometimes have (non-standard) extension (e.g. `.bat`).
283 name.flatten.map { |n| objfile(n) }
285 "#{name}#{exts.object}"
291 name.flatten.map { |n| libfile(n) }
293 "#{name}#{exts.library}"
297 def build_mrbtest_lib_only
298 @build_mrbtest_lib_only = true
301 def build_mrbtest_lib_only?
302 @build_mrbtest_lib_only
306 $verbose ? ' -v' : ''
310 puts ">>> Test #{name} <<<"
311 mrbtest = exefile("#{build_dir}/bin/mrbtest")
312 sh "#{filename mrbtest.relative_path}#{verbose_flag}"
317 puts ">>> Bintest #{name} <<<"
318 targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
319 targets << filename(".") if File.directory? "./bintest"
320 sh "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}"
323 def print_build_summary
324 puts "================================================"
325 puts " Config Name: #{@name}"
326 puts " Output Directory: #{self.build_dir.relative_path}"
327 puts " Binaries: #{@bins.join(', ')}" unless @bins.empty?
329 puts " Included Gems:"
331 gem_version = " - #{gem.version}" if gem.version != '0.0.0'
332 gem_summary = " - #{gem.summary}" if gem.summary
333 puts " #{gem.name}#{gem_version}#{gem_summary}"
334 puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty?
337 puts "================================================"
342 libfile("#{build_dir}/lib/libmruby")
345 def libmruby_core_static
346 libfile("#{build_dir}/lib/libmruby_core")
354 class CrossBuild < Build
355 attr_block %w(test_runner)
356 # cross compiling targets for building native extensions.
357 # host - arch of where the built binary will run
358 # build - arch of the machine building the binary
359 attr_accessor :host_target, :build_target
361 def initialize(name, build_dir=nil, &block)
363 @test_runner = Command::CrossTestRunner.new(self)
368 MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc")
372 @test_runner.runner_options << ' -v' if $verbose
373 mrbtest = exefile("#{build_dir}/bin/mrbtest")
374 if (@test_runner.command == nil)
375 puts "You should run #{mrbtest} on target device."
378 @test_runner.run(mrbtest)
384 puts "Endian has already specified as #{@endian}."
388 @mrbc.compile_options += ' -E'
389 compilers.each do |c|
390 c.defines += %w(MRB_ENDIAN_BIG)
396 puts "Endian has already specified as #{@endian}."
400 @mrbc.compile_options += ' -e'