3 $name = $library = $description = nil
6 autoload :CONFIG, "./rbconfig"
10 PrivateNames = /(?:Init_|InitVM_|ruby_static_id_|threadptr|_ec_|DllMain\b)/
12 def self.create(*args, &block)
13 platform = RUBY_PLATFORM
14 klass = constants.find do |p|
15 break const_get(p) if platform.include?(p.to_s.downcase)
18 raise ArgumentError, "unsupported platform: #{platform}"
20 klass.new(*args, &block)
23 def self.extract(objs, *rest)
24 create(objs).exports(*rest)
27 def self.output(output = $output, &block)
29 File.open(output, 'wb', &block)
38 syms["ruby_sysinit_real"] = "ruby_sysinit"
39 each_export(objs) do |internal, export|
40 syms[internal] = export
41 winapis[$1] = internal if /^_?(rb_w32_\w+)(?:@\d+)?$/ =~ internal
43 incdir = File.join(File.dirname(File.dirname(__FILE__)), "include/ruby")
44 read_substitution(incdir+"/win32.h", syms, winapis)
45 read_substitution(incdir+"/subst.h", syms, winapis)
46 syms["rb_w32_vsnprintf"] ||= "ruby_vsnprintf"
47 syms["rb_w32_snprintf"] ||= "ruby_snprintf"
51 def read_substitution(header, syms, winapis)
52 File.foreach(header) do |line|
53 if /^#define (\w+)\((.*?)\)\s+(?:\(void\))?(rb_w32_\w+)\((.*?)\)\s*$/ =~ line and
54 $2.delete(" ") == $4.delete(" ")
55 export, internal = $1, $3
56 if syms[internal] or internal = winapis[internal]
57 syms[forwarding(internal, export)] = internal
63 def exports(name = $name, library = $library, description = $description)
66 exports << "Name " + name
68 exports << "Library " + library
70 exports << "Description " + description.dump if description
71 exports << "VERSION #{RbConfig::CONFIG['MAJOR']}.#{RbConfig::CONFIG['MINOR']}"
72 exports << "EXPORTS" << symbols()
77 def forwarding(internal, export)
78 internal.sub(/^[^@]+/, "\\1#{export}")
84 def objdump(objs, &block)
86 $stdin.each_line(&block)
88 each_line(objs, &block)
93 @syms.sort.collect {|k, v| v ? v == true ? "#{k} DATA" : "#{k}=#{v}" : k}
97 class Exports::Mswin < Exports
98 def each_line(objs, &block)
99 IO.popen(%w"dumpbin -symbols -exports" + objs) do |f|
104 def each_export(objs)
105 noprefix = ($arch ||= nil and /^(sh|i\d86)/ !~ $arch)
106 objs = objs.collect {|s| s.tr('/', '\\')}
115 when /OBJECT/, /LIBRARY/
117 next if /^[[:xdigit:]]+ 0+ UNDEF / =~ l
118 next unless /External/ =~ l
119 next if /(?:_local_stdio_printf_options|v(f|sn?)printf(_s)?_l)\Z/ =~ l
120 next unless l.sub!(/.*?\s(\(\)\s+)?External\s+\|\s+/, '')
122 if noprefix or /^[@_]/ =~ l
123 next if /(?!^)@.*@/ =~ l || /@[[:xdigit:]]{8,32}$/ =~ l ||
124 /^_?#{PrivateNames}/o =~ l
125 l.sub!(/^[@_]/, '') if /@\d+$/ !~ l
126 elsif !l.sub!(/^(\S+) \([^@?\`\']*\)$/, '\1')
130 next unless l.sub!(/^\s*\d+\s+[[:xdigit:]]+\s+[[:xdigit:]]+\s+/, '')
134 yield l.strip, is_data
136 filetype = l[/^File Type: (.+)/, 1]
139 yield "strcasecmp", "msvcrt.stricmp"
140 yield "strncasecmp", "msvcrt.strnicmp"
144 class Exports::Cygwin < Exports
146 @@nm ||= RbConfig::CONFIG["NM"]
153 def each_line(objs, &block)
154 IO.foreach("|#{self.class.nm} --extern-only --defined-only #{objs.join(' ')}", &block)
157 def each_export(objs)
158 symprefix = RbConfig::CONFIG["SYMBOL_PREFIX"]
159 symprefix.strip! if symprefix
160 re = /\s(?:(T)|[[:upper:]])\s#{symprefix}((?!#{PrivateNames}).*)$/
163 yield $2, !$1 if re =~ l
168 class Exports::Msys < Exports::Cygwin
171 class Exports::Mingw < Exports::Cygwin
172 def each_export(objs)
174 yield "strcasecmp", "_stricmp"
175 yield "strncasecmp", "_strnicmp"
180 exports = Exports.extract(ARGV)
181 Exports.output {|f| f.puts(*exports)}