1#!./miniruby -sI.
2
3$name = $library = $description = nil
4
5module RbConfig
6  autoload :CONFIG, "./rbconfig"
7end
8
9class Exports
10  @@subclass = []
11  def self.inherited(klass)
12    @@subclass << [/#{klass.name.sub(/.*::/, '').downcase}/i, klass]
13  end
14
15  def self.create(*args, &block)
16    platform = RUBY_PLATFORM
17    pat, klass = @@subclass.find {|p, k| p =~ platform}
18    unless klass
19      raise ArgumentError, "unsupported platform: #{platform}"
20    end
21    klass.new(*args, &block)
22  end
23
24  def self.extract(objs, *rest)
25    create(objs).exports(*rest)
26  end
27
28  def self.output(output = $output, &block)
29    if output
30      open(output, 'wb', &block)
31    else
32      yield STDOUT
33    end
34  end
35
36  def initialize(objs)
37    syms = {}
38    winapis = {}
39    syms["ruby_sysinit_real"] = "ruby_sysinit"
40    each_export(objs) do |internal, export|
41      syms[internal] = export
42      winapis[$1] = internal if /^_?(rb_w32_\w+)(?:@\d+)?$/ =~ internal
43    end
44    incdir = File.join(File.dirname(File.dirname(__FILE__)), "include/ruby")
45    read_substitution(incdir+"/win32.h", syms, winapis)
46    read_substitution(incdir+"/subst.h", syms, winapis)
47    syms["rb_w32_vsnprintf"] ||= "ruby_vsnprintf"
48    syms["rb_w32_snprintf"] ||= "ruby_snprintf"
49    @syms = syms
50  end
51
52  def read_substitution(header, syms, winapis)
53    IO.foreach(header) do |line|
54      if /^#define (\w+)\((.*?)\)\s+(?:\(void\))?(rb_w32_\w+)\((.*?)\)\s*$/ =~ line and
55          $2.delete(" ") == $4.delete(" ")
56        export, internal = $1, $3
57        if syms[internal] or internal = winapis[internal]
58          syms[forwarding(internal, export)] = internal
59        end
60      end
61    end
62  end
63
64  def exports(name = $name, library = $library, description = $description)
65    exports = []
66    if name
67      exports << "Name " + name
68    elsif library
69      exports << "Library " + library
70    end
71    exports << "Description " + description.dump if description
72    exports << "VERSION #{RbConfig::CONFIG['MAJOR']}.#{RbConfig::CONFIG['MINOR']}"
73    exports << "EXPORTS" << symbols()
74    exports
75  end
76
77  private
78  def forwarding(internal, export)
79    internal.sub(/^[^@]+/, "\\1#{export}")
80  end
81
82  def each_export(objs)
83  end
84
85  def objdump(objs, &block)
86    if objs.empty?
87      $stdin.each_line(&block)
88    else
89      each_line(objs, &block)
90    end
91  end
92
93  def symbols()
94    @syms.sort.collect {|k, v| v ? v == true ? "#{k} DATA" : "#{k}=#{v}" : k}
95  end
96end
97
98class Exports::Mswin < Exports
99  def each_line(objs, &block)
100    IO.popen(%w"dumpbin -symbols -exports" + objs) do |f|
101      f.each(&block)
102    end
103  end
104
105  def each_export(objs)
106    noprefix = ($arch ||= nil and /^(sh|i\d86)/ !~ $arch)
107    objs = objs.collect {|s| s.tr('/', '\\')}
108    filetype = nil
109    objdump(objs) do |l|
110      if (filetype = l[/^File Type: (.+)/, 1])..(/^\f/ =~ l)
111        case filetype
112        when /OBJECT/, /LIBRARY/
113          next if /^[[:xdigit:]]+ 0+ UNDEF / =~ l
114          next unless /External/ =~ l
115          next unless l.sub!(/.*?\s(\(\)\s+)?External\s+\|\s+/, '')
116          is_data = !$1
117          if noprefix or /^[@_]/ =~ l
118            next if /(?!^)@.*@/ =~ l || /@[[:xdigit:]]{8,32}$/ =~ l ||
119              /^_?(?:Init_|.*_threadptr_|DllMain\b)/ =~ l
120            l.sub!(/^[@_]/, '') if /@\d+$/ !~ l
121          elsif !l.sub!(/^(\S+) \([^@?\`\']*\)$/, '\1')
122            next
123          end
124        when /DLL/
125          next unless l.sub!(/^\s*\d+\s+[[:xdigit:]]+\s+[[:xdigit:]]+\s+/, '')
126        else
127          next
128        end
129        yield l.strip, is_data
130      end
131    end
132    yield "strcasecmp", "msvcrt.stricmp"
133    yield "strncasecmp", "msvcrt.strnicmp"
134  end
135end
136
137class Exports::Cygwin < Exports
138  def self.nm
139    @@nm ||= RbConfig::CONFIG["NM"]
140  end
141
142  def exports(*)
143    super()
144  end
145
146  def each_line(objs, &block)
147    IO.foreach("|#{self.class.nm} --extern --defined #{objs.join(' ')}", &block)
148  end
149
150  def each_export(objs)
151    symprefix = RbConfig::CONFIG["SYMBOL_PREFIX"]
152    symprefix.strip! if symprefix
153    re = /\s(?:(T)|[[:upper:]])\s#{symprefix}((?!Init_|.*_threadptr_|DllMain\b).*)$/
154    objdump(objs) do |l|
155      next if /@.*@/ =~ l
156      yield $2, !$1 if re =~ l
157    end
158  end
159end
160
161class Exports::Mingw < Exports::Cygwin
162  def each_export(objs)
163    super
164    yield "strcasecmp", "_stricmp"
165    yield "strncasecmp", "_strnicmp"
166  end
167end
168
169END {
170  exports = Exports.extract(ARGV)
171  Exports.output {|f| f.puts(*exports)}
172}
173