1# This file is interpreted by $(BASERUBY) and miniruby. 2# $(BASERUBY) is used for miniprelude.c. 3# miniruby is used for prelude.c. 4# Since $(BASERUBY) may be older than Ruby 1.9, 5# Ruby 1.9 feature should not be used. 6 7require 'erb' 8 9class Prelude 10 SRCDIR = File.dirname(File.dirname(__FILE__)) 11 $:.unshift(SRCDIR) 12 13 C_ESC = { 14 "\\" => "\\\\", 15 '"' => '\"', 16 "\n" => '\n', 17 } 18 19 0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } 20 0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } 21 C_ESC_PAT = Regexp.union(*C_ESC.keys) 22 23 def c_esc(str) 24 '"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"' 25 end 26 def prelude_base(filename) 27 filename[/\A#{Regexp.quote(SRCDIR)}\/(.*?)(\.rb)?\z/om, 1] 28 end 29 def prelude_name(filename) 30 "<internal:" + prelude_base(filename) + ">" 31 end 32 33 def initialize(preludes) 34 @mkconf = nil 35 @have_sublib = false 36 @need_ruby_prefix = false 37 @preludes = {} 38 @mains = preludes.map {|filename| translate(filename)[0]} 39 @preludes.delete_if {|_, (_, _, lines, sub)| !sub && lines.empty?} 40 end 41 42 def translate(filename, sub = false) 43 idx = @preludes[filename] 44 return idx if idx 45 lines = [] 46 @preludes[filename] = result = [@preludes.size, filename, lines, sub] 47 File.readlines(filename).each do |line| 48 line.gsub!(/RbConfig::CONFIG\["(\w+)"\]/) { 49 key = $1 50 unless @mkconf 51 require './rbconfig' 52 @mkconf = RbConfig::MAKEFILE_CONFIG.merge('prefix'=>'#{TMP_RUBY_PREFIX}') 53 end 54 if RbConfig::MAKEFILE_CONFIG.has_key? key 55 val = RbConfig.expand("$(#{key})", @mkconf) 56 @need_ruby_prefix ||= /\A\#\{TMP_RUBY_PREFIX\}/ =~ val 57 c_esc(val) 58 else 59 "nil" 60 end 61 } 62 line.sub!(/require\s*\(?\s*(["'])(.*?)\1\)?/) do 63 orig, path = $&, $2 64 path = File.join(SRCDIR, path) 65 if File.exist?(path) 66 @have_sublib = true 67 "TMP_RUBY_PREFIX.require(#{translate(path, true)[0]})" 68 else 69 orig 70 end 71 end 72 lines << c_esc(line) 73 end 74 result 75 end 76 77 def emit(outfile) 78 @init_name = outfile[/\w+(?=_prelude.c\b)/] || 'prelude' 79 erb = ERB.new(<<'EOS', nil, '%') 80/* -*-c-*- 81 THIS FILE WAS AUTOGENERATED BY tool/compile_prelude.rb. DO NOT EDIT. 82 83 sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %> 84*/ 85#include "ruby/ruby.h" 86#include "internal.h" 87#include "vm_core.h" 88 89% preludes = @preludes.values.sort 90% preludes.each {|i, prelude, lines, sub| 91 92static const char prelude_name<%=i%>[] = <%=c_esc(prelude_name(*prelude))%>; 93static const char prelude_code<%=i%>[] = 94% lines.each {|line| 95<%=line%> 96% } 97; 98% } 99 100#define PRELUDE_COUNT <%=@have_sublib ? preludes.size : 0%> 101 102% if @have_sublib or @need_ruby_prefix 103struct prelude_env { 104 volatile VALUE prefix_path; 105#if PRELUDE_COUNT > 0 106 char loaded[PRELUDE_COUNT]; 107#endif 108}; 109 110static VALUE 111prelude_prefix_path(VALUE self) 112{ 113 struct prelude_env *ptr = DATA_PTR(self); 114 return ptr->prefix_path; 115} 116% end 117 118% unless preludes.empty? 119static void 120prelude_eval(VALUE code, VALUE name, VALUE line) 121{ 122 rb_iseq_eval(rb_iseq_compile_with_option(code, name, Qnil, line, 0, Qtrue)); 123} 124% end 125 126% if @have_sublib 127static VALUE 128prelude_require(VALUE self, VALUE nth) 129{ 130 struct prelude_env *ptr = DATA_PTR(self); 131 VALUE code, name; 132 int n = FIX2INT(nth); 133 134 if (n > PRELUDE_COUNT) return Qfalse; 135 if (ptr->loaded[n]) return Qfalse; 136 ptr->loaded[n] = 1; 137 switch (n) { 138% @preludes.each_value do |i, prelude, lines, sub| 139% if sub 140 case <%=i%>: 141 code = rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1); 142 name = rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1); 143 break; 144% end 145% end 146 default: 147 return Qfalse; 148 } 149 prelude_eval(code, name, INT2FIX(1)); 150 return Qtrue; 151} 152 153% end 154void 155Init_<%=@init_name%>(void) 156{ 157% if @have_sublib or @need_ruby_prefix 158 struct prelude_env memo; 159 ID name = rb_intern("TMP_RUBY_PREFIX"); 160 VALUE prelude = Data_Wrap_Struct(rb_cData, 0, 0, &memo); 161 162 memo.prefix_path = rb_const_remove(rb_cObject, name); 163 rb_const_set(rb_cObject, name, prelude); 164 rb_define_singleton_method(prelude, "to_s", prelude_prefix_path, 0); 165% end 166% if @have_sublib 167 memset(memo.loaded, 0, sizeof(memo.loaded)); 168 rb_define_singleton_method(prelude, "require", prelude_require, 1); 169% end 170% preludes.each do |i, prelude, lines, sub| 171% next if sub 172 prelude_eval( 173 rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1), 174 rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1), 175 INT2FIX(1)); 176% end 177% if @have_sublib or @need_ruby_prefix 178 rb_gc_force_recycle(prelude); 179% end 180 181#if 0 182% preludes.length.times {|i| 183 puts(prelude_code<%=i%>); 184% } 185#endif 186} 187EOS 188 tmp = erb.result(binding) 189 open(outfile, 'w'){|f| 190 f << tmp 191 } 192 end 193end 194 195preludes = ARGV.dup 196outfile = preludes.pop 197Prelude.new(preludes).emit(outfile) 198 199