1require "mruby/build/load_gems" 2require "mruby/build/command" 3 4module MRuby 5 class << self 6 def targets 7 @targets ||= {} 8 end 9 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) 14 end 15 end 16 end 17 18 class Toolchain 19 class << self 20 attr_accessor :toolchains 21 end 22 23 def initialize(name, &block) 24 @name, @initializer = name.to_s, block 25 MRuby::Toolchain.toolchains ||= {} 26 MRuby::Toolchain.toolchains[@name] = self 27 end 28 29 def setup(conf,params={}) 30 conf.instance_exec(conf, params, &@initializer) 31 end 32 33 def self.load 34 Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file| 35 Kernel.load file 36 end 37 end 38 end 39 Toolchain.load 40 41 class Build 42 class << self 43 attr_accessor :current 44 end 45 include Rake::DSL 46 include LoadGems 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 50 51 alias libmruby libmruby_objs 52 53 COMPILERS = %w(cc cxx objc asm) 54 COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc) 55 attr_block MRuby::Build::COMMANDS 56 57 Exts = Struct.new(:object, :executable, :library) 58 59 def initialize(name='host', build_dir=nil, &block) 60 @name = name.to_s 61 62 unless MRuby.targets[@name] 63 if ENV['OS'] == 'Windows_NT' 64 @exts = Exts.new('.o', '.exe', '.a') 65 else 66 @exts = Exts.new('.o', '', '.a') 67 end 68 69 build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build" 70 71 @file_separator = '/' 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) 84 85 @bins = [] 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 92 @enable_test = false 93 @toolchains = [] 94 95 MRuby.targets[@name] = self 96 end 97 98 MRuby::Build.current = MRuby.targets[@name] 99 MRuby.targets[@name].instance_eval(&block) 100 101 build_mrbc_exec if name == 'host' 102 build_mrbtest if test_enabled? 103 end 104 105 def debug_enabled? 106 @enable_debug 107 end 108 109 def enable_debug 110 compilers.each do |c| 111 c.defines += %w(MRB_DEBUG) 112 if toolchains.any? { |toolchain| toolchain == "gcc" } 113 c.flags += %w(-g3 -O0) 114 end 115 end 116 @mrbc.compile_options += ' -g' 117 118 @enable_debug = true 119 end 120 121 def disable_cxx_exception 122 if @cxx_exception_enabled or @cxx_abi_enabled 123 raise "cxx_exception already enabled" 124 end 125 @cxx_exception_disabled = true 126 end 127 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" 133 end 134 @cxx_exception_enabled = true 135 compilers.each { |c| 136 c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) 137 c.flags << c.cxx_exception_flag 138 } 139 linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } 140 end 141 142 def cxx_exception_enabled? 143 @cxx_exception_enabled 144 end 145 146 def cxx_abi_enabled? 147 @cxx_abi_enabled 148 end 149 150 def enable_cxx_abi 151 return if @cxx_abi_enabled 152 if @cxx_exception_enabled 153 raise "cxx_exception already enabled" 154 end 155 compilers.each { |c| 156 c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI) 157 c.flags << c.cxx_compile_flag 158 } 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 162 end 163 164 def compile_as_cxx src, cxx_src, obj = nil, includes = [] 165 obj = objfile(cxx_src) if obj.nil? 166 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 172 173#ifndef MRB_ENABLE_CXX_ABI 174extern "C" { 175#endif 176#include "#{File.absolute_path src}" 177#ifndef MRB_ENABLE_CXX_ABI 178} 179#endif 180EOS 181 end 182 183 file obj => cxx_src do |t| 184 cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes 185 end 186 187 obj 188 end 189 190 def enable_bintest 191 @enable_bintest = true 192 end 193 194 def bintest_enabled? 195 @enable_bintest 196 end 197 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 203 end 204 205 def primary_toolchain 206 @toolchains.first 207 end 208 209 def root 210 MRUBY_ROOT 211 end 212 213 def enable_test 214 @enable_test = true 215 end 216 217 def test_enabled? 218 @enable_test 219 end 220 221 def build_mrbtest 222 gem :core => 'mruby-test' 223 end 224 225 def build_mrbc_exec 226 gem :core => 'mruby-bin-mrbc' 227 end 228 229 def mrbcfile 230 return @mrbcfile if @mrbcfile 231 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") 235 end 236 237 def compilers 238 COMPILERS.map do |c| 239 instance_variable_get("@#{c}") 240 end 241 end 242 243 def define_rules 244 compilers.each do |compiler| 245 if respond_to?(:enable_gems?) && enable_gems? 246 compiler.defines -= %w(DISABLE_GEMS) 247 else 248 compiler.defines += %w(DISABLE_GEMS) 249 end 250 compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) 251 end 252 end 253 254 def filename(name) 255 if name.is_a?(Array) 256 name.flatten.map { |n| filename(n) } 257 else 258 '"%s"' % name.gsub('/', file_separator) 259 end 260 end 261 262 def cygwin_filename(name) 263 if name.is_a?(Array) 264 name.flatten.map { |n| cygwin_filename(n) } 265 else 266 '"%s"' % `cygpath -w "#{filename(name)}"`.strip 267 end 268 end 269 270 def exefile(name) 271 if name.is_a?(Array) 272 name.flatten.map { |n| exefile(n) } 273 elsif File.extname(name).empty? 274 "#{name}#{exts.executable}" 275 else 276 # `name` sometimes have (non-standard) extension (e.g. `.bat`). 277 name 278 end 279 end 280 281 def objfile(name) 282 if name.is_a?(Array) 283 name.flatten.map { |n| objfile(n) } 284 else 285 "#{name}#{exts.object}" 286 end 287 end 288 289 def libfile(name) 290 if name.is_a?(Array) 291 name.flatten.map { |n| libfile(n) } 292 else 293 "#{name}#{exts.library}" 294 end 295 end 296 297 def build_mrbtest_lib_only 298 @build_mrbtest_lib_only = true 299 end 300 301 def build_mrbtest_lib_only? 302 @build_mrbtest_lib_only 303 end 304 305 def verbose_flag 306 $verbose ? ' -v' : '' 307 end 308 309 def run_test 310 puts ">>> Test #{name} <<<" 311 mrbtest = exefile("#{build_dir}/bin/mrbtest") 312 sh "#{filename mrbtest.relative_path}#{verbose_flag}" 313 puts 314 end 315 316 def run_bintest 317 targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } 318 targets << filename(".") if File.directory? "./bintest" 319 sh "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}" 320 end 321 322 def print_build_summary 323 puts "================================================" 324 puts " Config Name: #{@name}" 325 puts " Output Directory: #{self.build_dir.relative_path}" 326 puts " Binaries: #{@bins.join(', ')}" unless @bins.empty? 327 unless @gems.empty? 328 puts " Included Gems:" 329 @gems.map do |gem| 330 gem_version = " - #{gem.version}" if gem.version != '0.0.0' 331 gem_summary = " - #{gem.summary}" if gem.summary 332 puts " #{gem.name}#{gem_version}#{gem_summary}" 333 puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty? 334 end 335 end 336 puts "================================================" 337 puts 338 end 339 340 def libmruby_static 341 libfile("#{build_dir}/lib/libmruby") 342 end 343 344 def libmruby_core_static 345 libfile("#{build_dir}/lib/libmruby_core") 346 end 347 348 def libraries 349 [libmruby_static] 350 end 351 end # Build 352 353 class CrossBuild < Build 354 attr_block %w(test_runner) 355 # cross compiling targets for building native extensions. 356 # host - arch of where the built binary will run 357 # build - arch of the machine building the binary 358 attr_accessor :host_target, :build_target 359 360 def initialize(name, build_dir=nil, &block) 361 @endian = nil 362 @test_runner = Command::CrossTestRunner.new(self) 363 super 364 end 365 366 def mrbcfile 367 MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc") 368 end 369 370 def run_test 371 @test_runner.runner_options << ' -v' if $verbose 372 mrbtest = exefile("#{build_dir}/bin/mrbtest") 373 if (@test_runner.command == nil) 374 puts "You should run #{mrbtest} on target device." 375 puts 376 else 377 @test_runner.run(mrbtest) 378 end 379 end 380 381 def big_endian 382 if @endian 383 puts "Endian has already specified as #{@endian}." 384 return 385 end 386 @endian = :big 387 @mrbc.compile_options += ' -E' 388 compilers.each do |c| 389 c.defines += %w(MRB_ENDIAN_BIG) 390 end 391 end 392 393 def little_endian 394 if @endian 395 puts "Endian has already specified as #{@endian}." 396 return 397 end 398 @endian = :little 399 @mrbc.compile_options += ' -e' 400 end 401 end # CrossBuild 402end # MRuby 403