# This file is interpreted by $(BASERUBY) and miniruby. # $(BASERUBY) is used for miniprelude.c. # miniruby is used for prelude.c. # Since $(BASERUBY) may be older than Ruby 1.9, # Ruby 1.9 feature should not be used. require 'erb' class Prelude SRCDIR = File.dirname(File.dirname(__FILE__)) $:.unshift(SRCDIR) C_ESC = { "\\" => "\\\\", '"' => '\"', "\n" => '\n', } 0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } 0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } C_ESC_PAT = Regexp.union(*C_ESC.keys) def c_esc(str) '"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"' end def prelude_base(filename) filename[/\A#{Regexp.quote(SRCDIR)}\/(.*?)(\.rb)?\z/om, 1] end def prelude_name(filename) "" end def initialize(preludes) @mkconf = nil @have_sublib = false @need_ruby_prefix = false @preludes = {} @mains = preludes.map {|filename| translate(filename)[0]} @preludes.delete_if {|_, (_, _, lines, sub)| !sub && lines.empty?} end def translate(filename, sub = false) idx = @preludes[filename] return idx if idx lines = [] @preludes[filename] = result = [@preludes.size, filename, lines, sub] File.readlines(filename).each do |line| line.sub!(/(?:^|\s+)\#(?:$|\s.*)/, '') line.gsub!(/RbConfig::CONFIG\["(\w+)"\]/) { key = $1 unless @mkconf require './rbconfig' @mkconf = RbConfig::MAKEFILE_CONFIG.merge('prefix'=>'#{TMP_RUBY_PREFIX}') end if RbConfig::MAKEFILE_CONFIG.has_key? key val = RbConfig.expand("$(#{key})", @mkconf) @need_ruby_prefix ||= /\A\#\{TMP_RUBY_PREFIX\}/ =~ val c_esc(val) else "nil" end } line.sub!(/require\s*\(?\s*(["'])(.*?)\1\)?/) do orig, path = $&, $2 path = File.join(SRCDIR, path) if File.exist?(path) @have_sublib = true "TMP_RUBY_PREFIX.require(#{translate(path, true)[0]})" else orig end end lines << c_esc(line) end result end def emit(outfile) @init_name = outfile[/\w+(?=_prelude.c\b)/] || 'prelude' erb = ERB.new(<<'EOS', nil, '%') /* -*-c-*- THIS FILE WAS AUTOGENERATED BY tool/compile_prelude.rb. DO NOT EDIT. sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %> */ #include "ruby/ruby.h" #include "internal.h" #include "vm_core.h" % preludes = @preludes.values.sort % preludes.each {|i, prelude, lines, sub| static const char prelude_name<%=i%>[] = <%=c_esc(prelude_name(*prelude))%>; static const char prelude_code<%=i%>[] = % lines.each {|line| <%=line%> % } ; % } #define PRELUDE_COUNT <%=@have_sublib ? preludes.size : 0%> % if @have_sublib or @need_ruby_prefix struct prelude_env { volatile VALUE prefix_path; #if PRELUDE_COUNT > 0 char loaded[PRELUDE_COUNT]; #endif }; static VALUE prelude_prefix_path(VALUE self) { struct prelude_env *ptr = DATA_PTR(self); return ptr->prefix_path; } % end % unless preludes.empty? static void prelude_eval(VALUE code, VALUE name, VALUE line) { rb_iseq_eval(rb_iseq_compile_with_option(code, name, Qnil, line, 0, Qtrue)); } % end % if @have_sublib static VALUE prelude_require(VALUE self, VALUE nth) { struct prelude_env *ptr = DATA_PTR(self); VALUE code, name; int n = FIX2INT(nth); if (n > PRELUDE_COUNT) return Qfalse; if (ptr->loaded[n]) return Qfalse; ptr->loaded[n] = 1; switch (n) { % @preludes.each_value do |i, prelude, lines, sub| % if sub case <%=i%>: code = rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1); name = rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1); break; % end % end default: return Qfalse; } prelude_eval(code, name, INT2FIX(1)); return Qtrue; } % end void Init_<%=@init_name%>(void) { % if @have_sublib or @need_ruby_prefix struct prelude_env memo; ID name = rb_intern("TMP_RUBY_PREFIX"); VALUE prelude = Data_Wrap_Struct(rb_cData, 0, 0, &memo); memo.prefix_path = rb_const_remove(rb_cObject, name); rb_const_set(rb_cObject, name, prelude); rb_define_singleton_method(prelude, "to_s", prelude_prefix_path, 0); % end % if @have_sublib memset(memo.loaded, 0, sizeof(memo.loaded)); rb_define_singleton_method(prelude, "require", prelude_require, 1); % end % preludes.each do |i, prelude, lines, sub| % next if sub prelude_eval( rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1), rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1), INT2FIX(1)); % end % if @have_sublib or @need_ruby_prefix rb_gc_force_recycle(prelude); % end #if 0 % preludes.length.times {|i| puts(prelude_code<%=i%>); % } #endif } EOS tmp = erb.result(binding) open(outfile, 'w'){|f| f << tmp } end end preludes = ARGV.dup outfile = preludes.pop Prelude.new(preludes).emit(outfile)