Dir.chdir __dir__ version_re = /\d+(\.\d+)*/ version = `command grep 'VERSION =' lib/zscan.rb`[version_re] gem_files = Dir.glob('{rakefile,zscan.gemspec,readme.md,**/*.{rb,c}}') gem_package = "zscan-#{version}.gem" bspec_types = %w[INT8 INT16 INT32 INT64 UINT8 UINT16 UINT32 UINT64 SINGLE DOUBLE] bspec_insns = bspec_types.flat_map{|ty| if ty =~ /INT8/ ty else [ty, "#{ty}_SWAP"] end } def bspec_incr ins case ins when /INT(\d+)/; $1.to_i / 8 when /SINGLE/; 4 when /DOUBLE/; 8 else; raise 'bad' end end def bspec_c_type ins case ins when /(U?INT\d+)/; "#{$1.downcase}_t" when /SINGLE/; 'float' when /DOUBLE/; 'double' else; raise 'bad' end end def bspec_extract ins type = bspec_c_type ins len = bspec_incr(ins) * 8 r = "((uint#{len}_t*)s)[0]" if ins.end_with?('SWAP') r = "swap#{len}(#{r})" end "uint#{len}_t r = #{r}" end def bspec_convert ins case ins when /(U)?INT64|UINT32/ if ins.start_with?('U') "UINT64toNUM(r)" else "INT64toNUM(CAST(r, int64_t))" end when /INT32/ "INT2NUM(CAST(r, int32_t))" when /INT(16|8)/ "INT2FIX(CAST(r, #{bspec_c_type ins}))" when /SINGLE/ "DBL2NUM((double)CAST(r, float))" when /DOUBLE/ "DBL2NUM(CAST(r, double))" else raise 'bad' end end desc "build and test" task :default => [:test, gem_package] desc "build and run test" task :test do sh "make -C ext" sh "rspec" end desc "pack gem" file gem_package => gem_files do sh "rm zscan-*.gem" new_version = false lines = File.readlines('zscan.gemspec') lines.each do |line| if line =~ /s\.version =/ and (line.sub! version_re, version) new_version = true break end end if new_version File.open('zscan.gemspec', 'w'){|f| f << lines.join } end sh "gem build zscan.gemspec" end desc "generate files" task :gen => %w[ext/bspec_exec.inc ext/bspec_init.inc lib/zscan/instructions.rb] file 'ext/bspec_exec.inc' => __FILE__ do puts "generating ext/bspec_exec.inc" opcode_list = bspec_insns.map do |ins| "&&BS_#{ins}" end.join ', ' opcode_segs = bspec_insns.map do |ins| %Q{BS_#{ins}: { #{bspec_extract ins}; rb_ary_push(a, #{bspec_convert ins}); s += #{bspec_incr ins}; goto **(ip++); } } end.join "\n" File.open 'ext/bspec_exec.inc', 'w' do |f| f.puts %Q|// GENERATED WITH: rake gen #line 2 "ext/bspec_exec.inc" __attribute__((__noinline__)) static VALUE bspec_exec(void** ip, char* s, VALUE a) { static void* opcodes[] = { &&BS_RET, #{opcode_list} }; if (ip == NULL) { return (VALUE)opcodes; } goto **(ip++); BS_RET: return a; #{opcode_segs} }| end end file 'ext/bspec_init.inc' => __FILE__ do puts 'generating ext/bspec_init.inc' opcode_incrs = bspec_insns.map{|ins| bspec_incr ins}.join ', ' File.open 'ext/bspec_init.inc', 'w' do |f| f.puts "// GENERATED WITH: rake gen" f.puts %Q|static const long bspec_s_sizes[] = {0, #{opcode_incrs}};| f.puts %Q|static const long bspec_opcodes_size = #{bspec_insns.size + 1};| end end file 'lib/zscan/instructions.rb' => __FILE__ do puts 'generating lib/zscan/instructions.rb' File.open 'lib/zscan/instructions.rb', 'w' do |f| f.puts "# GENERATED WITH: rake gen" f.puts "class ZScan::BinarySpec" bspec_insns.each_with_index do |ins, i| f.puts <<-RUBY def #{ins.downcase} n=1 raise ArgumentError, "repeat count should be >= 1, but got \#{n}" if n < 1 n.times do append #{i + 1} end end RUBY end alias_ins = (bspec_types - ['INT8', 'UINT8']).map &:downcase f.puts " if ZScan::BinarySpec.big_endian?" alias_ins.each do |ins| f.puts " alias #{ins}_be #{ins}" f.puts " alias #{ins}_le #{ins}_swap" end f.puts " else" alias_ins.each do |ins| f.puts " alias #{ins}_le #{ins}" f.puts " alias #{ins}_be #{ins}_swap" end f.puts " end" swap_ins = alias_ins.map{|ins| "#{ins}_swap"} f.puts " undef #{swap_ins.join ', '}" f.puts "end" end end