Added spec for Kernel#eval with binding from method defined by #eval.
[rbx.git] / stdlib / ext / extmk.rb
blobac396a0d8013ded54fc06e306f68f38fd8c40b89
1 #! /usr/local/bin/ruby
2 # -*- ruby -*-
4 $extension = nil
5 $extstatic = nil
6 $force_static = nil
7 $install = nil
8 $destdir = nil
9 $dryrun = false
10 $clean = nil
11 $nodynamic = nil
12 $extinit = nil
13 $extobjs = nil
14 $extflags = ""
15 $extlibs = nil
16 $extpath = nil
17 $ignore = nil
18 $message = nil
20 $progname = $0
21 alias $PROGRAM_NAME $0
22 alias $0 $progname
24 $extlist = []
25 $compiled = {}
27 $:.replace([Dir.pwd])
28 require 'rbconfig'
30 srcdir = File.dirname(File.dirname(__FILE__))
32 $:.unshift(srcdir, File.expand_path("lib", srcdir))
34 $topdir = "."
35 $top_srcdir = srcdir
37 require 'mkmf'
38 require 'optparse/shellwords'
40 def sysquote(x)
41   @quote ||= /human|os2|macos/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
42   @quote ? x.quote : x
43 end
45 def relative_from(path, base)
46   dir = File.join(path, "")
47   if File.expand_path(dir) == File.expand_path(dir, base)
48     path
49   else
50     File.join(base, path)
51   end
52 end
54 def extract_makefile(makefile, keep = true)
55   m = File.read(makefile)
56   if !(target = m[/^TARGET[ \t]*=[ \t]*(\S*)/, 1])
57     return keep
58   end
59   installrb = {}
60   m.scan(/^install-rb-default:[ \t]*(\S+)\n\1:[ \t]*(\S+)/) {installrb[$2] = $1}
61   oldrb = installrb.keys.sort
62   newrb = install_rb(nil, "").collect {|d, *f| f}.flatten.sort
63   if target_prefix = m[/^target_prefix[ \t]*=[ \t]*\/(.*)/, 1]
64     target = "#{target_prefix}/#{target}"
65   end
66   unless oldrb == newrb
67     if $extout
68       newrb.each {|f| installrb.delete(f)}
69       unless installrb.empty?
70         config = CONFIG.dup
71         install_dirs(target_prefix).each {|var, val| config[var] = val}
72         FileUtils.rm_f(installrb.values.collect {|f| Config.expand(f, config)}, :verbose => true)
73       end
74     end
75     return false
76   end
77   $target = target
78   $extconf_h = m[/^RUBY_EXTCONF_H[ \t]*=[ \t]*(\S+)/, 1]
79   $static ||= m[/^EXTSTATIC[ \t]*=[ \t]*(\S+)/, 1] || false
80   /^STATIC_LIB[ \t]*=[ \t]*\S+/ =~ m or $static = nil
81   $preload = Shellwords.shellwords(m[/^preload[ \t]*=[ \t]*(.*)/, 1] || "")
82   $DLDFLAGS += " " + (m[/^DLDFLAGS[ \t]*=[ \t]*(.*)/, 1] || "")
83   if s = m[/^LIBS[ \t]*=[ \t]*(.*)/, 1]
84     s.sub!(/^#{Regexp.quote($LIBRUBYARG)} */, "")
85     s.sub!(/ *#{Regexp.quote($LIBS)}$/, "")
86     $libs = s
87   end
88   $objs = (m[/^OBJS[ \t]*=[ \t](.*)/, 1] || "").split
89   $srcs = (m[/^SRCS[ \t]*=[ \t](.*)/, 1] || "").split
90   $LOCAL_LIBS = m[/^LOCAL_LIBS[ \t]*=[ \t]*(.*)/, 1] || ""
91   $LIBPATH = Shellwords.shellwords(m[/^libpath[ \t]*=[ \t]*(.*)/, 1] || "") - %w[$(libdir) $(topdir)]
92   true
93 end
95 def extmake(target)
96   print "#{$message} #{target}\n"
97   $stdout.flush
98   if $force_static or $static_ext[target]
99     $static = target
100   else
101     $static = false
102   end
104   unless $ignore
105     return true if $nodynamic and not $static
106   end
108   FileUtils.mkpath target unless File.directory?(target)
109   begin
110     dir = Dir.pwd
111     FileUtils.mkpath target unless File.directory?(target)
112     Dir.chdir target
113     top_srcdir = $top_srcdir
114     topdir = $topdir
115     mk_srcdir = CONFIG["srcdir"]
116     mk_topdir = CONFIG["topdir"]
117     prefix = "../" * (target.count("/")+1)
118     $hdrdir = $top_srcdir = relative_from(top_srcdir, prefix)
119     $topdir = prefix + $topdir
120     $target = target
121     $mdir = target
122     $srcdir = File.join($top_srcdir, "ext", $mdir)
123     $preload = nil
124     $objs = ""
125     $srcs = ""
126     $compiled[target] = false
127     makefile = "./Makefile"
128     ok = File.exist?(makefile)
129     unless $ignore
130       Config::CONFIG["hdrdir"] = $hdrdir
131       Config::CONFIG["srcdir"] = $srcdir
132       Config::CONFIG["topdir"] = $topdir
133       CONFIG["hdrdir"] = ($hdrdir == top_srcdir) ? top_srcdir : "$(topdir)"+top_srcdir[2..-1]
134       CONFIG["srcdir"] = "$(hdrdir)/ext/#{$mdir}"
135       CONFIG["topdir"] = $topdir
136       begin
137         $extconf_h = nil
138         ok &&= extract_makefile(makefile)
139         if (($extconf_h && !File.exist?($extconf_h)) ||
140             !(t = modified?(makefile, MTIMES)) ||
141             ["#{$srcdir}/makefile.rb", "#{$srcdir}/extconf.rb", "#{$srcdir}/depend"].any? {|f| modified?(f, [t])})
142         then
143           ok = false
144           init_mkmf
145           Logging::logfile 'mkmf.log'
146           rm_f makefile
147           if File.exist?($0 = "#{$srcdir}/makefile.rb")
148             load $0
149           elsif File.exist?($0 = "#{$srcdir}/extconf.rb")
150             load $0
151           else
152             create_makefile(target)
153           end
154           $defs << "-DRUBY_EXPORT" if $static
155           ok = File.exist?(makefile)
156         end
157       rescue SystemExit
158         # ignore
159       ensure
160         rm_f "conftest*"
161         config = $0
162         $0 = $PROGRAM_NAME
163       end
164     end
165     ok = yield(ok) if block_given?
166     unless ok
167       open(makefile, "w") do |f|
168         f.print dummy_makefile(CONFIG["srcdir"])
169       end
170       return true
171     end
172     args = sysquote($mflags)
173     unless $destdir.to_s.empty? or $mflags.include?("DESTDIR")
174       args += [sysquote("DESTDIR=" + relative_from($destdir, "../"+prefix))]
175     end
176     if $static
177       args += ["static"] unless $clean
178       $extlist.push [$static, $target, File.basename($target), $preload]
179     end
180     unless system($make, *args)
181       $ignore or $continue or return false
182     end
183     $compiled[target] = true
184     if $clean and $clean != true
185       File.unlink(makefile) rescue nil
186     end
187     if $static
188       $extflags ||= ""
189       $extlibs ||= []
190       $extpath ||= []
191       unless $mswin
192         $extflags = ($extflags.split | $DLDFLAGS.split | $LDFLAGS.split).join(" ")
193       end
194       $extlibs = merge_libs($extlibs, $libs.split, $LOCAL_LIBS.split)
195       $extpath |= $LIBPATH
196     end
197   ensure
198     Config::CONFIG["srcdir"] = $top_srcdir
199     Config::CONFIG["topdir"] = topdir
200     CONFIG["srcdir"] = mk_srcdir
201     CONFIG["topdir"] = mk_topdir
202     CONFIG.delete("hdrdir")
203     $hdrdir = $top_srcdir = top_srcdir
204     $topdir = topdir
205     Dir.chdir dir
206   end
207   begin
208     Dir.rmdir target
209     target = File.dirname(target)
210   rescue SystemCallError
211     break
212   end while true
213   true
216 def compiled?(target)
217   $compiled[target]
220 def parse_args()
221   $mflags = []
223   opts = nil
224   $optparser ||= OptionParser.new do |opts|
225     opts.on('-n') {$dryrun = true}
226     opts.on('--[no-]extension [EXTS]', Array) do |v|
227       $extension = (v == false ? [] : v)
228     end
229     opts.on('--[no-]extstatic [STATIC]', Array) do |v|
230       if ($extstatic = v) == false
231         $extstatic = []
232       elsif v
233         $force_static = true if $extstatic.delete("static")
234         $extstatic = nil if $extstatic.empty?
235       end
236     end
237     opts.on('--dest-dir=DIR') do |v|
238       $destdir = v
239     end
240     opts.on('--extout=DIR') do |v|
241       $extout = (v unless v.empty?)
242     end
243     opts.on('--make=MAKE') do |v|
244       $make = v || 'make'
245     end
246     opts.on('--make-flags=FLAGS', '--mflags', Shellwords) do |v|
247       v.grep(/\A([-\w]+)=(.*)/) {$configure_args["--#{$1}"] = $2}
248       if arg = v.first
249         arg.insert(0, '-') if /\A[^-][^=]*\Z/ =~ arg
250       end
251       $mflags.concat(v)
252     end
253     opts.on('--message [MESSAGE]', String) do |v|
254       $message = v
255     end
256   end
257   begin
258     $optparser.parse!(ARGV)
259   rescue OptionParser::InvalidOption => e
260     retry if /^--/ =~ e.args[0]
261     $optparser.warn(e)
262     abort opts.to_s
263   end
265   $destdir ||= ''
267   $make, *rest = Shellwords.shellwords($make)
268   $mflags.unshift(*rest) unless rest.empty?
270   def $mflags.set?(flag)
271     grep(/\A-(?!-).*#{'%c' % flag}/i) { return true }
272     false
273   end
274   def $mflags.defined?(var)
275     grep(/\A#{var}=(.*)/) {return $1}
276     false
277   end
279   if $mflags.set?(?n)
280     $dryrun = true
281   else
282     $mflags.unshift '-n' if $dryrun
283   end
285   $continue = $mflags.set?(?k)
286   if $extout
287     $extout = '$(topdir)/'+$extout
288     $extout_prefix = $extout ? "$(extout)$(target_prefix)/" : ""
289     $mflags << "extout=#$extout" << "extout_prefix=#$extout_prefix"
290   end
293 parse_args()
295 if target = ARGV.shift and /^[a-z-]+$/ =~ target
296   $mflags.push(target)
297   target = target.sub(/^(dist|real)(?=(?:clean)?$)/, '')
298   case target
299   when /clean/
300     $ignore ||= true
301     $clean = $1 ? $1[0] : true
302   when /^install\b/
303     $install = true
304     $ignore ||= true
305     $mflags.unshift("INSTALL_PROG=install -c -p -m 0755",
306                     "INSTALL_DATA=install -c -p -m 0644",
307                     "MAKEDIRS=mkdir -p") if $dryrun
308   end
310 unless $message
311   if target
312     $message = target.sub(/^(\w+)e?\b/, '\1ing').tr('-', ' ')
313   else
314     $message = "compiling"
315   end
318 EXEEXT = CONFIG['EXEEXT']
319 if CROSS_COMPILING
320   $ruby = CONFIG['MINIRUBY']
321 elsif sep = config_string('BUILD_FILE_SEPARATOR')
322   $ruby = "$(topdir:/=#{sep})#{sep}miniruby" + EXEEXT
323 else
324   $ruby = '$(topdir)/miniruby' + EXEEXT
326 $ruby << " -I'$(topdir)' -I'$(hdrdir)/lib'"
327 $config_h = '$(topdir)/config.h'
329 MTIMES = [__FILE__, 'rbconfig.rb', srcdir+'/lib/mkmf.rb'].collect {|f| File.mtime(f)}
331 # get static-link modules
332 $static_ext = {}
333 if $extstatic
334   $extstatic.each do |target|
335     target = target.downcase if /mswin32|bccwin32/ =~ RUBY_PLATFORM
336     $static_ext[target] = $static_ext.size
337   end
339 for dir in ["ext", File::join($top_srcdir, "ext")]
340   setup = File::join(dir, CONFIG['setup'])
341   if File.file? setup
342     f = open(setup)
343     while line = f.gets()
344       line.chomp!
345       line.sub!(/#.*$/, '')
346       next if /^\s*$/ =~ line
347       target, opt = line.split(nil, 3)
348       if target == 'option'
349         case opt
350         when 'nodynamic'
351           $nodynamic = true
352         end
353         next
354       end
355       target = target.downcase if /mswin32|bccwin32/ =~ RUBY_PLATFORM
356       $static_ext[target] = $static_ext.size
357     end
358     MTIMES << f.mtime
359     $setup = setup
360     f.close
361     break
362   end
363 end unless $extstatic
365 ext_prefix = "#{$top_srcdir}/ext"
366 exts = $static_ext.sort_by {|t, i| i}.collect {|t, i| t}
367 if $extension
368   exts |= $extension.select {|d| File.directory?("#{ext_prefix}/#{d}")}
369 else
370   withes, withouts = %w[--with --without].collect {|w|
371     if not (w = %w[-extensions -ext].collect {|opt|arg_config(w+opt)}).any?
372       proc {false}
373     elsif (w = w.grep(String)).empty?
374       proc {true}
375     else
376       proc {|c1| w.collect {|opt| opt.split(/,/)}.flatten.any?(&c1)}
377     end
378   }
379   cond = proc {|ext|
380     cond1 = proc {|n| File.fnmatch(n, ext, File::FNM_PATHNAME)}
381     withes.call(cond1) or !withouts.call(cond1)
382   }
383   exts |= Dir.glob("#{ext_prefix}/*/**/extconf.rb").collect {|d|
384     d = File.dirname(d)
385     d.slice!(0, ext_prefix.length + 1)
386     d
387   }.find_all {|ext|
388     with_config(ext, &cond)
389   }.sort
392 if $extout
393   extout = Config.expand("#{$extout}", Config::CONFIG.merge("topdir"=>$topdir))
394   unless $ignore
395     FileUtils.mkpath(extout)
396   end
399 dir = Dir.pwd
400 FileUtils::makedirs('ext')
401 Dir::chdir('ext')
403 $hdrdir = $top_srcdir = relative_from(srcdir, $topdir = "..")
404 exts.each do |d|
405   extmake(d) or abort
407 $hdrdir = $top_srcdir = srcdir
408 $topdir = "."
410 extinit = Struct.new(:c, :o) {
411   def initialize(src)
412     super("#{src}.c", "#{src}.#{$OBJEXT}")
413   end
414 }.new("extinit")
415 if $ignore
416   FileUtils.rm_f(extinit.to_a) if $clean
417   Dir.chdir ".."
418   if $clean
419     Dir.rmdir('ext') rescue nil
420     FileUtils.rm_rf(extout) if $extout
421   end
422   exit
425 $extinit ||= ""
426 $extobjs ||= ""
427 $extpath ||= []
428 $extflags ||= ""
429 $extlibs ||= []
430 unless $extlist.empty?
431   $extinit << "\n" unless $extinit.empty?
432   list = $extlist.dup
433   built = []
434   while e = list.shift
435     s,t,i,r = e
436     if r and !(r -= built).empty?
437       l = list.size
438       if (while l > 0; break true if r.include?(list[l-=1][1]) end)
439         list.insert(l + 1, e)
440       end
441       next
442     end
443     f = format("%s/%s.%s", s, i, $LIBEXT)
444     if File.exist?(f)
445       $extinit << "    init(Init_#{i}, \"#{t}.so\");\n"
446       $extobjs << "ext/#{f} "
447       built << t
448     end
449   end
451   src = %{\
452 #include "ruby.h"
454 #define init(func, name) {void func _((void)); ruby_init_ext(name, func);}
456 void ruby_init_ext _((const char *name, void (*init)(void)));
458 void Init_ext _((void))\n{\n#$extinit}
460   if !modified?(extinit.c, MTIMES) || IO.read(extinit.c) != src
461     open(extinit.c, "w") {|f| f.print src}
462   end
464   $extobjs = "ext/#{extinit.o} #{$extobjs}"
465   if RUBY_PLATFORM =~ /m68k-human|beos/
466     $extflags.delete("-L/usr/local/lib")
467   end
468   $extpath.delete("$(topdir)")
469   $extflags = libpathflag($extpath) << " " << $extflags.strip
470   conf = [
471     ['LIBRUBY_SO_UPDATE', '$(LIBRUBY_EXTS)'],
472     ['SETUP', $setup],
473     [enable_config("shared", $enable_shared) ? 'DLDOBJS' : 'EXTOBJS', $extobjs],
474     ['EXTLIBS', $extlibs.join(' ')], ['EXTLDFLAGS', $extflags]
475   ].map {|n, v|
476     "#{n}=#{v}" if v and !(v = v.strip).empty?
477   }.compact
478   puts conf
479   $stdout.flush
480   $mflags.concat(conf)
481 else
482   FileUtils.rm_f(extinit.to_a)
484 rubies = []
485 %w[RUBY RUBYW STATIC_RUBY].each {|r|
486   n = r
487   if r = arg_config("--"+r.downcase) || config_string(r+"_INSTALL_NAME")
488     rubies << Config.expand(r+=EXEEXT)
489     $mflags << "#{n}=#{r}"
490   end
493 Dir.chdir ".."
494 unless $destdir.to_s.empty?
495   $mflags.defined?("DESTDIR") or $mflags << "DESTDIR=#{$destdir}"
497 puts "making #{rubies.join(', ')}"
498 $stdout.flush
499 $mflags.concat(rubies)
501 if $nmake == ?b
502   unless (vars = $mflags.grep(/\A\w+=/n)).empty?
503     open(mkf = "libruby.mk", "wb") do |tmf|
504       tmf.puts("!include Makefile")
505       tmf.puts
506       tmf.puts(*vars.map {|v| v.sub(/=/, " = ")})
507       tmf.puts("PRE_LIBRUBY_UPDATE = del #{mkf}")
508     end
509     $mflags.unshift("-f#{mkf}")
510     vars.each {|flag| flag.sub!(/\A/, "-D")}
511   end
513 system($make, *sysquote($mflags)) or exit($?.exitstatus)
515 #Local variables:
516 # mode: ruby
517 #end: