Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / bin / compile
bloba57c9b2759a93965c99679d4b45524ecbfbf1330
1 class ExtensionCompiler
2   
3   def initialize(extra=[], flags=[])
4     @includes = []
5     @link_flags = []
6     @compile_flags = ["-g"]
7     
8     extra.each do |e|
9       if e.prefix?("-I")
10         @includes << e
11       elsif e.prefix?("-C,")
12         @compile_flags << e[3..-1]
13       else
14         @link_flags << e
15       end
16     end
17     
18     @output_name = nil
19     @preserve_objects = false
20     
21     @files = []
22     @flags = flags
23     flags.each do |i|
24       if m = /output=(.*)/.match(i)
25         @output_name = m[1]
26       end
27       
28       if i == "-p"
29         @preserve_objects = true
30       end
31     end
33     if $DEBUG
34       @preserve_objects = true
35     end 
36     
37     if File.exists?("shotgun/lib/subtend/ruby.h")
38       @includes << "-I#{Dir.pwd}/shotgun/lib/subtend"
39     end
40     
41   end
42   
43   def add_file(file)
44     @files << file
45     @output_name = file unless @output_name
46   end
47   
48   def set_output(output)
49     @output_name = output
50   end
51   
52   def add_flag(flags, link=true)
53     if link
54       @link_flags << flags
55     else
56       @compile_flags << flags
57     end
58   end
59   
60   def add_include(inc)
61     @includes << "-I#{inc}"
62   end
63   
64   attr_reader :output
65   
66   def calculate_output
67     m = /(.*)\.[^\.]+/.match(@output_name)
68     if m
69       @output = "#{m[1]}.#{Rubinius::LIBSUFFIX}"
70     else
71       @output = "#{@output_name}.#{Rubinius::LIBSUFFIX}"
72     end
73   end
74     
75   def windows?
76     Rubinius::OS == :win32
77   end
78   
79   def gcc?
80     Rubinius::COMPILER == :gcc
81   end
82   
83   def vcc?
84     Rubinius::OS == :win32 and Rubinius::COMPILER == :microsoft
85   end
86   
87   def mingw?
88     Rubinius::OS == :win32 and Rubinius::COMPILER == :gcc  
89   end
90   
91   def cygwin?
92     Rubinius::OS == :cygwin
93   end
94   
95   def darwin?
96     Rubinius::OS == :darwin
97   end
98   
99   def compile_options    
100     str = (@includes + @compile_flags).join(" ")
102     if Rubinius::PLATFORM == :amd64 || ( Rubinius::PLATFORM == :x86 && gcc? )
103       str += ' -fPIC '
104     end
106     str
107   end
108     
109   def compiler
110     if ENV['CC']
111       return ENV['CC']
112     end
113     
114     Rubinius::COMPILER_PATH
115   end
116   
117   # Adapted from RubyInline
118   def system_link_options
119     if vcc?
120       "-link /LIBPATH:\"#{@libdir}\" /DEFAULTLIB:\"#{@lib}\" /INCREMENTAL:no /EXPORT:#{@init}"
121     elsif mingw?
122       "-Wl,--enable-auto-import -L#{@libdir}"
123     elsif cygwin?
124       "-L/usr/local/lib"
125     elsif darwin?
126       "-dynamic -bundle -undefined suppress -flat_namespace"
127     else
128       "-shared"
129     end
130   end
131     
132   def link_options
133     opts = @link_flags.dup
134     opts << system_link_options
135     
136     return opts.join(" ")
137   end
138   
139   def compile_files
140     @objects = []
141     @files.each do |file|
142       cmd = "#{compiler} #{compile_options} -c -o #{file}.o #{file}"
143       puts cmd if $VERBOSE
144       system cmd
145       @objects << "#{file}.o"
146     end
147   end
148   
149   def compile(report=true)
150     compile_files
151     calculate_output
152     cmd = "#{compiler} #{link_options} #{@objects.join(' ')} -o #{@output}"
153     puts cmd if $VERBOSE
154     system cmd
155     
156     return unless report
157     
158     if File.exists?(@output)
159       unless @preserve_objects
160         puts "Cleaning up objects..."
161         @objects.each { |o| system "rm #{o}" }
162       end
163       
164       puts "Created #{@output}" if report
165     else
166       if report
167         puts "Unable to compile extension into #{@output}. Check compiler log."
168       end
169       exit 1
170     end
171     
172   end
173   
174   class DSL
175     def initialize(ec)
176       @ec = ec
177     end
178     
179     def name(name)
180       @ec.set_output name
181     end
182     
183     def files(glob)
184       Dir[glob].each { |f| @ec.add_file f }
185     end
186     
187     def flags(*args)
188       args.each { |a| @ec.add_flag a }
189     end
190     
191     def libs(*args)
192       args.each { |a| @ec.add_flag "-l#{a}", true }
193     end
194     
195     def includes(*args)
196       args.each { |a| @ec.add_include a }
197     end
198     
199     def setup
200       $ec_dsl = self
201     end
202   end
205 module Kernel
206   def extension
207     yield $ec_dsl if block_given?
208     $ec_dsl
209   end
212 rbx_flags = []
213 ext_flags = []
214 flags = []
216 while ARGV[0] and ARGV[0].prefix? "-f"
217   body = ARGV.shift[2..-1]
218   if body.empty?
219     flags << '-f'
220     next
221   end
223   if body.prefix? 'rbx'
224     rbx_flags << body
225   else
226     ext_flags << body
227   end
230 while ARGV[0] and ARGV[0].prefix? "-"
231   flags << ARGV.shift
234 file = ARGV.shift
236 if File.directory?(file)
237   rec = File.join(file, "build.rb")
238   unless File.exists?(rec)
239     puts "No build instructions found in #{file}"
240     exit 1
241   end
242   puts "Building from instructions at #{rec}" if $VERBOSE
243   ext = ExtensionCompiler.new(ext_flags, ARGV)
244   dsl = ExtensionCompiler::DSL.new(ext)
245   dsl.setup
246   cur = Dir.pwd
247   Dir.chdir file
248   load "build.rb"
249   ext.compile
250 elsif file.suffix?(".c")
251   puts "Compiling extension #{file}..."
252   ext = ExtensionCompiler.new(ext_flags, ARGV)
253   ext.add_file file
254   ext.compile
255 else
256   if File.exists?(file)
258     out = ARGV.shift || "#{file}c"
260     unless flags.include? '-f'
261       if File.exists?(out) and File.mtime(out) > File.mtime(file)
262         puts "Output '#{out}' is newer, no compile needed."
263         exit 0
264       end
265     end
267     if flags.include? "-e"
268       puts "Compiling (external) #{file}..."
269       require 'compiler/compiler'
270       cm = Compiler.compile_file file, rbx_flags
271       puts "Unable to compile '#{file}'" unless cm
272       Marshal.dump_to_file cm, out, Compile.version_number
273     else
274       puts "Compiling #{file}..."
275       compile(file, out, rbx_flags)
276     end
277   else
278     puts "Unable to compile '#{file}'"
279   end