Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lib/mruby/core_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,34 @@ def _pp(cmd, src, tgt=nil, indent: nil)
template = indent ? "%#{width * indent}s %s %s" : "%-#{width}s %s %s"
puts template % [cmd, src, tgt ? "-> #{tgt}" : nil]
end

# A file task like `file`, for a source that is generated: the block writes
# the text to the IO it is given. The file is written only when the text
# differs from what is there, so the object built from it stays when a
# newer `mrbc` makes the same bytecode.
#
# Rake holds an output out of date when anything behind its prerequisites
# is newer, so a source with `mrbc` among its prerequisites would take its
# object with it whether the text changed or not. The source task has no
# prerequisites of its own: it runs every time and invokes the stamp
# beside it, which has the prerequisites and holds the time of the last
# generation.
def generated_file(name, prerequisites, &block)
stamp = file "#{name}.stamp" => prerequisites do |t|
mkdir_p File.dirname(name)
fresh = "#{name}.tmp"
File.open(fresh, "w", &block)
if File.exist?(name) && FileUtils.identical?(fresh, name)
rm_f fresh
else
mv fresh, name
end
touch t.name
end
stamp.define_singleton_method(:needed?) { super() || !File.exist?(name) }
source = file name do
stamp.invoke
end
source.define_singleton_method(:needed?) { true }
source
end
60 changes: 29 additions & 31 deletions lib/mruby/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,43 +240,41 @@ def setup_build
end

def define_gem_init_builder
file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t|
mkdir_p build_dir
generate_gem_init("#{build_dir}/gem_init.c")
fname = "#{build_dir}/gem_init.c"
generated_file fname, [build.mrbcfile, __FILE__] + [rbfiles].flatten do |f|
_pp "GEN", fname.relative_path
generate_gem_init(f)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end
end

def generate_gem_init(fname)
_pp "GEN", fname.relative_path
open(fname, 'w') do |f|
print_gem_init_header f
unless rbfiles.empty?
opts = {cdump: cdump?, static: true}
if cdump?
build.mrbc.run f, rbfiles, "gem_mrblib_#{funcname}_proc", **opts
else
build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}", **opts
end
def generate_gem_init(f)
print_gem_init_header f
unless rbfiles.empty?
opts = {cdump: cdump?, static: true}
if cdump?
build.mrbc.run f, rbfiles, "gem_mrblib_#{funcname}_proc", **opts
else
build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}", **opts
end
f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);]
f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);]
f.puts %Q[]
f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {]
f.puts %Q[ gem_mrblib_#{funcname}_proc_init_syms(mrb);] if !rbfiles.empty? && cdump?
f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
unless rbfiles.empty?
if cdump?
f.puts %Q[ mrb_load_proc(mrb, gem_mrblib_#{funcname}_proc);]
else
f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
end
end
f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);]
f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);]
f.puts %Q[]
f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {]
f.puts %Q[ gem_mrblib_#{funcname}_proc_init_syms(mrb);] if !rbfiles.empty? && cdump?
f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
unless rbfiles.empty?
if cdump?
f.puts %Q[ mrb_load_proc(mrb, gem_mrblib_#{funcname}_proc);]
else
f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
end
f.puts %Q[}]
f.puts %Q[]
f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {]
f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
f.puts %Q[}]
end
f.puts %Q[}]
f.puts %Q[]
f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {]
f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
f.puts %Q[}]
end # generate_gem_init

def print_gem_comment(f)
Expand Down
242 changes: 112 additions & 130 deletions mrbgems/mruby-test/mrbgem.rake
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
spec.cc.defines << "MRBTEST_COMPILER_PRISM"
end

file assert_lib => assert_c
file assert_c => [assert_rb, build.mrbcfile] do |t|
_pp "GEN", t.name.relative_path
mkdir_p File.dirname(t.name)
tmpfile = t.name + ".tmp"
open(tmpfile, 'w') do |f|
mrbc.run f, assert_rb, 'mrbtest_assert_irep', cdump: false
end
File.rename(tmpfile, t.name)
generated_file assert_c, [assert_rb, build.mrbcfile] do |f|
_pp "GEN", assert_c.relative_path
mrbc.run f, assert_rb, 'mrbtest_assert_irep', cdump: false
end

gem_table = build.gems.generate_gem_table build
Expand All @@ -50,103 +44,97 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
gem_rakefiles = [g, *dep_list].map {|d| File.join(d.dir, "mrbgem.rake") }
.select {|f| File.exist?(f) }.uniq

file test_rbobj => g.test_rbireps
# __FILE__ holds the generator itself, as in the mrbtest.c rule below.
file g.test_rbireps => [g.test_rbfiles, build.mrbcfile, gem_rakefiles, __FILE__].flatten do |t|
_pp "GEN", t.name.relative_path
mkdir_p File.dirname(t.name)
tmpfile = t.name + ".tmp"
open(tmpfile, 'w') do |f|
g.print_gem_test_header(f)
test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
File.expand_path(g.test_preload, dir)
}.find {|file| File.exist?(file) }
generated_file g.test_rbireps, [g.test_rbfiles, build.mrbcfile, gem_rakefiles, __FILE__].flatten do |f|
_pp "GEN", g.test_rbireps.relative_path
g.print_gem_test_header(f)
test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
File.expand_path(g.test_preload, dir)
}.find {|file| File.exist?(file) }

f.puts %Q[/*]
f.puts %Q[ * This file contains a test code for #{g.name} gem.]
f.puts %Q[ *]
f.puts %Q[ * IMPORTANT:]
f.puts %Q[ * This file was generated!]
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[/*]
f.puts %Q[ * This file contains a test code for #{g.name} gem.]
f.puts %Q[ *]
f.puts %Q[ * IMPORTANT:]
f.puts %Q[ * This file was generated!]
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
if test_preload.nil?
f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
else
g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload", cdump: false
end
g.test_rbfiles.flatten.each_with_index do |rbfile, i|
g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}", cdump: false, static: true
end
f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] if g.custom_test_init?
dep_list.each do |d|
f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);]
f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);]
end
f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);]
f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[extern mrb_state *global_mrb;]
f.puts %Q[#endif]
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
unless g.test_rbfiles.empty?
unless g.test_args.empty?
f.puts %Q[ mrb_value test_args_hash;]
end
f.puts %Q[ mrb_state *mrb2 = mrb_open_core();]
f.puts %Q[ if (mrb2 == NULL) {]
f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __func__);]
f.puts %Q[ exit(EXIT_FAILURE);]
f.puts %Q[ }]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[ global_mrb = mrb2;]
f.puts %Q[#endif]
f.puts %Q[ int ai = mrb_gc_arena_save(mrb2);]
f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
if test_preload.nil?
f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);]
else
g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload", cdump: false
end
g.test_rbfiles.flatten.each_with_index do |rbfile, i|
g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}", cdump: false, static: true
f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
end
f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] if g.custom_test_init?
dep_list.each do |d|
f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);]
f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);]
f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);]
f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);]
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
end
f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);]
f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[extern mrb_state *global_mrb;]
f.puts %Q[#endif]
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
unless g.test_rbfiles.empty?
f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));]
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
f.puts %Q[ ]
g.test_rbfiles.count.times do |i|
unless g.test_args.empty?
f.puts %Q[ mrb_value test_args_hash;]
f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb2, #{g.test_args.length}); ]
g.test_args.each do |arg_name, arg_value|
escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
end
f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
end
f.puts %Q[ mrb_state *mrb2 = mrb_open_core();]
f.puts %Q[ if (mrb2 == NULL) {]
f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __func__);]
f.puts %Q[ exit(EXIT_FAILURE);]
f.puts %Q[ }]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[ global_mrb = mrb2;]
f.puts %Q[#endif]
f.puts %Q[ int ai = mrb_gc_arena_save(mrb2);]
f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
if test_preload.nil?
f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);]
else
f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
end
dep_list.each do |d|
f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);]
f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);]
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
end
f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));]

f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init?
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
f.puts %Q[ if (mrb2->exc) {]
f.puts %Q[ mrb_print_error(mrb2);]
f.puts %Q[ mrb_close(mrb2);]
f.puts %Q[ exit(EXIT_FAILURE);]
f.puts %Q[ }]
f.puts %Q[ ]
g.test_rbfiles.count.times do |i|
unless g.test_args.empty?
f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb2, #{g.test_args.length}); ]
g.test_args.each do |arg_name, arg_value|
escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
end
f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
end
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]

f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init?
f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);]
f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
f.puts %Q[ if (mrb2->exc) {]
f.puts %Q[ mrb_print_error(mrb2);]
f.puts %Q[ mrb_close(mrb2);]
f.puts %Q[ exit(EXIT_FAILURE);]
f.puts %Q[ }]
f.puts %Q[ ]
end
f.puts %Q[ mrb_t_pass_result(mrb, mrb2);]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[ global_mrb = mrb;]
f.puts %Q[#endif]
f.puts %Q[ mrb_close(mrb2);]
end
f.puts %Q[}]
f.puts %Q[ mrb_t_pass_result(mrb, mrb2);]
f.puts %Q[#if defined(MRBTEST_COMPILER_PRISM)]
f.puts %Q[ global_mrb = mrb;]
f.puts %Q[#endif]
f.puts %Q[ mrb_close(mrb2);]
end
File.rename(tmpfile, t.name)
f.puts %Q[}]
end
end

Expand All @@ -160,46 +148,40 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
end

file mlib => clib
file clib => ["#{build.build_dir}/mrbgems/active_gems.txt", build.mrbcfile, __FILE__] do |_t|
generated_file clib, ["#{build.build_dir}/mrbgems/active_gems.txt", build.mrbcfile, __FILE__] do |f|
_pp "GEN", clib.relative_path
mkdir_p File.dirname(clib)
tmpfile = clib + ".tmp"
open(tmpfile, 'w') do |f|
f.puts %Q[/*]
f.puts %Q[ * This file contains a list of all]
f.puts %Q[ * test functions.]
f.puts %Q[ *]
f.puts %Q[ * IMPORTANT:]
f.puts %Q[ * This file was generated!]
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
f.puts %Q[#include <mruby.h>]
f.puts %Q[#include <mruby/variable.h>]
f.puts %Q[#include <mruby/array.h>]
f.puts %Q[]
build.gems.each do |g|
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
end
f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
f.puts %Q[ int ai = mrb_gc_arena_save(mrb);]
build.gems.each do |g|
if g.skip_test?
f.puts %Q[ do {]
f.puts %Q[ mrb_value asserts = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$asserts"));]
f.puts %Q[ mrb_ary_push(mrb, asserts, mrb_str_new_lit(mrb, ]
f.puts %Q[ "Warn: Skipping tests for gem (#{
g.name == 'mruby-test' ? 'core' : "mrbgems: #{g.name}"
})"));]
f.puts %Q[ } while (0);]
else
f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
end
f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
f.puts %Q[/*]
f.puts %Q[ * This file contains a list of all]
f.puts %Q[ * test functions.]
f.puts %Q[ *]
f.puts %Q[ * IMPORTANT:]
f.puts %Q[ * This file was generated!]
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
f.puts %Q[#include <mruby.h>]
f.puts %Q[#include <mruby/variable.h>]
f.puts %Q[#include <mruby/array.h>]
f.puts %Q[]
build.gems.each do |g|
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
end
f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
f.puts %Q[ int ai = mrb_gc_arena_save(mrb);]
build.gems.each do |g|
if g.skip_test?
f.puts %Q[ do {]
f.puts %Q[ mrb_value asserts = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$asserts"));]
f.puts %Q[ mrb_ary_push(mrb, asserts, mrb_str_new_lit(mrb, ]
f.puts %Q[ "Warn: Skipping tests for gem (#{
g.name == 'mruby-test' ? 'core' : "mrbgems: #{g.name}"
})"));]
f.puts %Q[ } while (0);]
else
f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
end
f.puts %Q[}]
f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
end
File.rename(tmpfile, clib)
f.puts %Q[}]
end
end
Loading
Loading